From 91701b56adbade63db4708a4f251b05a931a5c55 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Thu, 11 Apr 2019 13:46:04 -0400 Subject: [PATCH] Example star wars done --- angel_graphql/lib/src/resolvers.dart | 18 ++++++++++++------ example_star_wars/lib/star_wars.dart | 7 ++++++- graphql_server/lib/graphql_server.dart | 21 ++++++++++++++++++--- graphql_server/lib/mirrors.dart | 9 +++++++++ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/angel_graphql/lib/src/resolvers.dart b/angel_graphql/lib/src/resolvers.dart index 28120817..40fec173 100644 --- a/angel_graphql/lib/src/resolvers.dart +++ b/angel_graphql/lib/src/resolvers.dart @@ -8,6 +8,11 @@ Map _fetchRequestInfo(Map arguments) { }; } +Map _getQuery(Map arguments) { + var f = Map.from(arguments)..remove('id')..remove('data'); + return f.isEmpty ? null : {}; +} + /// A GraphQL resolver that `index`es an Angel service. /// /// The arguments passed to the resolver will be forwarded to the service, and the @@ -16,7 +21,7 @@ GraphQLFieldResolver, Serialized> resolveViaServiceIndex(Service service) { return (_, arguments) async { var _requestInfo = _fetchRequestInfo(arguments); - var params = {'query': arguments, 'provider': Providers.graphQL} + var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} ..addAll(_requestInfo); return await service.index(params); @@ -32,7 +37,7 @@ GraphQLFieldResolver Service service) { return (_, arguments) async { var _requestInfo = _fetchRequestInfo(arguments); - var params = {'query': arguments, 'provider': Providers.graphQL} + var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} ..addAll(_requestInfo); return await service.findOne(params); }; @@ -50,7 +55,7 @@ GraphQLFieldResolver {String idField: 'id'}) { return (_, arguments) async { var _requestInfo = _fetchRequestInfo(arguments); - var params = {'query': arguments, 'provider': Providers.graphQL} + var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} ..addAll(_requestInfo); var id = arguments.remove(idField); return await service.read(id, params); @@ -70,8 +75,9 @@ GraphQLFieldResolver {String idField: 'id'}) { return (_, arguments) async { var _requestInfo = _fetchRequestInfo(arguments); - var params = {'query': arguments, 'provider': Providers.graphQL} + var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} ..addAll(_requestInfo); + print(params); var id = arguments.remove(idField); return await service.modify(id, arguments['data'] as Value, params); }; @@ -93,7 +99,7 @@ GraphQLFieldResolver {String idField: 'id'}) { return (_, arguments) async { var _requestInfo = _fetchRequestInfo(arguments); - var params = {'query': arguments, 'provider': Providers.graphQL} + var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} ..addAll(_requestInfo); var id = arguments.remove(idField); return await service.update(id, arguments['data'] as Value, params); @@ -112,7 +118,7 @@ GraphQLFieldResolver {String idField: 'id'}) { return (_, arguments) async { var _requestInfo = _fetchRequestInfo(arguments); - var params = {'query': arguments, 'provider': Providers.graphQL} + var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} ..addAll(_requestInfo); var id = arguments.remove(idField); return await service.remove(id, params); diff --git a/example_star_wars/lib/star_wars.dart b/example_star_wars/lib/star_wars.dart index 600d91b2..31495e02 100644 --- a/example_star_wars/lib/star_wars.dart +++ b/example_star_wars/lib/star_wars.dart @@ -4,6 +4,7 @@ import 'package:angel_framework/angel_framework.dart'; import 'package:angel_graphql/angel_graphql.dart'; import 'package:graphql_schema/graphql_schema.dart'; import 'package:graphql_server/graphql_server.dart'; +import 'package:graphql_server/mirrors.dart'; import 'src/models/models.dart'; Future configureServer(Angel app) async { @@ -90,7 +91,11 @@ Future configureServer(Angel app) async { // Next, create a GraphQL object, which will be passed to `graphQLHttp`, and // used to mount a spec-compliant GraphQL endpoint on the server. - var graphQL = GraphQL(schema); + // + // The `mirrorsFieldResolver` is unnecessary in this case, because we are using + // `Map`s only, but if our services returned concrete Dart objects, we'd need + // this to allow GraphQL to read field values. + var graphQL = GraphQL(schema, defaultFieldResolver: mirrorsFieldResolver); // Mount the GraphQL endpoint. app.all('/graphql', graphQLHttp(graphQL)); diff --git a/graphql_server/lib/graphql_server.dart b/graphql_server/lib/graphql_server.dart index 737ab34c..3df35c0a 100644 --- a/graphql_server/lib/graphql_server.dart +++ b/graphql_server/lib/graphql_server.dart @@ -1,10 +1,9 @@ import 'dart:async'; - import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_schema/graphql_schema.dart'; - import 'introspection.dart'; +/// Transforms any [Map] into `Map`. Map foldToStringDynamic(Map map) { return map == null ? null @@ -12,12 +11,21 @@ Map foldToStringDynamic(Map map) { {}, (out, k) => out..[k.toString()] = map[k]); } +/// A Dart implementation of a GraphQL server. class GraphQL { + /// Any custom types to include in introspection information. final List customTypes = []; + + /// An optional callback that can be used to resolve fields from objects that are not [Map]s, + /// when the related field has no resolver. + final FutureOr Function(T, String, Map) + defaultFieldResolver; + GraphQLSchema _schema; GraphQL(GraphQLSchema schema, {bool introspect: true, + this.defaultFieldResolver, List customTypes = const []}) : _schema = schema { if (customTypes?.isNotEmpty == true) { @@ -473,6 +481,14 @@ class GraphQL { var field = objectType.fields.firstWhere((f) => f.name == fieldName); if (field.resolve == null) { + if (defaultFieldResolver != null) + return await defaultFieldResolver( + objectValue, fieldName, argumentValues); + + if (objectValue is Map) { + return objectValue[fieldName] as T; + } + return null; } else { return await field.resolve(objectValue, argumentValues) as T; @@ -571,7 +587,6 @@ class GraphQL { var errors = []; - print(possibleTypes); for (var t in possibleTypes) { try { var validation = diff --git a/graphql_server/lib/mirrors.dart b/graphql_server/lib/mirrors.dart index 8dda6694..88ac66b7 100644 --- a/graphql_server/lib/mirrors.dart +++ b/graphql_server/lib/mirrors.dart @@ -9,6 +9,15 @@ import 'package:angel_serialize/angel_serialize.dart'; import 'package:graphql_schema/graphql_schema.dart'; import 'package:recase/recase.dart'; +/// Uses `dart:mirrors` to read field names from items. If they are Maps, performs a regular lookup. +T mirrorsFieldResolver(objectValue, String fieldName, + [Map objectValues]) { + if (objectValue is Map) + return objectValue[fieldName] as T; + else + return reflect(objectValue).getField(Symbol(fieldName)).reflectee as T; +} + /// Reflects upon a given [type] and dynamically generates a [GraphQLType] that corresponds to it. /// /// This function is aware of the annotations from `package:angel_serialize`, and works seamlessly