Merge pull request #79 from grktsh/string-format

Support unmarshaling string with format keyword
This commit is contained in:
A 2018-08-17 11:25:59 +01:00 committed by GitHub
commit 06e80079fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 3 deletions

View file

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

View file

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

View file

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