From 0d8d5a7215f15ae16e2127f0973cfb5364e18b16 Mon Sep 17 00:00:00 2001 From: Victor Petrovykh Date: Wed, 20 Jun 2018 12:13:43 -0400 Subject: [PATCH] graphql: Implement reflection of ObjectTypes. ObjectTypes get reflected as type INTERFACE into GraphQL. Additionally, non-abstract ObjectTypes get reflected as type OBJECT, but with the "Type" appended at the end of their name. This allows the return type of fields/functions to be specified in terms of INTERFACE types and the actual object __typename to reflect the OBJECT type of the specific instance. --- edb/lang/graphql/translator.py | 31 +- edb/lang/graphql/types.py | 54 ++-- edb/server/protocol.py | 7 +- tests/test_graphql_translator.py | 485 +++++++++++++++++++++++-------- 4 files changed, 395 insertions(+), 182 deletions(-) diff --git a/edb/lang/graphql/translator.py b/edb/lang/graphql/translator.py index bff50f6cff..7c2f86b65d 100644 --- a/edb/lang/graphql/translator.py +++ b/edb/lang/graphql/translator.py @@ -33,8 +33,7 @@ class GraphQLTranslatorContext: - def __init__(self, *, schema, gqlcore, variables, operation_name, query, - modules): + def __init__(self, *, schema, gqlcore, variables, operation_name, query): self.schema = schema self.variables = variables self.operation_name = operation_name @@ -44,11 +43,9 @@ def __init__(self, *, schema, gqlcore, variables, operation_name, query, self.fields = [] self.path = [] self.include_base = [False] - self.gql_schema = gt.Schema(schema, modules) - self.gqlcore = gqlcore + self.gql_schema = gt.Schema(gqlcore) + self.gqlcore_schema = gqlcore._gql_schema self.query = query - self.modules = list(modules) - self.modules.sort() Step = namedtuple('Step', ['name', 'type']) @@ -96,7 +93,7 @@ def visit_Document(self, node): } gqlresult = gql_proc( - self._context.gqlcore, + self._context.gqlcore_schema, self._context.query, variable_values={ name[1:]: val for name, val in self._context.variables.items() @@ -167,8 +164,7 @@ def _visit_query(self, node): self.visit(node.variables) # base Query needs to be configured specially - base = self._context.gql_schema.get( - 'Query', modules=self._context.modules) + base = self._context.gql_schema.get('Query') # special treatment of the selection_set, different from inner # recursion @@ -246,12 +242,7 @@ def _is_duplicate_field(self, node): name = node.alias or node.name dup = self._context.fields[-1].get(name) if dup: - if not ast.nodes_equal(dup, node): - raise GraphQLValidationError( - f"field {name!r} has ambiguous definition", - context=node.context) - else: - return True + return True else: self._context.fields[-1][name] = node @@ -578,23 +569,19 @@ def combine_field_results(self, results, *, flatten=True): return results -def translate(schema, graphql, *, variables=None, operation_name=None, - modules=None): +def translate(schema, graphql, *, variables=None, operation_name=None): if variables is None: variables = {} - if modules is None: - modules = set(modules) | {'default'} - # HACK query = re.sub(r'@edgedb\(.*?\)', '', graphql) - schema2 = gt.GQLCoreSchema(schema, *modules)._gql_schema + schema2 = gt.GQLCoreSchema(schema) parser = gqlparser.GraphQLParser() gqltree = parser.parse(graphql) context = GraphQLTranslatorContext( schema=schema, gqlcore=schema2, query=query, - variables=variables, operation_name=operation_name, modules=modules) + variables=variables, operation_name=operation_name) edge_forest_map = GraphQLTranslator(context=context).visit(gqltree) code = [] for name, (tree, critvars) in sorted(edge_forest_map.items()): diff --git a/edb/lang/graphql/types.py b/edb/lang/graphql/types.py index 620c6c10b6..b7d0e9f8ce 100644 --- a/edb/lang/graphql/types.py +++ b/edb/lang/graphql/types.py @@ -33,7 +33,6 @@ GraphQLBoolean, GraphQLID, ) -import itertools from edb.lang.edgeql import ast as qlast from edb.lang.edgeql import codegen @@ -65,11 +64,17 @@ class GQLCoreSchema: - def __init__(self, edb_schema, *modules): - '''Create a graphql schema based on specific modules from edgedb.''' + def __init__(self, edb_schema): + '''Create a graphql schema based on edgedb schema.''' self.edb_schema = edb_schema - self.modules = modules + # extract and sort modules to have a consistent type ordering + self.modules = { + m.name for m in + self.edb_schema.get_modules() + } - {'schema', 'graphql'} + self.modules = list(self.modules) + self.modules.sort() self._gql_interfaces = {} self._gql_objtypes = {} @@ -131,9 +136,8 @@ def get_fields(self, typename): fields = OrderedDict() if typename == 'Query': - for name, gqltype in sorted(itertools.chain( - self._gql_interfaces.items(), self._gql_objtypes.items()), - key=lambda x: x[1].name): + for name, gqltype in sorted(self._gql_interfaces.items(), + key=lambda x: x[1].name): if name == typename: continue fields[name.split('::', 1)[1]] = GraphQLField( @@ -176,20 +180,22 @@ def get_args(self, typename): return args def _define_types(self): - abstract_types = [] + interface_types = [] obj_types = [] for modname in self.modules: # get all descendants of this abstract type module = self.edb_schema.get_module(modname) - abstract_types += [t for t in module.get_objects() - if isinstance(t, ObjectType) and t.is_abstract] - obj_types += [t for t in module.get_objects() - if isinstance(t, ObjectType) and not t.is_abstract] + # every ObjectType is reflected as an interface + interface_types += [t for t in module.get_objects() + if isinstance(t, ObjectType)] + + # concrete types are also reflected as Type (with a 'Type' postfix) + obj_types += [t for t in interface_types if not t.is_abstract] # interfaces - for t in abstract_types: + for t in interface_types: gqltype = GraphQLInterfaceType( name=self.get_short_name(t.name), fields=partial(self.get_fields, t.name), @@ -206,12 +212,12 @@ def _define_types(self): continue for st in t.get_mro(): - if (isinstance(st, ObjectType) and st.is_abstract and + if (isinstance(st, ObjectType) and st.name in self._gql_interfaces): interfaces.append(self._gql_interfaces[st.name]) gqltype = GraphQLObjectType( - name=self.get_short_name(t.name), + name=self.get_short_name(t.name) + 'Type', fields=partial(self.get_fields, t.name), interfaces=interfaces, ) @@ -255,10 +261,10 @@ def __contains__(self, item): class Schema: '''This is the schema from GQL perspective.''' - def __init__(self, schema, modules): - self._schema = schema + def __init__(self, gqlcore): + self._schema = gqlcore.edb_schema self._type_map = {} - self.modules = modules + self.modules = gqlcore.modules @property def edb_schema(self): @@ -505,11 +511,7 @@ class GQLQuery(GQLBaseType): shadow_fields = {'__typename'} def __init__(self, schema, **kwargs): - self.modules = kwargs['modules'] - self.modules.sort() - # we give unusual full name, so that it doesn't clash with a - # potential ObjectType `Query` in one of the modules - kwargs['name'] = f'{self.modules}--Query' + self.modules = schema.modules super().__init__(schema, **kwargs) @property @@ -554,11 +556,7 @@ class GQLMutation(GQLBaseType): edb_type = 'graphql::Mutation' def __init__(self, schema, **kwargs): - self.modules = kwargs['modules'] - self.modules.sort() - # we give unusual full name, so that it doesn't clash with a - # potential ObjectType `Mutation` in one of the modules - kwargs['name'] = f'{self.modules}--Mutation' + self.modules = schema.modules super().__init__(schema, **kwargs) @property diff --git a/edb/server/protocol.py b/edb/server/protocol.py index 48a1b21b58..4626668e40 100644 --- a/edb/server/protocol.py +++ b/edb/server/protocol.py @@ -237,14 +237,9 @@ async def _run_script(self, script, *, graphql=False, flags={}): if graphql: with timer.timeit('graphql_translation'): - modules = { - m.name for m in - self.backend.schema.get_modules() - } - {'schema', 'graphql'} script = graphql_compiler.translate( self.backend.schema, script, - variables={}, - modules=modules) + ';' + variables={}) + ';' with timer.timeit('parse_eql'): statements = edgeql.parse_block(script) diff --git a/tests/test_graphql_translator.py b/tests/test_graphql_translator.py index 438f17d963..002c25bb55 100644 --- a/tests/test_graphql_translator.py +++ b/tests/test_graphql_translator.py @@ -27,7 +27,7 @@ from edb.lang.common import markup from edb.lang import graphql as edge_graphql from edb.lang import edgeql as edge_edgeql -from edb.lang.graphql.errors import GraphQLValidationError, GraphQLCoreError +from edb.lang.graphql.errors import GraphQLCoreError from edb.lang.schema import declarative as s_decl from edb.lang.schema import std as s_std @@ -48,13 +48,6 @@ def wrap(func): return wrap -def with_modules(*names): - def wrap(func): - tb._set_spec(func, 'modules', names) - return func - return wrap - - def translate_only(func): tb._set_spec(func, 'translate_only', True) return func @@ -102,7 +95,6 @@ def run_test(self, *, source, spec, expected=None): self.schema, source, variables=spec.get('variables'), operation_name=spec.get('operation_name'), - modules=set(spec.get('modules', {})) | {'test', 'default'}, ) if debug: @@ -146,15 +138,9 @@ class TestGraphQLTranslation(TranslatorTest): required property score -> float64 link profile -> Profile: cardinality := '*1' - """ - - SCHEMA_MOD2 = r""" - import test type Person extending test::User - """ - SCHEMA_123LIB = r""" type Foo: property `select` -> str property after -> str @@ -1912,60 +1898,6 @@ def test_graphql_translation_fragment_type_12(self): }; """ - @with_modules('test', 'mod2') - def test_graphql_translation_import_01(self): - r""" - fragment groupFrag on UserGroup { - id - name - } - - query { - Person { - id - name - groups { - ... groupFrag - } - } - } - -% OK % - - SELECT graphql::Query { - Person := (SELECT - mod2::Person { - id, - name, - groups: { - id, - name, - } - }) - }; - """ - - @tb.must_fail(GraphQLCoreError, - 'Cannot query field "Person" on type "Query"', - line=8, col=13) - def test_graphql_translation_import_02(self): - r""" - fragment groupFrag on UserGroup { - id - name - } - - query { - Person { - id - name - groups { - ... groupFrag - } - } - } - """ - def test_graphql_translation_duplicates_01(self): r""" query { @@ -2063,7 +1995,6 @@ def test_graphql_translation_duplicates_04(self): }; """ - @tb.must_fail(GraphQLValidationError, line=6, col=17) def test_graphql_translation_duplicates_05(self): r""" query { @@ -2074,9 +2005,20 @@ def test_graphql_translation_duplicates_05(self): name @skip(if: false) } } + +% OK % + + SELECT graphql::Query { + User := (SELECT + test::User { + id, + name, + }) + }; """ - @tb.must_fail(GraphQLCoreError, line=4, col=17) + # graphql parser has an issue here + @unittest.expectedFailure def test_graphql_translation_duplicates_06(self): r""" query { @@ -2088,9 +2030,18 @@ def test_graphql_translation_duplicates_06(self): name } } + +% OK % + + SELECT graphql::Query { + User := (SELECT + test::User { + name, + id, + }) + }; """ - @tb.must_fail(GraphQLValidationError, line=3, col=13) def test_graphql_translation_duplicates_07(self): r""" fragment f1 on User { @@ -2110,9 +2061,18 @@ def test_graphql_translation_duplicates_07(self): name @include(if: true) } } + +% OK % + + SELECT graphql::Query { + User := (SELECT + test::User { + id, + name, + }) + }; """ - @with_modules('123lib') def test_graphql_translation_quoting_01(self): r""" query { @@ -2126,12 +2086,12 @@ def test_graphql_translation_quoting_01(self): SELECT graphql::Query { Foo := (SELECT - `123lib`::Foo { + test::Foo { `select`, after } FILTER - (`123lib`::Foo.`select` = 'bar')) + (test::Foo.`select` = 'bar')) }; """ @@ -2447,16 +2407,19 @@ def test_graphql_translation_schema_07(self): __schema := '{ "types": [ {"kind": "OBJECT", "name": "Query"}, - {"kind": "SCALAR", "name": "ID"}, {"kind": "SCALAR", "name": "String"}, + {"kind": "SCALAR", "name": "ID"}, + {"kind": "INTERFACE", "name": "Foo"}, {"kind": "INTERFACE", "name": "NamedObject"}, - {"kind": "OBJECT", "name": "Profile"}, - {"kind": "SCALAR", "name": "Int"}, - {"kind": "OBJECT", "name": "Setting"}, + {"kind": "INTERFACE", "name": "Object"}, {"kind": "SCALAR", "name": "Boolean"}, + {"kind": "SCALAR", "name": "Int"}, {"kind": "SCALAR", "name": "Float"}, - {"kind": "OBJECT", "name": "User"}, - {"kind": "OBJECT", "name": "UserGroup"}, + {"kind": "INTERFACE", "name": "Person"}, + {"kind": "INTERFACE", "name": "UserGroup"}, + {"kind": "INTERFACE", "name": "Setting"}, + {"kind": "INTERFACE", "name": "Profile"}, + {"kind": "INTERFACE", "name": "User"}, {"kind": "OBJECT", "name": "__Schema"}, {"kind": "OBJECT", "name": "__Type"}, {"kind": "ENUM", "name": "__TypeKind"}, @@ -2464,7 +2427,13 @@ def test_graphql_translation_schema_07(self): {"kind": "OBJECT", "name": "__InputValue"}, {"kind": "OBJECT", "name": "__EnumValue"}, {"kind": "OBJECT", "name": "__Directive"}, - {"kind": "ENUM", "name": "__DirectiveLocation"} + {"kind": "ENUM", "name": "__DirectiveLocation"}, + {"kind": "OBJECT", "name": "UserGroupType"}, + {"kind": "OBJECT", "name": "SettingType"}, + {"kind": "OBJECT", "name": "ProfileType"}, + {"kind": "OBJECT", "name": "UserType"}, + {"kind": "OBJECT", "name": "PersonType"}, + {"kind": "OBJECT", "name": "FooType"} ] }' }; @@ -2487,16 +2456,19 @@ def test_graphql_translation_schema_08(self): Foo := '{ "types": [ {"kind": "OBJECT", "name": "Query"}, - {"kind": "SCALAR", "name": "ID"}, {"kind": "SCALAR", "name": "String"}, + {"kind": "SCALAR", "name": "ID"}, + {"kind": "INTERFACE", "name": "Foo"}, {"kind": "INTERFACE", "name": "NamedObject"}, - {"kind": "OBJECT", "name": "Profile"}, - {"kind": "SCALAR", "name": "Int"}, - {"kind": "OBJECT", "name": "Setting"}, + {"kind": "INTERFACE", "name": "Object"}, {"kind": "SCALAR", "name": "Boolean"}, + {"kind": "SCALAR", "name": "Int"}, {"kind": "SCALAR", "name": "Float"}, - {"kind": "OBJECT", "name": "User"}, - {"kind": "OBJECT", "name": "UserGroup"}, + {"kind": "INTERFACE", "name": "Person"}, + {"kind": "INTERFACE", "name": "UserGroup"}, + {"kind": "INTERFACE", "name": "Setting"}, + {"kind": "INTERFACE", "name": "Profile"}, + {"kind": "INTERFACE", "name": "User"}, {"kind": "OBJECT", "name": "__Schema"}, {"kind": "OBJECT", "name": "__Type"}, {"kind": "ENUM", "name": "__TypeKind"}, @@ -2504,7 +2476,13 @@ def test_graphql_translation_schema_08(self): {"kind": "OBJECT", "name": "__InputValue"}, {"kind": "OBJECT", "name": "__EnumValue"}, {"kind": "OBJECT", "name": "__Directive"}, - {"kind": "ENUM", "name": "__DirectiveLocation"} + {"kind": "ENUM", "name": "__DirectiveLocation"}, + {"kind": "OBJECT", "name": "UserGroupType"}, + {"kind": "OBJECT", "name": "SettingType"}, + {"kind": "OBJECT", "name": "ProfileType"}, + {"kind": "OBJECT", "name": "UserType"}, + {"kind": "OBJECT", "name": "PersonType"}, + {"kind": "OBJECT", "name": "FooType"} ] }' }; @@ -2590,16 +2568,19 @@ def test_graphql_translation_schema_10(self): __schema := '{ "types": [ {"kind": "OBJECT", "name": "Query"}, - {"kind": "SCALAR", "name": "ID"}, {"kind": "SCALAR", "name": "String"}, + {"kind": "SCALAR", "name": "ID"}, + {"kind": "INTERFACE", "name": "Foo"}, {"kind": "INTERFACE", "name": "NamedObject"}, - {"kind": "OBJECT", "name": "Profile"}, - {"kind": "SCALAR", "name": "Int"}, - {"kind": "OBJECT", "name": "Setting"}, + {"kind": "INTERFACE", "name": "Object"}, {"kind": "SCALAR", "name": "Boolean"}, + {"kind": "SCALAR", "name": "Int"}, {"kind": "SCALAR", "name": "Float"}, - {"kind": "OBJECT", "name": "User"}, - {"kind": "OBJECT", "name": "UserGroup"}, + {"kind": "INTERFACE", "name": "Person"}, + {"kind": "INTERFACE", "name": "UserGroup"}, + {"kind": "INTERFACE", "name": "Setting"}, + {"kind": "INTERFACE", "name": "Profile"}, + {"kind": "INTERFACE", "name": "User"}, {"kind": "OBJECT", "name": "__Schema"}, {"kind": "OBJECT", "name": "__Type"}, {"kind": "ENUM", "name": "__TypeKind"}, @@ -2607,7 +2588,13 @@ def test_graphql_translation_schema_10(self): {"kind": "OBJECT", "name": "__InputValue"}, {"kind": "OBJECT", "name": "__EnumValue"}, {"kind": "OBJECT", "name": "__Directive"}, - {"kind": "ENUM", "name": "__DirectiveLocation"} + {"kind": "ENUM", "name": "__DirectiveLocation"}, + {"kind": "OBJECT", "name": "UserGroupType"}, + {"kind": "OBJECT", "name": "SettingType"}, + {"kind": "OBJECT", "name": "ProfileType"}, + {"kind": "OBJECT", "name": "UserType"}, + {"kind": "OBJECT", "name": "PersonType"}, + {"kind": "OBJECT", "name": "FooType"} ] }' }; @@ -2629,7 +2616,7 @@ def test_graphql_translation_type_01(self): __type := '{ "__typename": "__Type", "name": "User", - "kind": "OBJECT" + "kind": "INTERFACE" }' }; """ @@ -2637,7 +2624,7 @@ def test_graphql_translation_type_01(self): def test_graphql_translation_type_02(self): r""" query { - __type(name: "User") { + __type(name: "UserType") { __typename kind name @@ -2667,11 +2654,17 @@ def test_graphql_translation_type_02(self): __type := '{ "__typename": "__Type", "kind": "OBJECT" - "name": "User", + "name": "UserType", "description": null, "interfaces": [ + { + "name": "User" + }, { "name": "NamedObject" + }, + { + "name": "Object" } ], "possibleTypes": null, @@ -2683,6 +2676,57 @@ def test_graphql_translation_type_02(self): """ def test_graphql_translation_type_03(self): + r""" + query { + __type(name: "User") { + __typename + kind + name + description + interfaces { + name + } + possibleTypes { + name + } + enumValues { + name + } + inputFields { + name + } + ofType { + name + } + + } + } + +% OK % + + SELECT graphql::Query { + __type := '{ + "__typename": "__Type", + "kind": "INTERFACE" + "name": "User", + "description": null, + "interfaces": null, + "possibleTypes": [ + { + "name": "UserType" + }, + { + "name": "PersonType" + } + ], + "enumValues": null, + "inputFields": null, + "ofType": null + }' + }; + """ + + def test_graphql_translation_type_04(self): r""" query { __type(name: "UserGroup") { @@ -2733,7 +2777,7 @@ def test_graphql_translation_type_03(self): __type := '{ "__typename": "__Type", "name": "UserGroup", - "kind": "OBJECT", + "kind": "INTERFACE", "fields": [ { "__typename": "__Field", @@ -2786,7 +2830,7 @@ def test_graphql_translation_type_03(self): "ofType": { "__typename": "__Type", "name": "Setting", - "kind": "OBJECT", + "kind": "INTERFACE", "ofType": null } } @@ -2799,7 +2843,7 @@ def test_graphql_translation_type_03(self): }; """ - def test_graphql_translation_type_04(self): + def test_graphql_translation_type_05(self): r""" fragment _t on __Type { __typename @@ -2808,7 +2852,7 @@ def test_graphql_translation_type_04(self): } query { - __type(name: "UserGroup") { + __type(name: "UserGroupType") { ..._t fields { __typename @@ -2843,7 +2887,7 @@ def test_graphql_translation_type_04(self): SELECT graphql::Query { __type := '{ "__typename": "__Type", - "name": "UserGroup", + "name": "UserGroupType", "kind": "OBJECT", "fields": [ { @@ -2897,7 +2941,7 @@ def test_graphql_translation_type_04(self): "ofType": { "__typename": "__Type", "name": "Setting", - "kind": "OBJECT", + "kind": "INTERFACE", "ofType": null } } @@ -2910,10 +2954,10 @@ def test_graphql_translation_type_04(self): }; """ - def test_graphql_translation_type_05(self): + def test_graphql_translation_type_06(self): r""" query { - __type(name: "Profile") { + __type(name: "ProfileType") { __typename name kind @@ -2960,7 +3004,7 @@ def test_graphql_translation_type_05(self): SELECT graphql::Query { __type := '{ "__typename": "__Type", - "name": "Profile", + "name": "ProfileType", "kind": "OBJECT", "fields": [ { @@ -3079,7 +3123,7 @@ def test_graphql_translation_type_05(self): """ - def test_graphql_translation_type_06(self): + def test_graphql_translation_type_07(self): r""" fragment _t on __Type { __typename @@ -3200,7 +3244,7 @@ def test_graphql_translation_type_06(self): { "__typename": "__Type", "kind": "OBJECT", - "name": "Profile", + "name": "UserGroupType", "description": null, "fields": [ { @@ -3239,7 +3283,7 @@ def test_graphql_translation_type_06(self): }, { "__typename": "__Field", - "name": "odd", + "name": "settings", "description": null, "type": { "__typename": "__Type", @@ -3253,19 +3297,65 @@ def test_graphql_translation_type_06(self): }, "isDeprecated": false, "deprecationReason": null + } + ], + "interfaces": [ + { + "__typename": "__Type", + "name": "UserGroup", + "kind": "INTERFACE" + }, + { + "__typename": "__Type", + "name": "NamedObject", + "kind": "INTERFACE" + }, + { + "__typename": "__Type", + "name": "Object", + "kind": "INTERFACE" + } + ], + "possibleTypes": null, + "enumValues": null, + "inputFields": null, + "ofType": null + }, + { + "__typename": "__Type", + "kind": "OBJECT", + "name": "SettingType", + "description": null, + "fields": [ + { + "__typename": "__Field", + "name": "id", + "description": null, + "type": { + "__typename": "__Type", + "name": null, + "kind": "NON_NULL", + "ofType": { + "__typename": "__Type", + "name": "ID", + "kind": "SCALAR" + } + }, + "isDeprecated": false, + "deprecationReason": null }, { "__typename": "__Field", - "name": "tags", + "name": "name", "description": null, "type": { "__typename": "__Type", "name": null, - "kind": "LIST", + "kind": "NON_NULL", "ofType": { "__typename": "__Type", - "name": null, - "kind": "NON_NULL" + "name": "String", + "kind": "SCALAR" } }, "isDeprecated": false, @@ -3290,10 +3380,20 @@ def test_graphql_translation_type_06(self): } ], "interfaces": [ + { + "__typename": "__Type", + "name": "Setting", + "kind": "INTERFACE" + }, { "__typename": "__Type", "name": "NamedObject", "kind": "INTERFACE" + }, + { + "__typename": "__Type", + "name": "Object", + "kind": "INTERFACE" } ], "possibleTypes": null, @@ -3304,7 +3404,7 @@ def test_graphql_translation_type_06(self): { "__typename": "__Type", "kind": "OBJECT", - "name": "Setting", + "name": "ProfileType", "description": null, "fields": [ { @@ -3341,6 +3441,40 @@ def test_graphql_translation_type_06(self): "isDeprecated": false, "deprecationReason": null }, + { + "__typename": "__Field", + "name": "odd", + "description": null, + "type": { + "__typename": "__Type", + "name": null, + "kind": "LIST", + "ofType": { + "__typename": "__Type", + "name": null, + "kind": "NON_NULL" + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "__typename": "__Field", + "name": "tags", + "description": null, + "type": { + "__typename": "__Type", + "name": null, + "kind": "LIST", + "ofType": { + "__typename": "__Type", + "name": null, + "kind": "NON_NULL" + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "__typename": "__Field", "name": "value", @@ -3360,10 +3494,20 @@ def test_graphql_translation_type_06(self): } ], "interfaces": [ + { + "__typename": "__Type", + "name": "Profile", + "kind": "INTERFACE" + }, { "__typename": "__Type", "name": "NamedObject", "kind": "INTERFACE" + }, + { + "__typename": "__Type", + "name": "Object", + "kind": "INTERFACE" } ], "possibleTypes": null, @@ -3374,7 +3518,7 @@ def test_graphql_translation_type_06(self): { "__typename": "__Type", "kind": "OBJECT", - "name": "User", + "name": "UserType", "description": null, "fields": [ { @@ -3469,7 +3613,7 @@ def test_graphql_translation_type_06(self): "type": { "__typename": "__Type", "name": "Profile", - "kind": "OBJECT", + "kind": "INTERFACE", "ofType": null }, "isDeprecated": false, @@ -3494,10 +3638,20 @@ def test_graphql_translation_type_06(self): } ], "interfaces": [ + { + "__typename": "__Type", + "name": "User", + "kind": "INTERFACE" + }, { "__typename": "__Type", "name": "NamedObject", "kind": "INTERFACE" + }, + { + "__typename": "__Type", + "name": "Object", + "kind": "INTERFACE" } ], "possibleTypes": null, @@ -3508,9 +3662,60 @@ def test_graphql_translation_type_06(self): { "__typename": "__Type", "kind": "OBJECT", - "name": "UserGroup", + "name": "PersonType", "description": null, "fields": [ + { + "__typename": "__Field", + "name": "active", + "description": null, + "type": { + "__typename": "__Type", + "name": null, + "kind": "NON_NULL", + "ofType": { + "__typename": "__Type", + "name": "Boolean", + "kind": "SCALAR" + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "__typename": "__Field", + "name": "age", + "description": null, + "type": { + "__typename": "__Type", + "name": null, + "kind": "NON_NULL", + "ofType": { + "__typename": "__Type", + "name": "Int", + "kind": "SCALAR" + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "__typename": "__Field", + "name": "groups", + "description": null, + "type": { + "__typename": "__Type", + "name": null, + "kind": "LIST", + "ofType": { + "__typename": "__Type", + "name": null, + "kind": "NON_NULL" + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "__typename": "__Field", "name": "id", @@ -3547,16 +3752,29 @@ def test_graphql_translation_type_06(self): }, { "__typename": "__Field", - "name": "settings", + "name": "profile", + "description": null, + "type": { + "__typename": "__Type", + "name": "Profile", + "kind": "INTERFACE", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "__typename": "__Field", + "name": "score", "description": null, "type": { "__typename": "__Type", "name": null, - "kind": "LIST", + "kind": "NON_NULL", "ofType": { "__typename": "__Type", - "name": null, - "kind": "NON_NULL" + "name": "Float", + "kind": "SCALAR" } }, "isDeprecated": false, @@ -3564,10 +3782,25 @@ def test_graphql_translation_type_06(self): } ], "interfaces": [ + { + "__typename": "__Type", + "name": "Person", + "kind": "INTERFACE" + }, + { + "__typename": "__Type", + "name": "User", + "kind": "INTERFACE" + }, { "__typename": "__Type", "name": "NamedObject", "kind": "INTERFACE" + }, + { + "__typename": "__Type", + "name": "Object", + "kind": "INTERFACE" } ], "possibleTypes": null, @@ -3583,10 +3816,10 @@ def test_graphql_translation_type_06(self): }; """ - def test_graphql_translation_type_07(self): + def test_graphql_translation_type_08(self): r""" query { - __type(name: "UserGroup") { + __type(name: "UserGroupType") { __typename name kind @@ -3631,7 +3864,7 @@ def test_graphql_translation_type_07(self): SELECT graphql::Query { __type := '{ "__typename": "__Type", - "name": "UserGroup", + "name": "UserGroupType", "kind": "OBJECT", "fields": [ {