From b029066addc712ababa70b6177d9648ff78668e8 Mon Sep 17 00:00:00 2001 From: Diogo Baeder de Paula Pinto Date: Tue, 26 Mar 2019 02:30:07 -0300 Subject: [PATCH 1/3] Fix #124: Checking "additionalProperties" in "oneOf" items. This is important because it does the correct validation over items that are restricted in "oneOf", so that it's possible to use schemas that are superset of one another as items of "oneOf". --- openapi_core/schema/schemas/models.py | 14 ++++++++++-- tests/unit/schema/test_schemas.py | 31 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/openapi_core/schema/schemas/models.py b/openapi_core/schema/schemas/models.py index fd26ce7..4855e55 100644 --- a/openapi_core/schema/schemas/models.py +++ b/openapi_core/schema/schemas/models.py @@ -311,7 +311,12 @@ class Schema(object): value_props_names = value.keys() extra_props = set(value_props_names) - set(all_props_names) - if extra_props and self.additional_properties is False: + no_more_properties_allowed = ( + (self.additional_properties is False) or + (one_of_schema is not None and + one_of_schema.additional_properties is False) + ) + if extra_props and no_more_properties_allowed: raise UndefinedSchemaProperty(extra_props) properties = {} @@ -543,7 +548,12 @@ class Schema(object): value_props_names = value.keys() extra_props = set(value_props_names) - set(all_props_names) - if extra_props and self.additional_properties is False: + no_more_properties_allowed = ( + (self.additional_properties is False) or + (one_of_schema is not None and + one_of_schema.additional_properties is False) + ) + if extra_props and no_more_properties_allowed: raise UndefinedSchemaProperty(extra_props) if self.additional_properties is not True: diff --git a/tests/unit/schema/test_schemas.py b/tests/unit/schema/test_schemas.py index fa608e2..50c92c9 100644 --- a/tests/unit/schema/test_schemas.py +++ b/tests/unit/schema/test_schemas.py @@ -711,6 +711,37 @@ class TestSchemaValidate(object): with pytest.raises(NoOneOfSchema): schema.validate(value) + @pytest.mark.parametrize('value', [ + Model({ + 'foo': 'FOO', + }), + Model({ + 'foo': 'FOO', + 'bar': 'BAR', + }), + ]) + def test_unambiguous_one_of(self, value): + one_of = [ + Schema( + 'object', + properties={ + 'bar': Schema('string'), + }, + additional_properties=False, + ), + Schema( + 'object', + properties={ + 'foo': Schema('string'), + 'bar': Schema('string'), + }, + additional_properties=False, + ), + ] + schema = Schema('object', one_of=one_of) + + schema.validate(value) + @pytest.mark.parametrize('value', [Model(), ]) def test_object_default_property(self, value): schema = Schema('object', default='value1') From 14196b6ce16fecf5a2071b92aaaaa03d7f44adb3 Mon Sep 17 00:00:00 2001 From: Diogo Baeder de Paula Pinto Date: Tue, 26 Mar 2019 03:41:14 -0300 Subject: [PATCH 2/3] Fix #124: Fixing test for Python 2. --- tests/unit/schema/test_schemas.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/unit/schema/test_schemas.py b/tests/unit/schema/test_schemas.py index 50c92c9..9fc7ed0 100644 --- a/tests/unit/schema/test_schemas.py +++ b/tests/unit/schema/test_schemas.py @@ -713,11 +713,11 @@ class TestSchemaValidate(object): @pytest.mark.parametrize('value', [ Model({ - 'foo': 'FOO', + u'foo': u'FOO', }), Model({ - 'foo': 'FOO', - 'bar': 'BAR', + u'foo': u'FOO', + u'bar': u'BAR', }), ]) def test_unambiguous_one_of(self, value): @@ -725,17 +725,19 @@ class TestSchemaValidate(object): Schema( 'object', properties={ - 'bar': Schema('string'), + u'foo': Schema('string'), }, additional_properties=False, + required=[u'foo'], ), Schema( 'object', properties={ - 'foo': Schema('string'), - 'bar': Schema('string'), + u'foo': Schema('string'), + u'bar': Schema('string'), }, additional_properties=False, + required=[u'foo', u'bar'], ), ] schema = Schema('object', one_of=one_of) From 4d99cbe7e13c10dbfb23b844efc755ae6b1cea1d Mon Sep 17 00:00:00 2001 From: Artur Maciag Date: Tue, 26 Mar 2019 13:27:44 +0000 Subject: [PATCH 3/3] Move additional props check to separate method --- openapi_core/schema/schemas/models.py | 25 +++++++++++++------------ tests/unit/schema/test_schemas.py | 16 ++++++++-------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/openapi_core/schema/schemas/models.py b/openapi_core/schema/schemas/models.py index 4855e55..7353dd0 100644 --- a/openapi_core/schema/schemas/models.py +++ b/openapi_core/schema/schemas/models.py @@ -161,6 +161,13 @@ class Schema(object): return defaultdict(lambda: lambda x: x, mapping) + def are_additional_properties_allowed(self, one_of_schema=None): + return ( + (self.additional_properties is not False) and + (one_of_schema is None or + one_of_schema.additional_properties is not False) + ) + def cast(self, value, custom_formatters=None, strict=True): """Cast value to schema type""" if value is None: @@ -311,12 +318,9 @@ class Schema(object): value_props_names = value.keys() extra_props = set(value_props_names) - set(all_props_names) - no_more_properties_allowed = ( - (self.additional_properties is False) or - (one_of_schema is not None and - one_of_schema.additional_properties is False) - ) - if extra_props and no_more_properties_allowed: + extra_props_allowed = self.are_additional_properties_allowed( + one_of_schema) + if extra_props and not extra_props_allowed: raise UndefinedSchemaProperty(extra_props) properties = {} @@ -548,12 +552,9 @@ class Schema(object): value_props_names = value.keys() extra_props = set(value_props_names) - set(all_props_names) - no_more_properties_allowed = ( - (self.additional_properties is False) or - (one_of_schema is not None and - one_of_schema.additional_properties is False) - ) - if extra_props and no_more_properties_allowed: + extra_props_allowed = self.are_additional_properties_allowed( + one_of_schema) + if extra_props and not extra_props_allowed: raise UndefinedSchemaProperty(extra_props) if self.additional_properties is not True: diff --git a/tests/unit/schema/test_schemas.py b/tests/unit/schema/test_schemas.py index 9fc7ed0..b66dad7 100644 --- a/tests/unit/schema/test_schemas.py +++ b/tests/unit/schema/test_schemas.py @@ -713,11 +713,11 @@ class TestSchemaValidate(object): @pytest.mark.parametrize('value', [ Model({ - u'foo': u'FOO', + 'foo': u("FOO"), }), Model({ - u'foo': u'FOO', - u'bar': u'BAR', + 'foo': u("FOO"), + 'bar': u("BAR"), }), ]) def test_unambiguous_one_of(self, value): @@ -725,19 +725,19 @@ class TestSchemaValidate(object): Schema( 'object', properties={ - u'foo': Schema('string'), + 'foo': Schema('string'), }, additional_properties=False, - required=[u'foo'], + required=['foo'], ), Schema( 'object', properties={ - u'foo': Schema('string'), - u'bar': Schema('string'), + 'foo': Schema('string'), + 'bar': Schema('string'), }, additional_properties=False, - required=[u'foo', u'bar'], + required=['foo', 'bar'], ), ] schema = Schema('object', one_of=one_of)