Add support for global variables in GraphQL
This commit is contained in:
parent
532f698aa2
commit
15e1d18224
4 changed files with 67 additions and 33 deletions
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Reference in a new issue