mirror of
https://github.com/correl/openapi-core.git
synced 2024-11-25 19:19:55 +00:00
Separate schema casting and validation
This commit is contained in:
parent
7aaa5170a6
commit
f9a7472c92
6 changed files with 170 additions and 3 deletions
|
@ -42,6 +42,11 @@ class MediaType(object):
|
||||||
raise InvalidMediaTypeValue(str(exc))
|
raise InvalidMediaTypeValue(str(exc))
|
||||||
|
|
||||||
try:
|
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:
|
except InvalidSchemaValue as exc:
|
||||||
raise InvalidMediaTypeValue(str(exc))
|
raise InvalidMediaTypeValue(str(exc))
|
||||||
|
|
|
@ -112,6 +112,11 @@ class Parameter(object):
|
||||||
raise InvalidParameterValue(str(exc))
|
raise InvalidParameterValue(str(exc))
|
||||||
|
|
||||||
try:
|
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:
|
except InvalidSchemaValue as exc:
|
||||||
raise InvalidParameterValue(str(exc))
|
raise InvalidParameterValue(str(exc))
|
||||||
|
|
|
@ -17,6 +17,9 @@ class Response(object):
|
||||||
self.links = links and dict(links) or {}
|
self.links = links and dict(links) or {}
|
||||||
|
|
||||||
def __getitem__(self, mimetype):
|
def __getitem__(self, mimetype):
|
||||||
|
return self.get_content_type(mimetype)
|
||||||
|
|
||||||
|
def get_content_type(self, mimetype):
|
||||||
try:
|
try:
|
||||||
return self.content[mimetype]
|
return self.content[mimetype]
|
||||||
except MimeTypeNotFound:
|
except MimeTypeNotFound:
|
||||||
|
|
|
@ -3,7 +3,7 @@ import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import warnings
|
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.extensions.models.factories import ModelFactory
|
||||||
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
|
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
|
||||||
|
@ -12,6 +12,9 @@ from openapi_core.schema.schemas.exceptions import (
|
||||||
OpenAPISchemaError, NoOneOfSchema, MultipleOneOfSchema,
|
OpenAPISchemaError, NoOneOfSchema, MultipleOneOfSchema,
|
||||||
)
|
)
|
||||||
from openapi_core.schema.schemas.util import forcebool, format_date
|
from openapi_core.schema.schemas.util import forcebool, format_date
|
||||||
|
from openapi_core.schema.schemas.validators import (
|
||||||
|
TypeValidator, AttributeValidator,
|
||||||
|
)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -29,6 +32,16 @@ class Schema(object):
|
||||||
SchemaFormat.DATE.value: format_date,
|
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__(
|
def __init__(
|
||||||
self, schema_type=None, model=None, properties=None, items=None,
|
self, schema_type=None, model=None, properties=None, items=None,
|
||||||
schema_format=None, required=None, default=None, nullable=False,
|
schema_format=None, required=None, default=None, nullable=False,
|
||||||
|
@ -222,3 +235,19 @@ class Schema(object):
|
||||||
prop_value = prop.default
|
prop_value = prop.default
|
||||||
properties[prop_name] = prop.unmarshal(prop_value)
|
properties[prop_name] = prop.unmarshal(prop_value)
|
||||||
return properties
|
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):
|
with pytest.raises(InvalidSchemaValue):
|
||||||
schema.unmarshal(value)
|
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