Merge pull request #307 from p1c2u/feature/media-type-finder

Media type finder
This commit is contained in:
A 2021-03-30 15:15:37 +01:00 committed by GitHub
commit 098b015cc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 78 additions and 79 deletions

View file

@ -5,7 +5,7 @@ from falcon.constants import MEDIA_JSON
from falcon.status_codes import (
HTTP_400, HTTP_404, HTTP_405, HTTP_415,
)
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.paths.exceptions import (
ServerNotFound, OperationNotFound, PathNotFound,
)
@ -17,7 +17,7 @@ class FalconOpenAPIErrorsHandler(object):
ServerNotFound: 400,
OperationNotFound: 405,
PathNotFound: 404,
InvalidContentType: 415,
MediaTypeNotFound: 415,
}
FALCON_STATUS_CODES = {

View file

@ -2,7 +2,7 @@
from flask.globals import current_app
from flask.json import dumps
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.paths.exceptions import (
ServerNotFound, OperationNotFound, PathNotFound,
)
@ -14,7 +14,7 @@ class FlaskOpenAPIErrorsHandler(object):
ServerNotFound: 400,
OperationNotFound: 405,
PathNotFound: 404,
InvalidContentType: 415,
MediaTypeNotFound: 415,
}
@classmethod

View file

@ -1,17 +0,0 @@
import attr
from openapi_core.schema.exceptions import OpenAPIMappingError
class OpenAPIContentError(OpenAPIMappingError):
pass
@attr.s(hash=True)
class MimeTypeNotFound(OpenAPIContentError):
mimetype = attr.ib()
availableMimetypes = attr.ib()
def __str__(self):
return "Mimetype not found: {0}. Valid mimetypes: {1}".format(
self.mimetype, self.availableMimetypes)

View file

@ -1,21 +1,5 @@
"""OpenAPI core content models module"""
import fnmatch
from six import iteritems
from openapi_core.schema.content.exceptions import MimeTypeNotFound
class Content(dict):
def __getitem__(self, mimetype):
try:
return super(Content, self).__getitem__(mimetype)
except KeyError:
pass
for key, value in iteritems(self):
if fnmatch.fnmatch(mimetype, key):
return value
raise MimeTypeNotFound(mimetype, list(self.keys()))

View file

@ -1,6 +1,4 @@
"""OpenAPI core request bodies models module"""
from openapi_core.schema.content.exceptions import MimeTypeNotFound
from openapi_core.schema.media_types.exceptions import InvalidContentType
class RequestBody(object):
@ -11,9 +9,3 @@ class RequestBody(object):
self.required = required
self.extensions = extensions and dict(extensions) or {}
def __getitem__(self, mimetype):
try:
return self.content[mimetype]
except MimeTypeNotFound:
raise InvalidContentType(mimetype)

View file

@ -1,6 +1,4 @@
"""OpenAPI core responses models module"""
from openapi_core.schema.content.exceptions import MimeTypeNotFound
from openapi_core.schema.media_types.exceptions import InvalidContentType
class Response(object):
@ -15,12 +13,3 @@ class Response(object):
self.links = links and dict(links) or {}
self.extensions = extensions and dict(extensions) or {}
def __getitem__(self, mimetype):
return self.get_content_type(mimetype)
def get_content_type(self, mimetype):
try:
return self.content[mimetype]
except MimeTypeNotFound:
raise InvalidContentType(mimetype)

View file

@ -0,0 +1,19 @@
import attr
from openapi_core.exceptions import OpenAPIError
class MediaTypeFinderError(OpenAPIError):
"""Media type finder error"""
@attr.s(hash=True)
class MediaTypeNotFound(MediaTypeFinderError):
mimetype = attr.ib()
availableMimetypes = attr.ib()
def __str__(self):
return (
"Content for the following mimetype not found: {0}. "
"Valid mimetypes: {1}"
).format(self.mimetype, self.availableMimetypes)

View file

@ -0,0 +1,24 @@
"""OpenAPI core templating media types finders module"""
import fnmatch
from six import iteritems
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
class MediaTypeFinder(object):
def __init__(self, content):
self.content = content
def find(self, request):
try:
return self.content[request.mimetype]
except KeyError:
pass
for key, value in iteritems(self.content):
if fnmatch.fnmatch(request.mimetype, key):
return value
raise MediaTypeNotFound(request.mimetype, list(self.content.keys()))

View file

@ -4,12 +4,12 @@ from six import iteritems
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.parameters.exceptions import (
MissingRequiredParameter, MissingParameter,
)
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
from openapi_core.security.exceptions import SecurityError
from openapi_core.templating.media_types.exceptions import MediaTypeFinderError
from openapi_core.templating.paths.exceptions import PathError
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
from openapi_core.unmarshalling.schemas.exceptions import (
@ -154,8 +154,9 @@ class RequestValidator(BaseValidator):
return None, []
try:
media_type = operation.request_body[request.mimetype]
except InvalidContentType as exc:
media_type = self._get_media_type(
operation.request_body.content, request)
except MediaTypeFinderError as exc:
return None, [exc, ]
try:

View file

@ -1,10 +1,10 @@
"""OpenAPI core validation response validators module"""
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.responses.exceptions import (
InvalidResponse, MissingResponseContent,
)
from openapi_core.templating.media_types.exceptions import MediaTypeFinderError
from openapi_core.templating.paths.exceptions import PathError
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
from openapi_core.unmarshalling.schemas.exceptions import (
@ -70,8 +70,9 @@ class ResponseValidator(BaseValidator):
return None, []
try:
media_type = operation_response[response.mimetype]
except InvalidContentType as exc:
media_type = self._get_media_type(
operation_response.content, response)
except MediaTypeFinderError as exc:
return None, [exc, ]
try:

View file

@ -21,6 +21,11 @@ class BaseValidator(object):
finder = PathFinder(self.spec, base_url=self.base_url)
return finder.find(request)
def _get_media_type(self, content, request_or_response):
from openapi_core.templating.media_types.finders import MediaTypeFinder
finder = MediaTypeFinder(content)
return finder.find(request_or_response)
def _deserialise_media_type(self, media_type, value):
from openapi_core.deserializing.media_types.factories import (
MediaTypeDeserializersFactory,

View file

@ -77,12 +77,13 @@ class TestFalconOpenAPIMiddleware(object):
'errors': [
{
'class': (
"<class 'openapi_core.schema.media_types.exceptions."
"InvalidContentType'>"
"<class 'openapi_core.templating.media_types."
"exceptions.MediaTypeNotFound'>"
),
'status': 415,
'title': (
'Content for following mimetype not found: text/html'
"Content for the following mimetype not found: "
"text/html. Valid mimetypes: ['application/json']"
)
}
]

View file

@ -70,12 +70,13 @@ class TestFlaskOpenAPIDecorator(object):
'errors': [
{
'class': (
"<class 'openapi_core.schema.media_types.exceptions."
"InvalidContentType'>"
"<class 'openapi_core.templating.media_types."
"exceptions.MediaTypeNotFound'>"
),
'status': 415,
'title': (
'Content for following mimetype not found: text/html'
"Content for the following mimetype not found: "
"text/html. Valid mimetypes: ['application/json']"
)
}
]

View file

@ -63,12 +63,13 @@ class TestFlaskOpenAPIView(object):
'errors': [
{
'class': (
"<class 'openapi_core.schema.media_types.exceptions."
"InvalidContentType'>"
"<class 'openapi_core.templating.media_types."
"exceptions.MediaTypeNotFound'>"
),
'status': 415,
'title': (
'Content for following mimetype not found: text/html'
"Content for the following mimetype not found: "
"text/html. Valid mimetypes: ['application/json']"
)
}
]

View file

@ -12,7 +12,6 @@ from openapi_core.deserializing.parameters.exceptions import (
EmptyParameterValue,
)
from openapi_core.extensions.models.models import BaseModel
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.parameters.exceptions import (
MissingRequiredParameter,
)
@ -20,6 +19,7 @@ from openapi_core.schema.schemas.enums import SchemaType
from openapi_core.shortcuts import (
create_spec, validate_parameters, validate_body, validate_data,
)
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.paths.exceptions import (
ServerNotFound,
)
@ -662,7 +662,7 @@ class TestPetstore(object):
},
)
with pytest.raises(InvalidContentType):
with pytest.raises(MediaTypeNotFound):
validate_body(spec, request)
def test_post_pets_missing_cookie(self, spec, spec_dict):

View file

@ -5,9 +5,6 @@ from six import text_type
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.schema.media_types.exceptions import (
InvalidContentType,
)
from openapi_core.extensions.models.models import BaseModel
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
@ -15,6 +12,7 @@ from openapi_core.schema.responses.exceptions import (
MissingResponseContent, InvalidResponse,
)
from openapi_core.shortcuts import create_spec
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.paths.exceptions import (
PathNotFound, OperationNotFound,
)
@ -184,7 +182,7 @@ class TestRequestValidator(object):
result = validator.validate(request)
assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidContentType
assert type(result.errors[0]) == MediaTypeNotFound
assert result.body is None
assert result.parameters == RequestParameters(
header={
@ -463,7 +461,7 @@ class TestResponseValidator(object):
result = validator.validate(request, response)
assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidContentType
assert type(result.errors[0]) == MediaTypeNotFound
assert result.data is None
assert result.headers == {}