From 1ca11c9d19123d1e2df4b02af2fe1978c6e26c30 Mon Sep 17 00:00:00 2001 From: Artur Maciag Date: Tue, 17 Oct 2017 14:02:21 +0100 Subject: [PATCH] Schema nullable value --- openapi_core/schemas.py | 14 ++++- tests/integration/data/v3.0/petstore.yaml | 1 + tests/integration/test_petstore.py | 4 +- tests/unit/test_schemas.py | 72 ++++++++++++++++++++++- 4 files changed, 84 insertions(+), 7 deletions(-) diff --git a/openapi_core/schemas.py b/openapi_core/schemas.py index 8509c21..c5e81a7 100644 --- a/openapi_core/schemas.py +++ b/openapi_core/schemas.py @@ -26,7 +26,7 @@ class Schema(object): def __init__( self, schema_type, model=None, properties=None, items=None, - spec_format=None, required=False, default=None): + spec_format=None, required=False, default=None, nullable=False): self.type = schema_type self.model = model self.properties = properties and dict(properties) or {} @@ -34,6 +34,7 @@ class Schema(object): self.format = spec_format self.required = required self.default = default + self.nullable = nullable def __getitem__(self, name): return self.properties[name] @@ -50,7 +51,11 @@ class Schema(object): def cast(self, value): """Cast value to schema type""" if value is None: - return None + if not self.nullable: + raise InvalidValueType( + "Failed to cast value of %s to %s", value, self.type, + ) + return self.default cast_mapping = self.get_cast_mapping() @@ -98,6 +103,8 @@ class Schema(object): if prop_name in self.required: raise MissingPropertyError( "Missing schema property {0}".format(prop_name)) + if not prop.nullable and not prop.default: + continue prop_value = prop.default properties[prop_name] = prop.unmarshal(prop_value) return ModelFactory().create(properties, name=self.model) @@ -130,6 +137,7 @@ class SchemaFactory(object): required = schema_deref.get('required', False) properties_spec = schema_deref.get('properties', None) items_spec = schema_deref.get('items', None) + nullable = schema_deref.get('nullable', False) properties = None if properties_spec: @@ -141,7 +149,7 @@ class SchemaFactory(object): return Schema( schema_type, model=model, properties=properties, items=items, - required=required, + required=required, nullable=nullable, ) @property diff --git a/tests/integration/data/v3.0/petstore.yaml b/tests/integration/data/v3.0/petstore.yaml index a11f4a9..29f7204 100644 --- a/tests/integration/data/v3.0/petstore.yaml +++ b/tests/integration/data/v3.0/petstore.yaml @@ -27,6 +27,7 @@ paths: schema: type: integer format: int32 + nullable: true - name: ids in: query description: Filter pets with Ids diff --git a/tests/integration/test_petstore.py b/tests/integration/test_petstore.py index 9ad7ea0..b459e44 100644 --- a/tests/integration/test_petstore.py +++ b/tests/integration/test_petstore.py @@ -323,8 +323,8 @@ class TestPetstore(object): pet_model = schemas['PetCreate']['x-model'] assert body.__class__.__name__ == pet_model assert body.name == pet_name - assert body.tag is None - assert body.address is None + assert not hasattr(body, 'tag') + assert not hasattr(body, 'address') def test_get_pets_wrong_body_type(self, spec): host_url = 'http://petstore.swagger.io/v1' diff --git a/tests/unit/test_schemas.py b/tests/unit/test_schemas.py index ed47996..6e9804b 100644 --- a/tests/unit/test_schemas.py +++ b/tests/unit/test_schemas.py @@ -1,10 +1,11 @@ import mock import pytest +from openapi_core.exceptions import InvalidValueType from openapi_core.schemas import Schema -class TestSchemas(object): +class TestSchemaIteritems(object): @pytest.fixture def schema(self): @@ -15,6 +16,73 @@ class TestSchemas(object): return Schema('object', properties=properties) @property - def test_iteritems(self, schema): + def test_valid(self, schema): for name in schema.properties.keys(): assert schema[name] == schema.properties[name] + + +class TestSchemaUnmarshal(object): + + def test_string_valid(self): + schema = Schema('string') + value = 'test' + + result = schema.unmarshal(value) + + assert result == value + + def test_string_none(self): + schema = Schema('string') + value = None + + with pytest.raises(InvalidValueType): + schema.unmarshal(value) + + def test_string_default(self): + default_value = 'default' + schema = Schema('string', default=default_value) + value = None + + with pytest.raises(InvalidValueType): + schema.unmarshal(value) + + def test_string_default_nullable(self): + default_value = 'default' + schema = Schema('string', default=default_value, nullable=True) + value = None + + result = schema.unmarshal(value) + + assert result == default_value + + def test_integer_valid(self): + schema = Schema('integer') + value = '123' + + result = schema.unmarshal(value) + + assert result == int(value) + + def test_integer_default(self): + default_value = '123' + schema = Schema('integer', default=default_value) + value = None + + with pytest.raises(InvalidValueType): + schema.unmarshal(value) + + def test_integer_default_nullable(self): + default_value = '123' + schema = Schema('integer', default=default_value, nullable=True) + value = None + + result = schema.unmarshal(value) + + assert result == default_value + + def test_integer_invalid(self): + schema = Schema('integer') + value = 'abc' + + with pytest.raises(InvalidValueType): + schema.unmarshal(value)