mirror of
https://github.com/correl/openapi-core.git
synced 2024-12-29 11:09:25 +00:00
Support unmarshaling string with format keyword
This commit is contained in:
parent
2487d0e5c1
commit
d4f65a2ed2
3 changed files with 68 additions and 3 deletions
|
@ -6,12 +6,12 @@ import warnings
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
|
||||||
from openapi_core.extensions.models.factories import ModelFactory
|
from openapi_core.extensions.models.factories import ModelFactory
|
||||||
from openapi_core.schema.schemas.enums import SchemaType
|
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
|
||||||
from openapi_core.schema.schemas.exceptions import (
|
from openapi_core.schema.schemas.exceptions import (
|
||||||
InvalidSchemaValue, UndefinedSchemaProperty, MissingSchemaProperty,
|
InvalidSchemaValue, UndefinedSchemaProperty, MissingSchemaProperty,
|
||||||
OpenAPISchemaError, NoOneOfSchema, MultipleOneOfSchema,
|
OpenAPISchemaError, NoOneOfSchema, MultipleOneOfSchema,
|
||||||
)
|
)
|
||||||
from openapi_core.schema.schemas.util import forcebool
|
from openapi_core.schema.schemas.util import forcebool, format_date
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ class Schema(object):
|
||||||
SchemaType.BOOLEAN: forcebool,
|
SchemaType.BOOLEAN: forcebool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORMAT_CALLABLE_GETTER = defaultdict(lambda: lambda x: x, {
|
||||||
|
SchemaFormat.DATE.value: format_date,
|
||||||
|
})
|
||||||
|
|
||||||
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,
|
||||||
|
@ -92,6 +96,7 @@ class Schema(object):
|
||||||
def get_cast_mapping(self):
|
def get_cast_mapping(self):
|
||||||
mapping = self.DEFAULT_CAST_CALLABLE_GETTER.copy()
|
mapping = self.DEFAULT_CAST_CALLABLE_GETTER.copy()
|
||||||
mapping.update({
|
mapping.update({
|
||||||
|
SchemaType.STRING: self._unmarshal_string,
|
||||||
SchemaType.ARRAY: self._unmarshal_collection,
|
SchemaType.ARRAY: self._unmarshal_collection,
|
||||||
SchemaType.OBJECT: self._unmarshal_object,
|
SchemaType.OBJECT: self._unmarshal_object,
|
||||||
})
|
})
|
||||||
|
@ -110,7 +115,7 @@ class Schema(object):
|
||||||
|
|
||||||
cast_mapping = self.get_cast_mapping()
|
cast_mapping = self.get_cast_mapping()
|
||||||
|
|
||||||
if self.type in cast_mapping and value == '':
|
if self.type is not SchemaType.STRING and value == '':
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cast_callable = cast_mapping[self.type]
|
cast_callable = cast_mapping[self.type]
|
||||||
|
@ -139,6 +144,16 @@ class Schema(object):
|
||||||
|
|
||||||
return casted
|
return casted
|
||||||
|
|
||||||
|
def _unmarshal_string(self, value):
|
||||||
|
formatter = self.FORMAT_CALLABLE_GETTER[self.format]
|
||||||
|
try:
|
||||||
|
return formatter(value)
|
||||||
|
except ValueError:
|
||||||
|
raise InvalidSchemaValue(
|
||||||
|
"Failed to format value of {0} to {1}".format(
|
||||||
|
value, self.format)
|
||||||
|
)
|
||||||
|
|
||||||
def _unmarshal_collection(self, value):
|
def _unmarshal_collection(self, value):
|
||||||
return list(map(self.items.unmarshal, value))
|
return list(map(self.items.unmarshal, value))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""OpenAPI core schemas util module"""
|
"""OpenAPI core schemas util module"""
|
||||||
|
import datetime
|
||||||
from distutils.util import strtobool
|
from distutils.util import strtobool
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
@ -13,3 +14,7 @@ def forcebool(val):
|
||||||
|
|
||||||
def dicthash(d):
|
def dicthash(d):
|
||||||
return hash(dumps(d, sort_keys=True))
|
return hash(dumps(d, sort_keys=True))
|
||||||
|
|
||||||
|
|
||||||
|
def format_date(value):
|
||||||
|
return datetime.datetime.strptime(value, '%Y-%m-%d').date()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -64,6 +66,49 @@ class TestSchemaUnmarshal(object):
|
||||||
|
|
||||||
assert result == default_value
|
assert result == default_value
|
||||||
|
|
||||||
|
def test_string_format_date(self):
|
||||||
|
schema = Schema('string', schema_format='date')
|
||||||
|
value = '2018-01-02'
|
||||||
|
|
||||||
|
result = schema.unmarshal(value)
|
||||||
|
|
||||||
|
assert result == datetime.date(2018, 1, 2)
|
||||||
|
|
||||||
|
def test_string_format_custom(self):
|
||||||
|
custom_format = 'custom'
|
||||||
|
schema = Schema('string', schema_format=custom_format)
|
||||||
|
value = 'x'
|
||||||
|
|
||||||
|
with mock.patch.dict(
|
||||||
|
Schema.FORMAT_CALLABLE_GETTER,
|
||||||
|
{custom_format: lambda x: x + '-custom'},
|
||||||
|
):
|
||||||
|
result = schema.unmarshal(value)
|
||||||
|
|
||||||
|
assert result == 'x-custom'
|
||||||
|
|
||||||
|
def test_string_format_unknown(self):
|
||||||
|
unknown_format = 'unknown'
|
||||||
|
schema = Schema('string', schema_format=unknown_format)
|
||||||
|
value = 'x'
|
||||||
|
|
||||||
|
result = schema.unmarshal(value)
|
||||||
|
|
||||||
|
assert result == 'x'
|
||||||
|
|
||||||
|
def test_string_format_invalid_value(self):
|
||||||
|
custom_format = 'custom'
|
||||||
|
schema = Schema('string', schema_format=custom_format)
|
||||||
|
value = 'x'
|
||||||
|
|
||||||
|
with mock.patch.dict(
|
||||||
|
Schema.FORMAT_CALLABLE_GETTER,
|
||||||
|
{custom_format: mock.Mock(side_effect=ValueError())},
|
||||||
|
), pytest.raises(
|
||||||
|
InvalidSchemaValue, message='Failed to format value'
|
||||||
|
):
|
||||||
|
schema.unmarshal(value)
|
||||||
|
|
||||||
def test_integer_valid(self):
|
def test_integer_valid(self):
|
||||||
schema = Schema('integer')
|
schema = Schema('integer')
|
||||||
value = '123'
|
value = '123'
|
||||||
|
|
Loading…
Reference in a new issue