mirror of
https://github.com/correl/openapi-core.git
synced 2024-11-22 03:00:10 +00:00
Merge pull request #307 from p1c2u/feature/media-type-finder
Media type finder
This commit is contained in:
commit
098b015cc6
17 changed files with 78 additions and 79 deletions
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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()))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
0
openapi_core/templating/media_types/__init__.py
Normal file
0
openapi_core/templating/media_types/__init__.py
Normal file
19
openapi_core/templating/media_types/exceptions.py
Normal file
19
openapi_core/templating/media_types/exceptions.py
Normal 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)
|
24
openapi_core/templating/media_types/finders.py
Normal file
24
openapi_core/templating/media_types/finders.py
Normal 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()))
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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']"
|
||||
)
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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']"
|
||||
)
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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']"
|
||||
)
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 == {}
|
||||
|
||||
|
|
Loading…
Reference in a new issue