Skip to content
This repository has been archived by the owner on Nov 14, 2023. It is now read-only.

Commit

Permalink
Refactored exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
silvanocerza committed Sep 24, 2016
1 parent cfcc4eb commit 2b91ca9
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 224 deletions.
36 changes: 5 additions & 31 deletions mkmsdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,8 @@ def request(self, url, method, **kwargs):

auth = self.create_auth(complete_url)

try:
response = request(method=method, url=complete_url, auth=auth, **kwargs)
return self.handle_response(response, response.content)
except exceptions.BadRequest as error:
return {'error': error.content}
response = request(method=method, url=complete_url, auth=auth, **kwargs)
return self.handle_response(response)

def create_auth(self, url,
app_token=None,
Expand Down Expand Up @@ -87,41 +84,18 @@ def create_auth(self, url,
client_class=client,
realm=url)

def handle_response(self, response, content):
def handle_response(self, response):
"""
Check the HTTP response
Params:
`response`: Response received from the server
`content`: Content of the response received
Return:
`response`: Returns the response received if positive or raise exception if negative
"""

status = response.status_code
if status in (301, 302, 303, 307):
raise exceptions.Redirection(response, content)
elif 200 <= status <= 299:
if 200 <= status <= 299:
return response
elif status == 400:
raise exceptions.BadRequest(response, content)
elif status == 401:
raise exceptions.UnauthorizedAccess(response, content)
elif status == 403:
raise exceptions.ForbiddenAccess(response, content)
elif status == 404:
raise exceptions.ResourceNotFound(response, content)
elif status == 405:
raise exceptions.MethodNotAllowed(response, content)
elif status == 409:
raise exceptions.ResourceConflict(response, content)
elif status == 410:
raise exceptions.ResourceGone(response, content)
elif status == 422:
raise exceptions.ResourceInvalid(response, content)
elif 401 <= status <= 499:
raise exceptions.ClientError(response, content)
elif 500 <= status <= 599:
raise exceptions.ServerError(response, content)
else:
raise exceptions.ConnectionError(response, content, "Unknown response code: #{response.code}")
raise exceptions.ConnectionError(response)
136 changes: 52 additions & 84 deletions mkmsdk/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,105 +1,73 @@
class ConnectionError(Exception):
def __init__(self, response, content=None, message=None):
"""
Wraps errors related with
requests to backend
"""

def __init__(self, response=None, message=None):
"""
Initializes the exception with the response
received from the backend
Params:
`response`: The response received from backend
`message`: A custom message
Return:
`response`: Returns the response received from the server
"""
self.response = response
self.content = content
self.message = message

def __str__(self):
message = 'Request failed.'
"""
Formats the error for the user so that it's
easier to understand
"""
message = 'Request failed'
if hasattr(self.response, 'status_code'):
message = '{}{}'.format(message, ' Status code: %s.' % self.response.status_code)
message = '{}{}'.format(message, '\nStatus code: %s' % self.response.status_code)
if hasattr(self.response, 'reason'):
message = '{}{}'.format(message, ' Response message: %s.' % self.response.reason)
if self.content:
message = '{}{}'.format(message, ' Error message: %s' % str(self.content))
message = '{}{}'.format(message, '\nResponse message: %s' % self.response.reason)
if hasattr(self.response, 'content'):
message = '{}{}'.format(message, '\n%s' % self.response.content)
if self.message:
message = '{}{}'.format(message, '\n%s' % self.message)
return message


class Redirection(ConnectionError):
class MissingParam(Exception):
"""
3xx
Wraps errors caught when setting up
the url with its parameters
"""
def __str__(self):
message = super(Redirection, self).__str__()
if self.response.get('Location'):
message = "{} => {}" .format(message, self.response.get('Location'))
return message


class MissingParam(TypeError):
pass

def __init__(self, param):
"""
Initializes the exception with the
missing parameter name
class MissingConfig(Exception):
pass
`param`: The missing parameter
"""
self.param = param


class ClientError(ConnectionError):
"""
4xx Client Error
"""
pass


class BadRequest(ClientError):
"""
400 Bad Request
"""
pass


class UnauthorizedAccess(ClientError):
"""
401 Unauthorized
"""
pass


class ForbiddenAccess(ClientError):
"""
403 Forbidden
"""


class ResourceNotFound(ClientError):
"""
404 Not Found
"""
pass


class ResourceConflict(ClientError):
"""
409 Conflict
"""
pass


class ResourceGone(ClientError):
"""
410 Gone
"""
pass


class ResourceInvalid(ClientError):
"""
422 Invalid
"""
pass
def __str__(self):
return 'Missing %s parameter' % self.param


class ServerError(ConnectionError):
class MissingEnvVar(Exception):
"""
5xx Server Error
Wraps errors for missing
environment variables
"""
pass
def __init__(self, env):
"""
Initializes the exception with the
missing variable name
`env`: The missing environment variable
"""
self.env = env

class MethodNotAllowed(ClientError):
"""
405 Method Not Allowed
"""

def allowed_methods(self):
return self.response['Allow']
def __str__(self):
return 'Missing %s environment variable' % self.env
6 changes: 2 additions & 4 deletions mkmsdk/resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def setup(self, api_map=None, **kwargs):

try:
url = url.format(**kwargs)
except KeyError as ke:
raise exceptions.MissingParam('Missing url sdk parameter: %s' % str(ke))
except KeyError as param:
raise exceptions.MissingParam(param=param)

# We percent encode the url so that if any string has spaces,
# commas or any other special character the url will be correctly formed anyway
Expand All @@ -43,15 +43,13 @@ def setup(self, api_map=None, **kwargs):

def resolve(self, api_map=None, data=None, **kwargs):
"""
Params:
`api_map`: Dict with urls and methods for the request
`data`: Data sent to MKM in PUT, POST and DELETE requests
`kwargs`: Optional additional parameters to be attached to the url
Return:
`response`: Returns the response received from the server
"""
self.setup(api_map=api_map, **kwargs)

Expand Down
2 changes: 1 addition & 1 deletion mkmsdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def _get_env_var(key):
try:
return os.environ[key]
except KeyError:
raise exceptions.MissingConfig('Missing MKM env var: ' + key)
raise exceptions.MissingEnvVar(key)


def get_mkm_app_token():
Expand Down
106 changes: 23 additions & 83 deletions tests/tests_unit/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_missing_env_var_raise_exception_correctly(mocker):
"""Verifies that an expection is thrown when necessary env vars are missing."""
os_mocked = mocker.patch('mkmsdk.utils.os')
os_mocked.environ = {}
with pytest.raises(exceptions.MissingConfig):
with pytest.raises(exceptions.MissingEnvVar):
get_mkm_app_secret()


Expand All @@ -71,109 +71,49 @@ def test_sandbox_mode():
assert sandbox_api.base_endpoint == expected_sandbox_base_endpoint


def test_redirection(mocked_response):
api = Api()

mocked_response.status_code = 301
with pytest.raises(exceptions.Redirection):
api.handle_response(mocked_response, mocked_response.content)

mocked_response.status_code = 302
with pytest.raises(exceptions.Redirection):
api.handle_response(mocked_response, mocked_response.content)

mocked_response.status_code = 303
with pytest.raises(exceptions.Redirection):
api.handle_response(mocked_response, mocked_response.content)

mocked_response.status_code = 307
with pytest.raises(exceptions.Redirection):
api.handle_response(mocked_response, mocked_response.content)


def test_bad_request(mocked_response):
def test_handle_request(mocked_response):
api = Api()

mocked_response.status_code = 400
with pytest.raises(exceptions.BadRequest):
api.handle_response(mocked_response, mocked_response.content)


def test_unauthorized_access(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 401
with pytest.raises(exceptions.UnauthorizedAccess):
api.handle_response(mocked_response, mocked_response.content)


def test_forbidden_access(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 403
with pytest.raises(exceptions.ForbiddenAccess):
api.handle_response(mocked_response, mocked_response.content)


def test_resource_not_found(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 404
with pytest.raises(exceptions.ResourceNotFound):
api.handle_response(mocked_response, mocked_response.content)


def test_method_not_allowed(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 405
with pytest.raises(exceptions.MethodNotAllowed):
api.handle_response(mocked_response, mocked_response.content)


def test_resource_conflict(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 409
with pytest.raises(exceptions.ResourceConflict):
api.handle_response(mocked_response, mocked_response.content)


def test_resource_gone(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 410
with pytest.raises(exceptions.ResourceGone):
api.handle_response(mocked_response, mocked_response.content)


def test_resource_invalid(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 422
with pytest.raises(exceptions.ResourceInvalid):
api.handle_response(mocked_response, mocked_response.content)


def test_client_error(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 480
with pytest.raises(exceptions.ClientError):
api.handle_response(mocked_response, mocked_response.content)


def test_server_error(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 545
with pytest.raises(exceptions.ServerError):
api.handle_response(mocked_response, mocked_response.content)


def test_unknown_error(mocked_response):
api = Api()
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response)

mocked_response.status_code = 1001
with pytest.raises(exceptions.ConnectionError):
api.handle_response(mocked_response, mocked_response.content)
api.handle_response(mocked_response)

0 comments on commit 2b91ca9

Please sign in to comment.