Skip to content

Separate schema casting and validation #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 17, 2018
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
7 changes: 6 additions & 1 deletion openapi_core/schema/media_types/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def unmarshal(self, value):
raise InvalidMediaTypeValue(str(exc))

try:
return self.schema.unmarshal(deserialized)
unmarshalled = self.schema.unmarshal(deserialized)
except InvalidSchemaValue as exc:
raise InvalidMediaTypeValue(str(exc))

try:
return self.schema.validate(unmarshalled)
except InvalidSchemaValue as exc:
raise InvalidMediaTypeValue(str(exc))
7 changes: 6 additions & 1 deletion openapi_core/schema/parameters/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ def unmarshal(self, value):
raise InvalidParameterValue(str(exc))

try:
return self.schema.unmarshal(deserialized)
unmarshalled = self.schema.unmarshal(deserialized)
except InvalidSchemaValue as exc:
raise InvalidParameterValue(str(exc))

try:
return self.schema.validate(unmarshalled)
except InvalidSchemaValue as exc:
raise InvalidParameterValue(str(exc))
3 changes: 3 additions & 0 deletions openapi_core/schema/responses/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ def __init__(
self.links = links and dict(links) or {}

def __getitem__(self, mimetype):
return self.get_content_type(mimetype)

def get_content_type(self, mimetype):
try:
return self.content[mimetype]
except MimeTypeNotFound:
Expand Down
31 changes: 30 additions & 1 deletion openapi_core/schema/schemas/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from collections import defaultdict
import warnings

from six import iteritems
from six import iteritems, integer_types, binary_type, text_type

from openapi_core.extensions.models.factories import ModelFactory
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
Expand All @@ -12,6 +12,9 @@
OpenAPISchemaError, NoOneOfSchema, MultipleOneOfSchema,
)
from openapi_core.schema.schemas.util import forcebool, format_date
from openapi_core.schema.schemas.validators import (
TypeValidator, AttributeValidator,
)

log = logging.getLogger(__name__)

Expand All @@ -29,6 +32,16 @@ class Schema(object):
SchemaFormat.DATE.value: format_date,
})

VALIDATOR_CALLABLE_GETTER = {
None: lambda x: x,
SchemaType.BOOLEAN: TypeValidator(bool),
SchemaType.INTEGER: TypeValidator(integer_types, exclude=bool),
SchemaType.NUMBER: TypeValidator(integer_types, float, exclude=bool),
SchemaType.STRING: TypeValidator(binary_type, text_type),
SchemaType.ARRAY: TypeValidator(list, tuple),
SchemaType.OBJECT: AttributeValidator('__class__'),
}

def __init__(
self, schema_type=None, model=None, properties=None, items=None,
schema_format=None, required=None, default=None, nullable=False,
Expand Down Expand Up @@ -222,3 +235,19 @@ def _unmarshal_properties(self, value, one_of_schema=None):
prop_value = prop.default
properties[prop_name] = prop.unmarshal(prop_value)
return properties

def validate(self, value):
if value is None:
if not self.nullable:
raise InvalidSchemaValue("Null value for non-nullable schema")
return self.default

validator = self.VALIDATOR_CALLABLE_GETTER[self.type]

if not validator(value):
raise InvalidSchemaValue(
"Value of {0} not valid type of {1}".format(
value, self.type.value)
)

return value
26 changes: 26 additions & 0 deletions openapi_core/schema/schemas/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class TypeValidator(object):

def __init__(self, *types, **options):
self.types = types
self.exclude = options.get('exclude')

def __call__(self, value):
if self.exclude is not None and isinstance(value, self.exclude):
return False

if not isinstance(value, self.types):
return False

return True


class AttributeValidator(object):

def __init__(self, attribute):
self.attribute = attribute

def __call__(self, value):
if not hasattr(value, self.attribute):
return False

return True
99 changes: 99 additions & 0 deletions tests/unit/schema/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,102 @@ def test_integer_invalid(self):

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


class TestSchemaValidate(object):

@pytest.mark.parametrize('schema_type', [
'boolean', 'array', 'integer', 'number', 'string',
])
def test_null(self, schema_type):
schema = Schema(schema_type)
value = None

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

@pytest.mark.parametrize('schema_type', [
'boolean', 'array', 'integer', 'number', 'string',
])
def test_nullable(self, schema_type):
schema = Schema(schema_type, nullable=True)
value = None

result = schema.validate(value)

assert result is None

@pytest.mark.parametrize('value', [False, True])
def test_boolean(self, value):
schema = Schema('boolean')

result = schema.validate(value)

assert result == value

@pytest.mark.parametrize('value', [1, 3.14, 'true', [True, False]])
def test_boolean_invalid(self, value):
schema = Schema('boolean')

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

@pytest.mark.parametrize('value', [[1, 2], (3, 4)])
def test_array(self, value):
schema = Schema('array')

result = schema.validate(value)

assert result == value

@pytest.mark.parametrize('value', [False, 1, 3.14, 'true'])
def test_array_invalid(self, value):
schema = Schema('array')

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

@pytest.mark.parametrize('value', [1, 3])
def test_integer(self, value):
schema = Schema('integer')

result = schema.validate(value)

assert result == value

@pytest.mark.parametrize('value', [False, 3.14, 'true', [1, 2]])
def test_integer_invalid(self, value):
schema = Schema('integer')

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

@pytest.mark.parametrize('value', [1, 3.14])
def test_number(self, value):
schema = Schema('number')

result = schema.validate(value)

assert result == value

@pytest.mark.parametrize('value', [False, 'true', [1, 3]])
def test_number_invalid(self, value):
schema = Schema('number')

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

@pytest.mark.parametrize('value', ['true', b'true'])
def test_string(self, value):
schema = Schema('string')

result = schema.validate(value)

assert result == value

@pytest.mark.parametrize('value', [False, 1, 3.14, [1, 3]])
def test_string_invalid(self, value):
schema = Schema('string')

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