diff --git a/openapi_core/schema/schemas.py b/openapi_core/schema/schemas.py index 9cdc2e92..fd6631b1 100644 --- a/openapi_core/schema/schemas.py +++ b/openapi_core/schema/schemas.py @@ -4,7 +4,7 @@ from openapi_core.spec import Spec -def get_all_properties(schema: Spec) -> Dict[str, Any]: +def get_schema_properties(schema: Spec) -> Dict[str, Any]: properties = schema.get("properties", {}) properties_dict = dict(list(properties.items())) @@ -12,7 +12,7 @@ def get_all_properties(schema: Spec) -> Dict[str, Any]: return properties_dict for subschema in schema / "allOf": - subschema_props = get_all_properties(subschema) + subschema_props = get_schema_properties(subschema) properties_dict.update(subschema_props) return properties_dict diff --git a/openapi_core/unmarshalling/schemas/unmarshallers.py b/openapi_core/unmarshalling/schemas/unmarshallers.py index 941e28cb..b2dc9ef0 100644 --- a/openapi_core/unmarshalling/schemas/unmarshallers.py +++ b/openapi_core/unmarshalling/schemas/unmarshallers.py @@ -20,7 +20,7 @@ from openapi_schema_validator._types import is_string from openapi_core.extensions.models.factories import ModelPathFactory -from openapi_core.schema.schemas import get_all_properties +from openapi_core.schema.schemas import get_schema_properties from openapi_core.spec import Spec from openapi_core.unmarshalling.schemas.datatypes import FormattersDict from openapi_core.unmarshalling.schemas.enums import UnmarshalContext @@ -268,7 +268,9 @@ def _unmarshal_properties(self, value: Any) -> Any: else: properties.update(any_of_properties) - for prop_name, prop in get_all_properties(self.schema).items(): + # unmarshal schema properties + schema_properties = get_schema_properties(self.schema) + for prop_name, prop in schema_properties.items(): read_only = prop.getkey("readOnly", False) if self.context == UnmarshalContext.REQUEST and read_only: continue @@ -286,23 +288,24 @@ def _unmarshal_properties(self, value: Any) -> Any: prop_value ) + # unmarshal additional properties additional_properties = self.schema.getkey( "additionalProperties", True ) if additional_properties is not False: # free-form object if additional_properties is True: - additional_prop_schema = Spec.from_dict({}) + unmarshal_func = lambda x: x # defined schema else: additional_prop_schema = self.schema / "additionalProperties" - additional_prop_unmarshaler = self.unmarshallers_factory.create( - additional_prop_schema - ) + unmarshal_func = ( + self.unmarshallers_factory.create(additional_prop_schema) + ) for prop_name, prop_value in value.items(): - if prop_name in properties: + if prop_name in schema_properties: continue - properties[prop_name] = additional_prop_unmarshaler(prop_value) + properties[prop_name] = unmarshal_func(prop_value) return properties diff --git a/tests/integration/validation/test_petstore.py b/tests/integration/validation/test_petstore.py index c52feeb3..6925bbf9 100644 --- a/tests/integration/validation/test_petstore.py +++ b/tests/integration/validation/test_petstore.py @@ -752,6 +752,7 @@ def test_post_cats(self, spec, spec_dict): "ears": { "healthy": pet_healthy, }, + "extra": None, } data = json.dumps(data_json) headers = { @@ -799,6 +800,7 @@ def test_post_cats(self, spec, spec_dict): assert result.body.address.street == pet_street assert result.body.address.city == pet_city assert result.body.healthy == pet_healthy + assert result.body.extra is None def test_post_cats_boolean_string(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1"