Repository: edgedb/edgedb

Issue:
GraphQL crash
Our benchmarks app is no longer able to create a test edgedb db. See the below error dump:

```
 Exception occurred: Mutation fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping.

1. AssertionError: Mutation fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping.

-------------------------------------------- Traceback --------------------------------------------

    /Users/yury/dev/edge/edgedb/edb/server/compiler/compiler.py, line 204, in connect
        > await self._get_database(dbver)
    /Users/yury/dev/edge/edgedb/edb/server/compiler/compiler.py, line 193, in _get_database
        > db = self._wrap_schema(dbver, con_args, schema)
    /Users/yury/dev/edge/edgedb/edb/server/http_graphql_port/compiler.py, line 56, in _wrap_schema
        > gqlcore = graphql.GQLCoreSchema(schema)
    /Users/yury/dev/edge/edgedb/edb/graphql/types.py, line 150, in __init__
        > query=query, mutation=mutation, types=types)
    /Users/yury/dev/venvs/edgedb/lib/python3.7/site-packages/graphql/type/schema.py, line 104, in __init__
        > self._type_map = GraphQLTypeMap(initial_types)  # type: GraphQLTypeMap
    /Users/yury/dev/venvs/edgedb/lib/python3.7/site-packages/graphql/type/typemap.py, line 28, in __init__
        > self.update(reduce(self.reducer, types, OrderedDict()))  # type: ignore
    /Users/yury/dev/venvs/edgedb/lib/python3.7/site-packages/graphql/type/typemap.py, line 106, in reducer
        > field_map = type.fields
    /Users/yury/dev/venvs/edgedb/lib/python3.7/site-packages/graphql/pyutils/cached_property.py, line 22, in __get__
        > value = obj.__dict__[self.func.__name__] = self.func(obj)
    /Users/yury/dev/venvs/edgedb/lib/python3.7/site-packages/graphql/type/definition.py, line 226, in fields
        > return define_field_map(self, self._fields)
    /Users/yury/dev/venvs/edgedb/lib/python3.7/site-packages/graphql/type/definition.py, line 245, in define_field_map
        > ).format(type)

AssertionError: Mutation fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping.
 Exception occurred: Mutation fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping.

1. AssertionError: Mutation fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping.
```


Discussion:
Steps to reproduce:

1. create a brand new edgedb cluster
2. run `make load-edgedb` in the webapp-bench repo.
Hmm... I have an even shorter way to reproduce the same error:
1. Create a new edgedb cluster.
2. Expose a blank (freshly initialized) database on a GraphQL port.

I'm looking into it.

Resolving patch:
diff --git a/edb/graphql/types.py b/edb/graphql/types.py
--- a/edb/graphql/types.py
+++ b/edb/graphql/types.py
@@ -132,10 +132,19 @@ def __init__(self, edb_schema):
             fields=self.get_fields('Query'),
         )
 
-        mutation = self._gql_objtypes['Mutation'] = GraphQLObjectType(
-            name='Mutation',
-            fields=self.get_fields('Mutation'),
-        )
+        # If a database only has abstract types and scalars, no
+        # mutations will be possible (such as in a blank database),
+        # but we would still want the reflection to work without
+        # error, even if all that can be discovered through GraphQL
+        # then is the schema.
+        fields = self.get_fields('Mutation')
+        if fields:
+            mutation = self._gql_objtypes['Mutation'] = GraphQLObjectType(
+                name='Mutation',
+                fields=fields,
+            )
+        else:
+            mutation = None
 
         # get a sorted list of types relevant for the Schema
         types = [


Test patch:
diff --git a/tests/test_http_graphql_query.py b/tests/test_http_graphql_query.py
--- a/tests/test_http_graphql_query.py
+++ b/tests/test_http_graphql_query.py
@@ -5094,3 +5094,28 @@ def test_graphql_functional_type_14(self):
                 ]
             }
         })
+
+
+class TestGraphQLInit(tb.GraphQLTestCase):
+    """Test GraphQL initialization on an empty database."""
+
+    # GraphQL queries cannot run in a transaction
+    ISOLATED_METHODS = False
+
+    def test_graphql_init_type_01(self):
+        # An empty database should still have an "Object" interface.
+        self.assert_graphql_query_result(r"""
+            query {
+                __type(name: "Object") {
+                    __typename
+                    name
+                    kind
+                }
+            }
+        """, {
+            "__type": {
+                "kind": "INTERFACE",
+                "name": "Object",
+                "__typename": "__Type"
+            }
+        })