Skip to content
4 changes: 4 additions & 0 deletions openapi_core/casting/schemas/casters.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ def items_caster(self):
def __call__(self, value):
if value in (None, NoValue):
return value

if not isinstance(value, list):
raise CastError(value, self.schema.type.value)

return list(map(self.items_caster, value))
2 changes: 1 addition & 1 deletion openapi_core/casting/schemas/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class SchemaCastersFactory(object):

DUMMY_CASTERS = [
SchemaType.STRING, SchemaType.OBJECT, SchemaType.ANY,
SchemaType.STRING, SchemaType.OBJECT, SchemaType.ANY
]
PRIMITIVE_CASTERS = {
SchemaType.INTEGER: int,
Expand Down
19 changes: 17 additions & 2 deletions openapi_core/unmarshalling/schemas/unmarshallers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
UnmarshalError, ValidateError, InvalidSchemaValue,
InvalidSchemaFormatValue,
)
from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.unmarshalling.schemas.formatters import Formatter
from openapi_core.unmarshalling.schemas.util import (
forcebool, format_date, format_byte, format_uuid,
Expand All @@ -44,9 +45,23 @@ def __call__(self, value=NoValue):
if value is None:
return

self.validate(value)
try:
casted = self._cast(self.schema, value)
except CastError as exc:
raise InvalidSchemaValue(
value, self.schema.type, schema_errors=exc)
self.validate(casted)

return self.unmarshal(casted)

def _cast(self, schema, value):
if not schema:
return value

return self.unmarshal(value)
from openapi_core.casting.schemas.factories import SchemaCastersFactory
casters_factory = SchemaCastersFactory()
caster = casters_factory.create(schema)
return caster(value)

def _formatter_validate(self, value):
result = self.formatter.validate(value)
Expand Down
18 changes: 3 additions & 15 deletions openapi_core/validation/request/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from itertools import chain
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 (
Expand Down Expand Up @@ -124,7 +123,7 @@ def _get_parameters(self, request, params):
except MissingParameter:
if not param.schema or not param.schema.has_default():
continue
casted = param.schema.default
deserialised = param.schema.default
else:
try:
deserialised = self._deserialise_parameter(
Expand All @@ -133,14 +132,8 @@ def _get_parameters(self, request, params):
errors.append(exc)
continue

try:
casted = self._cast(param, deserialised)
except CastError as exc:
errors.append(exc)
continue

try:
unmarshalled = self._unmarshal(param, casted)
unmarshalled = self._unmarshal(param, deserialised)
except (ValidateError, UnmarshalError) as exc:
errors.append(exc)
else:
Expand Down Expand Up @@ -169,12 +162,7 @@ def _get_body(self, request, operation):
return None, [exc, ]

try:
casted = self._cast(media_type, deserialised)
except CastError as exc:
return None, [exc, ]

try:
body = self._unmarshal(media_type, casted)
body = self._unmarshal(media_type, deserialised)
except (ValidateError, UnmarshalError) as exc:
return None, [exc, ]

Expand Down
8 changes: 1 addition & 7 deletions openapi_core/validation/response/validators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""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 (
Expand Down Expand Up @@ -85,12 +84,7 @@ def _get_data(self, response, operation_response):
return None, [exc, ]

try:
casted = self._cast(media_type, deserialised)
except CastError as exc:
return None, [exc, ]

try:
data = self._unmarshal(media_type, casted)
data = self._unmarshal(media_type, deserialised)
except (ValidateError, UnmarshalError) as exc:
return None, [exc, ]

Expand Down
10 changes: 0 additions & 10 deletions openapi_core/validation/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,6 @@ def _deserialise_media_type(self, media_type, value):
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, context):
if not param_or_media_type.schema:
return value
Expand Down
9 changes: 6 additions & 3 deletions tests/integration/contrib/falcon/test_falcon_middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,19 @@ def test_endpoint_error(self, client):
'errors': [
{
'class': (
"<class 'openapi_core.casting.schemas.exceptions."
"CastError'>"
"<class 'openapi_core.unmarshalling.schemas.exceptions"
".InvalidSchemaValue'>"
),
'status': 400,
'title': (
"Failed to cast value invalidparameter to type integer"
"Value invalidparameter not valid for schema of type "
"SchemaType.INTEGER: Failed to cast value "
"invalidparameter to type integer"
)
}
]
}
assert result.status_code == 400
assert result.json == expected_data

def test_valid(self, client):
Expand Down
9 changes: 6 additions & 3 deletions tests/integration/contrib/flask/test_flask_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,16 +151,19 @@ def test_endpoint_error(self, client):
'errors': [
{
'class': (
"<class 'openapi_core.casting.schemas.exceptions."
"CastError'>"
"<class 'openapi_core.unmarshalling.schemas.exceptions"
".InvalidSchemaValue'>"
),
'status': 400,
'title': (
"Failed to cast value invalidparameter to type integer"
"Value invalidparameter not valid for schema of type "
"SchemaType.INTEGER: Failed to cast value "
"invalidparameter to type integer"
)
}
]
}
assert result.status_code == 400
assert result.json == expected_data

def test_valid(self, client):
Expand Down
8 changes: 5 additions & 3 deletions tests/integration/contrib/flask/test_flask_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,14 @@ def test_endpoint_error(self, client):
'errors': [
{
'class': (
"<class 'openapi_core.casting.schemas.exceptions."
"CastError'>"
"<class 'openapi_core.unmarshalling.schemas.exceptions"
".InvalidSchemaValue'>"
),
'status': 400,
'title': (
"Failed to cast value invalidparameter to type integer"
"Value invalidparameter not valid for schema of type "
"SchemaType.INTEGER: Failed to cast value "
"invalidparameter to type integer"
)
}
]
Expand Down
3 changes: 1 addition & 2 deletions tests/integration/validation/test_petstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from isodate.tzinfo import UTC
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,
Expand Down Expand Up @@ -302,7 +301,7 @@ def test_get_pets_wrong_parameter_type(self, spec):
path_pattern=path_pattern, args=query_params,
)

with pytest.raises(CastError):
with pytest.raises(InvalidSchemaValue):
validate_parameters(spec, request)

body = validate_body(spec, request)
Expand Down
3 changes: 1 addition & 2 deletions tests/integration/validation/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
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,
Expand Down Expand Up @@ -345,7 +344,7 @@ def test_request_invalid_param(self, validator):
result = validator.validate(request)

assert len(result.errors) == 1
assert type(result.errors[0]) == CastError
assert type(result.errors[0]) == InvalidSchemaValue
assert result.body is None
assert result.parameters == RequestParameters()

Expand Down
53 changes: 37 additions & 16 deletions tests/unit/unmarshalling/test_unmarshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
from openapi_core.schema.schemas.types import NoValue
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
from openapi_core.unmarshalling.schemas.exceptions import (
InvalidSchemaFormatValue, InvalidSchemaValue, UnmarshalError,
FormatterNotFoundError,
InvalidSchemaFormatValue, InvalidSchemaValue, FormatterNotFoundError
)
from openapi_core.unmarshalling.schemas.factories import (
SchemaUnmarshallersFactory,
Expand Down Expand Up @@ -206,7 +205,7 @@ def test_string_format_date(self, unmarshaller_factory):

def test_string_format_datetime_invalid(self, unmarshaller_factory):
schema = Schema('string', schema_format='date-time')
value = '2018-01-02T00:00:00'
value = '2018-01-02A00:00:00'

with pytest.raises(InvalidSchemaValue):
unmarshaller_factory(schema)(value)
Expand Down Expand Up @@ -294,18 +293,20 @@ def test_integer_valid(self, unmarshaller_factory):

assert result == int(value)

def test_integer_string_invalid(self, unmarshaller_factory):
def test_integer_string_cast(self, unmarshaller_factory):
schema = Schema('integer')
value = '123'
expected = 123

with pytest.raises(InvalidSchemaValue):
unmarshaller_factory(schema)(value)
result = unmarshaller_factory(schema)(value)

def test_integer_enum_invalid(self, unmarshaller_factory):
schema = Schema('integer', enum=[1, 2, 3])
value = '123'
assert result == expected

with pytest.raises(UnmarshalError):
def test_integer_string_invalid(self, unmarshaller_factory):
schema = Schema('integer')
value = 'not-a-number'

with pytest.raises(InvalidSchemaValue):
unmarshaller_factory(schema)(value)

def test_integer_enum(self, unmarshaller_factory):
Expand All @@ -316,12 +317,13 @@ def test_integer_enum(self, unmarshaller_factory):

assert result == int(value)

def test_integer_enum_string_invalid(self, unmarshaller_factory):
def test_integer_enum_string_cast(self, unmarshaller_factory):
schema = Schema('integer', enum=[1, 2, 3])
value = '2'

with pytest.raises(UnmarshalError):
unmarshaller_factory(schema)(value)
result = unmarshaller_factory(schema)(value)

assert result == int(value)

def test_integer_default(self, unmarshaller_factory):
default_value = 123
Expand Down Expand Up @@ -399,9 +401,18 @@ def test_boolean_valid(self, unmarshaller_factory):

assert result == value

def test_boolean_string_invalid(self, unmarshaller_factory):
def test_boolean_string_cast(self, unmarshaller_factory):
schema = Schema('boolean')
value = 'True'
expected = True

result = unmarshaller_factory(schema)(value)

assert result == expected

def test_boolean_string_invalid(self, unmarshaller_factory):
schema = Schema('boolean')
value = 'positive'

with pytest.raises(InvalidSchemaValue):
unmarshaller_factory(schema)(value)
Expand All @@ -414,15 +425,25 @@ def test_number_valid(self, unmarshaller_factory):

assert result == value

def test_number_string_invalid(self, unmarshaller_factory):
def test_number_string_cast(self, unmarshaller_factory):
schema = Schema('number')
value = '1.23'
expected = 1.23

# with pytest.raises(InvalidSchemaValue):
result = unmarshaller_factory(schema)(value)

assert result == expected

def test_number_string_invalid(self, unmarshaller_factory):
schema = Schema('number')
value = 'not-a-number'

with pytest.raises(InvalidSchemaValue):
unmarshaller_factory(schema)(value)

def test_number_int(self, unmarshaller_factory):
schema = Schema('number')
schema = Schema('integer')
value = 1
result = unmarshaller_factory(schema)(value)

Expand Down