Repository: strawberry-graphql/strawberry

Issue:
Error adding more than one implementation of an interface
**Observed Behaviour**: When i try to add two implementations of an interface, i get a duplicated type name exception

**Expected Behaviour**: Instead of trying to recreate the interface type again, reuse it.

**Steps to reproduce**:
1. Create an interface
2. Create two types which implement the interface
3. Launch `strawberry server app`
4. See it fails with ` Schema must contain uniquely named types but contains multiple types named '<InterfaceName>'`

**Snippet to reproduce the issue**
````python
from typing import List, Optional, Union
import strawberry
from strawberry import field


@strawberry.interface
class Person:
    name: str
    email: str


@strawberry.type
class Speaker(Person):
    job: str 


@strawberry.type
class Attendee(Person):
    interests: List[str]


def get_people_by_name(name: str): 
    return []


@strawberry.type
class Query:
    searchPeopleByName: List[Union[Speaker, Attendee]] = field(resolver=get_people_by_name)

schema = strawberry.Schema(query=Query)
````
**Full traceback:**
```
  File "/mnt/c/Users/<User>/code/nerdearla/test_app.py", line 30, in <module>
    schema = strawberry.Schema(query=Query)
  File "/home/crow/.virtualenvs/venv/lib/python3.8/site-packages/strawberry/schema/schema.py", line 42, in __init__
    self._schema = GraphQLSchema(
  File "/home/crow/.virtualenvs/venv/lib/python3.8/site-packages/graphql/type/schema.py", line 240, in __init__
    raise TypeError(
TypeError: Schema must contain uniquely named types but contains multiple types named 'Person'.
```


Discussion:
My bad, will fix today or tomorrow :)
Thank you Patrick! Let me know if I can be of any help testing or perhaps with code review 🙂

Resolving patch:
diff --git a/strawberry/schema/types/object_type.py b/strawberry/schema/types/object_type.py
--- a/strawberry/schema/types/object_type.py
+++ b/strawberry/schema/types/object_type.py
@@ -12,32 +12,43 @@ def _get_object_type_for_type_definition(
     type_definition: TypeDefinition, type_map: TypeMap
 ) -> GraphQLType:
 
-    TypeClass: Type = GraphQLObjectType
-
-    kwargs = {}
-
-    if type_definition.is_input:
-        TypeClass = GraphQLInputObjectType
-    elif type_definition.is_interface:
-        TypeClass = GraphQLInterfaceType
-
-    if type_definition.interfaces:
-        kwargs["interfaces"] = [
-            _get_object_type_for_type_definition(interface, type_map)
-            for interface in type_definition.interfaces
-        ]
-
-    assert not type_definition.is_generic
-
-    return TypeClass(
-        name=type_definition.name,
-        fields=lambda: {
-            field.name: get_field(field, type_definition.is_input, type_map)
-            for field in type_definition.fields
-        },
-        description=type_definition.description,
-        **kwargs,
-    )
+    if type_definition.name not in type_map:
+        TypeClass: Type = GraphQLObjectType
+
+        kwargs = {}
+
+        if type_definition.is_input:
+            TypeClass = GraphQLInputObjectType
+        elif type_definition.is_interface:
+            TypeClass = GraphQLInterfaceType
+
+        if type_definition.interfaces:
+            kwargs["interfaces"] = [
+                _get_object_type_for_type_definition(interface, type_map)
+                for interface in type_definition.interfaces
+            ]
+            # this tells GraphQL core what the returned object's actual type is
+            kwargs["is_type_of"] = lambda obj, _: isinstance(  # type: ignore
+                obj, type_definition.origin
+            )
+
+        assert not type_definition.is_generic
+
+        object_type = TypeClass(
+            name=type_definition.name,
+            fields=lambda: {
+                field.name: get_field(field, type_definition.is_input, type_map)
+                for field in type_definition.fields
+            },
+            description=type_definition.description,
+            **kwargs,
+        )
+
+        type_map[type_definition.name] = ConcreteType(
+            definition=type_definition, implementation=object_type
+        )
+
+    return type_map[type_definition.name].implementation
 
 
 def get_object_type(origin: Type, type_map: TypeMap) -> GraphQLObjectType:
@@ -48,13 +59,7 @@ def get_object_type(origin: Type, type_map: TypeMap) -> GraphQLObjectType:
 
     type_definition: TypeDefinition = origin._type_definition
 
-    name = type_definition.name
-
-    if name not in type_map:
-        object_type = _get_object_type_for_type_definition(type_definition, type_map)
-
-        type_map[name] = ConcreteType(
-            definition=type_definition, implementation=object_type
-        )
-
-    return cast(GraphQLObjectType, type_map[name].implementation)
+    return cast(
+        GraphQLObjectType,
+        _get_object_type_for_type_definition(type_definition, type_map),
+    )


Test patch:
diff --git a/tests/schema/test_interface.py b/tests/schema/test_interface.py
--- a/tests/schema/test_interface.py
+++ b/tests/schema/test_interface.py
@@ -1,46 +1,44 @@
-# TODO: support for this
-
-# def test_query_interface():
-#     """Test that a query on an interface correctly detects instances of the dataclasses
-#        as one of the inherited types, given that types
-#        are registered in addition to the
-#        root type (since there is no field for them)"""
-
-#     @strawberry.interface
-#     class Cheese:
-#         name: str
-
-#     @strawberry.type
-#     class Swiss(Cheese):
-#         canton: str
-
-#     @strawberry.type
-#     class Italian(Cheese):
-#         province: str
-
-#     @strawberry.type
-#     class Root:
-#         @strawberry.field
-#         def assortment(self, info) -> typing.List[Cheese]:
-#             return [
-#                 Italian(name="Asiago", province="Friuli"),
-#                 Swiss(name="Tomme", canton="Vaud"),
-#             ]
-
-#     schema = strawberry.Schema(query=Root, types=[Swiss, Italian])
-
-#     query = """{
-#         assortment {
-#             name
-#             ... on Italian { province }
-#             ... on Swiss { canton }
-#         }
-#     }"""
-
-#     result = schema.execute_sync(query)
-
-#     assert not result.errors
-#     assert result.data["assortment"] == [
-#         {"name": "Asiago", "province": "Friuli"},
-#         {"canton": "Vaud", "name": "Tomme"},
-#     ]
+from typing import List
+
+import strawberry
+
+
+def test_query_interface():
+    @strawberry.interface
+    class Cheese:
+        name: str
+
+    @strawberry.type
+    class Swiss(Cheese):
+        canton: str
+
+    @strawberry.type
+    class Italian(Cheese):
+        province: str
+
+    @strawberry.type
+    class Root:
+        @strawberry.field
+        def assortment(self, info) -> List[Cheese]:
+            return [
+                Italian(name="Asiago", province="Friuli"),
+                Swiss(name="Tomme", canton="Vaud"),
+            ]
+
+    schema = strawberry.Schema(query=Root, types=[Swiss, Italian])
+
+    query = """{
+        assortment {
+            name
+            ... on Italian { province }
+            ... on Swiss { canton }
+        }
+    }"""
+
+    result = schema.execute_sync(query)
+
+    assert not result.errors
+    assert result.data["assortment"] == [
+        {"name": "Asiago", "province": "Friuli"},
+        {"canton": "Vaud", "name": "Tomme"},
+    ]
diff --git a/tests/types/test_interfaces.py b/tests/types/test_interfaces.py
--- a/tests/types/test_interfaces.py
+++ b/tests/types/test_interfaces.py
@@ -39,3 +39,45 @@ class User(Node):
 
     assert definition.is_interface is False
     assert definition.interfaces == [Node._type_definition]
+
+
+def test_implementing_interface_twice():
+    @strawberry.interface
+    class Node:
+        id: strawberry.ID
+
+    @strawberry.type
+    class User(Node):
+        name: str
+
+    @strawberry.type
+    class Person(Node):
+        name: str
+
+    definition = User._type_definition
+
+    assert definition.name == "User"
+    assert len(definition.fields) == 2
+
+    assert definition.fields[0].name == "id"
+    assert definition.fields[0].type == strawberry.ID
+
+    assert definition.fields[1].name == "name"
+    assert definition.fields[1].type == str
+
+    assert definition.is_interface is False
+    assert definition.interfaces == [Node._type_definition]
+
+    definition = Person._type_definition
+
+    assert definition.name == "Person"
+    assert len(definition.fields) == 2
+
+    assert definition.fields[0].name == "id"
+    assert definition.fields[0].type == strawberry.ID
+
+    assert definition.fields[1].name == "name"
+    assert definition.fields[1].type == str
+
+    assert definition.is_interface is False
+    assert definition.interfaces == [Node._type_definition]