Merge branch 'master' into 1.2.0
This commit is contained in:
commit
d23f899a6c
9 changed files with 144 additions and 124 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
# 1.0.1
|
||||||
|
* Fix a bug where `globalVariables` were not being properly passed
|
||||||
|
to field resolvers.
|
||||||
|
|
||||||
# 1.0.0
|
# 1.0.0
|
||||||
* Finish testing.
|
* Finish testing.
|
||||||
* Add `package:pedantic` fixes.
|
* Add `package:pedantic` fixes.
|
||||||
|
|
|
@ -23,16 +23,16 @@ void main() {
|
||||||
'todos',
|
'todos',
|
||||||
listOf(todoType),
|
listOf(todoType),
|
||||||
resolve: (_, __) => [
|
resolve: (_, __) => [
|
||||||
new Todo(
|
Todo(
|
||||||
text: 'Clean your room!',
|
text: 'Clean your room!',
|
||||||
completed: false,
|
completed: false,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
var graphql = new GraphQL(schema);
|
var graphql = GraphQL(schema);
|
||||||
var result = await graphql.parseAndExecute('{ todos { text } }');
|
var result = await graphql.parseAndExecute('{ todos { text } }');
|
||||||
|
|
||||||
print(result);
|
print(result);
|
||||||
|
|
|
@ -45,15 +45,17 @@ class GraphQL {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_schema.queryType != null) this.customTypes.add(_schema.queryType);
|
if (_schema.queryType != null) this.customTypes.add(_schema.queryType);
|
||||||
if (_schema.mutationType != null)
|
if (_schema.mutationType != null) {
|
||||||
this.customTypes.add(_schema.mutationType);
|
this.customTypes.add(_schema.mutationType);
|
||||||
if (_schema.subscriptionType != null)
|
}
|
||||||
|
if (_schema.subscriptionType != null) {
|
||||||
this.customTypes.add(_schema.subscriptionType);
|
this.customTypes.add(_schema.subscriptionType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphQLType convertType(TypeContext ctx) {
|
GraphQLType convertType(TypeContext ctx) {
|
||||||
if (ctx.listType != null) {
|
if (ctx.listType != null) {
|
||||||
return new GraphQLListType(convertType(ctx.listType.type));
|
return GraphQLListType(convertType(ctx.listType.type));
|
||||||
} else if (ctx.typeName != null) {
|
} else if (ctx.typeName != null) {
|
||||||
switch (ctx.typeName.name) {
|
switch (ctx.typeName.name) {
|
||||||
case 'Int':
|
case 'Int':
|
||||||
|
@ -71,11 +73,11 @@ class GraphQL {
|
||||||
return graphQLDate;
|
return graphQLDate;
|
||||||
default:
|
default:
|
||||||
return customTypes.firstWhere((t) => t.name == ctx.typeName.name,
|
return customTypes.firstWhere((t) => t.name == ctx.typeName.name,
|
||||||
orElse: () => throw new ArgumentError(
|
orElse: () => throw ArgumentError(
|
||||||
'Unknown GraphQL type: "${ctx.typeName.name}"'));
|
'Unknown GraphQL type: "${ctx.typeName.name}"'));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ArgumentError('Invalid GraphQL type: "${ctx.span.text}"');
|
throw ArgumentError('Invalid GraphQL type: "${ctx.span.text}"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,13 +88,13 @@ class GraphQL {
|
||||||
initialValue,
|
initialValue,
|
||||||
Map<String, dynamic> globalVariables}) {
|
Map<String, dynamic> globalVariables}) {
|
||||||
var tokens = scan(text, sourceUrl: sourceUrl);
|
var tokens = scan(text, sourceUrl: sourceUrl);
|
||||||
var parser = new Parser(tokens);
|
var parser = Parser(tokens);
|
||||||
var document = parser.parseDocument();
|
var document = parser.parseDocument();
|
||||||
|
|
||||||
if (parser.errors.isNotEmpty) {
|
if (parser.errors.isNotEmpty) {
|
||||||
throw new GraphQLException(parser.errors
|
throw GraphQLException(parser.errors
|
||||||
.map((e) => new GraphQLExceptionError(e.message, locations: [
|
.map((e) => GraphQLExceptionError(e.message, locations: [
|
||||||
new GraphExceptionErrorLocation.fromSourceLocation(e.span.start)
|
GraphExceptionErrorLocation.fromSourceLocation(e.span.start)
|
||||||
]))
|
]))
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
|
@ -115,10 +117,10 @@ class GraphQL {
|
||||||
var operation = getOperation(document, operationName);
|
var operation = getOperation(document, operationName);
|
||||||
var coercedVariableValues = coerceVariableValues(
|
var coercedVariableValues = coerceVariableValues(
|
||||||
schema, operation, variableValues ?? <String, dynamic>{});
|
schema, operation, variableValues ?? <String, dynamic>{});
|
||||||
if (operation.isQuery)
|
if (operation.isQuery) {
|
||||||
return await executeQuery(document, operation, schema,
|
return await executeQuery(document, operation, schema,
|
||||||
coercedVariableValues, initialValue, globalVariables);
|
coercedVariableValues, initialValue, globalVariables);
|
||||||
else if (operation.isSubscription) {
|
} else if (operation.isSubscription) {
|
||||||
return await subscribe(document, operation, schema, coercedVariableValues,
|
return await subscribe(document, operation, schema, coercedVariableValues,
|
||||||
globalVariables, initialValue);
|
globalVariables, initialValue);
|
||||||
} else {
|
} else {
|
||||||
|
@ -129,20 +131,17 @@ class GraphQL {
|
||||||
|
|
||||||
OperationDefinitionContext getOperation(
|
OperationDefinitionContext getOperation(
|
||||||
DocumentContext document, String operationName) {
|
DocumentContext document, String operationName) {
|
||||||
var ops =
|
var ops = document.definitions.whereType<OperationDefinitionContext>();
|
||||||
document.definitions.where((d) => d is OperationDefinitionContext);
|
|
||||||
|
|
||||||
if (operationName == null) {
|
if (operationName == null) {
|
||||||
return ops.length == 1
|
return ops.length == 1
|
||||||
? ops.first as OperationDefinitionContext
|
? ops.first
|
||||||
: throw new GraphQLException.fromMessage(
|
: throw GraphQLException.fromMessage(
|
||||||
'This document does not define any operations.');
|
'This document does not define any operations.');
|
||||||
} else {
|
} else {
|
||||||
return ops.firstWhere(
|
return ops.firstWhere((d) => d.name == operationName,
|
||||||
(d) => (d as OperationDefinitionContext).name == operationName,
|
orElse: () => throw GraphQLException.fromMessage(
|
||||||
orElse: () => throw new GraphQLException.fromMessage(
|
'Missing required operation "$operationName".'));
|
||||||
'Missing required operation "$operationName".'))
|
|
||||||
as OperationDefinitionContext;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +163,7 @@ class GraphQL {
|
||||||
if (defaultValue != null) {
|
if (defaultValue != null) {
|
||||||
coercedValues[variableName] = defaultValue.value.value;
|
coercedValues[variableName] = defaultValue.value.value;
|
||||||
} else if (!variableType.isNullable) {
|
} else if (!variableType.isNullable) {
|
||||||
throw new GraphQLException.fromSourceSpan(
|
throw GraphQLException.fromSourceSpan(
|
||||||
'Missing required variable "$variableName".',
|
'Missing required variable "$variableName".',
|
||||||
variableDefinition.span);
|
variableDefinition.span);
|
||||||
}
|
}
|
||||||
|
@ -173,9 +172,9 @@ class GraphQL {
|
||||||
var validation = type.validate(variableName, value);
|
var validation = type.validate(variableName, value);
|
||||||
|
|
||||||
if (!validation.successful) {
|
if (!validation.successful) {
|
||||||
throw new GraphQLException(validation.errors
|
throw GraphQLException(validation.errors
|
||||||
.map((e) => new GraphQLExceptionError(e, locations: [
|
.map((e) => GraphQLExceptionError(e, locations: [
|
||||||
new GraphExceptionErrorLocation.fromSourceLocation(
|
GraphExceptionErrorLocation.fromSourceLocation(
|
||||||
variableDefinition.span.start)
|
variableDefinition.span.start)
|
||||||
]))
|
]))
|
||||||
.toList());
|
.toList());
|
||||||
|
@ -211,7 +210,7 @@ class GraphQL {
|
||||||
var mutationType = schema.mutationType;
|
var mutationType = schema.mutationType;
|
||||||
|
|
||||||
if (mutationType == null) {
|
if (mutationType == null) {
|
||||||
throw new GraphQLException.fromMessage(
|
throw GraphQLException.fromMessage(
|
||||||
'The schema does not define a mutation type.');
|
'The schema does not define a mutation type.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,15 +240,17 @@ class GraphQL {
|
||||||
initialValue) {
|
initialValue) {
|
||||||
var selectionSet = subscription.selectionSet;
|
var selectionSet = subscription.selectionSet;
|
||||||
var subscriptionType = schema.subscriptionType;
|
var subscriptionType = schema.subscriptionType;
|
||||||
if (subscriptionType == null)
|
if (subscriptionType == null) {
|
||||||
throw GraphQLException.fromSourceSpan(
|
throw GraphQLException.fromSourceSpan(
|
||||||
'The schema does not define a subscription type.', subscription.span);
|
'The schema does not define a subscription type.', subscription.span);
|
||||||
|
}
|
||||||
var groupedFieldSet =
|
var groupedFieldSet =
|
||||||
collectFields(document, subscriptionType, selectionSet, variableValues);
|
collectFields(document, subscriptionType, selectionSet, variableValues);
|
||||||
if (groupedFieldSet.length != 1)
|
if (groupedFieldSet.length != 1) {
|
||||||
throw GraphQLException.fromSourceSpan(
|
throw GraphQLException.fromSourceSpan(
|
||||||
'The grouped field set from this query must have exactly one entry.',
|
'The grouped field set from this query must have exactly one entry.',
|
||||||
selectionSet.span);
|
selectionSet.span);
|
||||||
|
}
|
||||||
var fields = groupedFieldSet.entries.first.value;
|
var fields = groupedFieldSet.entries.first.value;
|
||||||
var fieldName = fields.first.field.fieldName.alias?.name ??
|
var fieldName = fields.first.field.fieldName.alias?.name ??
|
||||||
fields.first.field.fieldName.name;
|
fields.first.field.fieldName.name;
|
||||||
|
@ -284,10 +285,10 @@ class GraphQL {
|
||||||
Map<String, dynamic> globalVariables) async {
|
Map<String, dynamic> globalVariables) async {
|
||||||
var selectionSet = subscription.selectionSet;
|
var selectionSet = subscription.selectionSet;
|
||||||
var subscriptionType = schema.subscriptionType;
|
var subscriptionType = schema.subscriptionType;
|
||||||
if (subscriptionType == null)
|
if (subscriptionType == null) {
|
||||||
throw GraphQLException.fromSourceSpan(
|
throw GraphQLException.fromSourceSpan(
|
||||||
'The schema does not define a subscription type.', subscription.span);
|
'The schema does not define a subscription type.', subscription.span);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
var data = await executeSelectionSet(document, selectionSet,
|
var data = await executeSelectionSet(document, selectionSet,
|
||||||
subscriptionType, initialValue, variableValues, globalVariables);
|
subscriptionType, initialValue, variableValues, globalVariables);
|
||||||
|
@ -309,10 +310,11 @@ class GraphQL {
|
||||||
});
|
});
|
||||||
var resolver = field.resolve;
|
var resolver = field.resolve;
|
||||||
var result = await resolver(rootValue, argumentValues);
|
var result = await resolver(rootValue, argumentValues);
|
||||||
if (result is Stream)
|
if (result is Stream) {
|
||||||
return result;
|
return result;
|
||||||
else
|
} else {
|
||||||
return Stream.fromIterable([result]);
|
return Stream.fromIterable([result]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> executeSelectionSet(
|
Future<Map<String, dynamic>> executeSelectionSet(
|
||||||
|
@ -348,8 +350,7 @@ class GraphQL {
|
||||||
objectValue,
|
objectValue,
|
||||||
fields,
|
fields,
|
||||||
fieldType,
|
fieldType,
|
||||||
new Map<String, dynamic>.from(
|
Map<String, dynamic>.from(globalVariables ?? <String, dynamic>{})
|
||||||
globalVariables ?? <String, dynamic>{})
|
|
||||||
..addAll(variableValues),
|
..addAll(variableValues),
|
||||||
globalVariables);
|
globalVariables);
|
||||||
}
|
}
|
||||||
|
@ -374,7 +375,11 @@ class GraphQL {
|
||||||
var argumentValues =
|
var argumentValues =
|
||||||
coerceArgumentValues(objectType, field, variableValues);
|
coerceArgumentValues(objectType, field, variableValues);
|
||||||
var resolvedValue = await resolveFieldValue(
|
var resolvedValue = await resolveFieldValue(
|
||||||
objectType, objectValue, fieldName, argumentValues);
|
objectType,
|
||||||
|
objectValue,
|
||||||
|
fieldName,
|
||||||
|
Map<String, dynamic>.from(globalVariables ?? {})
|
||||||
|
..addAll(argumentValues ?? {}));
|
||||||
return completeValue(document, fieldName, fieldType, fields, resolvedValue,
|
return completeValue(document, fieldName, fieldType, fields, resolvedValue,
|
||||||
variableValues, globalVariables);
|
variableValues, globalVariables);
|
||||||
}
|
}
|
||||||
|
@ -407,7 +412,7 @@ class GraphQL {
|
||||||
} else if (defaultValue != null || argumentDefinition.defaultsToNull) {
|
} else if (defaultValue != null || argumentDefinition.defaultsToNull) {
|
||||||
coercedValues[argumentName] = defaultValue;
|
coercedValues[argumentName] = defaultValue;
|
||||||
} else if (argumentType is GraphQLNonNullableType) {
|
} else if (argumentType is GraphQLNonNullableType) {
|
||||||
throw new GraphQLException.fromSourceSpan(
|
throw GraphQLException.fromSourceSpan(
|
||||||
'Missing value for argument "$argumentName" of field "$fieldName".',
|
'Missing value for argument "$argumentName" of field "$fieldName".',
|
||||||
value.valueOrVariable.span);
|
value.valueOrVariable.span);
|
||||||
} else {
|
} else {
|
||||||
|
@ -417,7 +422,7 @@ class GraphQL {
|
||||||
if (defaultValue != null || argumentDefinition.defaultsToNull) {
|
if (defaultValue != null || argumentDefinition.defaultsToNull) {
|
||||||
coercedValues[argumentName] = defaultValue;
|
coercedValues[argumentName] = defaultValue;
|
||||||
} else if (argumentType is GraphQLNonNullableType) {
|
} else if (argumentType is GraphQLNonNullableType) {
|
||||||
throw new GraphQLException.fromMessage(
|
throw GraphQLException.fromMessage(
|
||||||
'Missing value for argument "$argumentName" of field "$fieldName".');
|
'Missing value for argument "$argumentName" of field "$fieldName".');
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
@ -429,10 +434,10 @@ class GraphQL {
|
||||||
|
|
||||||
if (!validation.successful) {
|
if (!validation.successful) {
|
||||||
var errors = <GraphQLExceptionError>[
|
var errors = <GraphQLExceptionError>[
|
||||||
new GraphQLExceptionError(
|
GraphQLExceptionError(
|
||||||
'Type coercion error for value of argument "$argumentName" of field "$fieldName".',
|
'Type coercion error for value of argument "$argumentName" of field "$fieldName".',
|
||||||
locations: [
|
locations: [
|
||||||
new GraphExceptionErrorLocation.fromSourceLocation(
|
GraphExceptionErrorLocation.fromSourceLocation(
|
||||||
value.valueOrVariable.span.start)
|
value.valueOrVariable.span.start)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -440,34 +445,34 @@ class GraphQL {
|
||||||
|
|
||||||
for (var error in validation.errors) {
|
for (var error in validation.errors) {
|
||||||
errors.add(
|
errors.add(
|
||||||
new GraphQLExceptionError(
|
GraphQLExceptionError(
|
||||||
error,
|
error,
|
||||||
locations: [
|
locations: [
|
||||||
new GraphExceptionErrorLocation.fromSourceLocation(
|
GraphExceptionErrorLocation.fromSourceLocation(
|
||||||
value.valueOrVariable.span.start)
|
value.valueOrVariable.span.start)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new GraphQLException(errors);
|
throw GraphQLException(errors);
|
||||||
} else {
|
} else {
|
||||||
var coercedValue = validation.value;
|
var coercedValue = validation.value;
|
||||||
coercedValues[argumentName] = coercedValue;
|
coercedValues[argumentName] = coercedValue;
|
||||||
}
|
}
|
||||||
} on TypeError catch (e) {
|
} on TypeError catch (e) {
|
||||||
throw new GraphQLException(<GraphQLExceptionError>[
|
throw GraphQLException(<GraphQLExceptionError>[
|
||||||
new GraphQLExceptionError(
|
GraphQLExceptionError(
|
||||||
'Type coercion error for value of argument "$argumentName" of field "$fieldName".',
|
'Type coercion error for value of argument "$argumentName" of field "$fieldName".',
|
||||||
locations: [
|
locations: [
|
||||||
new GraphExceptionErrorLocation.fromSourceLocation(
|
GraphExceptionErrorLocation.fromSourceLocation(
|
||||||
value.valueOrVariable.span.start)
|
value.valueOrVariable.span.start)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
new GraphQLExceptionError(
|
GraphQLExceptionError(
|
||||||
e.message.toString(),
|
e.message.toString(),
|
||||||
locations: [
|
locations: [
|
||||||
new GraphExceptionErrorLocation.fromSourceLocation(
|
GraphExceptionErrorLocation.fromSourceLocation(
|
||||||
value.valueOrVariable.span.start)
|
value.valueOrVariable.span.start)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -486,9 +491,10 @@ class GraphQL {
|
||||||
if (objectValue is Map) {
|
if (objectValue is Map) {
|
||||||
return objectValue[fieldName] as T;
|
return objectValue[fieldName] as T;
|
||||||
} else if (field.resolve == null) {
|
} else if (field.resolve == null) {
|
||||||
if (defaultFieldResolver != null)
|
if (defaultFieldResolver != null) {
|
||||||
return await defaultFieldResolver(
|
return await defaultFieldResolver(
|
||||||
objectValue, fieldName, argumentValues);
|
objectValue, fieldName, argumentValues);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -510,7 +516,7 @@ class GraphQL {
|
||||||
fields, result, variableValues, globalVariables);
|
fields, result, variableValues, globalVariables);
|
||||||
|
|
||||||
if (completedResult == null) {
|
if (completedResult == null) {
|
||||||
throw new GraphQLException.fromMessage(
|
throw GraphQLException.fromMessage(
|
||||||
'Null value provided for non-nullable field "$fieldName".');
|
'Null value provided for non-nullable field "$fieldName".');
|
||||||
} else {
|
} else {
|
||||||
return completedResult;
|
return completedResult;
|
||||||
|
@ -523,7 +529,7 @@ class GraphQL {
|
||||||
|
|
||||||
if (fieldType is GraphQLListType) {
|
if (fieldType is GraphQLListType) {
|
||||||
if (result is! Iterable) {
|
if (result is! Iterable) {
|
||||||
throw new GraphQLException.fromMessage(
|
throw GraphQLException.fromMessage(
|
||||||
'Value of field "$fieldName" must be a list or iterable, got $result instead.');
|
'Value of field "$fieldName" must be a list or iterable, got $result instead.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,7 +554,7 @@ class GraphQL {
|
||||||
return validation.value;
|
return validation.value;
|
||||||
}
|
}
|
||||||
} on TypeError {
|
} on TypeError {
|
||||||
throw new GraphQLException.fromMessage(
|
throw GraphQLException.fromMessage(
|
||||||
'Value of field "$fieldName" must be ${fieldType.valueType}, got $result instead.');
|
'Value of field "$fieldName" must be ${fieldType.valueType}, got $result instead.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -567,7 +573,7 @@ class GraphQL {
|
||||||
result, variableValues, globalVariables);
|
result, variableValues, globalVariables);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnsupportedError('Unsupported type: $fieldType');
|
throw UnsupportedError('Unsupported type: $fieldType');
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphQLObjectType resolveAbstractType(
|
GraphQLObjectType resolveAbstractType(
|
||||||
|
@ -583,7 +589,7 @@ class GraphQL {
|
||||||
} else if (type is GraphQLUnionType) {
|
} else if (type is GraphQLUnionType) {
|
||||||
possibleTypes = type.possibleTypes;
|
possibleTypes = type.possibleTypes;
|
||||||
} else {
|
} else {
|
||||||
throw new ArgumentError();
|
throw ArgumentError();
|
||||||
}
|
}
|
||||||
|
|
||||||
var errors = <GraphQLExceptionError>[];
|
var errors = <GraphQLExceptionError>[];
|
||||||
|
@ -597,19 +603,16 @@ class GraphQL {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
errors
|
errors.addAll(validation.errors.map((m) => GraphQLExceptionError(m)));
|
||||||
.addAll(validation.errors.map((m) => new GraphQLExceptionError(m)));
|
|
||||||
} on GraphQLException catch (e) {
|
} on GraphQLException catch (e) {
|
||||||
errors.addAll(e.errors);
|
errors.addAll(e.errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.insert(
|
errors.insert(0,
|
||||||
0,
|
GraphQLExceptionError('Cannot convert value $result to type $type.'));
|
||||||
new GraphQLExceptionError(
|
|
||||||
'Cannot convert value $result to type $type.'));
|
|
||||||
|
|
||||||
throw new GraphQLException(errors);
|
throw GraphQLException(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionSetContext mergeSelectionSets(List<SelectionContext> fields) {
|
SelectionSetContext mergeSelectionSets(List<SelectionContext> fields) {
|
||||||
|
@ -623,7 +626,7 @@ class GraphQL {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SelectionSetContext.merged(selections);
|
return SelectionSetContext.merged(selections);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, List<SelectionContext>> collectFields(
|
Map<String, List<SelectionContext>> collectFields(
|
||||||
|
@ -636,10 +639,13 @@ class GraphQL {
|
||||||
visitedFragments ??= [];
|
visitedFragments ??= [];
|
||||||
|
|
||||||
for (var selection in selectionSet.selections) {
|
for (var selection in selectionSet.selections) {
|
||||||
if (getDirectiveValue('skip', 'if', selection, variableValues) == true)
|
if (getDirectiveValue('skip', 'if', selection, variableValues) == true) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
if (getDirectiveValue('include', 'if', selection, variableValues) ==
|
if (getDirectiveValue('include', 'if', selection, variableValues) ==
|
||||||
false) continue;
|
false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (selection.field != null) {
|
if (selection.field != null) {
|
||||||
var responseKey = selection.field.fieldName.alias?.alias ??
|
var responseKey = selection.field.fieldName.alias?.alias ??
|
||||||
|
@ -652,11 +658,9 @@ class GraphQL {
|
||||||
if (visitedFragments.contains(fragmentSpreadName)) continue;
|
if (visitedFragments.contains(fragmentSpreadName)) continue;
|
||||||
visitedFragments.add(fragmentSpreadName);
|
visitedFragments.add(fragmentSpreadName);
|
||||||
var fragment = document.definitions
|
var fragment = document.definitions
|
||||||
.where((d) => d is FragmentDefinitionContext)
|
.whereType<FragmentDefinitionContext>()
|
||||||
.firstWhere(
|
.firstWhere((f) => f.name == fragmentSpreadName,
|
||||||
(f) =>
|
orElse: () => null);
|
||||||
(f as FragmentDefinitionContext).name == fragmentSpreadName,
|
|
||||||
orElse: () => null) as FragmentDefinitionContext;
|
|
||||||
|
|
||||||
if (fragment == null) continue;
|
if (fragment == null) continue;
|
||||||
var fragmentType = fragment.typeCondition;
|
var fragmentType = fragment.typeCondition;
|
||||||
|
@ -708,19 +712,21 @@ class GraphQL {
|
||||||
if (vv.value != null) return vv.value.value;
|
if (vv.value != null) return vv.value.value;
|
||||||
|
|
||||||
var vname = vv.variable.name;
|
var vname = vv.variable.name;
|
||||||
if (!variableValues.containsKey(vname))
|
if (!variableValues.containsKey(vname)) {
|
||||||
throw new GraphQLException.fromSourceSpan(
|
throw GraphQLException.fromSourceSpan(
|
||||||
'Unknown variable: "$vname"', vv.span);
|
'Unknown variable: "$vname"', vv.span);
|
||||||
|
}
|
||||||
|
|
||||||
return variableValues[vname];
|
return variableValues[vname];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool doesFragmentTypeApply(
|
bool doesFragmentTypeApply(
|
||||||
GraphQLObjectType objectType, TypeConditionContext fragmentType) {
|
GraphQLObjectType objectType, TypeConditionContext fragmentType) {
|
||||||
var type = convertType(new TypeContext(fragmentType.typeName, null));
|
var type = convertType(TypeContext(fragmentType.typeName, null));
|
||||||
if (type is GraphQLObjectType && !type.isInterface) {
|
if (type is GraphQLObjectType && !type.isInterface) {
|
||||||
for (var field in type.fields)
|
for (var field in type.fields) {
|
||||||
if (!objectType.fields.any((f) => f.name == field.name)) return false;
|
if (!objectType.fields.any((f) => f.name == field.name)) return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (type is GraphQLObjectType && type.isInterface) {
|
} else if (type is GraphQLObjectType && type.isInterface) {
|
||||||
return objectType.isImplementationOf(type);
|
return objectType.isImplementationOf(type);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:graphql_parser/graphql_parser.dart';
|
import 'package:graphql_parser/graphql_parser.dart';
|
||||||
import 'package:graphql_schema/graphql_schema.dart';
|
import 'package:graphql_schema/graphql_schema.dart';
|
||||||
|
|
||||||
/// Performs introspection over a GraphQL [schema], and returns a new one, containing
|
/// Performs introspection over a GraphQL [schema], and returns a one, containing
|
||||||
/// introspective information.
|
/// introspective information.
|
||||||
///
|
///
|
||||||
/// [allTypes] should contain all types, not directly defined in the schema, that you
|
/// [allTypes] should contain all types, not directly defined in the schema, that you
|
||||||
|
@ -79,11 +79,11 @@ GraphQLSchema reflectSchema(GraphQLSchema schema, List<GraphQLType> allTypes) {
|
||||||
field(
|
field(
|
||||||
'__type',
|
'__type',
|
||||||
typeType,
|
typeType,
|
||||||
inputs: [new GraphQLFieldInput('name', graphQLString.nonNullable())],
|
inputs: [GraphQLFieldInput('name', graphQLString.nonNullable())],
|
||||||
resolve: (_, args) {
|
resolve: (_, args) {
|
||||||
var name = args['name'] as String;
|
var name = args['name'] as String;
|
||||||
return allTypes.firstWhere((t) => t.name == name,
|
return allTypes.firstWhere((t) => t.name == name,
|
||||||
orElse: () => throw new GraphQLException.fromMessage(
|
orElse: () => throw GraphQLException.fromMessage(
|
||||||
'No type named "$name" exists.'));
|
'No type named "$name" exists.'));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -91,7 +91,7 @@ GraphQLSchema reflectSchema(GraphQLSchema schema, List<GraphQLType> allTypes) {
|
||||||
|
|
||||||
fields.addAll(schema.queryType.fields);
|
fields.addAll(schema.queryType.fields);
|
||||||
|
|
||||||
return new GraphQLSchema(
|
return GraphQLSchema(
|
||||||
queryType: objectType(schema.queryType.name, fields: fields),
|
queryType: objectType(schema.queryType.name, fields: fields),
|
||||||
mutationType: schema.mutationType,
|
mutationType: schema.mutationType,
|
||||||
subscriptionType: schema.subscriptionType,
|
subscriptionType: schema.subscriptionType,
|
||||||
|
@ -108,9 +108,9 @@ GraphQLObjectType _reflectSchemaTypes() {
|
||||||
'ofType',
|
'ofType',
|
||||||
_reflectSchemaTypes(),
|
_reflectSchemaTypes(),
|
||||||
resolve: (type, _) {
|
resolve: (type, _) {
|
||||||
if (type is GraphQLListType)
|
if (type is GraphQLListType) {
|
||||||
return type.ofType;
|
return type.ofType;
|
||||||
else if (type is GraphQLNonNullableType) return type.ofType;
|
} else if (type is GraphQLNonNullableType) return type.ofType;
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -213,29 +213,30 @@ GraphQLObjectType _createTypeType() {
|
||||||
resolve: (type, _) {
|
resolve: (type, _) {
|
||||||
var t = type as GraphQLType;
|
var t = type as GraphQLType;
|
||||||
|
|
||||||
if (t is GraphQLEnumType)
|
if (t is GraphQLEnumType) {
|
||||||
return 'ENUM';
|
return 'ENUM';
|
||||||
else if (t is GraphQLScalarType)
|
} else if (t is GraphQLScalarType) {
|
||||||
return 'SCALAR';
|
return 'SCALAR';
|
||||||
else if (t is GraphQLInputObjectType)
|
} else if (t is GraphQLInputObjectType) {
|
||||||
return 'INPUT_OBJECT';
|
return 'INPUT_OBJECT';
|
||||||
else if (t is GraphQLObjectType)
|
} else if (t is GraphQLObjectType) {
|
||||||
return t.isInterface ? 'INTERFACE' : 'OBJECT';
|
return t.isInterface ? 'INTERFACE' : 'OBJECT';
|
||||||
else if (t is GraphQLListType)
|
} else if (t is GraphQLListType) {
|
||||||
return 'LIST';
|
return 'LIST';
|
||||||
else if (t is GraphQLNonNullableType)
|
} else if (t is GraphQLNonNullableType) {
|
||||||
return 'NON_NULL';
|
return 'NON_NULL';
|
||||||
else if (t is GraphQLUnionType)
|
} else if (t is GraphQLUnionType) {
|
||||||
return 'UNION';
|
return 'UNION';
|
||||||
else
|
} else {
|
||||||
throw new UnsupportedError('Cannot get the kind of $t.');
|
throw UnsupportedError('Cannot get the kind of $t.');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
field(
|
field(
|
||||||
'fields',
|
'fields',
|
||||||
listOf(fieldType),
|
listOf(fieldType),
|
||||||
inputs: [
|
inputs: [
|
||||||
new GraphQLFieldInput(
|
GraphQLFieldInput(
|
||||||
'includeDeprecated',
|
'includeDeprecated',
|
||||||
graphQLBoolean,
|
graphQLBoolean,
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
|
@ -252,7 +253,7 @@ GraphQLObjectType _createTypeType() {
|
||||||
'enumValues',
|
'enumValues',
|
||||||
listOf(enumValueType.nonNullable()),
|
listOf(enumValueType.nonNullable()),
|
||||||
inputs: [
|
inputs: [
|
||||||
new GraphQLFieldInput(
|
GraphQLFieldInput(
|
||||||
'includeDeprecated',
|
'includeDeprecated',
|
||||||
graphQLBoolean,
|
graphQLBoolean,
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
|
|
|
@ -7,10 +7,11 @@ import 'package:recase/recase.dart';
|
||||||
/// Uses `dart:mirrors` to read field names from items. If they are Maps, performs a regular lookup.
|
/// Uses `dart:mirrors` to read field names from items. If they are Maps, performs a regular lookup.
|
||||||
T mirrorsFieldResolver<T>(objectValue, String fieldName,
|
T mirrorsFieldResolver<T>(objectValue, String fieldName,
|
||||||
[Map<String, dynamic> objectValues]) {
|
[Map<String, dynamic> objectValues]) {
|
||||||
if (objectValue is Map)
|
if (objectValue is Map) {
|
||||||
return objectValue[fieldName] as T;
|
return objectValue[fieldName] as T;
|
||||||
else
|
} else {
|
||||||
return reflect(objectValue).getField(Symbol(fieldName)).reflectee as T;
|
return reflect(objectValue).getField(Symbol(fieldName)).reflectee as T;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reflects upon a given [type] and dynamically generates a [GraphQLType] that corresponds to it.
|
/// Reflects upon a given [type] and dynamically generates a [GraphQLType] that corresponds to it.
|
||||||
|
@ -42,10 +43,10 @@ GraphQLType _objectTypeFromDartType(Type type, [List<Type> typeArguments]) {
|
||||||
} else if (type == double) {
|
} else if (type == double) {
|
||||||
return graphQLFloat;
|
return graphQLFloat;
|
||||||
} else if (type == num) {
|
} else if (type == num) {
|
||||||
throw new UnsupportedError(
|
throw UnsupportedError(
|
||||||
'Cannot convert `num` to a GraphQL type. Choose `int` or `float` instead.');
|
'Cannot convert `num` to a GraphQL type. Choose `int` or `float` instead.');
|
||||||
} else if (type == Null) {
|
} else if (type == Null) {
|
||||||
throw new UnsupportedError('Cannot convert `Null` to a GraphQL type.');
|
throw UnsupportedError('Cannot convert `Null` to a GraphQL type.');
|
||||||
} else if (type == String) {
|
} else if (type == String) {
|
||||||
return graphQLString;
|
return graphQLString;
|
||||||
} else if (type == DateTime) {
|
} else if (type == DateTime) {
|
||||||
|
@ -56,7 +57,7 @@ GraphQLType _objectTypeFromDartType(Type type, [List<Type> typeArguments]) {
|
||||||
type, typeArguments?.isNotEmpty == true ? typeArguments : null);
|
type, typeArguments?.isNotEmpty == true ? typeArguments : null);
|
||||||
|
|
||||||
if (mirror is! ClassMirror) {
|
if (mirror is! ClassMirror) {
|
||||||
throw new StateError(
|
throw StateError(
|
||||||
'$type is not a class, and therefore cannot be converted into a GraphQL object type.');
|
'$type is not a class, and therefore cannot be converted into a GraphQL object type.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ GraphQLType _objectTypeFromDartType(Type type, [List<Type> typeArguments]) {
|
||||||
return listOf(inner.nonNullable());
|
return listOf(inner.nonNullable());
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentError(
|
throw ArgumentError(
|
||||||
'Cannot convert ${clazz.reflectedType}, an iterable WITHOUT a type argument, into a GraphQL type.');
|
'Cannot convert ${clazz.reflectedType}, an iterable WITHOUT a type argument, into a GraphQL type.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +214,7 @@ GraphQLEnumType enumTypeFromClassMirror(ClassMirror mirror) {
|
||||||
if (name != #values) {
|
if (name != #values) {
|
||||||
var methodMirror = mirror.staticMembers[name];
|
var methodMirror = mirror.staticMembers[name];
|
||||||
values.add(
|
values.add(
|
||||||
new GraphQLEnumValue(
|
GraphQLEnumValue(
|
||||||
MirrorSystem.getName(name),
|
MirrorSystem.getName(name),
|
||||||
mirror.getField(name).reflectee,
|
mirror.getField(name).reflectee,
|
||||||
description: _getDescription(methodMirror.metadata),
|
description: _getDescription(methodMirror.metadata),
|
||||||
|
@ -223,7 +224,7 @@ GraphQLEnumType enumTypeFromClassMirror(ClassMirror mirror) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GraphQLEnumType(
|
return GraphQLEnumType(
|
||||||
MirrorSystem.getName(mirror.simpleName),
|
MirrorSystem.getName(mirror.simpleName),
|
||||||
values,
|
values,
|
||||||
description: _getDescription(mirror.metadata),
|
description: _getDescription(mirror.metadata),
|
||||||
|
@ -294,7 +295,7 @@ String _getSerializedName(Symbol name, MethodMirror mirror, ClassMirror clazz) {
|
||||||
var ann = obj.reflectee as Serializable;
|
var ann = obj.reflectee as Serializable;
|
||||||
|
|
||||||
if (ann.autoSnakeCaseNames != false) {
|
if (ann.autoSnakeCaseNames != false) {
|
||||||
return new ReCase(MirrorSystem.getName(name)).snakeCase;
|
return ReCase(MirrorSystem.getName(name)).snakeCase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,12 @@ abstract class Server {
|
||||||
if ((msg.type == OperationMessage.gqlConnectionInit) && !_init) {
|
if ((msg.type == OperationMessage.gqlConnectionInit) && !_init) {
|
||||||
try {
|
try {
|
||||||
Map connectionParams;
|
Map connectionParams;
|
||||||
if (msg.payload is Map)
|
if (msg.payload is Map) {
|
||||||
connectionParams = msg.payload as Map;
|
connectionParams = msg.payload as Map;
|
||||||
else if (msg.payload != null)
|
} else if (msg.payload != null) {
|
||||||
throw FormatException(
|
throw FormatException(
|
||||||
'${msg.type} payload must be a map (object).');
|
'${msg.type} payload must be a map (object).');
|
||||||
|
}
|
||||||
|
|
||||||
var connect = await onConnect(client, connectionParams);
|
var connect = await onConnect(client, connectionParams);
|
||||||
if (!connect) throw false;
|
if (!connect) throw false;
|
||||||
|
@ -39,33 +40,39 @@ abstract class Server {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e == false)
|
if (e == false) {
|
||||||
_reportError('The connection was rejected.');
|
_reportError('The connection was rejected.');
|
||||||
else
|
} else {
|
||||||
_reportError(e.toString());
|
_reportError(e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (_init) {
|
} else if (_init) {
|
||||||
if (msg.type == OperationMessage.gqlStart) {
|
if (msg.type == OperationMessage.gqlStart) {
|
||||||
if (msg.id == null)
|
if (msg.id == null) {
|
||||||
throw FormatException('${msg.type} id is required.');
|
throw FormatException('${msg.type} id is required.');
|
||||||
if (msg.payload == null)
|
}
|
||||||
|
if (msg.payload == null) {
|
||||||
throw FormatException('${msg.type} payload is required.');
|
throw FormatException('${msg.type} payload is required.');
|
||||||
else if (msg.payload is! Map)
|
} else if (msg.payload is! Map) {
|
||||||
throw FormatException(
|
throw FormatException(
|
||||||
'${msg.type} payload must be a map (object).');
|
'${msg.type} payload must be a map (object).');
|
||||||
|
}
|
||||||
var payload = msg.payload as Map;
|
var payload = msg.payload as Map;
|
||||||
var query = payload['query'];
|
var query = payload['query'];
|
||||||
var variables = payload['variables'];
|
var variables = payload['variables'];
|
||||||
var operationName = payload['operationName'];
|
var operationName = payload['operationName'];
|
||||||
if (query == null || query is! String)
|
if (query == null || query is! String) {
|
||||||
throw FormatException(
|
throw FormatException(
|
||||||
'${msg.type} payload must contain a string named "query".');
|
'${msg.type} payload must contain a string named "query".');
|
||||||
if (variables != null && variables is! Map)
|
}
|
||||||
|
if (variables != null && variables is! Map) {
|
||||||
throw FormatException(
|
throw FormatException(
|
||||||
'${msg.type} payload\'s "variables" field must be a map (object).');
|
'${msg.type} payload\'s "variables" field must be a map (object).');
|
||||||
if (operationName != null && operationName is! String)
|
}
|
||||||
|
if (operationName != null && operationName is! String) {
|
||||||
throw FormatException(
|
throw FormatException(
|
||||||
'${msg.type} payload\'s "operationName" field must be a string.');
|
'${msg.type} payload\'s "operationName" field must be a string.');
|
||||||
|
}
|
||||||
var result = await onOperation(
|
var result = await onOperation(
|
||||||
msg.id,
|
msg.id,
|
||||||
query as String,
|
query as String,
|
||||||
|
|
|
@ -44,14 +44,15 @@ class OperationMessage {
|
||||||
var payload = map['payload'];
|
var payload = map['payload'];
|
||||||
var id = map['id'];
|
var id = map['id'];
|
||||||
|
|
||||||
if (type == null)
|
if (type == null) {
|
||||||
throw ArgumentError.notNull('type');
|
throw ArgumentError.notNull('type');
|
||||||
else if (type is! String)
|
} else if (type is! String) {
|
||||||
throw ArgumentError.value(type, 'type', 'must be a string');
|
throw ArgumentError.value(type, 'type', 'must be a string');
|
||||||
else if (id is num)
|
} else if (id is num) {
|
||||||
id = id.toString();
|
id = id.toString();
|
||||||
else if (id != null && id is! String)
|
} else if (id != null && id is! String) {
|
||||||
throw ArgumentError.value(id, 'id', 'must be a string or number');
|
throw ArgumentError.value(id, 'id', 'must be a string or number');
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This is technically a violation of the spec.
|
// TODO: This is technically a violation of the spec.
|
||||||
// https://github.com/apollographql/subscriptions-transport-ws/issues/551
|
// https://github.com/apollographql/subscriptions-transport-ws/issues/551
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: graphql_server
|
name: graphql_server
|
||||||
version: 1.0.0
|
version: 1.0.1
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
description: Base package for implementing GraphQL servers. You might prefer `package:angel_graphql`, the fastest way to implement GraphQL backends in Dart.
|
description: Base package for implementing GraphQL servers. You might prefer `package:angel_graphql`, the fastest way to implement GraphQL backends in Dart.
|
||||||
homepage: https://github.com/angel-dart/graphql
|
homepage: https://github.com/angel-dart/graphql
|
||||||
|
|
|
@ -23,16 +23,16 @@ void main() {
|
||||||
'todos',
|
'todos',
|
||||||
listOf(todoType),
|
listOf(todoType),
|
||||||
resolve: (_, __) => [
|
resolve: (_, __) => [
|
||||||
new Todo(
|
Todo(
|
||||||
text: 'Clean your room!',
|
text: 'Clean your room!',
|
||||||
completed: false,
|
completed: false,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
var graphql = new GraphQL(schema);
|
var graphql = GraphQL(schema);
|
||||||
var result = await graphql.parseAndExecute('{ todos { text } }');
|
var result = await graphql.parseAndExecute('{ todos { text } }');
|
||||||
|
|
||||||
print(result);
|
print(result);
|
||||||
|
|
Loading…
Reference in a new issue