Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions openapi_core/casting/schemas/casters.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,55 @@
from openapi_core.casting.schemas.exceptions import CastError


class PrimitiveCaster:
class BaseSchemaCaster:

def __init__(self, schema, caster_callable):
def __init__(self, schema):
self.schema = schema
self.caster_callable = caster_callable

def __call__(self, value):
if value is None:
return value

return self.cast(value)

def cast(self, value):
raise NotImplementedError


class CallableSchemaCaster(BaseSchemaCaster):

def __init__(self, schema, caster_callable):
super().__init__(schema)
self.caster_callable = caster_callable

def cast(self, value):
try:
return self.caster_callable(value)
except (ValueError, TypeError):
raise CastError(value, self.schema['type'])


class DummyCaster:
class DummyCaster(BaseSchemaCaster):

def __call__(self, value):
def cast(self, value):
return value


class ArrayCaster:
class ComplexCaster(BaseSchemaCaster):

def __init__(self, schema, casters_factory):
self.schema = schema
super().__init__(schema)
self.casters_factory = casters_factory


class ArrayCaster(ComplexCaster):

@property
def items_caster(self):
return self.casters_factory.create(self.schema / 'items')

def __call__(self, value):
if value is None:
return value
return list(map(self.items_caster, value))
def cast(self, value):
try:
return list(map(self.items_caster, value))
except (ValueError, TypeError):
raise CastError(value, self.schema['type'])
4 changes: 2 additions & 2 deletions openapi_core/casting/schemas/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ class CastError(OpenAPIError):
type: str

def __str__(self):
return "Failed to cast value {value} to type {type}".format(
value=self.value, type=self.type)
return "Failed to cast value to {type} type: {value}".format(
type=self.type, value=self.value)
15 changes: 8 additions & 7 deletions openapi_core/casting/schemas/factories.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from openapi_core.casting.schemas.casters import (
PrimitiveCaster, DummyCaster, ArrayCaster
ArrayCaster, CallableSchemaCaster, DummyCaster,
)
from openapi_core.casting.schemas.util import forcebool

Expand All @@ -20,11 +20,12 @@ class SchemaCastersFactory:

def create(self, schema):
schema_type = schema.getkey('type', 'any')

if schema_type in self.DUMMY_CASTERS:
return DummyCaster()
elif schema_type in self.PRIMITIVE_CASTERS:
return DummyCaster(schema)

if 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, self)
return CallableSchemaCaster(schema, caster_callable)

return ArrayCaster(schema, self)
3 changes: 2 additions & 1 deletion tests/integration/contrib/falcon/test_falcon_middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ def test_endpoint_error(self, client):
),
'status': 400,
'title': (
"Failed to cast value invalidparameter to type integer"
"Failed to cast value to integer type: "
"invalidparameter"
)
}
]
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/contrib/flask/test_flask_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ def test_endpoint_error(self, client):
),
'status': 400,
'title': (
"Failed to cast value invalidparameter to type integer"
"Failed to cast value to integer type: "
"invalidparameter"
)
}
]
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/contrib/flask/test_flask_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ def test_endpoint_error(self, client):
),
'status': 400,
'title': (
"Failed to cast value invalidparameter to type integer"
"Failed to cast value to integer type: "
"invalidparameter"
)
}
]
Expand Down
40 changes: 40 additions & 0 deletions tests/unit/casting/test_schema_casters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import pytest

from openapi_core.casting.schemas.exceptions import CastError
from openapi_core.casting.schemas.factories import SchemaCastersFactory
from openapi_core.spec.paths import SpecPath


class TestSchemaCaster:

@pytest.fixture
def caster_factory(self):
def create_caster(schema):
return SchemaCastersFactory().create(schema)
return create_caster

def test_array_invalid_type(self, caster_factory):
spec = {
'type': 'array',
'items': {
'type': 'number',
},
}
schema = SpecPath.from_spec(spec)
value = ['test', 'test2']

with pytest.raises(CastError):
caster_factory(schema)(value)

def test_array_invalid_value(self, caster_factory):
spec = {
'type': 'array',
'items': {
'type': 'number',
},
}
schema = SpecPath.from_spec(spec)
value = 3.14

with pytest.raises(CastError):
caster_factory(schema)(value)