Repository: tartiflette/tartiflette (python)

Issue:
Exception is raised when querying `__typename` field on `Union`
* **Tartiflette version:** 0.10.0
* **Python version:** 3.7.1
* **Executed in docker:** No
* **Is a regression from a previous versions?** No

SDL example:
```graphql
type FirstType {
  id: Int!
  firstField: String
}

type SecondType {
  id: Int!
  secondField: String
}

union BothTypes = FirstType | SecondType

type Query {
  bothTypesField: BothTypes
}
```

Query:
```graphql
query {
  bothTypesField {
    __typename
    ... on FirstType {
      id
      firstField
    }
    ... on SecondType {
      id
      secondField
    } 
  }
} == {
  "data": null,
  "errors": [
    {
      "message": "field `BothTypes.__typename` was not found in GraphQL schema.",
      "path": [
        "bothTypesField",
        "__typename"
      ],
      "locations": [
        {
          "column": 17,
          "line": 4
        }
      ]
    }
  ]
}
```

Expected:
```json
{
  "data": {
    "bothTypesField": {
      "__typename": "FirstType",
      "id": 1,
      "firstField": "firstField"
    }
  }
}
```

Pull request:
ISSUE-235 - Fix error when querying `__typename` field on `Union`
Closes #235.

- [x] Code
- [x] Tests
- [x] Changelog

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index abcf7fe..11b88c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 ## [Released]
 
 - [0.10.x]
+  - [0.10.2](./changelogs/0.10.2.md) - 2019-06-11
   - [0.10.1](./changelogs/0.10.1.md) - 2019-06-07
   - [0.10.0](./changelogs/0.10.0.md) - 2019-05-13
 - [0.9.x]
diff --git a/changelogs/0.10.2.md b/changelogs/0.10.2.md
new file mode 100644
index 0000000..c7d2294
--- /dev/null
+++ b/changelogs/0.10.2.md
@@ -0,0 +1,5 @@
+# [0.10.2] - 2019-06-11
+
+## Fixed
+
+[ISSUE-235](https://github.com/tartiflette/tartiflette/issues/235) - Fix handling of `__typename` on Union
diff --git a/setup.py b/setup.py
index e946b0f..984b07a 100644
--- a/setup.py
+++ b/setup.py
@@ -60,7 +60,7 @@ _TEST_REQUIRE = [
 
 _BENCHMARK_REQUIRE = ["pytest-benchmark==3.2.2"]
 
-_VERSION = "0.10.1"
+_VERSION = "0.10.2"
 
 _PACKAGES = find_packages(exclude=["tests*"])
 
diff --git a/tartiflette/schema/schema.py b/tartiflette/schema/schema.py
index 1b21d15..a812144 100644
--- a/tartiflette/schema/schema.py
+++ b/tartiflette/schema/schema.py
@@ -132,7 +132,7 @@ class GraphQLSchema:
             return self.subscription_type
         return None
 
-    #  Introspection Attribute
+    # Introspection Attribute
     @property
     def queryType(  # pylint: disable=invalid-name
         self
@@ -143,7 +143,7 @@ class GraphQLSchema:
             pass
         return None
 
-    #  Introspection Attribute
+    # Introspection Attribute
     @property
     def subscriptionType(  # pylint: disable=invalid-name
         self
@@ -154,7 +154,7 @@ class GraphQLSchema:
             pass
         return None
 
-    #  Introspection Attribute
+    # Introspection Attribute
     @property
     def mutationType(  # pylint: disable=invalid-name
         self
@@ -165,7 +165,7 @@ class GraphQLSchema:
             pass
         return None
 
-    #  Introspection Attribute
+    # Introspection Attribute
     @property
     def types(self) -> List[GraphQLType]:
         return [
@@ -184,7 +184,7 @@ class GraphQLSchema:
     def gql_types(self) -> Dict[str, GraphQLType]:
         return self._gql_types
 
-    #  Introspection Attribute
+    # Introspection Attribute
     @property
     def directives(self) -> List[GraphQLDirective]:
         return list(self._directives.values())
@@ -524,7 +524,10 @@ class GraphQLSchema:
             directive.bake(self)
 
         for gql_type in self._gql_types.values():
-            if isinstance(gql_type, (GraphQLObjectType, GraphQLInterfaceType)):
+            if isinstance(
+                gql_type,
+                (GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType),
+            ):
                 gql_type.bake_fields(custom_default_resolver)
 
     def call_onbuild_directives(self) -> None:
diff --git a/tartiflette/types/union.py b/tartiflette/types/union.py
index 90d225e..ce960b4 100644
--- a/tartiflette/types/union.py
+++ b/tartiflette/types/union.py
@@ -29,6 +29,7 @@ class GraphQLUnionType(GraphQLType):
         self.gql_types = gql_types
         self._possible_types = []
         self._directives = directives
+        self._fields = {}
 
     def __repr__(self) -> str:
         return "{}(name={!r}, gql_types={!r}, description={!r})".format(
@@ -70,3 +71,17 @@ class GraphQLUnionType(GraphQLType):
             ),
             directive_hook="on_introspection",
         )
+
+    def add_field(self, value: "GraphQLField") -> None:
+        if value.name == "__typename":
+            self._fields[value.name] = value
+
+    def find_field(self, name: str) -> "GraphQLField":
+        return self._fields[name]
+
+    def bake_fields(self, custom_default_resolver):
+        for field in self._fields.values():
+            try:
+                field.bake(self._schema, self, custom_default_resolver)
+            except AttributeError:
+                pass


Test patch:
diff --git a/tests/functional/regressions/test_issue_233.py b/tests/functional/regressions/test_issue233.py
similarity index 100%
rename from tests/functional/regressions/test_issue_233.py
rename to tests/functional/regressions/test_issue233.py
diff --git a/tests/functional/regressions/test_issue235.py b/tests/functional/regressions/test_issue235.py
new file mode 100644
index 0000000..6ebb222
--- /dev/null
+++ b/tests/functional/regressions/test_issue235.py
@@ -0,0 +1,153 @@
+import pytest
+
+from tartiflette import Engine, Resolver
+
+_SDL = """
+type FirstType {
+  id: Int!
+  firstField: String
+}
+
+type SecondType {
+  id: Int!
+  secondField: String
+}
+
+union BothTypes = FirstType | SecondType
+
+type Query {
+  bothTypesField: BothTypes
+}
+"""
+
+
+@Resolver("Query.bothTypesField", schema_name="test_issue_235")
+async def resolve_query_both_types_field(*_, **__):
+    return {"_typename": "FirstType", "id": 1, "firstField": "firstField"}
+
+
+_ENGINE = Engine(_SDL, schema_name="test_issue_235")
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "query,expected",
+    [
+        (
+            """
+            {
+              bothTypesField {
+                ... on FirstType {
+                  __typename
+                  id
+                  firstField
+                }
+                ... on SecondType {
+                  __typename
+                  id
+                  secondField
+                } 
+              }
+            }
+            """,
+            {
+                "data": {
+                    "bothTypesField": {
+                        "__typename": "FirstType",
+                        "id": 1,
+                        "firstField": "firstField",
+                    }
+                }
+            },
+        ),
+        (
+            """
+            fragment FirstTypeFields on FirstType {
+              __typename
+              id
+              firstField
+            }
+            
+            fragment SecondTypeFields on SecondType {
+              __typename
+              id
+              secondField
+            }
+            
+            {
+              bothTypesField {
+                ...FirstTypeFields
+                ...SecondTypeFields
+              }
+            }
+            """,
+            {
+                "data": {
+                    "bothTypesField": {
+                        "__typename": "FirstType",
+                        "id": 1,
+                        "firstField": "firstField",
+                    }
+                }
+            },
+        ),
+        (
+            """
+            {
+              bothTypesField {
+                __typename
+                ... on FirstType {
+                  id
+                  firstField
+                }
+                ... on SecondType {
+                  id
+                  secondField
+                } 
+              }
+            }
+            """,
+            {
+                "data": {
+                    "bothTypesField": {
+                        "__typename": "FirstType",
+                        "id": 1,
+                        "firstField": "firstField",
+                    }
+                }
+            },
+        ),
+        (
+            """
+            fragment FirstTypeFields on FirstType {
+              id
+              firstField
+            }
+            
+            fragment SecondTypeFields on SecondType {
+              id
+              secondField
+            }
+            
+            {
+              bothTypesField {
+                __typename
+                ...FirstTypeFields
+                ...SecondTypeFields
+              }
+            }
+            """,
+            {
+                "data": {
+                    "bothTypesField": {
+                        "__typename": "FirstType",
+                        "id": 1,
+                        "firstField": "firstField",
+                    }
+                }
+            },
+        ),
+    ],
+)
+async def test_issue_235(query, expected):
+    assert await _ENGINE.execute(query) == expected