Repository: tartiflette/tartiflette (python)

Issue:
Cannot resolve extended types
With the schema 
```
type Query {
    hello: String
}

extend type Query {
    world: String
}
```
I can see the extended fields in graphiql schema but as soon as i try to declare a resolver for the extended type like this
```
@Resolver('Query.world')
async def revolve_stuff(_, args, ctx, info):
    return 'hi'
```
baking the schema i get the error
```
Traceback (most recent call last):
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette/schema/schema.py", line 348, in get_field_by_name
    return self.type_definitions[parent_name].find_field(field_name)
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette/types/object.py", line 125, in find_field
    return self.implemented_fields[name]
KeyError: 'world'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/Users/morse/Documents/GitHub/play-tartiflette/src/__main__.py", line 54, in <module>
    run()
  File "/Users/morse/Documents/GitHub/play-tartiflette/src/__main__.py", line 52, in run
    web.run_app(app, port=8090,)
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/aiohttp/web.py", line 415, in run_app
    reuse_port=reuse_port))
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
    return future.result()
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/aiohttp/web.py", line 287, in _run_app
    await runner.setup()
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/aiohttp/web_runner.py", line 203, in setup
    self._server = await self._make_server()
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/aiohttp/web_runner.py", line 302, in _make_server
    await self._app.startup()
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/aiohttp/web_app.py", line 389, in startup
    await self.on_startup.send(self)
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/aiohttp/signals.py", line 34, in send
    await receiver(*args, **kwargs)  # type: ignore
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette_aiohttp/__init__.py", line 97, in _cook_on_startup
    sdl=sdl, schema_name=schema_name, modules=modules
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette/engine.py", line 250, in cook
    schema_name, custom_default_resolver, custom_default_type_resolver
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette/schema/bakery.py", line 65, in bake
    schema = SchemaBakery._preheat(schema_name)
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette/schema/bakery.py", line 39, in _preheat
    obj.bake(schema)
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette/resolver/resolver.py", line 67, in bake
    field = schema.get_field_by_name(self.name)
  File "/Users/morse/.pyenv/versions/3.7.2/lib/python3.7/site-packages/tartiflette/schema/schema.py", line 351, in get_field_by_name
    f"field `{name}` was not found in GraphQL schema."
tartiflette.types.exceptions.tartiflette.UnknownSchemaFieldResolver: field `Query.world` was not found in GraphQL schema.
```
I am using the last published 1.0.0rc1 together with tartiflette-aiohttp==1.0.0


Pull request description:
Bakes schema objects after extensions baking
Closes #292.

Patch:
diff --git a/changelogs/1.0.0.md b/changelogs/1.0.0.md
index 142a601..f8bc379 100644
--- a/changelogs/1.0.0.md
+++ b/changelogs/1.0.0.md
@@ -40,3 +40,4 @@
 - The coercion of the arguments is fully functional and the directives applied to the arguments of another directives are now well triggered and computed
 - In some specific and complex cases, the `@if` and `@skip` built-in directives could behave unexpectedly and undesirably, their behaviour has been fixed
 - [ISSUE-277](https://github.com/dailymotion/tartiflette/issues/277) - Abstract types are better handled especially the interfaces that are now executed and resolved correctly when using fragment and the `__typename` field
+- Bakes schema objects after extensions baking - [ISSUE-292](https://github.com/tartiflette/tartiflette/issues/292) thrown by @remorses
diff --git a/setup.py b/setup.py
index 182dcf7..7f99f30 100644
--- a/setup.py
+++ b/setup.py
@@ -60,7 +60,7 @@ _TEST_REQUIRE = [
 
 _BENCHMARK_REQUIRE = ["pytest-benchmark==3.2.2"]
 
-_VERSION = "1.0.0rc1"
+_VERSION = "1.0.0rc2"
 
 _PACKAGES = find_packages(exclude=["tests*"])
 
diff --git a/tartiflette/schema/bakery.py b/tartiflette/schema/bakery.py
index 1f9e095..2c7d28f 100644
--- a/tartiflette/schema/bakery.py
+++ b/tartiflette/schema/bakery.py
@@ -5,14 +5,6 @@ from tartiflette.schema.transformer import schema_from_sdl
 
 __all__ = ("SchemaBakery",)
 
-_SCHEMA_OBJECT_IDS = [
-    "directives",
-    "resolvers",
-    "type_resolvers",
-    "scalars",
-    "subscriptions",
-]
-
 
 class SchemaBakery:
     """
@@ -31,15 +23,8 @@ class SchemaBakery:
         """
         schema_info = SchemaRegistry.find_schema_info(schema_name)
         sdl = schema_info["sdl"]
-
         schema = schema_from_sdl(sdl, schema_name=schema_name)
-
-        for object_ids in _SCHEMA_OBJECT_IDS:
-            for obj in schema_info.get(object_ids, {}).values():
-                obj.bake(schema)
-
         schema_info["inst"] = schema
-
         return schema
 
     @staticmethod
diff --git a/tartiflette/schema/registry.py b/tartiflette/schema/registry.py
index 59f7934..2e1f615 100644
--- a/tartiflette/schema/registry.py
+++ b/tartiflette/schema/registry.py
@@ -7,6 +7,14 @@ from tartiflette.types.exceptions.tartiflette import ImproperlyConfigured
 
 __all__ = ("SchemaRegistry",)
 
+_SCHEMA_OBJECT_IDS = [
+    "directives",
+    "resolvers",
+    "type_resolvers",
+    "scalars",
+    "subscriptions",
+]
+
 
 class SchemaRegistry:
     """
@@ -212,6 +220,13 @@ class SchemaRegistry:
         """
         return SchemaRegistry.find_schema_info(schema_name)["inst"]
 
+    @staticmethod
+    def bake_registered_objects(schema: "GraphQLSchema"):
+        schema_info = SchemaRegistry._schemas[schema.name]
+        for object_id in _SCHEMA_OBJECT_IDS:
+            for obj in schema_info.get(object_id, {}).values():
+                obj.bake(schema)
+
     @classmethod
     def clean(cls) -> None:
         """
diff --git a/tartiflette/schema/schema.py b/tartiflette/schema/schema.py
index 6153d81..f4b5591 100644
--- a/tartiflette/schema/schema.py
+++ b/tartiflette/schema/schema.py
@@ -6,6 +6,7 @@ from tartiflette.schema.introspection import (
     TYPENAME_ROOT_FIELD_DEFINITION,
     prepare_type_root_field,
 )
+from tartiflette.schema.registry import SchemaRegistry
 from tartiflette.types.enum import GraphQLEnumType, GraphQLEnumTypeExtension
 from tartiflette.types.exceptions.tartiflette import (
     GraphQLSchemaError,
@@ -483,10 +484,11 @@ class GraphQLSchema:
         """
         errors = []
         for type_name, gql_type in self.type_definitions.items():
-            if (
-                isinstance(gql_type, GraphQLObjectType)
-                and not gql_type.implemented_fields.values()
-            ):
+            if isinstance(gql_type, GraphQLObjectType) and not [
+                field_name
+                for field_name in gql_type.implemented_fields
+                if not field_name.startswith("__")
+            ]:
                 errors.append(f"Type < {type_name} > has no fields.")
         return errors
 
@@ -978,6 +980,13 @@ class GraphQLSchema:
 
         try:
             self._bake_extensions()
+        except Exception:  # pylint: disable=broad-except
+            # Exceptions should be collected at validation time
+            pass
+
+        SchemaRegistry.bake_registered_objects(self)
+
+        try:
             await self._bake_types(custom_default_resolver)
         except Exception:  # pylint: disable=broad-except
             # Exceptions should be collected at validation time


Test patch:
diff --git a/tests/functional/regressions/test_issue292.py b/tests/functional/regressions/test_issue292.py
new file mode 100644
index 0000000..3245a7b
--- /dev/null
+++ b/tests/functional/regressions/test_issue292.py
@@ -0,0 +1,73 @@
+import pytest
+
+from tartiflette import Resolver, create_engine
+
+
+@pytest.fixture(scope="module")
+async def ttftt_engine():
+    sdl = """
+    type Query {
+      hello: String
+    }
+    
+    type Mutation {
+      hello: String
+    }
+    
+    type Subscription {
+      hello: String
+    }
+
+    extend type Query {
+      world: String
+    }
+
+    extend type Mutation {
+      world: String
+    }
+
+    extend type Subscription {
+      world: String
+    }
+    """
+
+    @Resolver("Query.hello", schema_name="test_issue292")
+    @Resolver("Query.world", schema_name="test_issue292")
+    async def resolve_query_world(*_):
+        return "World"
+
+    @Resolver("Mutation.world", schema_name="test_issue292")
+    async def resolve_mutation_world(*_):
+        return "World"
+
+    @Resolver("Subscription.world", schema_name="test_issue292")
+    async def resolve_subscription_world(*_):
+        return "World"
+
+    return await create_engine(sdl, schema_name="test_issue292")
+
+
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+    "query,expected",
+    [
+        (
+            """
+            query {
+              world
+            }
+            """,
+            {"data": {"world": "World"}},
+        ),
+        (
+            """
+            mutation {
+              world
+            }
+            """,
+            {"data": {"world": "World"}},
+        ),
+    ],
+)
+async def test_issue292(query, expected, ttftt_engine):
+    assert await ttftt_engine.execute(query) == expected
diff --git a/tests/unit/test_schema_unit.py b/tests/unit/test_schema_unit.py
index 4460e0e..a131ab8 100644
--- a/tests/unit/test_schema_unit.py
+++ b/tests/unit/test_schema_unit.py
@@ -85,7 +85,7 @@ async def test_schema_object_get_field_name(clean_registry):
 
 
 @pytest.mark.parametrize(
-    "full_sdl,expected_error,expected_value",
+    "full_sdl,expected_error",
     [
         # Happy path
         (
@@ -104,7 +104,6 @@ async def test_schema_object_get_field_name(clean_registry):
         }
         """,
             False,
-            True,
         ),
         (
             """
@@ -114,7 +113,6 @@ async def test_schema_object_get_field_name(clean_registry):
         }
         """,
             False,
-            True,
         ),
         (
             """
@@ -123,7 +121,6 @@ async def test_schema_object_get_field_name(clean_registry):
         }
         """,
             False,
-            True,
         ),
         (
             """
@@ -143,7 +140,6 @@ async def test_schema_object_get_field_name(clean_registry):
         }
         """,
             False,
-            True,
         ),
         # Sad path
         (
@@ -157,13 +153,12 @@ async def test_schema_object_get_field_name(clean_registry):
         }
         """,
             True,
-            False,
         ),
     ],
 )
 @pytest.mark.asyncio
 async def test_schema_validate_named_types(
-    full_sdl, expected_error, expected_value, clean_registry
+    full_sdl, expected_error, clean_registry
 ):
 
     _, full_sdl = await _import_builtins([], full_sdl, "A")
@@ -172,13 +167,13 @@ async def test_schema_validate_named_types(
 
     if expected_error:
         with pytest.raises(GraphQLSchemaError):
-            generated_schema._validate()
+            await generated_schema.bake()
     else:
-        assert generated_schema._validate() == expected_value
+        await generated_schema.bake()
 
 
 @pytest.mark.parametrize(
-    "full_sdl,expected_error,expected_value",
+    "full_sdl,expected_error",
     [
         # Happy path
         (
@@ -200,7 +195,6 @@ async def test_schema_validate_named_types(
         }
         """,
             False,
-            True,
         ),
         (
             """
@@ -223,7 +217,6 @@ async def test_schema_validate_named_types(
         }
         """,
             False,
-            True,
         ),
         (
             """
@@ -249,7 +242,6 @@ async def test_schema_validate_named_types(
         }
         """,
             False,
-            True,
         ),
         # Sad path
         (
@@ -273,7 +265,6 @@ async def test_schema_validate_named_types(
         }
         """,
             True,
-            False,
         ),
         (
             """
@@ -288,7 +279,6 @@ async def test_schema_validate_named_types(
         }
         """,
             True,
-            False,
         ),
         (
             """
@@ -303,7 +293,6 @@ async def test_schema_validate_named_types(
         }
         """,
             True,
-            False,
         ),
         (
             """
@@ -326,14 +315,13 @@ async def test_schema_validate_named_types(
         }
         """,
             True,
-            False,
         ),
     ],
     ids=["1", "2", "3", "4", "5", "6", "7"],
 )
 @pytest.mark.asyncio
 async def test_schema_validate_object_follow_interfaces(
-    full_sdl, expected_error, expected_value, clean_registry
+    full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "A")
     clean_registry.register_sdl("A", full_sdl)
@@ -355,13 +343,13 @@ async def test_schema_validate_object_follow_interfaces(
 
     if expected_error:
         with pytest.raises(GraphQLSchemaError):
-            generated_schema._validate()
+            await generated_schema.bake()
     else:
-        assert generated_schema._validate() == expected_value
+        await generated_schema.bake()
 
 
 @pytest.mark.parametrize(
-    "full_sdl,expected_error,expected_value",
+    "full_sdl,expected_error",
     [
         # Happy path
         (
@@ -371,7 +359,6 @@ async def test_schema_validate_object_follow_interfaces(
         }
         """,
             False,
-            True,
         ),
         (
             """
@@ -392,7 +379,6 @@ async def test_schema_validate_object_follow_interfaces(
         }
         """,
             False,
-            True,
         ),
         # Sad path
         (
@@ -402,7 +388,6 @@ async def test_schema_validate_object_follow_interfaces(
         }
         """,
             True,
-            False,
         ),
         (
             """
@@ -420,7 +405,6 @@ async def test_schema_validate_object_follow_interfaces(
         }
         """,
             True,
-            False,
         ),
         (
             """
@@ -438,7 +422,6 @@ async def test_schema_validate_object_follow_interfaces(
         }
         """,
             True,
-            False,
         ),
         (
             """
@@ -456,13 +439,12 @@ async def test_schema_validate_object_follow_interfaces(
         }
         """,
             True,
-            False,
         ),
     ],
 )
 @pytest.mark.asyncio
 async def test_schema_validate_root_types_exist(
-    full_sdl, expected_error, expected_value, clean_registry
+    full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "a")
     clean_registry.register_sdl("a", full_sdl)
@@ -470,13 +452,13 @@ async def test_schema_validate_root_types_exist(
 
     if expected_error:
         with pytest.raises(GraphQLSchemaError):
-            generated_schema._validate()
+            await generated_schema.bake()
     else:
-        assert generated_schema._validate() == expected_value
+        await generated_schema.bake()
 
 
 @pytest.mark.parametrize(
-    "full_sdl,expected_error,expected_value",
+    "full_sdl,expected_error",
     [
         # Happy path
         (
@@ -486,21 +468,20 @@ async def test_schema_validate_root_types_exist(
         }
         """,
             False,
-            True,
         ),
         # Sad path
         (
             """
+        type Foo
         type Query
         """,
             True,
-            False,
         ),
     ],
 )
 @pytest.mark.asyncio
 async def test_schema_validate_non_empty_object(
-    full_sdl, expected_error, expected_value, clean_registry
+    full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "a")
     clean_registry.register_sdl("a", full_sdl)
@@ -508,13 +489,13 @@ async def test_schema_validate_non_empty_object(
 
     if expected_error:
         with pytest.raises(GraphQLSchemaError):
-            generated_schema._validate()
+            await generated_schema.bake()
     else:
-        assert generated_schema._validate() == expected_value
+        await generated_schema.bake()
 
 
 @pytest.mark.parametrize(
-    "full_sdl,expected_error,expected_value",
+    "full_sdl,expected_error",
     [
         # Happy path
         (
@@ -534,7 +515,6 @@ async def test_schema_validate_non_empty_object(
         union Test = Something | Else
         """,
             False,
-            True,
         ),
         # Sad path
         (
@@ -546,13 +526,12 @@ async def test_schema_validate_non_empty_object(
         union Test = Test | Test
         """,
             True,
-            False,
         ),
     ],
 )
 @pytest.mark.asyncio
 async def test_schema_validate_union_is_acceptable(
-    full_sdl, expected_error, expected_value, clean_registry
+    full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "a")
     clean_registry.register_sdl("a", full_sdl)
@@ -560,9 +539,9 @@ async def test_schema_validate_union_is_acceptable(
 
     if expected_error:
         with pytest.raises(GraphQLSchemaError):
-            generated_schema._validate()
+            await generated_schema.bake()
     else:
-        assert generated_schema._validate() == expected_value
+        await generated_schema.bake()
 
 
 @pytest.mark.asyncio