From 109afa17cabf7525ff5db4704c19a68a6fe4d076 Mon Sep 17 00:00:00 2001 From: kimeug Date: Tue, 1 Oct 2024 16:13:02 -0700 Subject: [PATCH 1/6] [PJL-10221] update some edge cases --- .../marshmallow_to_swagger.py | 12 ++++++ .../test_marshmallow_to_swagger.py | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/flask_rebar/swagger_generation/marshmallow_to_swagger.py b/flask_rebar/swagger_generation/marshmallow_to_swagger.py index 6c3c55c..e95888f 100644 --- a/flask_rebar/swagger_generation/marshmallow_to_swagger.py +++ b/flask_rebar/swagger_generation/marshmallow_to_swagger.py @@ -37,6 +37,7 @@ from marshmallow import Schema from marshmallow.validate import Range from marshmallow.validate import OneOf +from marshmallow.validate import ContainsOnly from marshmallow.validate import Length from marshmallow.validate import Validator @@ -604,6 +605,16 @@ def get_maximum( return UNSET +class ContainsOnlyConverter(ValidatorConverter): + MARSHMALLOW_TYPE = ContainsOnly + + @sets_swagger_attr(sw.items) + def get_items( + self, obj: ContainsOnly, context: _Context + ) -> Union[Type[UNSET], m.fields.List]: + return {"type": context.memo["items"]["type"], "enum": obj.choices} + + class OneOfConverter(ValidatorConverter): MARSHMALLOW_TYPE = OneOf @@ -794,6 +805,7 @@ def _common_converters() -> List[MarshmallowConverter]: """Instantiates the converters we use in ALL of the registries below""" converters: List[MarshmallowConverter] = [ BooleanConverter(), + ContainsOnlyConverter(), DateConverter(), DateTimeConverter(), FunctionConverter(), diff --git a/tests/swagger_generation/test_marshmallow_to_swagger.py b/tests/swagger_generation/test_marshmallow_to_swagger.py index ed98f4d..f6ad865 100644 --- a/tests/swagger_generation/test_marshmallow_to_swagger.py +++ b/tests/swagger_generation/test_marshmallow_to_swagger.py @@ -195,6 +195,46 @@ class Foo(m.Schema): }, ) + def test_list_enum_openapi_v3(self): + for field, result in [ + (m.fields.Integer(allow_none=True), {"type": ["integer", "null"]}), + ( + QueryParamList(m.fields.Integer(), validate=v.ContainsOnly([1, 2, 3])), + { + "type": "array", + "items": {"type": "integer", "enum": [1, 2, 3]}, + "explode": True, + }, + ), + ( + CommaSeparatedList( + m.fields.String(), validate=v.ContainsOnly(["a", "b", "c"]) + ), + { + "type": "array", + "items": {"type": "string", "enum": ["a", "b", "c"]}, + "style": "form", + "explode": False, + }, + ), + ]: + + class Foo(m.Schema): + a = field + + schema = Foo() + json_schema = self.registry.convert(schema, openapi_version=3) + + self.assertEqual( + json_schema, + { + "additionalProperties": False, + "type": "object", + "title": "Foo", + "properties": {"a": result}, + }, + ) + def test_data_key(self): registry = ConverterRegistry() registry.register_types(ALL_CONVERTERS) From 36f6d9913b40b6721b3c947362c09d2641a1f324 Mon Sep 17 00:00:00 2001 From: kimeug Date: Wed, 2 Oct 2024 08:46:17 -0700 Subject: [PATCH 2/6] updates --- .../marshmallow_to_swagger.py | 9 ++++ .../test_marshmallow_to_swagger.py | 48 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/flask_rebar/swagger_generation/marshmallow_to_swagger.py b/flask_rebar/swagger_generation/marshmallow_to_swagger.py index e95888f..c06eac2 100644 --- a/flask_rebar/swagger_generation/marshmallow_to_swagger.py +++ b/flask_rebar/swagger_generation/marshmallow_to_swagger.py @@ -424,6 +424,15 @@ class DictConverter(FieldConverter[m.fields.Dict]): def get_type(self, obj: m.fields.Dict, context: _Context) -> Union[str, List[str]]: return self.null_type_determination(obj, context, sw.object_) + @sets_swagger_attr(sw.additional_properties) + def get_additional_properties( + self, obj: m.fields.Dict, context: _Context + ) -> Union[UNSET, m.fields.Dict]: + if obj.value_field: + return context.convert(obj.value_field, context) + else: + return UNSET + class IntegerConverter(FieldConverter[m.fields.Integer]): MARSHMALLOW_TYPE = m.fields.Integer diff --git a/tests/swagger_generation/test_marshmallow_to_swagger.py b/tests/swagger_generation/test_marshmallow_to_swagger.py index f6ad865..87f5a27 100644 --- a/tests/swagger_generation/test_marshmallow_to_swagger.py +++ b/tests/swagger_generation/test_marshmallow_to_swagger.py @@ -197,7 +197,6 @@ class Foo(m.Schema): def test_list_enum_openapi_v3(self): for field, result in [ - (m.fields.Integer(allow_none=True), {"type": ["integer", "null"]}), ( QueryParamList(m.fields.Integer(), validate=v.ContainsOnly([1, 2, 3])), { @@ -235,6 +234,53 @@ class Foo(m.Schema): }, ) + def test_custom_dicts_openapi_v3(self): + for field, result in [ + ( + m.fields.Dict(), + { + "type": "object", + }, + ), + ( + m.fields.Dict( + keys=m.fields.String(), + values=m.fields.Integer(), + dump_default={"a": 1}, + ), + { + "type": "object", + "additionalProperties": {"type": "integer"}, + }, + ), + ( + m.fields.Dict( + keys=m.fields.String(validate=v.OneOf("a", "b", "c")), + values=m.fields.String(), + ), + { + "type": "object", + "additionalProperties": {"type": "string"}, + }, + ), + ]: + + class Foo(m.Schema): + a = field + + schema = Foo() + json_schema = self.registry.convert(schema, openapi_version=3) + + self.assertEqual( + json_schema, + { + "additionalProperties": False, + "type": "object", + "title": "Foo", + "properties": {"a": result}, + }, + ) + def test_data_key(self): registry = ConverterRegistry() registry.register_types(ALL_CONVERTERS) From 7909df5b566ba662e6486a50a5e46386e94ff69b Mon Sep 17 00:00:00 2001 From: kimeug Date: Wed, 2 Oct 2024 09:03:55 -0700 Subject: [PATCH 3/6] update tests --- tests/swagger_generation/test_marshmallow_to_swagger.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/swagger_generation/test_marshmallow_to_swagger.py b/tests/swagger_generation/test_marshmallow_to_swagger.py index 87f5a27..08453da 100644 --- a/tests/swagger_generation/test_marshmallow_to_swagger.py +++ b/tests/swagger_generation/test_marshmallow_to_swagger.py @@ -244,9 +244,7 @@ def test_custom_dicts_openapi_v3(self): ), ( m.fields.Dict( - keys=m.fields.String(), values=m.fields.Integer(), - dump_default={"a": 1}, ), { "type": "object", @@ -255,7 +253,7 @@ def test_custom_dicts_openapi_v3(self): ), ( m.fields.Dict( - keys=m.fields.String(validate=v.OneOf("a", "b", "c")), + keys=m.fields.String(), values=m.fields.String(), ), { From c5c12ac49ae1a6effddb27e68c7bdd4cd8a0b482 Mon Sep 17 00:00:00 2001 From: kimeug Date: Wed, 2 Oct 2024 09:09:47 -0700 Subject: [PATCH 4/6] typo --- flask_rebar/swagger_generation/marshmallow_to_swagger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask_rebar/swagger_generation/marshmallow_to_swagger.py b/flask_rebar/swagger_generation/marshmallow_to_swagger.py index c06eac2..3fcb0d4 100644 --- a/flask_rebar/swagger_generation/marshmallow_to_swagger.py +++ b/flask_rebar/swagger_generation/marshmallow_to_swagger.py @@ -427,7 +427,7 @@ def get_type(self, obj: m.fields.Dict, context: _Context) -> Union[str, List[str @sets_swagger_attr(sw.additional_properties) def get_additional_properties( self, obj: m.fields.Dict, context: _Context - ) -> Union[UNSET, m.fields.Dict]: + ) -> Union[Type[UNSET], m.fields.Dict]: if obj.value_field: return context.convert(obj.value_field, context) else: From d6631a78877d89bde7ae3446c390083d44f65acd Mon Sep 17 00:00:00 2001 From: kimeug Date: Wed, 2 Oct 2024 09:10:29 -0700 Subject: [PATCH 5/6] fix type --- flask_rebar/swagger_generation/marshmallow_to_swagger.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flask_rebar/swagger_generation/marshmallow_to_swagger.py b/flask_rebar/swagger_generation/marshmallow_to_swagger.py index 3fcb0d4..c5a8336 100644 --- a/flask_rebar/swagger_generation/marshmallow_to_swagger.py +++ b/flask_rebar/swagger_generation/marshmallow_to_swagger.py @@ -618,9 +618,7 @@ class ContainsOnlyConverter(ValidatorConverter): MARSHMALLOW_TYPE = ContainsOnly @sets_swagger_attr(sw.items) - def get_items( - self, obj: ContainsOnly, context: _Context - ) -> Union[Type[UNSET], m.fields.List]: + def get_items(self, obj: ContainsOnly, context: _Context) -> dict[str, Any]: return {"type": context.memo["items"]["type"], "enum": obj.choices} From 6291551097cf2631ef497af64e339c08d8639918 Mon Sep 17 00:00:00 2001 From: kimeug Date: Wed, 2 Oct 2024 09:12:41 -0700 Subject: [PATCH 6/6] typo --- flask_rebar/swagger_generation/marshmallow_to_swagger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask_rebar/swagger_generation/marshmallow_to_swagger.py b/flask_rebar/swagger_generation/marshmallow_to_swagger.py index c5a8336..c4a579b 100644 --- a/flask_rebar/swagger_generation/marshmallow_to_swagger.py +++ b/flask_rebar/swagger_generation/marshmallow_to_swagger.py @@ -618,7 +618,7 @@ class ContainsOnlyConverter(ValidatorConverter): MARSHMALLOW_TYPE = ContainsOnly @sets_swagger_attr(sw.items) - def get_items(self, obj: ContainsOnly, context: _Context) -> dict[str, Any]: + def get_items(self, obj: ContainsOnly, context: _Context) -> Dict[str, Any]: return {"type": context.memo["items"]["type"], "enum": obj.choices}