Skip to content

Commit 3526fa0

Browse files
committed
Media type finder
1 parent 1747433 commit 3526fa0

File tree

17 files changed

+78
-72
lines changed

17 files changed

+78
-72
lines changed

Diff for: openapi_core/contrib/falcon/handlers.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from falcon.status_codes import (
66
HTTP_400, HTTP_404, HTTP_405, HTTP_415,
77
)
8-
from openapi_core.schema.media_types.exceptions import InvalidContentType
8+
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
99
from openapi_core.templating.paths.exceptions import (
1010
ServerNotFound, OperationNotFound, PathNotFound,
1111
)
@@ -17,7 +17,7 @@ class FalconOpenAPIErrorsHandler(object):
1717
ServerNotFound: 400,
1818
OperationNotFound: 405,
1919
PathNotFound: 404,
20-
InvalidContentType: 415,
20+
MediaTypeNotFound: 415,
2121
}
2222

2323
FALCON_STATUS_CODES = {

Diff for: openapi_core/contrib/flask/handlers.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from flask.globals import current_app
33
from flask.json import dumps
44

5-
from openapi_core.schema.media_types.exceptions import InvalidContentType
5+
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
66
from openapi_core.templating.paths.exceptions import (
77
ServerNotFound, OperationNotFound, PathNotFound,
88
)
@@ -14,7 +14,7 @@ class FlaskOpenAPIErrorsHandler(object):
1414
ServerNotFound: 400,
1515
OperationNotFound: 405,
1616
PathNotFound: 404,
17-
InvalidContentType: 415,
17+
MediaTypeNotFound: 415,
1818
}
1919

2020
@classmethod

Diff for: openapi_core/schema/content/exceptions.py

-10
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,3 @@
55

66
class OpenAPIContentError(OpenAPIMappingError):
77
pass
8-
9-
10-
@attr.s(hash=True)
11-
class MimeTypeNotFound(OpenAPIContentError):
12-
mimetype = attr.ib()
13-
availableMimetypes = attr.ib()
14-
15-
def __str__(self):
16-
return "Mimetype not found: {0}. Valid mimetypes: {1}".format(
17-
self.mimetype, self.availableMimetypes)

Diff for: openapi_core/schema/content/models.py

+1-17
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
"""OpenAPI core content models module"""
2-
import fnmatch
3-
4-
from six import iteritems
5-
6-
from openapi_core.schema.content.exceptions import MimeTypeNotFound
72

83

94
class Content(dict):
10-
11-
def __getitem__(self, mimetype):
12-
try:
13-
return super(Content, self).__getitem__(mimetype)
14-
except KeyError:
15-
pass
16-
17-
for key, value in iteritems(self):
18-
if fnmatch.fnmatch(mimetype, key):
19-
return value
20-
21-
raise MimeTypeNotFound(mimetype, list(self.keys()))
5+
pass

Diff for: openapi_core/schema/request_bodies/models.py

-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
"""OpenAPI core request bodies models module"""
2-
from openapi_core.schema.content.exceptions import MimeTypeNotFound
3-
from openapi_core.schema.media_types.exceptions import InvalidContentType
42

53

64
class RequestBody(object):
@@ -11,9 +9,3 @@ def __init__(self, content, required=False, extensions=None):
119
self.required = required
1210

1311
self.extensions = extensions and dict(extensions) or {}
14-
15-
def __getitem__(self, mimetype):
16-
try:
17-
return self.content[mimetype]
18-
except MimeTypeNotFound:
19-
raise InvalidContentType(mimetype)

Diff for: openapi_core/schema/responses/models.py

-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
"""OpenAPI core responses models module"""
2-
from openapi_core.schema.content.exceptions import MimeTypeNotFound
3-
from openapi_core.schema.media_types.exceptions import InvalidContentType
42

53

64
class Response(object):
@@ -15,12 +13,3 @@ def __init__(
1513
self.links = links and dict(links) or {}
1614

1715
self.extensions = extensions and dict(extensions) or {}
18-
19-
def __getitem__(self, mimetype):
20-
return self.get_content_type(mimetype)
21-
22-
def get_content_type(self, mimetype):
23-
try:
24-
return self.content[mimetype]
25-
except MimeTypeNotFound:
26-
raise InvalidContentType(mimetype)

Diff for: openapi_core/templating/media_types/__init__.py

Whitespace-only changes.

Diff for: openapi_core/templating/media_types/exceptions.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import attr
2+
3+
from openapi_core.exceptions import OpenAPIError
4+
5+
6+
class MediaTypeFinderError(OpenAPIError):
7+
"""Media type finder error"""
8+
9+
10+
@attr.s(hash=True)
11+
class MediaTypeNotFound(MediaTypeFinderError):
12+
mimetype = attr.ib()
13+
availableMimetypes = attr.ib()
14+
15+
def __str__(self):
16+
return (
17+
"Content for the following mimetype not found: {0}. "
18+
"Valid mimetypes: {1}"
19+
).format(self.mimetype, self.availableMimetypes)

Diff for: openapi_core/templating/media_types/finders.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""OpenAPI core templating media types finders module"""
2+
import fnmatch
3+
4+
from six import iteritems
5+
6+
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
7+
8+
9+
class MediaTypeFinder(object):
10+
11+
def __init__(self, content):
12+
self.content = content
13+
14+
def find(self, request):
15+
try:
16+
return self.content[request.mimetype]
17+
except KeyError:
18+
pass
19+
20+
for key, value in iteritems(self.content):
21+
if fnmatch.fnmatch(request.mimetype, key):
22+
return value
23+
24+
raise MediaTypeNotFound(request.mimetype, list(self.content.keys()))

Diff for: openapi_core/validation/request/validators.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
from openapi_core.casting.schemas.exceptions import CastError
66
from openapi_core.deserializing.exceptions import DeserializeError
7-
from openapi_core.schema.media_types.exceptions import InvalidContentType
87
from openapi_core.schema.parameters.exceptions import (
98
MissingRequiredParameter, MissingParameter,
109
)
1110
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
1211
from openapi_core.security.exceptions import SecurityError
12+
from openapi_core.templating.media_types.exceptions import MediaTypeFinderError
1313
from openapi_core.templating.paths.exceptions import PathError
1414
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
1515
from openapi_core.unmarshalling.schemas.exceptions import (
@@ -154,8 +154,9 @@ def _get_body(self, request, operation):
154154
return None, []
155155

156156
try:
157-
media_type = operation.request_body[request.mimetype]
158-
except InvalidContentType as exc:
157+
media_type = self._get_media_type(
158+
operation.request_body.content, request)
159+
except MediaTypeFinderError as exc:
159160
return None, [exc, ]
160161

161162
try:

Diff for: openapi_core/validation/response/validators.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""OpenAPI core validation response validators module"""
22
from openapi_core.casting.schemas.exceptions import CastError
33
from openapi_core.deserializing.exceptions import DeserializeError
4-
from openapi_core.schema.media_types.exceptions import InvalidContentType
54
from openapi_core.schema.responses.exceptions import (
65
InvalidResponse, MissingResponseContent,
76
)
7+
from openapi_core.templating.media_types.exceptions import MediaTypeFinderError
88
from openapi_core.templating.paths.exceptions import PathError
99
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
1010
from openapi_core.unmarshalling.schemas.exceptions import (
@@ -70,8 +70,9 @@ def _get_data(self, response, operation_response):
7070
return None, []
7171

7272
try:
73-
media_type = operation_response[response.mimetype]
74-
except InvalidContentType as exc:
73+
media_type = self._get_media_type(
74+
operation_response.content, response)
75+
except MediaTypeFinderError as exc:
7576
return None, [exc, ]
7677

7778
try:

Diff for: openapi_core/validation/validators.py

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ def _find_path(self, request):
2121
finder = PathFinder(self.spec, base_url=self.base_url)
2222
return finder.find(request)
2323

24+
def _get_media_type(self, content, request_or_response):
25+
from openapi_core.templating.media_types.finders import MediaTypeFinder
26+
finder = MediaTypeFinder(content)
27+
return finder.find(request_or_response)
28+
2429
def _deserialise_media_type(self, media_type, value):
2530
from openapi_core.deserializing.media_types.factories import (
2631
MediaTypeDeserializersFactory,

Diff for: tests/integration/contrib/falcon/test_falcon_middlewares.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ def view_response_callable(request, response, id):
7777
'errors': [
7878
{
7979
'class': (
80-
"<class 'openapi_core.schema.media_types.exceptions."
81-
"InvalidContentType'>"
80+
"<class 'openapi_core.templating.media_types."
81+
"exceptions.MediaTypeNotFound'>"
8282
),
8383
'status': 415,
8484
'title': (
85-
'Content for following mimetype not found: text/html'
85+
"Content for the following mimetype not found: "
86+
"text/html. Valid mimetypes: ['application/json']"
8687
)
8788
}
8889
]

Diff for: tests/integration/contrib/flask/test_flask_decorator.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,13 @@ def view_response_callable(*args, **kwargs):
7070
'errors': [
7171
{
7272
'class': (
73-
"<class 'openapi_core.schema.media_types.exceptions."
74-
"InvalidContentType'>"
73+
"<class 'openapi_core.templating.media_types."
74+
"exceptions.MediaTypeNotFound'>"
7575
),
7676
'status': 415,
7777
'title': (
78-
'Content for following mimetype not found: text/html'
78+
"Content for the following mimetype not found: "
79+
"text/html. Valid mimetypes: ['application/json']"
7980
)
8081
}
8182
]

Diff for: tests/integration/contrib/flask/test_flask_views.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,13 @@ def test_invalid_content_type(self, client):
6363
'errors': [
6464
{
6565
'class': (
66-
"<class 'openapi_core.schema.media_types.exceptions."
67-
"InvalidContentType'>"
66+
"<class 'openapi_core.templating.media_types."
67+
"exceptions.MediaTypeNotFound'>"
6868
),
6969
'status': 415,
7070
'title': (
71-
'Content for following mimetype not found: text/html'
71+
"Content for the following mimetype not found: "
72+
"text/html. Valid mimetypes: ['application/json']"
7273
)
7374
}
7475
]

Diff for: tests/integration/validation/test_petstore.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
EmptyParameterValue,
1313
)
1414
from openapi_core.extensions.models.models import BaseModel
15-
from openapi_core.schema.media_types.exceptions import InvalidContentType
1615
from openapi_core.schema.parameters.exceptions import (
1716
MissingRequiredParameter,
1817
)
1918
from openapi_core.schema.schemas.enums import SchemaType
2019
from openapi_core.shortcuts import (
2120
create_spec, validate_parameters, validate_body, validate_data,
2221
)
22+
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
2323
from openapi_core.templating.paths.exceptions import (
2424
ServerNotFound,
2525
)
@@ -662,7 +662,7 @@ def test_post_pets_raises_invalid_mimetype(self, spec):
662662
},
663663
)
664664

665-
with pytest.raises(InvalidContentType):
665+
with pytest.raises(MediaTypeNotFound):
666666
validate_body(spec, request)
667667

668668
def test_post_pets_missing_cookie(self, spec, spec_dict):

Diff for: tests/integration/validation/test_validators.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@
55

66
from openapi_core.casting.schemas.exceptions import CastError
77
from openapi_core.deserializing.exceptions import DeserializeError
8-
from openapi_core.schema.media_types.exceptions import (
9-
InvalidContentType,
10-
)
118
from openapi_core.extensions.models.models import BaseModel
129
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
1310
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
1411
from openapi_core.schema.responses.exceptions import (
1512
MissingResponseContent, InvalidResponse,
1613
)
1714
from openapi_core.shortcuts import create_spec
15+
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
1816
from openapi_core.templating.paths.exceptions import (
1917
PathNotFound, OperationNotFound,
2018
)
@@ -184,7 +182,7 @@ def test_invalid_content_type(self, validator):
184182
result = validator.validate(request)
185183

186184
assert len(result.errors) == 1
187-
assert type(result.errors[0]) == InvalidContentType
185+
assert type(result.errors[0]) == MediaTypeNotFound
188186
assert result.body is None
189187
assert result.parameters == RequestParameters(
190188
header={
@@ -463,7 +461,7 @@ def test_invalid_content_type(self, validator):
463461
result = validator.validate(request, response)
464462

465463
assert len(result.errors) == 1
466-
assert type(result.errors[0]) == InvalidContentType
464+
assert type(result.errors[0]) == MediaTypeNotFound
467465
assert result.data is None
468466
assert result.headers == {}
469467

0 commit comments

Comments
 (0)