diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..2b1b6a50d4 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +Release type: patch + +This fixes a bug where codegen would choke trying to find a field in the schema for a generic type. diff --git a/strawberry/codegen/query_codegen.py b/strawberry/codegen/query_codegen.py index bdd527ffd9..8d55897f15 100644 --- a/strawberry/codegen/query_codegen.py +++ b/strawberry/codegen/query_codegen.py @@ -678,12 +678,32 @@ def _field_from_selection_set( ) -> GraphQLField: assert selection.selection_set is not None + parent_type_name = parent_type.name + + # Check if the parent type is generic. + # This seems to be tracked by `strawberry` in the `type_var_map` + # If the type is generic, then the strawberry generated schema + # naming convention is + # The implementation here assumes that the `type_var_map` is ordered, + # but insertion order is maintained in python3.6+ (for CPython) and + # guaranteed for all python implementations in python3.7+, so that + # should be pretty safe. + if parent_type.type_var_map: + parent_type_name = ( + "".join( + c.__name__ # type: ignore[union-attr] + for c in parent_type.type_var_map.values() + ) + + parent_type.name + ) + selected_field = self.schema.get_field_for_type( - selection.name.value, parent_type.name + selection.name.value, parent_type_name ) + assert ( selected_field - ), f"Couldn't find {parent_type.name}.{selection.name.value}" + ), f"Couldn't find {parent_type_name}.{selection.name.value}" selected_field_type, wrapper = self._unwrap_type(selected_field.type) name = capitalize_first(to_camel_case(selection.name.value)) diff --git a/tests/codegen/conftest.py b/tests/codegen/conftest.py index 977d761fc9..1b7f80e467 100644 --- a/tests/codegen/conftest.py +++ b/tests/codegen/conftest.py @@ -2,7 +2,7 @@ import decimal import enum import random -from typing import TYPE_CHECKING, List, NewType, Optional, Union +from typing import TYPE_CHECKING, Generic, List, NewType, Optional, TypeVar, Union from typing_extensions import Annotated from uuid import UUID @@ -35,6 +35,16 @@ class Animal: age: int +LivingThing1 = TypeVar("LivingThing1") +LivingThing2 = TypeVar("LivingThing2") + + +@strawberry.type +class LifeContainer(Generic[LivingThing1, LivingThing2]): + items1: List[LivingThing1] + items2: List[LivingThing2] + + PersonOrAnimal = Annotated[Union[Person, Animal], strawberry.union("PersonOrAnimal")] @@ -110,6 +120,13 @@ def get_person_or_animal(self) -> Union[Person, Animal]: p_or_a.age = 7 return p_or_a + @strawberry.field + def list_life() -> LifeContainer[Person, Animal]: + """Get lists of living things.""" + person = Person(name="Henry", age=10) + dinosaur = Animal(name="rex", age=66_000_000) + return LifeContainer([person], [dinosaur]) + @strawberry.input class BlogPostInput: diff --git a/tests/codegen/queries/generic_types.graphql b/tests/codegen/queries/generic_types.graphql new file mode 100644 index 0000000000..bdd3c42ccf --- /dev/null +++ b/tests/codegen/queries/generic_types.graphql @@ -0,0 +1,12 @@ +query ListLifeGeneric { + listLife { + items1 { + name + age + } + items2 { + name + age + } + } +} diff --git a/tests/codegen/snapshots/python/generic_types.py b/tests/codegen/snapshots/python/generic_types.py new file mode 100644 index 0000000000..ccffbb8fb5 --- /dev/null +++ b/tests/codegen/snapshots/python/generic_types.py @@ -0,0 +1,16 @@ +from typing import List + +class ListLifeGenericResultListLifeItems1: + name: str + age: int + +class ListLifeGenericResultListLifeItems2: + name: str + age: int + +class ListLifeGenericResultListLife: + items1: List[ListLifeGenericResultListLifeItems1] + items2: List[ListLifeGenericResultListLifeItems2] + +class ListLifeGenericResult: + list_life: ListLifeGenericResultListLife diff --git a/tests/codegen/snapshots/typescript/generic_types.ts b/tests/codegen/snapshots/typescript/generic_types.ts new file mode 100644 index 0000000000..c1ef748a99 --- /dev/null +++ b/tests/codegen/snapshots/typescript/generic_types.ts @@ -0,0 +1,18 @@ +type ListLifeGenericResultListLifeItems1 = { + name: string + age: number +} + +type ListLifeGenericResultListLifeItems2 = { + name: string + age: number +} + +type ListLifeGenericResultListLife = { + items1: ListLifeGenericResultListLifeItems1[] + items2: ListLifeGenericResultListLifeItems2[] +} + +type ListLifeGenericResult = { + list_life: ListLifeGenericResultListLife +}