From 418049e2078e82ab583d33ede453f70978cf0cfe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 10 Oct 2022 19:14:38 +0100 Subject: [PATCH 1/2] Add NullUnmarshaller The value 'null' is allowed in OpenAPI 3.1. Signed-off-by: Stephen Finucane --- openapi_core/unmarshalling/schemas/factories.py | 2 ++ openapi_core/unmarshalling/schemas/unmarshallers.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/openapi_core/unmarshalling/schemas/factories.py b/openapi_core/unmarshalling/schemas/factories.py index dda8a1a2..89db3047 100644 --- a/openapi_core/unmarshalling/schemas/factories.py +++ b/openapi_core/unmarshalling/schemas/factories.py @@ -30,6 +30,7 @@ from openapi_core.unmarshalling.schemas.unmarshallers import ( IntegerUnmarshaller, ) +from openapi_core.unmarshalling.schemas.unmarshallers import NullUnmarshaller from openapi_core.unmarshalling.schemas.unmarshallers import NumberUnmarshaller from openapi_core.unmarshalling.schemas.unmarshallers import ObjectUnmarshaller from openapi_core.unmarshalling.schemas.unmarshallers import StringUnmarshaller @@ -45,6 +46,7 @@ class SchemaUnmarshallersFactory: "boolean": BooleanUnmarshaller, "array": ArrayUnmarshaller, "object": ObjectUnmarshaller, + "null": NullUnmarshaller, "any": AnyUnmarshaller, } diff --git a/openapi_core/unmarshalling/schemas/unmarshallers.py b/openapi_core/unmarshalling/schemas/unmarshallers.py index 9bddaddf..3a738440 100644 --- a/openapi_core/unmarshalling/schemas/unmarshallers.py +++ b/openapi_core/unmarshalling/schemas/unmarshallers.py @@ -2,7 +2,6 @@ from functools import partial from typing import TYPE_CHECKING from typing import Any -from typing import Dict from typing import Iterable from typing import List from typing import Optional @@ -12,6 +11,7 @@ from jsonschema._types import is_array from jsonschema._types import is_bool from jsonschema._types import is_integer +from jsonschema._types import is_null from jsonschema._types import is_number from jsonschema._types import is_object from jsonschema.protocols import Validator @@ -159,6 +159,13 @@ class BooleanUnmarshaller(BaseSchemaUnmarshaller): } +class NullUnmarshaller(BaseSchemaUnmarshaller): + + FORMATTERS: FormattersDict = { + None: Formatter.from_callables(partial(is_null, None), None), + } + + class ComplexUnmarshaller(BaseSchemaUnmarshaller): def __init__( self, From cd6996c26f1e47ac17efe80be58dcb489b102e79 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Mon, 10 Oct 2022 21:15:25 +0100 Subject: [PATCH 2/2] Add NullUnmarshaller tests --- tests/unit/unmarshalling/test_unmarshal.py | 42 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/tests/unit/unmarshalling/test_unmarshal.py b/tests/unit/unmarshalling/test_unmarshal.py index f31a0f69..224b00a7 100644 --- a/tests/unit/unmarshalling/test_unmarshal.py +++ b/tests/unit/unmarshalling/test_unmarshal.py @@ -1,10 +1,12 @@ import datetime import uuid +from functools import partial import pytest from isodate.tzinfo import UTC from isodate.tzinfo import FixedOffset from openapi_schema_validator import OAS30Validator +from openapi_schema_validator import OAS31Validator from openapi_core.spec.paths import Spec from openapi_core.unmarshalling.schemas.enums import UnmarshalContext @@ -23,13 +25,13 @@ @pytest.fixture -def unmarshaller_factory(): +def schema_unmarshaller_factory(): def create_unmarshaller( - schema, custom_formatters=None, context=UnmarshalContext.REQUEST + validator, schema, custom_formatters=None, context=None ): custom_formatters = custom_formatters or {} return SchemaUnmarshallersFactory( - OAS30Validator, + validator, custom_formatters=custom_formatters, context=context, ).create(schema) @@ -37,7 +39,11 @@ def create_unmarshaller( return create_unmarshaller -class TestUnmarshal: +class TestOAS30SchemaUnmarshallerUnmarshal: + @pytest.fixture + def unmarshaller_factory(self, schema_unmarshaller_factory): + return partial(schema_unmarshaller_factory, OAS30Validator) + def test_no_schema(self, unmarshaller_factory): spec = None value = "test" @@ -79,7 +85,11 @@ def unmarshal(self, value): ).unmarshal(value) -class TestSchemaUnmarshallerCall: +class TestOAS30SchemaUnmarshallerCall: + @pytest.fixture + def unmarshaller_factory(self, schema_unmarshaller_factory): + return partial(schema_unmarshaller_factory, OAS30Validator) + def test_deprecated(self, unmarshaller_factory): schema = { "type": "string", @@ -824,3 +834,25 @@ def test_additional_properties_list(self, unmarshaller_factory): assert result == { "user_ids": [1, 2, 3, 4], } + + +class TestOAS31SchemaUnmarshallerCall: + @pytest.fixture + def unmarshaller_factory(self, schema_unmarshaller_factory): + return partial(schema_unmarshaller_factory, OAS31Validator) + + def test_null(self, unmarshaller_factory): + schema = {"type": "null"} + spec = Spec.from_dict(schema) + + result = unmarshaller_factory(spec)(None) + + assert result is None + + @pytest.mark.parametrize("value", ["string", 2, 3.14, True, [1, 2], {}]) + def test_null_invalid(self, unmarshaller_factory, value): + schema = {"type": "null"} + spec = Spec.from_dict(schema) + + with pytest.raises(InvalidSchemaValue): + unmarshaller_factory(spec)(value)