diff --git a/openapi_core/schema/schemas/factories.py b/openapi_core/schema/schemas/factories.py index d0fb7cc..b054898 100644 --- a/openapi_core/schema/schemas/factories.py +++ b/openapi_core/schema/schemas/factories.py @@ -28,6 +28,7 @@ class SchemaFactory(object): deprecated = schema_deref.get('deprecated', False) all_of_spec = schema_deref.get('allOf', None) one_of_spec = schema_deref.get('oneOf', None) + additional_properties_spec = schema_deref.get('additionalProperties') properties = None if properties_spec: @@ -45,11 +46,16 @@ class SchemaFactory(object): if items_spec: items = self._create_items(items_spec) + additional_properties = None + if additional_properties_spec: + additional_properties = self.create(additional_properties_spec) + return Schema( schema_type=schema_type, model=model, properties=properties, items=items, schema_format=schema_format, required=required, default=default, nullable=nullable, enum=enum, deprecated=deprecated, all_of=all_of, one_of=one_of, + additional_properties=additional_properties, ) @property diff --git a/openapi_core/schema/schemas/models.py b/openapi_core/schema/schemas/models.py index 624839c..4e37cde 100644 --- a/openapi_core/schema/schemas/models.py +++ b/openapi_core/schema/schemas/models.py @@ -28,7 +28,8 @@ class Schema(object): def __init__( self, schema_type=None, model=None, properties=None, items=None, schema_format=None, required=None, default=None, nullable=False, - enum=None, deprecated=False, all_of=None, one_of=None): + enum=None, deprecated=False, all_of=None, one_of=None, + additional_properties=None): self.type = schema_type and SchemaType(schema_type) self.model = model self.properties = properties and dict(properties) or {} @@ -41,6 +42,7 @@ class Schema(object): self.deprecated = deprecated self.all_of = all_of and list(all_of) or [] self.one_of = one_of and list(one_of) or [] + self.additional_properties = additional_properties self._all_required_properties_cache = None self._all_optional_properties_cache = None @@ -183,11 +185,16 @@ class Schema(object): value_props_names = value.keys() extra_props = set(value_props_names) - set(all_props_names) - if extra_props: + if extra_props and self.additional_properties is None: raise UndefinedSchemaProperty( "Undefined properties in schema: {0}".format(extra_props)) properties = {} + for prop_name in extra_props: + prop_value = value[prop_name] + properties[prop_name] = self.additional_properties.unmarshal( + prop_value) + for prop_name, prop in iteritems(all_props): try: prop_value = value[prop_name] diff --git a/tests/integration/data/v3.0/petstore.yaml b/tests/integration/data/v3.0/petstore.yaml index d788ebb..0e837e4 100644 --- a/tests/integration/data/v3.0/petstore.yaml +++ b/tests/integration/data/v3.0/petstore.yaml @@ -284,6 +284,8 @@ components: properties: rootCause: type: string + additionalProperties: + type: string responses: ErrorResponse: description: unexpected error diff --git a/tests/integration/test_petstore.py b/tests/integration/test_petstore.py index b3ed51d..9b41617 100644 --- a/tests/integration/test_petstore.py +++ b/tests/integration/test_petstore.py @@ -757,3 +757,38 @@ class TestPetstore(object): with pytest.raises(InvalidMediaTypeValue): request.get_body(spec) + + def test_post_tags_additional_properties( + self, spec, response_validator): + host_url = 'http://petstore.swagger.io/v1' + path_pattern = '/v1/tags' + pet_name = 'Dog' + data_json = { + 'name': pet_name, + } + data = json.dumps(data_json) + + request = MockRequest( + host_url, 'POST', '/tags', + path_pattern=path_pattern, data=data, + ) + + parameters = request.get_parameters(spec) + body = request.get_body(spec) + + assert parameters == {} + assert body == data_json + + data_json = { + 'code': 400, + 'message': 'Bad request', + 'rootCause': 'Tag already exist', + 'additionalinfo': 'Tag Dog already exist', + } + data = json.dumps(data_json) + response = MockResponse(data, status_code=404) + + response_result = response_validator.validate(request, response) + + assert response_result.errors == [] + assert response_result.data == data_json