Skip to content

Commit 1ca11c9

Browse files
committed
Schema nullable value
1 parent 01e7a5b commit 1ca11c9

File tree

4 files changed

+84
-7
lines changed

4 files changed

+84
-7
lines changed

openapi_core/schemas.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ class Schema(object):
2626

2727
def __init__(
2828
self, schema_type, model=None, properties=None, items=None,
29-
spec_format=None, required=False, default=None):
29+
spec_format=None, required=False, default=None, nullable=False):
3030
self.type = schema_type
3131
self.model = model
3232
self.properties = properties and dict(properties) or {}
3333
self.items = items
3434
self.format = spec_format
3535
self.required = required
3636
self.default = default
37+
self.nullable = nullable
3738

3839
def __getitem__(self, name):
3940
return self.properties[name]
@@ -50,7 +51,11 @@ def get_cast_mapping(self):
5051
def cast(self, value):
5152
"""Cast value to schema type"""
5253
if value is None:
53-
return None
54+
if not self.nullable:
55+
raise InvalidValueType(
56+
"Failed to cast value of %s to %s", value, self.type,
57+
)
58+
return self.default
5459

5560
cast_mapping = self.get_cast_mapping()
5661

@@ -98,6 +103,8 @@ def _unmarshal_object(self, value):
98103
if prop_name in self.required:
99104
raise MissingPropertyError(
100105
"Missing schema property {0}".format(prop_name))
106+
if not prop.nullable and not prop.default:
107+
continue
101108
prop_value = prop.default
102109
properties[prop_name] = prop.unmarshal(prop_value)
103110
return ModelFactory().create(properties, name=self.model)
@@ -130,6 +137,7 @@ def create(self, schema_spec):
130137
required = schema_deref.get('required', False)
131138
properties_spec = schema_deref.get('properties', None)
132139
items_spec = schema_deref.get('items', None)
140+
nullable = schema_deref.get('nullable', False)
133141

134142
properties = None
135143
if properties_spec:
@@ -141,7 +149,7 @@ def create(self, schema_spec):
141149

142150
return Schema(
143151
schema_type, model=model, properties=properties, items=items,
144-
required=required,
152+
required=required, nullable=nullable,
145153
)
146154

147155
@property

tests/integration/data/v3.0/petstore.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ paths:
2727
schema:
2828
type: integer
2929
format: int32
30+
nullable: true
3031
- name: ids
3132
in: query
3233
description: Filter pets with Ids

tests/integration/test_petstore.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,8 @@ def test_post_pets_only_required_body(self, spec, spec_dict):
323323
pet_model = schemas['PetCreate']['x-model']
324324
assert body.__class__.__name__ == pet_model
325325
assert body.name == pet_name
326-
assert body.tag is None
327-
assert body.address is None
326+
assert not hasattr(body, 'tag')
327+
assert not hasattr(body, 'address')
328328

329329
def test_get_pets_wrong_body_type(self, spec):
330330
host_url = 'http://petstore.swagger.io/v1'

tests/unit/test_schemas.py

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import mock
22
import pytest
33

4+
from openapi_core.exceptions import InvalidValueType
45
from openapi_core.schemas import Schema
56

67

7-
class TestSchemas(object):
8+
class TestSchemaIteritems(object):
89

910
@pytest.fixture
1011
def schema(self):
@@ -15,6 +16,73 @@ def schema(self):
1516
return Schema('object', properties=properties)
1617

1718
@property
18-
def test_iteritems(self, schema):
19+
def test_valid(self, schema):
1920
for name in schema.properties.keys():
2021
assert schema[name] == schema.properties[name]
22+
23+
24+
class TestSchemaUnmarshal(object):
25+
26+
def test_string_valid(self):
27+
schema = Schema('string')
28+
value = 'test'
29+
30+
result = schema.unmarshal(value)
31+
32+
assert result == value
33+
34+
def test_string_none(self):
35+
schema = Schema('string')
36+
value = None
37+
38+
with pytest.raises(InvalidValueType):
39+
schema.unmarshal(value)
40+
41+
def test_string_default(self):
42+
default_value = 'default'
43+
schema = Schema('string', default=default_value)
44+
value = None
45+
46+
with pytest.raises(InvalidValueType):
47+
schema.unmarshal(value)
48+
49+
def test_string_default_nullable(self):
50+
default_value = 'default'
51+
schema = Schema('string', default=default_value, nullable=True)
52+
value = None
53+
54+
result = schema.unmarshal(value)
55+
56+
assert result == default_value
57+
58+
def test_integer_valid(self):
59+
schema = Schema('integer')
60+
value = '123'
61+
62+
result = schema.unmarshal(value)
63+
64+
assert result == int(value)
65+
66+
def test_integer_default(self):
67+
default_value = '123'
68+
schema = Schema('integer', default=default_value)
69+
value = None
70+
71+
with pytest.raises(InvalidValueType):
72+
schema.unmarshal(value)
73+
74+
def test_integer_default_nullable(self):
75+
default_value = '123'
76+
schema = Schema('integer', default=default_value, nullable=True)
77+
value = None
78+
79+
result = schema.unmarshal(value)
80+
81+
assert result == default_value
82+
83+
def test_integer_invalid(self):
84+
schema = Schema('integer')
85+
value = 'abc'
86+
87+
with pytest.raises(InvalidValueType):
88+
schema.unmarshal(value)

0 commit comments

Comments
 (0)