Skip to content

Commit

Permalink
Merge pull request #98 from beck3905/process_response_status_code
Browse files Browse the repository at this point in the history
Raise Auth0Error for bad status code
  • Loading branch information
lbalmaceda authored Apr 27, 2018
2 parents 2e45f89 + 9155777 commit 6484229
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 10 deletions.
19 changes: 16 additions & 3 deletions auth0/v3/authentication/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
import requests
from ..exceptions import Auth0Error


UNKNOWN_ERROR = 'a0.sdk.internal.unknown'


class AuthenticationBase(object):

def post(self, url, data=None, headers=None):
Expand All @@ -25,6 +27,7 @@ def _parse(self, response):
except ValueError:
return PlainResponse(response)


class Response(object):
def __init__(self, status_code, content):
self._status_code = status_code
Expand All @@ -41,10 +44,18 @@ def content(self):
def _is_error(self):
return self._status_code is None or self._status_code >= 400

# Adding these methods to force implementation in subclasses because they are references in this parent class
def _error_code(self):
raise NotImplementedError

def _error_message(self):
raise NotImplementedError


class JsonResponse(Response):
def __init__(self, response):
content = json.loads(response.text)
super().__init__(response.status_code, content)
super(JsonResponse, self).__init__(response.status_code, content)

def _error_code(self):
if 'error' in self._content:
Expand All @@ -57,19 +68,21 @@ def _error_code(self):
def _error_message(self):
return self._content.get('error_description', '')


class PlainResponse(Response):
def __init__(self, response):
super().__init__(response.status_code, response.text)
super(PlainResponse, self).__init__(response.status_code, response.text)

def _error_code(self):
return UNKNOWN_ERROR

def _error_message(self):
return self._content


class EmptyResponse(Response):
def __init__(self, status_code):
super().__init__(status_code, '')
super(EmptyResponse, self).__init__(status_code, '')

def _error_code(self):
return UNKNOWN_ERROR
Expand Down
76 changes: 70 additions & 6 deletions auth0/v3/management/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from ..exceptions import Auth0Error


UNKNOWN_ERROR = 'a0.sdk.internal.unknown'


class RestClient(object):

"""Provides simple methods for handling all RESTful api endpoints. """
Expand Down Expand Up @@ -103,10 +106,71 @@ def delete(self, url, params={}):
return self._process_response(response)

def _process_response(self, response):
text = json.loads(response.text) if response.text else {}
return self._parse(response).content()

def _parse(self, response):
if not response.text:
return EmptyResponse(response.status_code)
try:
return JsonResponse(response)
except ValueError:
return PlainResponse(response)

class Response(object):
def __init__(self, status_code, content):
self._status_code = status_code
self._content = content

def content(self):
if self._is_error():
raise Auth0Error(status_code=self._status_code,
error_code=self._error_code(),
message=self._error_message())
else:
return self._content

def _is_error(self):
return self._status_code is None or self._status_code >= 400

# Adding these methods to force implementation in subclasses because they are references in this parent class
def _error_code(self):
raise NotImplementedError

def _error_message(self):
raise NotImplementedError

class JsonResponse(Response):
def __init__(self, response):
content = json.loads(response.text)
super(JsonResponse, self).__init__(response.status_code, content)

def _error_code(self):
if 'errorCode' in self._content:
return self._content.get('errorCode')
elif 'error' in self._content:
return self._content.get('error')
else:
return UNKNOWN_ERROR

def _error_message(self):
return self._content.get('error', self._content.get('message', ''))

class PlainResponse(Response):
def __init__(self, response):
super(PlainResponse, self).__init__(response.status_code, response.text)

def _error_code(self):
return UNKNOWN_ERROR

def _error_message(self):
return self._content

class EmptyResponse(Response):
def __init__(self, status_code):
super(EmptyResponse, self).__init__(status_code, '')

def _error_code(self):
return UNKNOWN_ERROR

if isinstance(text, dict) and 'errorCode' in text:
raise Auth0Error(status_code=text['statusCode'],
error_code=text['errorCode'],
message=text['message'])
return text
def _error_message(self):
return ''
72 changes: 71 additions & 1 deletion auth0/v3/test/management/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def test_get(self, mock_get):
headers = {'Authorization': 'Bearer a-token'}

mock_get.return_value.text = '["a", "b"]'
mock_get.return_value.status_code = 200

response = rc.get('the-url')
mock_get.assert_called_with('the-url', params={}, headers=headers)
Expand All @@ -27,7 +28,7 @@ def test_get(self, mock_get):

mock_get.return_value.text = ''
response = rc.get('the/url')
self.assertEqual(response, {})
self.assertEqual(response, '')

@mock.patch('requests.get')
def test_get_errors(self, mock_get):
Expand All @@ -37,6 +38,7 @@ def test_get_errors(self, mock_get):
mock_get.return_value.text = '{"statusCode": 999,' \
' "errorCode": "code",' \
' "message": "message"}'
mock_get.return_value.status_code = 999

with self.assertRaises(Auth0Error) as context:
rc.get('the/url')
Expand All @@ -55,6 +57,7 @@ def test_post(self, mock_post):

data = {'some': 'data'}

mock_post.return_value.status_code = 200
response = rc.post('the/url', data=data)
mock_post.assert_called_with('the/url', data=json.dumps(data),
headers=headers)
Expand All @@ -68,6 +71,7 @@ def test_post_errors(self, mock_post):
mock_post.return_value.text = '{"statusCode": 999,' \
' "errorCode": "code",' \
' "message": "message"}'
mock_post.return_value.status_code = 999

with self.assertRaises(Auth0Error) as context:
rc.post('the-url')
Expand All @@ -76,13 +80,76 @@ def test_post_errors(self, mock_post):
self.assertEqual(context.exception.error_code, 'code')
self.assertEqual(context.exception.message, 'message')

@mock.patch('requests.post')
def test_post_error_with_code_property(self, mock_post):
rc = RestClient(jwt='a-token', telemetry=False)

for error_status in [400, 500, None]:
mock_post.return_value.status_code = error_status
mock_post.return_value.text = '{"errorCode": "e0",' \
'"message": "desc"}'

with self.assertRaises(Auth0Error) as context:
rc.post('the-url')

self.assertEqual(context.exception.status_code, error_status)
self.assertEqual(context.exception.error_code, 'e0')
self.assertEqual(context.exception.message, 'desc')

@mock.patch('requests.post')
def test_post_error_with_no_error_code(self, mock_post):
rc = RestClient(jwt='a-token', telemetry=False)

for error_status in [400, 500, None]:
mock_post.return_value.status_code = error_status
mock_post.return_value.text = '{"message": "desc"}'

with self.assertRaises(Auth0Error) as context:
rc.post('the-url')

self.assertEqual(context.exception.status_code, error_status)
self.assertEqual(context.exception.error_code, 'a0.sdk.internal.unknown')
self.assertEqual(context.exception.message, 'desc')

@mock.patch('requests.post')
def test_post_error_with_text_response(self, mock_post):
rc = RestClient(jwt='a-token', telemetry=False)

for error_status in [400, 500, None]:
mock_post.return_value.status_code = error_status
mock_post.return_value.text = 'there has been a terrible error'

with self.assertRaises(Auth0Error) as context:
rc.post('the-url')

self.assertEqual(context.exception.status_code, error_status)
self.assertEqual(context.exception.error_code, 'a0.sdk.internal.unknown')
self.assertEqual(context.exception.message,
'there has been a terrible error')

@mock.patch('requests.post')
def test_post_error_with_no_response_text(self, mock_post):
rc = RestClient(jwt='a-token', telemetry=False)

for error_status in [400, 500, None]:
mock_post.return_value.status_code = error_status
mock_post.return_value.text = None

with self.assertRaises(Auth0Error) as context:
rc.post('the-url')

self.assertEqual(context.exception.status_code, error_status)
self.assertEqual(context.exception.error_code, 'a0.sdk.internal.unknown')
self.assertEqual(context.exception.message, '')

@mock.patch('requests.patch')
def test_patch(self, mock_patch):
rc = RestClient(jwt='a-token', telemetry=False)
headers = {'Authorization': 'Bearer a-token',
'Content-Type': 'application/json'}

mock_patch.return_value.text = '["a", "b"]'
mock_patch.return_value.status_code = 200

data = {'some': 'data'}

Expand All @@ -99,6 +166,7 @@ def test_patch_errors(self, mock_patch):
mock_patch.return_value.text = '{"statusCode": 999,' \
' "errorCode": "code",' \
' "message": "message"}'
mock_patch.return_value.status_code = 999

with self.assertRaises(Auth0Error) as context:
rc.patch(url='the/url')
Expand All @@ -113,6 +181,7 @@ def test_delete(self, mock_delete):
headers = {'Authorization': 'Bearer a-token'}

mock_delete.return_value.text = '["a", "b"]'
mock_delete.return_value.status_code = 200

response = rc.delete(url='the-url/ID')
mock_delete.assert_called_with('the-url/ID', headers=headers, params={})
Expand All @@ -126,6 +195,7 @@ def test_delete_errors(self, mock_delete):
mock_delete.return_value.text = '{"statusCode": 999,' \
' "errorCode": "code",' \
' "message": "message"}'
mock_delete.return_value.status_code = 999

with self.assertRaises(Auth0Error) as context:
rc.delete(url='the-url')
Expand Down

0 comments on commit 6484229

Please sign in to comment.