Media type finder

This commit is contained in:
p1c2u 2021-03-30 13:09:39 +01:00
parent 1747433ded
commit 841e999710
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 ( from falcon.status_codes import (
HTTP_400, HTTP_404, HTTP_405, HTTP_415, 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 ( from openapi_core.templating.paths.exceptions import (
ServerNotFound, OperationNotFound, PathNotFound, ServerNotFound, OperationNotFound, PathNotFound,
) )
@ -17,7 +17,7 @@ class FalconOpenAPIErrorsHandler(object):
ServerNotFound: 400, ServerNotFound: 400,
OperationNotFound: 405, OperationNotFound: 405,
PathNotFound: 404, PathNotFound: 404,
InvalidContentType: 415, MediaTypeNotFound: 415,
} }
FALCON_STATUS_CODES = { FALCON_STATUS_CODES = {

View file

@ -2,7 +2,7 @@
from flask.globals import current_app from flask.globals import current_app
from flask.json import dumps 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 ( from openapi_core.templating.paths.exceptions import (
ServerNotFound, OperationNotFound, PathNotFound, ServerNotFound, OperationNotFound, PathNotFound,
) )
@ -14,7 +14,7 @@ class FlaskOpenAPIErrorsHandler(object):
ServerNotFound: 400, ServerNotFound: 400,
OperationNotFound: 405, OperationNotFound: 405,
PathNotFound: 404, PathNotFound: 404,
InvalidContentType: 415, MediaTypeNotFound: 415,
} }
@classmethod @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""" """OpenAPI core content models module"""
import fnmatch
from six import iteritems
from openapi_core.schema.content.exceptions import MimeTypeNotFound
class Content(dict): class Content(dict):
pass
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""" """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): class RequestBody(object):
@ -11,9 +9,3 @@ class RequestBody(object):
self.required = required self.required = required
self.extensions = extensions and dict(extensions) or {} 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""" """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): class Response(object):
@ -15,12 +13,3 @@ class Response(object):
self.links = links and dict(links) or {} self.links = links and dict(links) or {}
self.extensions = extensions and dict(extensions) 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.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError from openapi_core.deserializing.exceptions import DeserializeError
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.parameters.exceptions import ( from openapi_core.schema.parameters.exceptions import (
MissingRequiredParameter, MissingParameter, MissingRequiredParameter, MissingParameter,
) )
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
from openapi_core.security.exceptions import SecurityError 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.templating.paths.exceptions import PathError
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
from openapi_core.unmarshalling.schemas.exceptions import ( from openapi_core.unmarshalling.schemas.exceptions import (
@ -154,8 +154,9 @@ class RequestValidator(BaseValidator):
return None, [] return None, []
try: try:
media_type = operation.request_body[request.mimetype] media_type = self._get_media_type(
except InvalidContentType as exc: operation.request_body.content, request)
except MediaTypeFinderError as exc:
return None, [exc, ] return None, [exc, ]
try: try:

View file

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

View file

@ -21,6 +21,11 @@ class BaseValidator(object):
finder = PathFinder(self.spec, base_url=self.base_url) finder = PathFinder(self.spec, base_url=self.base_url)
return finder.find(request) 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): def _deserialise_media_type(self, media_type, value):
from openapi_core.deserializing.media_types.factories import ( from openapi_core.deserializing.media_types.factories import (
MediaTypeDeserializersFactory, MediaTypeDeserializersFactory,

View file

@ -77,12 +77,13 @@ class TestFalconOpenAPIMiddleware(object):
'errors': [ 'errors': [
{ {
'class': ( 'class': (
"<class 'openapi_core.schema.media_types.exceptions." "<class 'openapi_core.templating.media_types."
"InvalidContentType'>" "exceptions.MediaTypeNotFound'>"
), ),
'status': 415, 'status': 415,
'title': ( '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': [ 'errors': [
{ {
'class': ( 'class': (
"<class 'openapi_core.schema.media_types.exceptions." "<class 'openapi_core.templating.media_types."
"InvalidContentType'>" "exceptions.MediaTypeNotFound'>"
), ),
'status': 415, 'status': 415,
'title': ( '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': [ 'errors': [
{ {
'class': ( 'class': (
"<class 'openapi_core.schema.media_types.exceptions." "<class 'openapi_core.templating.media_types."
"InvalidContentType'>" "exceptions.MediaTypeNotFound'>"
), ),
'status': 415, 'status': 415,
'title': ( '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, EmptyParameterValue,
) )
from openapi_core.extensions.models.models import BaseModel from openapi_core.extensions.models.models import BaseModel
from openapi_core.schema.media_types.exceptions import InvalidContentType
from openapi_core.schema.parameters.exceptions import ( from openapi_core.schema.parameters.exceptions import (
MissingRequiredParameter, MissingRequiredParameter,
) )
@ -20,6 +19,7 @@ from openapi_core.schema.schemas.enums import SchemaType
from openapi_core.shortcuts import ( from openapi_core.shortcuts import (
create_spec, validate_parameters, validate_body, validate_data, create_spec, validate_parameters, validate_body, validate_data,
) )
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.paths.exceptions import ( from openapi_core.templating.paths.exceptions import (
ServerNotFound, ServerNotFound,
) )
@ -662,7 +662,7 @@ class TestPetstore(object):
}, },
) )
with pytest.raises(InvalidContentType): with pytest.raises(MediaTypeNotFound):
validate_body(spec, request) validate_body(spec, request)
def test_post_pets_missing_cookie(self, spec, spec_dict): 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.casting.schemas.exceptions import CastError
from openapi_core.deserializing.exceptions import DeserializeError 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.extensions.models.models import BaseModel
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
@ -15,6 +12,7 @@ from openapi_core.schema.responses.exceptions import (
MissingResponseContent, InvalidResponse, MissingResponseContent, InvalidResponse,
) )
from openapi_core.shortcuts import create_spec from openapi_core.shortcuts import create_spec
from openapi_core.templating.media_types.exceptions import MediaTypeNotFound
from openapi_core.templating.paths.exceptions import ( from openapi_core.templating.paths.exceptions import (
PathNotFound, OperationNotFound, PathNotFound, OperationNotFound,
) )
@ -184,7 +182,7 @@ class TestRequestValidator(object):
result = validator.validate(request) result = validator.validate(request)
assert len(result.errors) == 1 assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidContentType assert type(result.errors[0]) == MediaTypeNotFound
assert result.body is None assert result.body is None
assert result.parameters == RequestParameters( assert result.parameters == RequestParameters(
header={ header={
@ -463,7 +461,7 @@ class TestResponseValidator(object):
result = validator.validate(request, response) result = validator.validate(request, response)
assert len(result.errors) == 1 assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidContentType assert type(result.errors[0]) == MediaTypeNotFound
assert result.data is None assert result.data is None
assert result.headers == {} assert result.headers == {}