mirror of
https://github.com/correl/openapi-core.git
synced 2024-12-28 03:00:11 +00:00
Merge pull request #81 from p1c2u/feature/separate-casting-and-validation
Separate schema casting and validation
This commit is contained in:
commit
17855ae145
6 changed files with 170 additions and 3 deletions
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
26
openapi_core/schema/schemas/validators.py
Normal file
26
openapi_core/schema/schemas/validators.py
Normal 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
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue