mirror of
https://github.com/correl/openapi-core.git
synced 2024-11-21 19:18:41 +00:00
Merge pull request #191 from p1c2u/refactor/move-cast-out-of-schema-models
Move deserialize/cast out of schema models
This commit is contained in:
commit
80485788c0
27 changed files with 351 additions and 284 deletions
|
@ -1,15 +1,20 @@
|
|||
from openapi_core.casting.schemas.exceptions import CastError
|
||||
from openapi_core.schema.schemas.types import NoValue
|
||||
|
||||
|
||||
class PrimitiveCaster(object):
|
||||
|
||||
def __init__(self, caster_callable):
|
||||
def __init__(self, schema, caster_callable):
|
||||
self.schema = schema
|
||||
self.caster_callable = caster_callable
|
||||
|
||||
def __call__(self, value):
|
||||
if value in (None, NoValue):
|
||||
return value
|
||||
return self.caster_callable(value)
|
||||
try:
|
||||
return self.caster_callable(value)
|
||||
except (ValueError, TypeError):
|
||||
raise CastError(value, self.schema.type.value)
|
||||
|
||||
|
||||
class DummyCaster(object):
|
||||
|
|
|
@ -8,22 +8,24 @@ from openapi_core.casting.schemas.util import forcebool
|
|||
|
||||
class SchemaCastersFactory(object):
|
||||
|
||||
DUMMY_CASTER = DummyCaster()
|
||||
DUMMY_CASTERS = [
|
||||
SchemaType.STRING, SchemaType.OBJECT, SchemaType.ANY,
|
||||
]
|
||||
PRIMITIVE_CASTERS = {
|
||||
SchemaType.STRING: DUMMY_CASTER,
|
||||
SchemaType.INTEGER: PrimitiveCaster(int),
|
||||
SchemaType.NUMBER: PrimitiveCaster(float),
|
||||
SchemaType.BOOLEAN: PrimitiveCaster(forcebool),
|
||||
SchemaType.OBJECT: DUMMY_CASTER,
|
||||
SchemaType.ANY: DUMMY_CASTER,
|
||||
SchemaType.INTEGER: int,
|
||||
SchemaType.NUMBER: float,
|
||||
SchemaType.BOOLEAN: forcebool,
|
||||
}
|
||||
COMPLEX_CASTERS = {
|
||||
SchemaType.ARRAY: ArrayCaster,
|
||||
}
|
||||
|
||||
def create(self, schema):
|
||||
if schema.type in self.PRIMITIVE_CASTERS:
|
||||
return self.PRIMITIVE_CASTERS[schema.type]
|
||||
if schema.type in self.DUMMY_CASTERS:
|
||||
return DummyCaster()
|
||||
elif schema.type in self.PRIMITIVE_CASTERS:
|
||||
caster_callable = self.PRIMITIVE_CASTERS[schema.type]
|
||||
return PrimitiveCaster(schema, caster_callable)
|
||||
elif schema.type in self.COMPLEX_CASTERS:
|
||||
caster_class = self.COMPLEX_CASTERS[schema.type]
|
||||
return caster_class(schema=schema, casters_factory=self)
|
||||
return caster_class(schema, self)
|
||||
|
|
0
openapi_core/deserializing/__init__.py
Normal file
0
openapi_core/deserializing/__init__.py
Normal file
14
openapi_core/deserializing/exceptions.py
Normal file
14
openapi_core/deserializing/exceptions.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import attr
|
||||
|
||||
from openapi_core.exceptions import OpenAPIError
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class DeserializeError(OpenAPIError):
|
||||
"""Deserialize operation error"""
|
||||
value = attr.ib()
|
||||
style = attr.ib()
|
||||
|
||||
def __str__(self):
|
||||
return "Failed to deserialize value {value} with style {style}".format(
|
||||
value=self.value, style=self.style)
|
0
openapi_core/deserializing/media_types/__init__.py
Normal file
0
openapi_core/deserializing/media_types/__init__.py
Normal file
14
openapi_core/deserializing/media_types/deserializers.py
Normal file
14
openapi_core/deserializing/media_types/deserializers.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from openapi_core.deserializing.exceptions import DeserializeError
|
||||
|
||||
|
||||
class PrimitiveDeserializer(object):
|
||||
|
||||
def __init__(self, mimetype, deserializer_callable):
|
||||
self.mimetype = mimetype
|
||||
self.deserializer_callable = deserializer_callable
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
return self.deserializer_callable(value)
|
||||
except (ValueError, TypeError, AttributeError):
|
||||
raise DeserializeError(value, self.mimetype)
|
18
openapi_core/deserializing/media_types/factories.py
Normal file
18
openapi_core/deserializing/media_types/factories.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from openapi_core.schema.media_types.util import json_loads
|
||||
|
||||
from openapi_core.deserializing.media_types.deserializers import (
|
||||
PrimitiveDeserializer,
|
||||
)
|
||||
|
||||
|
||||
class MediaTypeDeserializersFactory(object):
|
||||
|
||||
MEDIA_TYPE_DESERIALIZERS = {
|
||||
'application/json': json_loads,
|
||||
}
|
||||
|
||||
def create(self, media_type):
|
||||
deserialize_callable = self.MEDIA_TYPE_DESERIALIZERS.get(
|
||||
media_type.mimetype, lambda x: x)
|
||||
return PrimitiveDeserializer(
|
||||
media_type.mimetype, deserialize_callable)
|
0
openapi_core/deserializing/parameters/__init__.py
Normal file
0
openapi_core/deserializing/parameters/__init__.py
Normal file
25
openapi_core/deserializing/parameters/deserializers.py
Normal file
25
openapi_core/deserializing/parameters/deserializers.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from openapi_core.deserializing.exceptions import DeserializeError
|
||||
from openapi_core.deserializing.parameters.exceptions import (
|
||||
EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.parameters.enums import ParameterLocation
|
||||
|
||||
|
||||
class PrimitiveDeserializer(object):
|
||||
|
||||
def __init__(self, param, deserializer_callable):
|
||||
self.param = param
|
||||
self.deserializer_callable = deserializer_callable
|
||||
|
||||
def __call__(self, value):
|
||||
if (self.param.location == ParameterLocation.QUERY and value == "" and
|
||||
not self.param.allow_empty_value):
|
||||
raise EmptyParameterValue(
|
||||
value, self.param.style, self.param.name)
|
||||
|
||||
if not self.param.aslist or self.param.explode:
|
||||
return value
|
||||
try:
|
||||
return self.deserializer_callable(value)
|
||||
except (ValueError, TypeError, AttributeError):
|
||||
raise DeserializeError(value, self.param.style)
|
11
openapi_core/deserializing/parameters/exceptions.py
Normal file
11
openapi_core/deserializing/parameters/exceptions.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
import attr
|
||||
|
||||
from openapi_core.deserializing.exceptions import DeserializeError
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class EmptyParameterValue(DeserializeError):
|
||||
name = attr.ib()
|
||||
|
||||
def __str__(self):
|
||||
return "Value of parameter cannot be empty: {0}".format(self.name)
|
26
openapi_core/deserializing/parameters/factories.py
Normal file
26
openapi_core/deserializing/parameters/factories.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import warnings
|
||||
|
||||
from openapi_core.deserializing.parameters.deserializers import (
|
||||
PrimitiveDeserializer,
|
||||
)
|
||||
from openapi_core.schema.parameters.enums import ParameterStyle
|
||||
|
||||
|
||||
class ParameterDeserializersFactory(object):
|
||||
|
||||
PARAMETER_STYLE_DESERIALIZERS = {
|
||||
ParameterStyle.FORM: lambda x: x.split(','),
|
||||
ParameterStyle.SIMPLE: lambda x: x.split(','),
|
||||
ParameterStyle.SPACE_DELIMITED: lambda x: x.split(' '),
|
||||
ParameterStyle.PIPE_DELIMITED: lambda x: x.split('|'),
|
||||
}
|
||||
|
||||
def create(self, param):
|
||||
if param.deprecated:
|
||||
warnings.warn(
|
||||
"{0} parameter is deprecated".format(param.name),
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
deserialize_callable = self.PARAMETER_STYLE_DESERIALIZERS[param.style]
|
||||
return PrimitiveDeserializer(param, deserialize_callable)
|
|
@ -7,14 +7,6 @@ class OpenAPIMediaTypeError(OpenAPIMappingError):
|
|||
pass
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class InvalidMediaTypeValue(OpenAPIMediaTypeError):
|
||||
original_exception = attr.ib()
|
||||
|
||||
def __str__(self):
|
||||
return "Mimetype invalid: {0}".format(self.original_exception)
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class InvalidContentType(OpenAPIMediaTypeError):
|
||||
mimetype = attr.ib()
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
"""OpenAPI core media types models module"""
|
||||
from collections import defaultdict
|
||||
|
||||
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
|
||||
from openapi_core.schema.media_types.util import json_loads
|
||||
from openapi_core.casting.schemas.exceptions import CastError
|
||||
|
||||
|
||||
MEDIA_TYPE_DESERIALIZERS = {
|
||||
'application/json': json_loads,
|
||||
}
|
||||
|
||||
|
||||
class MediaType(object):
|
||||
|
@ -18,29 +8,3 @@ class MediaType(object):
|
|||
self.mimetype = mimetype
|
||||
self.schema = schema
|
||||
self.example = example
|
||||
|
||||
def get_deserializer_mapping(self):
|
||||
mapping = MEDIA_TYPE_DESERIALIZERS.copy()
|
||||
return defaultdict(lambda: lambda x: x, mapping)
|
||||
|
||||
def get_dererializer(self):
|
||||
mapping = self.get_deserializer_mapping()
|
||||
return mapping[self.mimetype]
|
||||
|
||||
def deserialize(self, value):
|
||||
deserializer = self.get_dererializer()
|
||||
return deserializer(value)
|
||||
|
||||
def cast(self, value):
|
||||
try:
|
||||
deserialized = self.deserialize(value)
|
||||
except ValueError as exc:
|
||||
raise InvalidMediaTypeValue(exc)
|
||||
|
||||
if not self.schema:
|
||||
return deserialized
|
||||
|
||||
try:
|
||||
return self.schema.cast(deserialized)
|
||||
except CastError as exc:
|
||||
raise InvalidMediaTypeValue(exc)
|
||||
|
|
|
@ -27,21 +27,3 @@ class MissingRequiredParameter(MissingParameterError):
|
|||
|
||||
def __str__(self):
|
||||
return "Missing required parameter: {0}".format(self.name)
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class EmptyParameterValue(OpenAPIParameterError):
|
||||
name = attr.ib()
|
||||
|
||||
def __str__(self):
|
||||
return "Value of parameter cannot be empty: {0}".format(self.name)
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class InvalidParameterValue(OpenAPIParameterError):
|
||||
name = attr.ib()
|
||||
original_exception = attr.ib()
|
||||
|
||||
def __str__(self):
|
||||
return "Invalid parameter value for `{0}`: {1}".format(
|
||||
self.name, self.original_exception)
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
"""OpenAPI core parameters models module"""
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
from openapi_core.schema.parameters.enums import (
|
||||
ParameterLocation, ParameterStyle,
|
||||
)
|
||||
from openapi_core.schema.parameters.exceptions import (
|
||||
MissingRequiredParameter, MissingParameter, InvalidParameterValue,
|
||||
EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
from openapi_core.casting.schemas.exceptions import CastError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -18,13 +12,6 @@ log = logging.getLogger(__name__)
|
|||
class Parameter(object):
|
||||
"""Represents an OpenAPI operation Parameter."""
|
||||
|
||||
PARAMETER_STYLE_DESERIALIZERS = {
|
||||
ParameterStyle.FORM: lambda x: x.split(','),
|
||||
ParameterStyle.SIMPLE: lambda x: x.split(','),
|
||||
ParameterStyle.SPACE_DELIMITED: lambda x: x.split(' '),
|
||||
ParameterStyle.PIPE_DELIMITED: lambda x: x.split('|'),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self, name, location, schema=None, required=False,
|
||||
deprecated=False, allow_empty_value=False,
|
||||
|
@ -61,53 +48,3 @@ class Parameter(object):
|
|||
@property
|
||||
def default_explode(self):
|
||||
return self.style == ParameterStyle.FORM
|
||||
|
||||
def get_dererializer(self):
|
||||
return self.PARAMETER_STYLE_DESERIALIZERS[self.style]
|
||||
|
||||
def deserialize(self, value):
|
||||
if not self.aslist or self.explode:
|
||||
return value
|
||||
|
||||
deserializer = self.get_dererializer()
|
||||
return deserializer(value)
|
||||
|
||||
def get_raw_value(self, request):
|
||||
location = request.parameters[self.location.value]
|
||||
|
||||
if self.name not in location:
|
||||
if self.required:
|
||||
raise MissingRequiredParameter(self.name)
|
||||
|
||||
raise MissingParameter(self.name)
|
||||
|
||||
if self.aslist and self.explode:
|
||||
if hasattr(location, 'getall'):
|
||||
return location.getall(self.name)
|
||||
return location.getlist(self.name)
|
||||
|
||||
return location[self.name]
|
||||
|
||||
def cast(self, value):
|
||||
if self.deprecated:
|
||||
warnings.warn(
|
||||
"{0} parameter is deprecated".format(self.name),
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if (self.location == ParameterLocation.QUERY and value == "" and
|
||||
not self.allow_empty_value):
|
||||
raise EmptyParameterValue(self.name)
|
||||
|
||||
try:
|
||||
deserialized = self.deserialize(value)
|
||||
except (ValueError, AttributeError) as exc:
|
||||
raise InvalidParameterValue(self.name, exc)
|
||||
|
||||
if not self.schema:
|
||||
return deserialized
|
||||
|
||||
try:
|
||||
return self.schema.cast(deserialized)
|
||||
except CastError as exc:
|
||||
raise InvalidParameterValue(self.name, exc)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
from openapi_core.schema.content.exceptions import MimeTypeNotFound
|
||||
from openapi_core.schema.content.models import Content
|
||||
from openapi_core.schema.media_types.exceptions import InvalidContentType
|
||||
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
|
||||
|
||||
|
||||
class RequestBody(object):
|
||||
|
@ -17,8 +16,3 @@ class RequestBody(object):
|
|||
return self.content[mimetype]
|
||||
except MimeTypeNotFound:
|
||||
raise InvalidContentType(mimetype)
|
||||
|
||||
def get_value(self, request):
|
||||
if not request.body and self.required:
|
||||
raise MissingRequestBody(request)
|
||||
return request.body
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
from openapi_core.schema.content.exceptions import MimeTypeNotFound
|
||||
from openapi_core.schema.content.models import Content
|
||||
from openapi_core.schema.media_types.exceptions import InvalidContentType
|
||||
from openapi_core.schema.responses.exceptions import MissingResponseContent
|
||||
|
||||
|
||||
class Response(object):
|
||||
|
@ -24,9 +23,3 @@ class Response(object):
|
|||
return self.content[mimetype]
|
||||
except MimeTypeNotFound:
|
||||
raise InvalidContentType(mimetype)
|
||||
|
||||
def get_value(self, response):
|
||||
if not response.data:
|
||||
raise MissingResponseContent(response)
|
||||
|
||||
return response.data
|
||||
|
|
|
@ -90,14 +90,3 @@ class Schema(object):
|
|||
def get_all_properties_names(self):
|
||||
all_properties = self.get_all_properties()
|
||||
return set(all_properties.keys())
|
||||
|
||||
def cast(self, value):
|
||||
"""Cast value from string to schema type"""
|
||||
from openapi_core.casting.schemas.exceptions import CastError
|
||||
from openapi_core.casting.schemas.factories import SchemaCastersFactory
|
||||
casters_factory = SchemaCastersFactory()
|
||||
caster = casters_factory.create(self)
|
||||
try:
|
||||
return caster(value)
|
||||
except (ValueError, TypeError):
|
||||
raise CastError(value, self.type)
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
from itertools import chain
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.schema.media_types.exceptions import (
|
||||
InvalidMediaTypeValue, InvalidContentType,
|
||||
)
|
||||
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.operations.exceptions import InvalidOperation
|
||||
from openapi_core.schema.parameters.exceptions import (
|
||||
OpenAPIParameterError, MissingRequiredParameter, MissingParameter,
|
||||
MissingRequiredParameter, MissingParameter,
|
||||
)
|
||||
from openapi_core.schema.paths.exceptions import InvalidPath
|
||||
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
|
||||
|
@ -99,7 +99,7 @@ class RequestValidator(object):
|
|||
continue
|
||||
seen.add((param_name, param.location.value))
|
||||
try:
|
||||
raw_value = param.get_raw_value(request)
|
||||
raw_value = self._get_parameter_value(param, request)
|
||||
except MissingRequiredParameter as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
|
@ -109,8 +109,15 @@ class RequestValidator(object):
|
|||
casted = param.schema.default
|
||||
else:
|
||||
try:
|
||||
casted = param.cast(raw_value)
|
||||
except OpenAPIParameterError as exc:
|
||||
deserialised = self._deserialise_parameter(
|
||||
param, raw_value)
|
||||
except DeserializeError as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
|
||||
try:
|
||||
casted = self._cast(param, deserialised)
|
||||
except CastError as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
|
||||
|
@ -125,33 +132,82 @@ class RequestValidator(object):
|
|||
return RequestParameters(**locations), errors
|
||||
|
||||
def _get_body(self, request, operation):
|
||||
errors = []
|
||||
|
||||
if operation.request_body is None:
|
||||
return None, errors
|
||||
return None, []
|
||||
|
||||
body = None
|
||||
try:
|
||||
media_type = operation.request_body[request.mimetype]
|
||||
except InvalidContentType as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
raw_body = operation.request_body.get_value(request)
|
||||
except MissingRequestBody as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
casted = media_type.cast(raw_body)
|
||||
except InvalidMediaTypeValue as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
body = self._unmarshal(media_type, casted)
|
||||
except (ValidateError, UnmarshalError) as exc:
|
||||
errors.append(exc)
|
||||
return None, [exc, ]
|
||||
|
||||
return body, errors
|
||||
try:
|
||||
raw_body = self._get_body_value(operation.request_body, request)
|
||||
except MissingRequestBody as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
deserialised = self._deserialise_media_type(media_type, raw_body)
|
||||
except DeserializeError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
casted = self._cast(media_type, deserialised)
|
||||
except CastError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
body = self._unmarshal(media_type, casted)
|
||||
except (ValidateError, UnmarshalError) as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
return body, []
|
||||
|
||||
def _get_parameter_value(self, param, request):
|
||||
location = request.parameters[param.location.value]
|
||||
|
||||
if param.name not in location:
|
||||
if param.required:
|
||||
raise MissingRequiredParameter(param.name)
|
||||
|
||||
raise MissingParameter(param.name)
|
||||
|
||||
if param.aslist and param.explode:
|
||||
if hasattr(location, 'getall'):
|
||||
return location.getall(param.name)
|
||||
return location.getlist(param.name)
|
||||
|
||||
return location[param.name]
|
||||
|
||||
def _get_body_value(self, request_body, request):
|
||||
if not request.body and request_body.required:
|
||||
raise MissingRequestBody(request)
|
||||
return request.body
|
||||
|
||||
def _deserialise_media_type(self, media_type, value):
|
||||
from openapi_core.deserializing.media_types.factories import (
|
||||
MediaTypeDeserializersFactory,
|
||||
)
|
||||
deserializers_factory = MediaTypeDeserializersFactory()
|
||||
deserializer = deserializers_factory.create(media_type)
|
||||
return deserializer(value)
|
||||
|
||||
def _deserialise_parameter(self, param, value):
|
||||
from openapi_core.deserializing.parameters.factories import (
|
||||
ParameterDeserializersFactory,
|
||||
)
|
||||
deserializers_factory = ParameterDeserializersFactory()
|
||||
deserializer = deserializers_factory.create(param)
|
||||
return deserializer(value)
|
||||
|
||||
def _cast(self, param_or_media_type, value):
|
||||
# return param_or_media_type.cast(value)
|
||||
if not param_or_media_type.schema:
|
||||
return value
|
||||
|
||||
from openapi_core.casting.schemas.factories import SchemaCastersFactory
|
||||
casters_factory = SchemaCastersFactory()
|
||||
caster = casters_factory.create(param_or_media_type.schema)
|
||||
return caster(value)
|
||||
|
||||
def _unmarshal(self, param_or_media_type, value):
|
||||
if not param_or_media_type.schema:
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""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.operations.exceptions import InvalidOperation
|
||||
from openapi_core.schema.media_types.exceptions import (
|
||||
InvalidMediaTypeValue, InvalidContentType,
|
||||
)
|
||||
from openapi_core.schema.media_types.exceptions import InvalidContentType
|
||||
from openapi_core.schema.responses.exceptions import (
|
||||
InvalidResponse, MissingResponseContent,
|
||||
)
|
||||
|
@ -64,33 +64,35 @@ class ResponseValidator(object):
|
|||
return ResponseValidationResult(data_errors, data, None)
|
||||
|
||||
def _get_data(self, response, operation_response):
|
||||
errors = []
|
||||
|
||||
if not operation_response.content:
|
||||
return None, errors
|
||||
return None, []
|
||||
|
||||
data = None
|
||||
try:
|
||||
media_type = operation_response[response.mimetype]
|
||||
except InvalidContentType as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
raw_data = operation_response.get_value(response)
|
||||
except MissingResponseContent as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
casted = media_type.cast(raw_data)
|
||||
except InvalidMediaTypeValue as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
data = self._unmarshal(media_type, casted)
|
||||
except (ValidateError, UnmarshalError) as exc:
|
||||
errors.append(exc)
|
||||
return None, [exc, ]
|
||||
|
||||
return data, errors
|
||||
try:
|
||||
raw_data = self._get_data_value(response)
|
||||
except MissingResponseContent as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
deserialised = self._deserialise_media_type(media_type, raw_data)
|
||||
except DeserializeError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
casted = self._cast(media_type, deserialised)
|
||||
except CastError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
data = self._unmarshal(media_type, casted)
|
||||
except (ValidateError, UnmarshalError) as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
return data, []
|
||||
|
||||
def _get_headers(self, response, operation_response):
|
||||
errors = []
|
||||
|
@ -100,6 +102,30 @@ class ResponseValidator(object):
|
|||
|
||||
return headers, errors
|
||||
|
||||
def _get_data_value(self, response):
|
||||
if not response.data:
|
||||
raise MissingResponseContent(response)
|
||||
|
||||
return response.data
|
||||
|
||||
def _deserialise_media_type(self, media_type, value):
|
||||
from openapi_core.deserializing.media_types.factories import (
|
||||
MediaTypeDeserializersFactory,
|
||||
)
|
||||
deserializers_factory = MediaTypeDeserializersFactory()
|
||||
deserializer = deserializers_factory.create(media_type)
|
||||
return deserializer(value)
|
||||
|
||||
def _cast(self, param_or_media_type, value):
|
||||
# return param_or_media_type.cast(value)
|
||||
if not param_or_media_type.schema:
|
||||
return value
|
||||
|
||||
from openapi_core.casting.schemas.factories import SchemaCastersFactory
|
||||
casters_factory = SchemaCastersFactory()
|
||||
caster = casters_factory.create(param_or_media_type.schema)
|
||||
return caster(value)
|
||||
|
||||
def _unmarshal(self, param_or_media_type, value):
|
||||
if not param_or_media_type.schema:
|
||||
return value
|
||||
|
|
|
@ -100,14 +100,12 @@ class TestFlaskOpenAPIDecorator(object):
|
|||
'errors': [
|
||||
{
|
||||
'class': (
|
||||
"<class 'openapi_core.schema.parameters."
|
||||
"exceptions.InvalidParameterValue'>"
|
||||
"<class 'openapi_core.casting.schemas.exceptions."
|
||||
"CastError'>"
|
||||
),
|
||||
'status': 400,
|
||||
'title': (
|
||||
'Invalid parameter value for `id`: '
|
||||
'Failed to cast value invalidparameter to type '
|
||||
'SchemaType.INTEGER'
|
||||
"Failed to cast value invalidparameter to type integer"
|
||||
)
|
||||
}
|
||||
]
|
||||
|
|
|
@ -87,14 +87,12 @@ class TestFlaskOpenAPIView(object):
|
|||
'errors': [
|
||||
{
|
||||
'class': (
|
||||
"<class 'openapi_core.schema.parameters."
|
||||
"exceptions.InvalidParameterValue'>"
|
||||
"<class 'openapi_core.casting.schemas.exceptions."
|
||||
"CastError'>"
|
||||
),
|
||||
'status': 400,
|
||||
'title': (
|
||||
'Invalid parameter value for `id`: '
|
||||
'Failed to cast value invalidparameter to type '
|
||||
'SchemaType.INTEGER'
|
||||
"Failed to cast value invalidparameter to type integer"
|
||||
)
|
||||
}
|
||||
]
|
||||
|
|
|
@ -5,10 +5,15 @@ from base64 import b64encode
|
|||
from uuid import UUID
|
||||
from six import text_type
|
||||
|
||||
from openapi_core.casting.schemas.exceptions import CastError
|
||||
from openapi_core.deserializing.exceptions import DeserializeError
|
||||
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, InvalidParameterValue, EmptyParameterValue,
|
||||
MissingRequiredParameter,
|
||||
)
|
||||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
from openapi_core.schema.servers.exceptions import InvalidServer
|
||||
|
@ -272,7 +277,7 @@ class TestPetstore(object):
|
|||
path_pattern=path_pattern, args=query_params,
|
||||
)
|
||||
|
||||
with pytest.raises(InvalidParameterValue):
|
||||
with pytest.raises(DeserializeError):
|
||||
validate_parameters(spec, request)
|
||||
|
||||
body = validate_body(spec, request)
|
||||
|
@ -291,7 +296,7 @@ class TestPetstore(object):
|
|||
path_pattern=path_pattern, args=query_params,
|
||||
)
|
||||
|
||||
with pytest.raises(InvalidParameterValue):
|
||||
with pytest.raises(CastError):
|
||||
validate_parameters(spec, request)
|
||||
|
||||
body = validate_body(spec, request)
|
||||
|
|
|
@ -3,13 +3,14 @@ import json
|
|||
import pytest
|
||||
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, InvalidMediaTypeValue,
|
||||
InvalidContentType,
|
||||
)
|
||||
from openapi_core.extensions.models.models import BaseModel
|
||||
from openapi_core.schema.operations.exceptions import InvalidOperation
|
||||
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
|
||||
from openapi_core.schema.parameters.exceptions import InvalidParameterValue
|
||||
from openapi_core.schema.paths.exceptions import InvalidPath
|
||||
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
|
||||
from openapi_core.schema.responses.exceptions import (
|
||||
|
@ -317,7 +318,7 @@ class TestPathItemParamsValidator(object):
|
|||
result = validator.validate(request)
|
||||
|
||||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == InvalidParameterValue
|
||||
assert type(result.errors[0]) == CastError
|
||||
assert result.body is None
|
||||
assert result.parameters == RequestParameters()
|
||||
|
||||
|
@ -455,7 +456,7 @@ class TestResponseValidator(object):
|
|||
result = validator.validate(request, response)
|
||||
|
||||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == InvalidMediaTypeValue
|
||||
assert type(result.errors[0]) == DeserializeError
|
||||
assert result.data is None
|
||||
assert result.headers == {}
|
||||
|
||||
|
|
71
tests/unit/deserializing/test_deserialize.py
Normal file
71
tests/unit/deserializing/test_deserialize.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.deserializing.exceptions import DeserializeError
|
||||
from openapi_core.deserializing.media_types.factories import (
|
||||
MediaTypeDeserializersFactory,
|
||||
)
|
||||
from openapi_core.deserializing.parameters.factories import (
|
||||
ParameterDeserializersFactory,
|
||||
)
|
||||
from openapi_core.deserializing.parameters.exceptions import (
|
||||
EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.media_types.models import MediaType
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
|
||||
|
||||
class TestParameterDeserialise(object):
|
||||
|
||||
@pytest.fixture
|
||||
def deserializer_factory(self):
|
||||
def create_deserializer(param):
|
||||
return ParameterDeserializersFactory().create(param)
|
||||
return create_deserializer
|
||||
|
||||
def test_deprecated(self, deserializer_factory):
|
||||
param = Parameter('param', 'query', deprecated=True)
|
||||
value = 'test'
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
result = deserializer_factory(param)(value)
|
||||
|
||||
assert result == value
|
||||
|
||||
def test_query_empty(self, deserializer_factory):
|
||||
param = Parameter('param', 'query')
|
||||
value = ''
|
||||
|
||||
with pytest.raises(EmptyParameterValue):
|
||||
deserializer_factory(param)(value)
|
||||
|
||||
def test_query_valid(self, deserializer_factory):
|
||||
param = Parameter('param', 'query')
|
||||
value = 'test'
|
||||
|
||||
result = deserializer_factory(param)(value)
|
||||
|
||||
assert result == value
|
||||
|
||||
|
||||
class TestMediaTypeDeserialise(object):
|
||||
|
||||
@pytest.fixture
|
||||
def deserializer_factory(self):
|
||||
def create_deserializer(media_type):
|
||||
return MediaTypeDeserializersFactory().create(media_type)
|
||||
return create_deserializer
|
||||
|
||||
def test_empty(self, deserializer_factory):
|
||||
media_type = MediaType('application/json')
|
||||
value = ''
|
||||
|
||||
with pytest.raises(DeserializeError):
|
||||
deserializer_factory(media_type)(value)
|
||||
|
||||
def test_no_schema_deserialised(self, deserializer_factory):
|
||||
media_type = MediaType('application/json')
|
||||
value = "{}"
|
||||
|
||||
result = deserializer_factory(media_type)(value)
|
||||
|
||||
assert result == {}
|
|
@ -1,22 +0,0 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
|
||||
from openapi_core.schema.media_types.models import MediaType
|
||||
|
||||
|
||||
class TestMediaTypeCast(object):
|
||||
|
||||
def test_empty(self):
|
||||
media_type = MediaType('application/json')
|
||||
value = ''
|
||||
|
||||
with pytest.raises(InvalidMediaTypeValue):
|
||||
media_type.cast(value)
|
||||
|
||||
def test_no_schema_deserialised(self):
|
||||
media_type = MediaType('application/json')
|
||||
value = "{}"
|
||||
|
||||
result = media_type.cast(value)
|
||||
|
||||
assert result == {}
|
|
@ -1,8 +1,3 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.schema.parameters.exceptions import (
|
||||
EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.parameters.enums import ParameterStyle
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
|
||||
|
@ -36,30 +31,3 @@ class TestParameterInit(object):
|
|||
assert param.allow_empty_value is False
|
||||
assert param.style == ParameterStyle.FORM
|
||||
assert param.explode is True
|
||||
|
||||
|
||||
class TestParameterCast(object):
|
||||
|
||||
def test_deprecated(self):
|
||||
param = Parameter('param', 'query', deprecated=True)
|
||||
value = 'test'
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
result = param.cast(value)
|
||||
|
||||
assert result == value
|
||||
|
||||
def test_query_empty(self):
|
||||
param = Parameter('param', 'query')
|
||||
value = ''
|
||||
|
||||
with pytest.raises(EmptyParameterValue):
|
||||
param.cast(value)
|
||||
|
||||
def test_query_valid(self):
|
||||
param = Parameter('param', 'query')
|
||||
value = 'test'
|
||||
|
||||
result = param.cast(value)
|
||||
|
||||
assert result == value
|
||||
|
|
Loading…
Reference in a new issue