From 9b8aaa7f14466af249e12d4c00e03dccd7a0d643 Mon Sep 17 00:00:00 2001 From: Checho3388 Date: Sat, 20 Jan 2024 12:36:57 -0300 Subject: [PATCH 1/6] Add TypedDict for schema config --- strawberry/schema/config.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/strawberry/schema/config.py b/strawberry/schema/config.py index 18afbb7940..d757526ff3 100644 --- a/strawberry/schema/config.py +++ b/strawberry/schema/config.py @@ -1,11 +1,18 @@ from __future__ import annotations from dataclasses import InitVar, dataclass, field -from typing import Any, Callable +from typing import Any, Callable, TypedDict from .name_converter import NameConverter +class StrawberryConfigDict(TypedDict, total=False): + auto_camel_case: bool + name_converter: NameConverter + default_resolver: Callable[[Any, str], object] + relay_max_results: int + + @dataclass class StrawberryConfig: auto_camel_case: InitVar[bool] = None # pyright: reportGeneralTypeIssues=false @@ -19,3 +26,9 @@ def __post_init__( ): if auto_camel_case is not None: self.name_converter.auto_camel_case = auto_camel_case + + @classmethod + def from_dict(cls, data: StrawberryConfigDict | None) -> StrawberryConfig: + if not data: + return cls() + return cls(**data) From be290d48a9764166e699208ed0f1d4397ca88dee Mon Sep 17 00:00:00 2001 From: Checho3388 Date: Sat, 20 Jan 2024 23:45:06 -0300 Subject: [PATCH 2/6] Make schema config expect a dict instead of dataclass --- strawberry/federation/schema.py | 4 ++-- strawberry/schema/schema.py | 5 +++-- tests/schema/test_name_converter.py | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/strawberry/federation/schema.py b/strawberry/federation/schema.py index ccdc0fe8f9..46f33f926b 100644 --- a/strawberry/federation/schema.py +++ b/strawberry/federation/schema.py @@ -43,7 +43,7 @@ from strawberry.enum import EnumDefinition from strawberry.extensions import SchemaExtension from strawberry.federation.schema_directives import ComposeDirective - from strawberry.schema.config import StrawberryConfig + from strawberry.schema.config import StrawberryConfigDict from strawberry.schema.types.concrete_type import TypeMap from strawberry.schema_directive import StrawberrySchemaDirective from strawberry.union import StrawberryUnion @@ -60,7 +60,7 @@ def __init__( types: Iterable[Type] = (), extensions: Iterable[Union[Type["SchemaExtension"], "SchemaExtension"]] = (), execution_context_class: Optional[Type["GraphQLExecutionContext"]] = None, - config: Optional["StrawberryConfig"] = None, + config: Optional["StrawberryConfigDict"] = None, scalar_overrides: Optional[ Dict[object, Union[Type, "ScalarWrapper", "ScalarDefinition"]] ] = None, diff --git a/strawberry/schema/schema.py b/strawberry/schema/schema.py index dcb961baad..f6308c5383 100644 --- a/strawberry/schema/schema.py +++ b/strawberry/schema/schema.py @@ -54,6 +54,7 @@ from strawberry.enum import EnumDefinition from strawberry.extensions import SchemaExtension from strawberry.field import StrawberryField + from strawberry.schema.config import StrawberryConfigDict from strawberry.type import StrawberryType from strawberry.types import ExecutionResult from strawberry.union import StrawberryUnion @@ -77,7 +78,7 @@ def __init__( types: Iterable[Union[Type, StrawberryType]] = (), extensions: Iterable[Union[Type[SchemaExtension], SchemaExtension]] = (), execution_context_class: Optional[Type[GraphQLExecutionContext]] = None, - config: Optional[StrawberryConfig] = None, + config: Optional[StrawberryConfigDict] = None, scalar_overrides: Optional[ Dict[object, Union[Type, ScalarWrapper, ScalarDefinition]] ] = None, @@ -89,7 +90,7 @@ def __init__( self.extensions = extensions self.execution_context_class = execution_context_class - self.config = config or StrawberryConfig() + self.config = StrawberryConfig.from_dict(config) SCALAR_OVERRIDES_DICT_TYPE = Dict[ object, Union["ScalarWrapper", "ScalarDefinition"] diff --git a/tests/schema/test_name_converter.py b/tests/schema/test_name_converter.py index ba22e0b1d0..6bec2c27fe 100644 --- a/tests/schema/test_name_converter.py +++ b/tests/schema/test_name_converter.py @@ -8,7 +8,6 @@ from strawberry.directive import StrawberryDirective from strawberry.enum import EnumDefinition, EnumValue from strawberry.field import StrawberryField -from strawberry.schema.config import StrawberryConfig from strawberry.schema.name_converter import NameConverter from strawberry.schema_directive import Location, StrawberrySchemaDirective from strawberry.type import StrawberryType @@ -125,7 +124,7 @@ def print(self, enum: MyEnum) -> str: schema = strawberry.Schema( query=Query, types=[MyScalar, Node], - config=StrawberryConfig(name_converter=AppendsNameConverter("X")), + config={"name_converter": AppendsNameConverter("X")}, ) From 514b2c18bcb323c3a3984a23ab4b3b530763034c Mon Sep 17 00:00:00 2001 From: Checho3388 Date: Sat, 20 Jan 2024 23:55:10 -0300 Subject: [PATCH 3/6] Make schema config expect a dict instead of dataclass --- tests/schema/test_camel_casing.py | 25 ++++++------------------- tests/schema/test_directives.py | 3 +-- tests/schema/test_fields.py | 3 +-- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/tests/schema/test_camel_casing.py b/tests/schema/test_camel_casing.py index 47a91fc2d8..dc280b1060 100644 --- a/tests/schema/test_camel_casing.py +++ b/tests/schema/test_camel_casing.py @@ -1,5 +1,4 @@ import strawberry -from strawberry.schema.config import StrawberryConfig def test_camel_case_is_on_by_default(): @@ -30,9 +29,7 @@ def test_can_set_camel_casing(): class Query: example_field: str = "Example" - schema = strawberry.Schema( - query=Query, config=StrawberryConfig(auto_camel_case=True) - ) + schema = strawberry.Schema(query=Query, config={"auto_camel_case": True}) query = """ { @@ -55,9 +52,7 @@ def test_can_set_camel_casing_to_false(): class Query: example_field: str = "Example" - schema = strawberry.Schema( - query=Query, config=StrawberryConfig(auto_camel_case=False) - ) + schema = strawberry.Schema(query=Query, config={"auto_camel_case": False}) query = """ { @@ -80,9 +75,7 @@ def test_can_set_camel_casing_to_false_uses_name(): class Query: example_field: str = strawberry.field(name="exampleField") - schema = strawberry.Schema( - query=Query, config=StrawberryConfig(auto_camel_case=False) - ) + schema = strawberry.Schema(query=Query, config={"auto_camel_case": False}) query = """ { @@ -107,9 +100,7 @@ class Query: def example_field(self) -> str: return "ABC" - schema = strawberry.Schema( - query=Query, config=StrawberryConfig(auto_camel_case=False) - ) + schema = strawberry.Schema(query=Query, config={"auto_camel_case": False}) query = """ { @@ -162,9 +153,7 @@ class Query: def example_field(self, example_input: str) -> str: return example_input - schema = strawberry.Schema( - query=Query, config=StrawberryConfig(auto_camel_case=False) - ) + schema = strawberry.Schema(query=Query, config={"auto_camel_case": False}) query = """ { @@ -192,9 +181,7 @@ class Query: def example_field(self, example_input: str) -> str: return example_input - schema = strawberry.Schema( - query=Query, config=StrawberryConfig(auto_camel_case=False) - ) + schema = strawberry.Schema(query=Query, config={"auto_camel_case": False}) query = """ { diff --git a/tests/schema/test_directives.py b/tests/schema/test_directives.py index 10a7bb6329..511590c76d 100644 --- a/tests/schema/test_directives.py +++ b/tests/schema/test_directives.py @@ -7,7 +7,6 @@ import strawberry from strawberry.directive import DirectiveLocation, DirectiveValue from strawberry.extensions import SchemaExtension -from strawberry.schema.config import StrawberryConfig from strawberry.type import get_object_definition from strawberry.types.info import Info from strawberry.utils.await_maybe import await_maybe @@ -224,7 +223,7 @@ def replace(value: str, old: str, new: str): schema = strawberry.Schema( query=Query, directives=[turn_uppercase, replace], - config=StrawberryConfig(auto_camel_case=False), + config={"auto_camel_case": False}, ) query = """query People($identified: Boolean!){ diff --git a/tests/schema/test_fields.py b/tests/schema/test_fields.py index 58c58b4cb1..0a7e9d0587 100644 --- a/tests/schema/test_fields.py +++ b/tests/schema/test_fields.py @@ -5,7 +5,6 @@ import strawberry from strawberry.field import StrawberryField from strawberry.printer import print_schema -from strawberry.schema.config import StrawberryConfig def test_custom_field(): @@ -62,7 +61,7 @@ def user(self) -> User: schema = strawberry.Schema( query=Query, - config=StrawberryConfig(default_resolver=getitem), + config={"default_resolver": getitem}, ) query = "{ user { name } }" From d2ed8eeb5dc5eb4cc759a0f7b53f0312b45f5436 Mon Sep 17 00:00:00 2001 From: Checho3388 Date: Sat, 20 Jan 2024 23:57:16 -0300 Subject: [PATCH 4/6] Fix docs for new typed dict config --- docs/types/schema-configurations.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/types/schema-configurations.md b/docs/types/schema-configurations.md index 2e8832bda8..e2e224624f 100644 --- a/docs/types/schema-configurations.md +++ b/docs/types/schema-configurations.md @@ -13,15 +13,13 @@ example below: ```python import strawberry -from strawberry.schema.config import StrawberryConfig - @strawberry.type class Query: example_field: str -schema = strawberry.Schema(query=Query, config=StrawberryConfig(auto_camel_case=False)) +schema = strawberry.Schema(query=Query, config={"auto_camel_case": False}) ``` In this case we are disabling the auto camel casing feature, so your output schema From 72d3c7658658eea8cdb1d5e2090ac556b3aff1c2 Mon Sep 17 00:00:00 2001 From: Checho3388 Date: Sun, 21 Jan 2024 13:53:17 -0300 Subject: [PATCH 5/6] Add RELEASE.md file --- RELEASE.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..17f044f2c2 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +Release type: major + +Make schema config parameter a TypedDict From 0ced727a87c5c6e8b6b3b781a88dc0dc18ebe7b1 Mon Sep 17 00:00:00 2001 From: Checho3388 Date: Mon, 29 Jan 2024 16:40:13 -0300 Subject: [PATCH 6/6] Add codemod for StrawberryConfig dataclass conversion to dict --- strawberry/codemods/schema_config.py | 20 +++++ tests/codemods/test_schema_config.py | 123 +++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 strawberry/codemods/schema_config.py create mode 100644 tests/codemods/test_schema_config.py diff --git a/strawberry/codemods/schema_config.py b/strawberry/codemods/schema_config.py new file mode 100644 index 0000000000..88f2e4bded --- /dev/null +++ b/strawberry/codemods/schema_config.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +import libcst as cst +import libcst.matchers as m +from libcst._nodes.expression import BaseExpression # noqa: TCH002 +from libcst.codemod import VisitorBasedCodemodCommand +from libcst.codemod.visitors import RemoveImportsVisitor + + +class ConvertStrawberryConfigToDict(VisitorBasedCodemodCommand): + DESCRIPTION: str = "Converts StrawberryConfig(...) to dict" + + @m.leave(m.Call(func=m.Name("StrawberryConfig"))) + def leave_strawberry_config_call( + self, original_node: cst.Call, _: cst.Call + ) -> BaseExpression: + RemoveImportsVisitor.remove_unused_import( + self.context, "strawberry.schema.config", "StrawberryConfig" + ) + return cst.Call(func=cst.Name(value="dict"), args=original_node.args) diff --git a/tests/codemods/test_schema_config.py b/tests/codemods/test_schema_config.py new file mode 100644 index 0000000000..f095809871 --- /dev/null +++ b/tests/codemods/test_schema_config.py @@ -0,0 +1,123 @@ +from libcst.codemod import CodemodTest + +from strawberry.codemods.schema_config import ConvertStrawberryConfigToDict + + +class TestConvertConstantCommand(CodemodTest): + TRANSFORM = ConvertStrawberryConfigToDict + + def test_update_config(self) -> None: + before = """ + import strawberry + from strawberry.schema.config import StrawberryConfig + + schema = strawberry.Schema( + query=Query, config=StrawberryConfig(auto_camel_case=False) + ) + """ + + after = """ + import strawberry + + schema = strawberry.Schema( + query=Query, config=dict(auto_camel_case=False) + ) + """ + + self.assertCodemod( + before, + after, + ) + + def test_update_config_default(self) -> None: + before = """ + import strawberry + from strawberry.schema.config import StrawberryConfig + + schema = strawberry.Schema( + query=Query, config=StrawberryConfig() + ) + """ + + after = """ + import strawberry + + schema = strawberry.Schema( + query=Query, config=dict() + ) + """ + + self.assertCodemod( + before, + after, + ) + + def test_update_config_with_two_args(self) -> None: + before = """ + import strawberry + from strawberry.schema.config import StrawberryConfig + + schema = strawberry.Schema( + query=Query, + config=StrawberryConfig(auto_camel_case=True, default_resolver=getitem) + ) + """ + + after = """ + import strawberry + + schema = strawberry.Schema( + query=Query, + config=dict(auto_camel_case=True, default_resolver=getitem) + ) + """ + + self.assertCodemod( + before, + after, + ) + + def test_update_config_declared_outside(self) -> None: + before = """ + import strawberry + from strawberry.schema.config import StrawberryConfig + + config = StrawberryConfig(auto_camel_case=True, default_resolver=getitem) + + schema = strawberry.Schema( + query=Query, + config=config + ) + """ + + after = """ + import strawberry + + config = dict(auto_camel_case=True, default_resolver=getitem) + + schema = strawberry.Schema( + query=Query, + config=config + ) + """ + + self.assertCodemod( + before, + after, + ) + + def test_update_config_declared_outside_not_used_in_module(self) -> None: + before = """ + from strawberry.schema.config import StrawberryConfig + + config = StrawberryConfig(auto_camel_case=True, default_resolver=getitem) + """ + + after = """ + config = dict(auto_camel_case=True, default_resolver=getitem) + """ + + self.assertCodemod( + before, + after, + )