Skip to content

Commit 6d45c4a

Browse files
committed
Fix unmarshaling for "anyOf"
1 parent a265b99 commit 6d45c4a

File tree

3 files changed

+51
-43
lines changed

3 files changed

+51
-43
lines changed

openapi_core/unmarshalling/schemas/unmarshallers.py

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
from openapi_schema_validator._format import oas30_format_checker
1111

1212
from openapi_core.extensions.models.factories import ModelFactory
13-
from openapi_core.schema.schemas import (
14-
get_all_properties, get_all_properties_names
15-
)
13+
from openapi_core.schema.schemas import get_all_properties
1614
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
1715
from openapi_core.unmarshalling.schemas.exceptions import (
1816
UnmarshalError, ValidateError, InvalidSchemaValue,
@@ -187,8 +185,7 @@ def _unmarshal_object(self, value):
187185
properties = None
188186
for one_of_schema in self.schema / 'oneOf':
189187
try:
190-
unmarshalled = self._unmarshal_properties(
191-
value, one_of_schema=one_of_schema)
188+
unmarshalled = self._unmarshal_properties(value)
192189
except (UnmarshalError, ValueError):
193190
pass
194191
else:
@@ -204,15 +201,12 @@ def _unmarshal_object(self, value):
204201
properties = None
205202
for any_of_schema in self.schema / 'anyOf':
206203
try:
207-
unmarshalled = self._unmarshal_properties(
208-
value, any_of_schema=any_of_schema)
204+
unmarshalled = self._unmarshal_properties(value)
209205
except (UnmarshalError, ValueError):
210206
pass
211207
else:
212-
if properties is not None:
213-
log.warning("multiple valid anyOf schemas found")
214-
continue
215208
properties = unmarshalled
209+
break
216210

217211
if properties is None:
218212
log.warning("valid anyOf schema not found")
@@ -226,37 +220,10 @@ def _unmarshal_object(self, value):
226220

227221
return properties
228222

229-
def _unmarshal_properties(
230-
self, value, one_of_schema=None, any_of_schema=None):
231-
all_props = get_all_properties(self.schema)
232-
all_props_names = get_all_properties_names(self.schema)
233-
234-
if one_of_schema is not None:
235-
all_props.update(get_all_properties(one_of_schema))
236-
all_props_names |= get_all_properties_names(one_of_schema)
237-
238-
if any_of_schema is not None:
239-
all_props.update(get_all_properties(any_of_schema))
240-
all_props_names |= get_all_properties_names(any_of_schema)
241-
242-
value_props_names = list(value.keys())
243-
extra_props = set(value_props_names) - set(all_props_names)
244-
223+
def _unmarshal_properties(self, value):
245224
properties = {}
246-
additional_properties = self.schema.getkey(
247-
'additionalProperties', True)
248-
if isinstance(additional_properties, dict):
249-
additional_prop_schema = self.schema / 'additionalProperties'
250-
for prop_name in extra_props:
251-
prop_value = value[prop_name]
252-
properties[prop_name] = self.unmarshallers_factory.create(
253-
additional_prop_schema)(prop_value)
254-
elif additional_properties is True:
255-
for prop_name in extra_props:
256-
prop_value = value[prop_name]
257-
properties[prop_name] = prop_value
258225

259-
for prop_name, prop in list(all_props.items()):
226+
for prop_name, prop in get_all_properties(self.schema).items():
260227
read_only = prop.getkey('readOnly', False)
261228
if self.context == UnmarshalContext.REQUEST and read_only:
262229
continue
@@ -273,6 +240,22 @@ def _unmarshal_properties(
273240
properties[prop_name] = self.unmarshallers_factory.create(
274241
prop)(prop_value)
275242

243+
additional_properties = self.schema.getkey(
244+
'additionalProperties', True)
245+
if isinstance(additional_properties, dict):
246+
additional_prop_schema = self.schema / 'additionalProperties'
247+
additional_prop_unmarshaler = self.unmarshallers_factory.create(
248+
additional_prop_schema)
249+
for prop_name, prop_value in value.items():
250+
if prop_name in properties:
251+
continue
252+
properties[prop_name] = additional_prop_unmarshaler(prop_value)
253+
elif additional_properties is True:
254+
for prop_name, prop_value in value.items():
255+
if prop_name in properties:
256+
continue
257+
properties[prop_name] = prop_value
258+
276259
return properties
277260

278261

tests/unit/unmarshalling/test_unmarshal.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,23 @@ def test_schema_any_one_of(self, unmarshaller_factory):
542542
schema = SpecPath.from_spec(spec)
543543
assert unmarshaller_factory(schema)(['hello']) == ['hello']
544544

545+
def test_schema_any_any_of(self, unmarshaller_factory):
546+
spec = {
547+
'anyOf': [
548+
{
549+
'type': 'string',
550+
},
551+
{
552+
'type': 'array',
553+
'items': {
554+
'type': 'string',
555+
}
556+
}
557+
],
558+
}
559+
schema = SpecPath.from_spec(spec)
560+
assert unmarshaller_factory(schema)(['hello']) == ['hello']
561+
545562
def test_schema_any_all_of(self, unmarshaller_factory):
546563
spec = {
547564
'allOf': [

tests/unit/unmarshalling/test_validate.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -761,17 +761,25 @@ def test_unambiguous_one_of(self, value, validator_factory):
761761

762762
assert result is None
763763

764-
@pytest.mark.parametrize('value', [Model(), ])
764+
@pytest.mark.parametrize('value', [{}, ])
765765
def test_object_multiple_any_of(self, value, validator_factory):
766-
any_of = [{'type': 'object'}, {'type': 'object'}]
766+
any_of = [
767+
{
768+
'type': 'object',
769+
},
770+
{
771+
'type': 'object',
772+
},
773+
]
767774
spec = {
768775
'type': 'object',
769776
'anyOf': any_of,
770777
}
771778
schema = SpecPath.from_spec(spec)
772779

773-
with pytest.raises(InvalidSchemaValue):
774-
validator_factory(schema).validate(value)
780+
result = validator_factory(schema).validate(value)
781+
782+
assert result is None
775783

776784
@pytest.mark.parametrize('value', [{}, ])
777785
def test_object_different_type_any_of(self, value, validator_factory):

0 commit comments

Comments
 (0)