Merge pull request #81 from p1c2u/feature/separate-casting-and-validation

Separate schema casting and validation
This commit is contained in:
A 2018-08-17 18:54:35 +01:00 committed by GitHub
commit 17855ae145
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 170 additions and 3 deletions

View file

@ -42,6 +42,11 @@ class MediaType(object):
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))

View file

@ -112,6 +112,11 @@ class Parameter(object):
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))

View file

@ -17,6 +17,9 @@ class Response(object):
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:

View file

@ -3,7 +3,7 @@ import logging
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
@ -12,6 +12,9 @@ from openapi_core.schema.schemas.exceptions import (
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__)
@ -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,
@ -222,3 +235,19 @@ class Schema(object):
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

View file

@ -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

View file

@ -155,3 +155,102 @@ class TestSchemaUnmarshal(object):
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)