Add support for global variables in GraphQL

This commit is contained in:
Tobe O 2018-08-04 15:54:18 -04:00
parent 532f698aa2
commit 15e1d18224
4 changed files with 67 additions and 33 deletions

View file

@ -22,6 +22,11 @@ final Validator graphQlPostBody = new Validator({
/// https://graphql.org/learn/serving-over-http/
RequestHandler graphQLHttp(GraphQL graphQL) {
return (req, res) async {
var globalVariables = <String, dynamic>{
'__requestctx': req,
'__responsectx': res,
};
executeMap(Map map) async {
var text = req.body['query'] as String;
var operationName = req.body['operation_name'] as String;
@ -37,6 +42,7 @@ RequestHandler graphQLHttp(GraphQL graphQL) {
sourceUrl: 'input',
operationName: operationName,
variableValues: foldToStringDynamic(variables as Map),
globalVariables: globalVariables,
),
};
}
@ -50,7 +56,11 @@ RequestHandler graphQLHttp(GraphQL graphQL) {
if (req.headers.contentType?.mimeType == graphQlContentType.mimeType) {
var text = utf8.decode(await req.lazyOriginalBuffer());
return {
'data': await graphQL.parseAndExecute(text, sourceUrl: 'input')
'data': await graphQL.parseAndExecute(
text,
sourceUrl: 'input',
globalVariables: globalVariables,
),
};
} else if (req.headers.contentType?.mimeType == 'application/json') {
if (await validate(graphQlPostBody)(req, res)) {

View file

@ -1,5 +1,6 @@
import 'package:angel_model/angel_model.dart';
import 'package:angel_serialize/angel_serialize.dart';
import 'package:graphql_schema/graphql_schema.dart';
import 'character.dart';
import 'episode.dart';
@ -7,6 +8,7 @@ import 'starship.dart';
@serializable
class Human extends Model implements Character {
@GraphQLDocumentation(description: "This human's name, of course.")
String name;
List<Character> friends;
List<Episode> appearsIn;

View file

@ -23,7 +23,8 @@ class GraphQLObjectType
GraphQLObjectType(this.name, this.description, {this.isInterface: false});
@override
GraphQLType<Map<String, dynamic>, Map<String, dynamic>> coerceToInputObject() {
GraphQLType<Map<String, dynamic>, Map<String, dynamic>>
coerceToInputObject() {
return asInputObject('${name}Input', description: description);
}
@ -31,8 +32,9 @@ class GraphQLObjectType
GraphQLInputObjectType asInputObject(String name, {String description}) {
return new GraphQLInputObjectType(name,
description: description ?? this.description,
inputFields:
fields.map((f) => new GraphQLInputObjectField(f.name, f.type.coerceToInputObject())));
inputFields: fields.map((f) => new GraphQLInputObjectField(
f.name, f.type.coerceToInputObject(),
description: f.description)));
}
void inheritFrom(GraphQLObjectType other) {
@ -242,7 +244,8 @@ class GraphQLInputObjectType
}
@override
GraphQLType<Map<String, dynamic>, Map<String, dynamic>> coerceToInputObject() => this;
GraphQLType<Map<String, dynamic>, Map<String, dynamic>>
coerceToInputObject() => this;
}
class GraphQLInputObjectField<Value, Serialized> {

View file

@ -73,7 +73,8 @@ class GraphQL {
{String operationName,
sourceUrl,
Map<String, dynamic> variableValues: const {},
initialValue}) {
initialValue,
Map<String, dynamic> globalVariables}) {
var tokens = scan(text, sourceUrl: sourceUrl);
var parser = new Parser(tokens);
var document = parser.parseDocument();
@ -86,26 +87,31 @@ class GraphQL {
.toList());
}
return executeRequest(_schema, document,
return executeRequest(
_schema,
document,
operationName: operationName,
initialValue: initialValue,
variableValues: variableValues);
variableValues: variableValues,
globalVariables: globalVariables,
);
}
Future<Map<String, dynamic>> executeRequest(
GraphQLSchema schema, DocumentContext document,
{String operationName,
Map<String, dynamic> variableValues: const {},
initialValue}) async {
initialValue,
Map<String, dynamic> globalVariables}) async {
var operation = getOperation(document, operationName);
var coercedVariableValues =
coerceVariableValues(schema, operation, variableValues ?? {});
if (operation.isQuery)
return await executeQuery(
document, operation, schema, coercedVariableValues, initialValue);
return await executeQuery(document, operation, schema,
coercedVariableValues, initialValue, globalVariables);
else {
return executeMutation(
document, operation, schema, coercedVariableValues, initialValue);
return executeMutation(document, operation, schema, coercedVariableValues,
initialValue, globalVariables);
}
}
@ -175,11 +181,12 @@ class GraphQL {
OperationDefinitionContext query,
GraphQLSchema schema,
Map<String, dynamic> variableValues,
initialValue) async {
initialValue,
Map<String, dynamic> globalVariables) async {
var queryType = schema.queryType;
var selectionSet = query.selectionSet;
return await executeSelectionSet(
document, selectionSet, queryType, initialValue, variableValues);
return await executeSelectionSet(document, selectionSet, queryType,
initialValue, variableValues, globalVariables);
}
Future<Map<String, dynamic>> executeMutation(
@ -187,7 +194,8 @@ class GraphQL {
OperationDefinitionContext mutation,
GraphQLSchema schema,
Map<String, dynamic> variableValues,
initialValue) async {
initialValue,
Map<String, dynamic> globalVariables) async {
var mutationType = schema.mutationType;
if (mutationType == null) {
@ -196,8 +204,8 @@ class GraphQL {
}
var selectionSet = mutation.selectionSet;
return await executeSelectionSet(
document, selectionSet, mutationType, initialValue, variableValues);
return await executeSelectionSet(document, selectionSet, mutationType,
initialValue, variableValues, globalVariables);
}
Future<Map<String, dynamic>> executeSelectionSet(
@ -205,7 +213,8 @@ class GraphQL {
SelectionSetContext selectionSet,
GraphQLObjectType objectType,
objectValue,
Map<String, dynamic> variableValues) async {
Map<String, dynamic> variableValues,
Map<String, dynamic> globalVariables) async {
var groupedFieldSet =
collectFields(document, objectType, selectionSet, variableValues);
var resultMap = <String, dynamic>{};
@ -224,8 +233,16 @@ class GraphQL {
.firstWhere((f) => f.name == fieldName, orElse: () => null)
?.type;
if (fieldType == null) continue;
responseValue = await executeField(document, fieldName, objectType,
objectValue, fields, fieldType, variableValues);
responseValue = await executeField(
document,
fieldName,
objectType,
objectValue,
fields,
fieldType,
new Map<String, dynamic>.from(globalVariables)
..addAll(variableValues),
globalVariables);
}
resultMap[responseKey] = responseValue;
@ -242,14 +259,15 @@ class GraphQL {
objectValue,
List<SelectionContext> fields,
GraphQLType fieldType,
Map<String, dynamic> variableValues) async {
Map<String, dynamic> variableValues,
Map<String, dynamic> globalVariables) async {
var field = fields[0];
var argumentValues =
coerceArgumentValues(objectType, field, variableValues);
var resolvedValue = await resolveFieldValue(
objectType, objectValue, field.field.fieldName.name, argumentValues);
return completeValue(
document, fieldName, fieldType, fields, resolvedValue, variableValues);
return completeValue(document, fieldName, fieldType, fields, resolvedValue,
variableValues, globalVariables);
}
Map<String, dynamic> coerceArgumentValues(GraphQLObjectType objectType,
@ -365,11 +383,12 @@ class GraphQL {
GraphQLType fieldType,
List<SelectionContext> fields,
result,
Map<String, dynamic> variableValues) async {
Map<String, dynamic> variableValues,
Map<String, dynamic> globalVariables) async {
if (fieldType is GraphQLNonNullableType) {
var innerType = fieldType.ofType;
var completedResult = completeValue(
document, fieldName, innerType, fields, result, variableValues);
var completedResult = completeValue(document, fieldName, innerType,
fields, result, variableValues, globalVariables);
if (completedResult == null) {
throw new GraphQLException.fromMessage(
@ -394,7 +413,7 @@ class GraphQL {
for (var resultItem in (result as Iterable)) {
out.add(await completeValue(document, '(item in "$fieldName")',
innerType, fields, resultItem, variableValues));
innerType, fields, resultItem, variableValues, globalVariables));
}
return out;
@ -425,8 +444,8 @@ class GraphQL {
}
var subSelectionSet = mergeSelectionSets(fields);
return await executeSelectionSet(
document, subSelectionSet, objectType, result, variableValues);
return await executeSelectionSet(document, subSelectionSet, objectType,
result, variableValues, globalVariables);
}
throw new UnsupportedError('Unsupported type: $fieldType');