From d96470a62a2a9b880755028078241058b2677133 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 3 Aug 2018 21:23:45 -0400 Subject: [PATCH] Implement interface and union type querying --- angel_graphql/lib/src/graphql_http.dart | 9 +---- graphql_schema/lib/src/object_type.dart | 18 ++++++++- graphql_server/lib/graphql_server.dart | 53 +++++++++++++++++++++---- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/angel_graphql/lib/src/graphql_http.dart b/angel_graphql/lib/src/graphql_http.dart index 0de6a02b..0f972b97 100644 --- a/angel_graphql/lib/src/graphql_http.dart +++ b/angel_graphql/lib/src/graphql_http.dart @@ -16,13 +16,6 @@ final Validator graphQlPostBody = new Validator({ 'variables': predicate((v) => v == null || v is Map), }); -Map _foldToStringDynamic(Map map) { - return map == null - ? null - : map.keys.fold>( - {}, (out, k) => out..[k.toString()] = map[k]); -} - RequestHandler graphQLHttp(GraphQL graphQl) { return (req, res) async { try { @@ -41,7 +34,7 @@ RequestHandler graphQLHttp(GraphQL graphQl) { text, sourceUrl: 'input', operationName: operationName, - variableValues: _foldToStringDynamic(variables), + variableValues: foldToStringDynamic(variables), ), }; } diff --git a/graphql_schema/lib/src/object_type.dart b/graphql_schema/lib/src/object_type.dart index bf8e114a..baf59107 100644 --- a/graphql_schema/lib/src/object_type.dart +++ b/graphql_schema/lib/src/object_type.dart @@ -13,10 +13,12 @@ class GraphQLObjectType final List _possibleTypes = []; /// A list of other types that this object type is known to implement. - List get interfaces => new List.unmodifiable(_interfaces); + List get interfaces => + new List.unmodifiable(_interfaces); /// A list of other types that implement this interface. - List get possibleTypes => new List.unmodifiable(_possibleTypes); + List get possibleTypes => + new List.unmodifiable(_possibleTypes); GraphQLObjectType(this.name, this.description, {this.isInterface: false}); @@ -88,6 +90,18 @@ class GraphQLObjectType return out..[k.toString()] = field.deserialize(value[k]); }); } + + bool isImplementationOf(GraphQLObjectType type) { + if (type == this) { + return true; + } else if (interfaces.contains(type)) { + return true; + } else if (interfaces.isNotEmpty) { + return interfaces.any((t) => t.isImplementationOf(type)); + } else { + return false; + } + } } Map _foldToStringDynamic(Map map) { diff --git a/graphql_server/lib/graphql_server.dart b/graphql_server/lib/graphql_server.dart index 8410a12a..e911cd3b 100644 --- a/graphql_server/lib/graphql_server.dart +++ b/graphql_server/lib/graphql_server.dart @@ -5,6 +5,13 @@ import 'package:graphql_schema/graphql_schema.dart'; import 'introspection.dart'; +Map foldToStringDynamic(Map map) { + return map == null + ? null + : map.keys.fold>( + {}, (out, k) => out..[k.toString()] = map[k]); +} + class GraphQL { final List customTypes = []; GraphQLSchema _schema; @@ -335,17 +342,48 @@ class GraphQL { } } - if (fieldType is GraphQLObjectType) { - var objectType = fieldType; + if (fieldType is GraphQLObjectType || fieldType is GraphQLUnionType) { + GraphQLObjectType objectType; + + if (fieldType is GraphQLObjectType && !fieldType.isInterface) { + objectType = fieldType; + } else { + objectType = resolveAbstractType(fieldType, result); + } + var subSelectionSet = mergeSelectionSets(fields); return await executeSelectionSet( document, subSelectionSet, objectType, result, variableValues); } - // TODO: Interface/union type throw new UnsupportedError('Unsupported type: $fieldType'); } + GraphQLObjectType resolveAbstractType(GraphQLType type, result) { + List possibleTypes; + + if (type is GraphQLObjectType) { + possibleTypes = type.possibleTypes; + } else if (type is GraphQLUnionType) { + possibleTypes = type.possibleTypes; + } else { + throw new ArgumentError(); + } + + for (var t in possibleTypes) { + try { + var validation = + t.validate('@root', foldToStringDynamic(result as Map)); + + if (validation.successful) { + return t; + } + } catch (_) {} + } + + throw new StateError('Cannot convert value $result to type $type.'); + } + SelectionSetContext mergeSelectionSets(List fields) { var selections = []; @@ -451,13 +489,14 @@ class GraphQL { bool doesFragmentTypeApply( GraphQLObjectType objectType, TypeConditionContext fragmentType) { var type = convertType(new TypeContext(fragmentType.typeName, null)); - // TODO: Handle interface type, union? - - if (type is GraphQLObjectType) { + if (type is GraphQLObjectType && !type.isInterface) { for (var field in type.fields) if (!objectType.fields.any((f) => f.name == field.name)) return false; - return true; + } else if (type is GraphQLObjectType && type.isInterface) { + return objectType.isImplementationOf(type); + } else if (type is GraphQLUnionType) { + return type.possibleTypes.any((t) => objectType.isImplementationOf(t)); } return false;