From 563598d764e20dbcf0adb637608a6f122475fa7c Mon Sep 17 00:00:00 2001 From: Philipp Gortan Date: Tue, 11 Jan 2022 17:54:47 +0100 Subject: [PATCH 1/2] unittest for additionalProperties added --- .gitignore | 1 + tests/additional_properties_test.py | 9 +++++++++ tests/conftest.py | 7 +++++++ tests/fixtures/additional-properties.yaml | 22 ++++++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 tests/additional_properties_test.py create mode 100644 tests/fixtures/additional-properties.yaml diff --git a/.gitignore b/.gitignore index 3fbdf68..a1c973f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ openapi3.egg-info/* build/* dist/* .idea +/.eggs/ diff --git a/tests/additional_properties_test.py b/tests/additional_properties_test.py new file mode 100644 index 0000000..e0ac268 --- /dev/null +++ b/tests/additional_properties_test.py @@ -0,0 +1,9 @@ +import pytest + +from unittest.mock import patch, MagicMock + + +def test_additional_properties(additional_properties_spec): + resp = MagicMock(status_code=200, headers={"Content-Type": "application/json"}, json=lambda: {"foo": "bar", "additional": "property"}) + with patch("requests.sessions.Session.send", return_value=resp) as s: + additional_properties_spec.call_test_operation() diff --git a/tests/conftest.py b/tests/conftest.py index cb3f71a..c291963 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -161,3 +161,10 @@ def with_ref_allof(): an allOf """ yield _get_parsed_yaml("ref-allof.yaml") + +@pytest.fixture +def additional_properties_spec(): + """ + Provides an OpenAPI version of the additional-properties.yaml spec + """ + yield _get_parsed_spec("additional-properties.yaml") diff --git a/tests/fixtures/additional-properties.yaml b/tests/fixtures/additional-properties.yaml new file mode 100644 index 0000000..54dcf55 --- /dev/null +++ b/tests/fixtures/additional-properties.yaml @@ -0,0 +1,22 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: This uses additionalProperties +servers: + - url: http://test.example/api +paths: + /additional_prop: + get: + operationId: test_operation + responses: + "200": + description: Success! + content: + application/json: + schema: + type: object + properties: + foo: + type: string + additionalProperties: + type: string From 887eb3752dd61c23135930c648f07a4f522d47b8 Mon Sep 17 00:00:00 2001 From: Philipp Gortan Date: Tue, 11 Jan 2022 18:36:05 +0100 Subject: [PATCH 2/2] support for additionalProperties --- openapi3/schemas.py | 10 ++++++---- tests/additional_properties_test.py | 8 +++++++- tests/fixtures/additional-properties.yaml | 14 +++++++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/openapi3/schemas.py b/openapi3/schemas.py index 5d341cf..f3b27d4 100644 --- a/openapi3/schemas.py +++ b/openapi3/schemas.py @@ -131,7 +131,7 @@ def get_type(self): self._model_type = type( type_name, (Model,), - {"__slots__": self.properties.keys()}, # pylint: disable=attribute-defined-outside-init + ({} if self.additionalProperties else {"__slots__": self.properties.keys()}), # pylint: disable=attribute-defined-outside-init ) return self._model_type @@ -278,14 +278,16 @@ def __init__(self, data, schema): setattr(self, s, None) keys = set(data.keys()) - frozenset(self.__slots__) - if keys: + if keys and not schema.additionalProperties: raise ModelError("Schema {} got unexpected attribute keys {}".format(self.__class__.__name__, keys)) # collect the data into this model for k, v in data.items(): - prop = schema.properties[k] + prop = schema.properties.get(k) if schema.properties else None - if prop.type == "array": + if (not prop) and schema.additionalProperties: + setattr(self, k, v) + elif prop.type == "array": # handle arrays item_schema = prop.items setattr(self, k, [item_schema.model(c) for c in v]) diff --git a/tests/additional_properties_test.py b/tests/additional_properties_test.py index e0ac268..01467de 100644 --- a/tests/additional_properties_test.py +++ b/tests/additional_properties_test.py @@ -6,4 +6,10 @@ def test_additional_properties(additional_properties_spec): resp = MagicMock(status_code=200, headers={"Content-Type": "application/json"}, json=lambda: {"foo": "bar", "additional": "property"}) with patch("requests.sessions.Session.send", return_value=resp) as s: - additional_properties_spec.call_test_operation() + additional_properties_spec.call_test_operation1() + + +def test_only_additional_properties(additional_properties_spec): + resp = MagicMock(status_code=200, headers={"Content-Type": "application/json"}, json=lambda: {"additional": "property"}) + with patch("requests.sessions.Session.send", return_value=resp) as s: + additional_properties_spec.call_test_operation2() diff --git a/tests/fixtures/additional-properties.yaml b/tests/fixtures/additional-properties.yaml index 54dcf55..ecd9d56 100644 --- a/tests/fixtures/additional-properties.yaml +++ b/tests/fixtures/additional-properties.yaml @@ -7,7 +7,7 @@ servers: paths: /additional_prop: get: - operationId: test_operation + operationId: test_operation1 responses: "200": description: Success! @@ -20,3 +20,15 @@ paths: type: string additionalProperties: type: string + /only_additional_prop2: + get: + operationId: test_operation2 + responses: + "200": + description: Success! + content: + application/json: + schema: + type: object + additionalProperties: + type: string