Skip to content

Commit 53512b5

Browse files
committed
Add rich errors
1 parent a58ca03 commit 53512b5

File tree

3 files changed

+83
-24
lines changed

3 files changed

+83
-24
lines changed

amadeus/client/errors.py

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,12 @@ class ResponseError(RuntimeError):
1313
``NetworkError``, ``ParserError``, ``ServerError``,
1414
``AuthenticationError``, ``NotFoundError`` and ``UnknownError``.
1515
:vartype code: str
16-
17-
:var description: The content of the response that describes the error
18-
:vartype description: str
1916
'''
2017

2118
def __init__(self, response):
2219
self.response = response
23-
self.description = self.__determine_description()
2420
self.code = self.__determine_code()
25-
RuntimeError.__init__(self, self.description)
21+
RuntimeError.__init__(self, self.description())
2622

2723
# PROTECTED
2824

@@ -35,13 +31,51 @@ def _log(self, client):
3531

3632
# PRIVATE
3733

38-
# extracts the error description from the response, if it exists
39-
def __determine_description(self):
40-
if (self.response and self.response.parsed):
41-
if 'errors' in self.response.result:
42-
return self.response.result['errors']
43-
if 'error_description' in self.response.result:
44-
return self.response.result
34+
# Determines the description for this error, as used in in the error output
35+
def description(self):
36+
description = self.short_description(self.response)
37+
return description + self.long_description(self.response)
38+
39+
# Determines the short description, printed after on the same line as the
40+
# error class name
41+
def short_description(self, response):
42+
if hasattr(response, 'status_code') and response.status_code:
43+
return '[{0}]'.format(response.status_code)
44+
else:
45+
return '[---]'
46+
47+
# Determines the longer description, printed after the initial error
48+
def long_description(self, response):
49+
message = ''
50+
if not(response and response.parsed):
51+
return message
52+
if 'error_description' in response.result:
53+
message += self.error_description(self.response)
54+
if 'errors' in response.result:
55+
message += self.errors_descriptions(self.response)
56+
return message
57+
58+
# Returns the description of a single error
59+
def error_description(self, response):
60+
message = ''
61+
if 'error' in response.result:
62+
message += '\n{0}'.format(response.result['error'])
63+
message += '\n{0}'.format(response.result['error_description'])
64+
return message
65+
66+
# Returns the description of multiple errors
67+
def errors_descriptions(self, response):
68+
messages = map(self.errors_description, response.result['errors'])
69+
return ''.join(messages)
70+
71+
# Returns the description of a single error in a multi error response
72+
def errors_description(self, error):
73+
message = '\n'
74+
if ('source' in error) and ('parameter' in error['source']):
75+
message += '[{0}] '.format(error['source']['parameter'])
76+
if 'detail' in error:
77+
message += error['detail']
78+
return message
4579

4680
# sets the error code to the name of this class
4781
def __determine_code(self):

specs/mixins/errors_spec.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,62 @@
11
from mamba import description, context, it, before
2-
from expects import expect, equal, be_none
2+
from expects import expect, equal
33
from doublex import Stub, Spy
44
from doublex_expects import have_been_called_with, have_been_called
55

66
from amadeus import NetworkError, Response, Client
77

88
with description('ResponseError') as self:
9-
with context('.description'):
10-
with it('should determine no description if no response is present'):
9+
with context('str(error)'):
10+
with it('should be undefine if no response is present'):
1111
error = NetworkError(None)
12-
expect(error.description).to(be_none)
12+
expect(str(error)).to(equal('[---]'))
1313

14-
with it('should determine no description if no data is present'):
14+
with it('should just return the code if no data is present'):
1515
response = Stub(Response)
1616
response.parsed = True
1717
response.result = {}
18+
response.status_code = 400
1819

1920
error = NetworkError(response)
20-
expect(error.description).to(be_none)
21+
expect(str(error)).to(equal('[400]'))
2122

22-
with it('should be set if errors are present'):
23+
with it('should be rich if errors are present'):
2324
response = Stub(Response)
2425
response.parsed = True
25-
response.result = {'errors': [{'detail': 'error'}]}
26+
response.result = {
27+
'errors': [
28+
{
29+
'detail': 'This field must be filled.',
30+
'source': {'parameter': 'departureDate'}
31+
},
32+
{
33+
'detail': 'This field must be filled.',
34+
'source': {'parameter': 'origin'}
35+
},
36+
{
37+
'detail': 'This field must be filled.',
38+
'source': {'parameter': 'destination'}
39+
}
40+
]
41+
}
42+
response.status_code = 401
43+
2644
error = NetworkError(response)
27-
expect(error.description).to(equal([{'detail': 'error'}]))
45+
expect(str(error)).to(equal(
46+
('''[401]
47+
[departureDate] This field must be filled.
48+
[origin] This field must be filled.
49+
[destination] This field must be filled.''')
50+
))
2851

29-
with it('should be set if an error_description is present'):
52+
with it('should be rich if an error_description is present'):
3053
response = Stub(Response)
3154
response.parsed = True
3255
response.result = {'error_description': 'error'}
56+
response.status_code = 401
57+
3358
error = NetworkError(response)
34-
expect(error.description).to(equal({'error_description': 'error'}))
59+
expect(str(error)).to(equal('[401]\nerror'))
3560

3661
with context('.code'):
3762
with it('should determine the code off the class name'):

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ deps =
1313
doublex-expects==0.7.0rc2
1414
flake8==3.5.0
1515
flake8-quotes==0.14.0
16-
usedevelop=true
16+
usedevelop=True
1717

1818
[tox:travis]
1919
2.7 = py27

0 commit comments

Comments
 (0)