Skip to content

Commit 2e6f2de

Browse files
authored
Merge pull request #94 from domenkozar/structured-exceptions
Structured exceptions
2 parents 032aecd + e1c6479 commit 2e6f2de

File tree

22 files changed

+305
-204
lines changed

22 files changed

+305
-204
lines changed
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
from openapi_core.schema.exceptions import OpenAPIMappingError
22

3+
import attr
4+
35

46
class OpenAPIContentError(OpenAPIMappingError):
57
pass
68

79

10+
@attr.s
811
class MimeTypeNotFound(OpenAPIContentError):
9-
pass
12+
mimetype = attr.ib()
13+
availableMimetypes = attr.ib()
14+
15+
def __str__(self):
16+
return "Mimetype not found: {0}. Valid mimetypes: {1}".format(self.mimetype, self.availableMimetypes)

openapi_core/schema/content/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ def __getitem__(self, mimetype):
1818
if fnmatch.fnmatch(mimetype, key):
1919
return value
2020

21-
raise MimeTypeNotFound("{0} mimetype not found")
21+
raise MimeTypeNotFound(mimetype, self.keys())
Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
from openapi_core.schema.exceptions import OpenAPIMappingError
22

3+
import attr
4+
35

46
class OpenAPIMediaTypeError(OpenAPIMappingError):
57
pass
68

7-
9+
@attr.s
810
class InvalidMediaTypeValue(OpenAPIMediaTypeError):
9-
pass
11+
original_exception = attr.ib()
1012

13+
def __str__(self):
14+
return "Mimetype invalid: {0}".format(self.original_exception)
1115

16+
@attr.s
1217
class InvalidContentType(OpenAPIMediaTypeError):
13-
pass
18+
mimetype = attr.ib()
19+
20+
def __str__(self):
21+
return "Content for following mimetype not found: {0}".format(self.mimetype)

openapi_core/schema/media_types/models.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from json import loads
55

66
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
7-
from openapi_core.schema.schemas.exceptions import InvalidSchemaValue
7+
from openapi_core.schema.schemas.exceptions import OpenAPISchemaError
88

99

1010
MEDIA_TYPE_DESERIALIZERS = {
@@ -32,21 +32,21 @@ def deserialize(self, value):
3232
deserializer = self.get_dererializer()
3333
return deserializer(value)
3434

35-
def unmarshal(self, value):
35+
def unmarshal(self, value, custom_formatters=None):
3636
if not self.schema:
3737
return value
3838

3939
try:
4040
deserialized = self.deserialize(value)
4141
except ValueError as exc:
42-
raise InvalidMediaTypeValue(str(exc))
42+
raise InvalidMediaTypeValue(exc)
4343

4444
try:
45-
unmarshalled = self.schema.unmarshal(deserialized)
46-
except InvalidSchemaValue as exc:
47-
raise InvalidMediaTypeValue(str(exc))
45+
unmarshalled = self.schema.unmarshal(deserialized, custom_formatters=custom_formatters)
46+
except OpenAPISchemaError as exc:
47+
raise InvalidMediaTypeValue(exc)
4848

4949
try:
50-
return self.schema.validate(unmarshalled)
51-
except InvalidSchemaValue as exc:
52-
raise InvalidMediaTypeValue(str(exc))
50+
return self.schema.validate(unmarshalled, custom_formatters=custom_formatters)
51+
except OpenAPISchemaError as exc:
52+
raise InvalidMediaTypeValue(exc)
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
from openapi_core.schema.exceptions import OpenAPIMappingError
22

3+
import attr
4+
35

46
class OpenAPIOperationError(OpenAPIMappingError):
57
pass
68

79

10+
@attr.s
811
class InvalidOperation(OpenAPIOperationError):
9-
pass
12+
path_pattern = attr.ib()
13+
http_method = attr.ib()
14+
15+
def __str__(self):
16+
return "Unknown operation path {0} with method {1}".format(
17+
self.path_pattern, self.http_method)

openapi_core/schema/operations/models.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def get_response(self, http_status='default'):
3232
return self.responses[http_status_range]
3333

3434
if 'default' not in self.responses:
35-
raise InvalidResponse(
36-
"Unknown response http status {0}".format(http_status))
35+
raise InvalidResponse(http_status, self.responses)
3736

3837
return self.responses['default']
Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
11
from openapi_core.schema.exceptions import OpenAPIMappingError
22

3+
import attr
4+
35

46
class OpenAPIParameterError(OpenAPIMappingError):
57
pass
68

79

10+
@attr.s
811
class MissingParameter(OpenAPIParameterError):
9-
pass
12+
name = attr.ib()
13+
14+
def __str__(self):
15+
return "Missing parameter (without default value): {0}".format(self.name)
1016

1117

18+
@attr.s
1219
class MissingRequiredParameter(OpenAPIParameterError):
13-
pass
20+
name = attr.ib()
21+
22+
def __str__(self):
23+
return "Missing required parameter: {0}".format(self.name)
1424

1525

26+
@attr.s
1627
class EmptyParameterValue(OpenAPIParameterError):
17-
pass
28+
name = attr.ib()
29+
30+
def __str__(self):
31+
return "Value of parameter cannot be empty: {0}".format(self.name)
1832

1933

34+
@attr.s
2035
class InvalidParameterValue(OpenAPIParameterError):
21-
pass
36+
name = attr.ib()
37+
original_exception = attr.ib()
38+
39+
def __str__(self):
40+
return "Invalid parameter value for `{0}`: {1}".format(self.name, self.original_exception)

openapi_core/schema/parameters/models.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
EmptyParameterValue,
1111
)
1212
from openapi_core.schema.schemas.enums import SchemaType
13-
from openapi_core.schema.schemas.exceptions import InvalidSchemaValue
13+
from openapi_core.schema.schemas.exceptions import OpenAPISchemaError
1414

1515
log = logging.getLogger(__name__)
1616

@@ -77,12 +77,10 @@ def get_value(self, request):
7777

7878
if self.name not in location:
7979
if self.required:
80-
raise MissingRequiredParameter(
81-
"Missing required `{0}` parameter".format(self.name))
80+
raise MissingRequiredParameter(self.name)
8281

8382
if not self.schema or self.schema.default is None:
84-
raise MissingParameter(
85-
"Missing `{0}` parameter".format(self.name))
83+
raise MissingParameter(self.name)
8684

8785
return self.schema.default
8886

@@ -91,7 +89,7 @@ def get_value(self, request):
9189

9290
return location[self.name]
9391

94-
def unmarshal(self, value):
92+
def unmarshal(self, value, custom_formatters=None):
9593
if self.deprecated:
9694
warnings.warn(
9795
"{0} parameter is deprecated".format(self.name),
@@ -100,23 +98,22 @@ def unmarshal(self, value):
10098

10199
if (self.location == ParameterLocation.QUERY and value == "" and
102100
not self.allow_empty_value):
103-
raise EmptyParameterValue(
104-
"Value of {0} parameter cannot be empty".format(self.name))
101+
raise EmptyParameterValue(self.name)
105102

106103
if not self.schema:
107104
return value
108105

109106
try:
110107
deserialized = self.deserialize(value)
111108
except (ValueError, AttributeError) as exc:
112-
raise InvalidParameterValue(str(exc))
109+
raise InvalidParameterValue(self.name, exc)
113110

114111
try:
115-
unmarshalled = self.schema.unmarshal(deserialized)
116-
except InvalidSchemaValue as exc:
117-
raise InvalidParameterValue(str(exc))
112+
unmarshalled = self.schema.unmarshal(deserialized, custom_formatters=custom_formatters)
113+
except OpenAPISchemaError as exc:
114+
raise InvalidParameterValue(self.name, exc)
118115

119116
try:
120-
return self.schema.validate(unmarshalled)
121-
except InvalidSchemaValue as exc:
122-
raise InvalidParameterValue(str(exc))
117+
return self.schema.validate(unmarshalled, custom_formatters=custom_formatters)
118+
except OpenAPISchemaError as exc:
119+
raise InvalidParameterValue(self.name, exc)
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
from openapi_core.schema.exceptions import OpenAPIMappingError
22

3+
import attr
4+
35

46
class OpenAPIRequestBodyError(OpenAPIMappingError):
57
pass
68

79

10+
@attr.s
811
class MissingRequestBody(OpenAPIRequestBodyError):
9-
pass
12+
request = attr.ib()
13+
14+
def __str__(self):
15+
return "Missing required request body"

openapi_core/schema/request_bodies/models.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ def __getitem__(self, mimetype):
1616
try:
1717
return self.content[mimetype]
1818
except MimeTypeNotFound:
19-
raise InvalidContentType(
20-
"Invalid mime type `{0}`".format(mimetype))
19+
raise InvalidContentType(mimetype)
2120

2221
def get_value(self, request):
2322
if not request.body and self.required:
24-
raise MissingRequestBody("Missing required request body")
25-
23+
raise MissingRequestBody(request)
2624
return request.body

0 commit comments

Comments
 (0)