Skip to content

Commit 13c3995

Browse files
authored
Improve CommercetoolsError parsing (#84)
* Improve CommercetoolsError parsing It now renders the error details as well for better troubleshooting * Assure errors is always a list * Parse error code * Improved error code parsing
1 parent 9c53533 commit 13c3995

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

src/commercetools/client.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,20 @@ def _process_error(self, response: requests.Response) -> None:
265265
if not response.content:
266266
response.raise_for_status()
267267
obj = schemas.ErrorResponseSchema().loads(response.content)
268-
raise CommercetoolsError(obj.message, obj, correlation_id)
268+
269+
# We'll fetch the 'raw' errors from the response because some of the
270+
# attributes are not included in the schemas.
271+
# With the raw errors in the CommercetoolsError object we can use that
272+
# information later to render more detailed error messages
273+
errors_raw = []
274+
try:
275+
response_json = response.json()
276+
except ValueError:
277+
pass
278+
else:
279+
errors_raw = response_json.get("errors", [])
280+
281+
raise CommercetoolsError(obj.message, errors_raw, obj, correlation_id)
269282

270283
def _read_env_vars(self, config: dict) -> dict:
271284
if not config.get("project_key"):

src/commercetools/exceptions.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,46 @@ class CommercetoolsError(Exception):
88
correlation_id: typing.Optional[str]
99

1010
def __init__(
11-
self, message: typing.Any, response: ErrorResponse, correlation_id: str = None
11+
self,
12+
message: typing.Any,
13+
errors: typing.List[dict],
14+
response: ErrorResponse,
15+
correlation_id: str = None,
1216
) -> None:
1317
super().__init__(message)
1418
self.response = response
19+
self.errors = errors
1520
self.correlation_id = correlation_id
21+
22+
def __str__(self):
23+
result = super().__str__()
24+
if self.details:
25+
return f"{result} ({', '.join(self.details)})"
26+
return result
27+
28+
@property
29+
def details(self) -> typing.List[str]:
30+
return [
31+
e["detailedErrorMessage"]
32+
for e in self.errors
33+
if "detailedErrorMessage" in e
34+
]
35+
36+
@property
37+
def codes(self) -> typing.List[str]:
38+
try:
39+
return [e.code for e in self.response.errors]
40+
except AttributeError:
41+
return []
42+
43+
@property
44+
def code(self) -> str:
45+
"""Convenience property to easily get the error code.
46+
47+
Returns the code of the first error, just as
48+
'message' is always the message of the first error.
49+
"""
50+
try:
51+
return self.codes[0]
52+
except KeyError:
53+
return ""

0 commit comments

Comments
 (0)