Finished querying (missing interface+union types)

This commit is contained in:
Tobe O 2018-08-02 10:16:46 -04:00
parent e267afe85b
commit 10e740b2b1
6 changed files with 120 additions and 41 deletions

View file

@ -1,4 +1,5 @@
import 'package:source_span/source_span.dart';
import '../token.dart';
import 'node.dart';
import 'selection.dart';
@ -9,6 +10,9 @@ class SelectionSetContext extends Node {
SelectionSetContext(this.LBRACE, this.RBRACE);
factory SelectionSetContext.merged(List<SelectionContext> selections) =
_MergedSelectionSetContext;
@override
FileSpan get span {
var out =
@ -16,3 +20,13 @@ class SelectionSetContext extends Node {
return out.expand(RBRACE.span);
}
}
class _MergedSelectionSetContext extends SelectionSetContext {
final List<SelectionContext> selections;
_MergedSelectionSetContext(this.selections) : super(null, null);
@override
FileSpan get span =>
selections.map((s) => s.span).reduce((a, b) => a.expand(b));
}

View file

@ -2,14 +2,20 @@ import 'package:graphql_schema/graphql_schema.dart';
final GraphQLSchema todoSchema = new GraphQLSchema(
query: objectType('Todo', [
field('text', type: graphQLString.nonNullable()),
field('created_at', type: graphQLDate)
]));
field(
'text',
innerType: graphQLString.nonNullable(),
),
field('created_at', innerType: graphQLDate),
]),
);
main() {
// Validation
var validation = todoSchema.query
.validate('@root', {'foo': 'bar', 'text': null, 'created_at': 24});
var validation = todoSchema.query.validate(
'@root',
{'foo': 'bar', 'text': null, 'created_at': 24,},
);
if (validation.successful) {
print('This is valid data!!!');

View file

@ -5,9 +5,10 @@ GraphQLObjectType objectType(String name,
new GraphQLObjectType(name)..fields.addAll(fields ?? []);
GraphQLField<T, Serialized> field<T, Serialized>(String name,
{GraphQLFieldArgument<T, Serialized> argument,
{Iterable<GraphQLFieldArgument<T, Serialized>> arguments:
const <GraphQLFieldArgument<T, Serialized>>[],
GraphQLFieldResolver<T, Serialized> resolve,
GraphQLType<T, Serialized> type}) {
GraphQLType<T, Serialized> innerType}) {
return new GraphQLField(name,
argument: argument, resolve: resolve, type: type);
arguments: arguments, resolve: resolve, type: innerType);
}

View file

@ -5,8 +5,6 @@ abstract class GraphQLType<Value, Serialized> {
Value deserialize(Serialized serialized);
ValidationResult<Serialized> validate(String key, Serialized input);
GraphQLType<Value, Serialized> nonNullable();
bool get isNullable => false;
}
/// Shorthand to create a [GraphQLListType].
@ -17,8 +15,8 @@ GraphQLListType<Value, Serialized> listType<Value, Serialized>(
class GraphQLListType<Value, Serialized>
extends GraphQLType<List<Value>, List<Serialized>>
with _NonNullableMixin<List<Value>, List<Serialized>> {
final GraphQLType<Value, Serialized> type;
GraphQLListType(this.type);
final GraphQLType<Value, Serialized> innerType;
GraphQLListType(this.innerType);
@override
ValidationResult<List<Serialized>> validate(
@ -32,7 +30,7 @@ class GraphQLListType<Value, Serialized>
for (int i = 0; i < input.length; i++) {
var k = '"$key" at index $i';
var v = input[i];
var result = type.validate(k, v);
var result = innerType.validate(k, v);
if (!result.successful)
errors.addAll(result.errors);
else
@ -45,12 +43,12 @@ class GraphQLListType<Value, Serialized>
@override
List<Value> deserialize(List<Serialized> serialized) {
return serialized.map<Value>(type.deserialize).toList();
return serialized.map<Value>(innerType.deserialize).toList();
}
@override
List<Serialized> serialize(List<Value> value) {
return value.map<Serialized>(type.serialize).toList();
return value.map<Serialized>(innerType.serialize).toList();
}
}
@ -58,16 +56,13 @@ abstract class _NonNullableMixin<Value, Serialized>
implements GraphQLType<Value, Serialized> {
GraphQLType<Value, Serialized> _nonNullableCache;
GraphQLType<Value, Serialized> nonNullable() => _nonNullableCache ??=
new _GraphQLNonNullableType<Value, Serialized>._(this);
new GraphQLNonNullableType<Value, Serialized>._(this);
}
class _GraphQLNonNullableType<Value, Serialized>
class GraphQLNonNullableType<Value, Serialized>
extends GraphQLType<Value, Serialized> {
final GraphQLType<Value, Serialized> type;
_GraphQLNonNullableType._(this.type);
@override
bool get isNullable => true;
final GraphQLType<Value, Serialized> innerType;
GraphQLNonNullableType._(this.innerType);
@override
GraphQLType<Value, Serialized> nonNullable() {
@ -80,16 +75,16 @@ class _GraphQLNonNullableType<Value, Serialized>
if (input == null)
return new ValidationResult._failure(
['Expected "$key" to be a non-null value.']);
return type.validate(key, input);
return innerType.validate(key, input);
}
@override
Value deserialize(Serialized serialized) {
return type.deserialize(serialized);
return innerType.deserialize(serialized);
}
@override
Serialized serialize(Value value) {
return type.serialize(value);
return innerType.serialize(value);
}
}

View file

@ -1,14 +1,14 @@
import 'package:graphql_schema/graphql_schema.dart';
final GraphQLObjectType pokemonType = objectType('Pokemon', [
field('species', type: graphQLString),
field('catch_date', type: graphQLDate)
field('species', innerType: graphQLString),
field('catch_date', innerType: graphQLDate)
]);
final GraphQLObjectType trainerType =
objectType('Trainer', [field('name', type: graphQLString)]);
objectType('Trainer', [field('name', innerType: graphQLString)]);
final GraphQLObjectType pokemonRegionType = objectType('PokemonRegion', [
field('trainer', type: trainerType),
field('pokemon_species', type: listType(pokemonType))
field('trainer', innerType: trainerType),
field('pokemon_species', innerType: listType(pokemonType))
]);

View file

@ -44,7 +44,7 @@ class GraphQL {
}
}
Future<GraphQLResult> executeRequest(
Future<Map<String, dynamic>> executeRequest(
GraphQLSchema schema, DocumentContext document, String operationName,
{Map<String, dynamic> variableValues: const {}, initialValue}) async {
var operation = getOperation(document, operationName);
@ -53,9 +53,11 @@ class GraphQL {
if (operation.isQuery)
return await executeQuery(
document, operation, schema, coercedVariableValues, initialValue);
else
return executeMutation(
document, operation, schema, coercedVariableValues, initialValue);
else {
throw new UnimplementedError('mutations');
// return executeMutation(
// document, operation, schema, coercedVariableValues, initialValue);
}
}
OperationDefinitionContext getOperation(
@ -110,7 +112,7 @@ class GraphQL {
return coercedValues;
}
Future<GraphQLResult> executeQuery(
Future<Map<String, dynamic>> executeQuery(
DocumentContext document,
OperationDefinitionContext query,
GraphQLSchema schema,
@ -140,8 +142,8 @@ class GraphQL {
var fieldType =
objectType.fields.firstWhere((f) => f.name == fieldName)?.type;
if (fieldType == null) continue;
var responseValue = await executeField(
objectType, objectValue, fields, fieldType, variableValues);
var responseValue = await executeField(document, fieldName, objectType,
objectValue, fields, fieldType, variableValues);
resultMap[responseKey] = responseValue;
}
}
@ -150,6 +152,8 @@ class GraphQL {
}
executeField(
DocumentContext document,
String fieldName,
GraphQLObjectType objectType,
objectValue,
List<SelectionContext> fields,
@ -160,7 +164,8 @@ class GraphQL {
coerceArgumentValues(objectType, field, variableValues);
var resolvedValue = await resolveFieldValue(
objectType, objectValue, field.field.fieldName.name, argumentValues);
return completeValue(fieldType, fields, resolvedValue, variableValues);
return completeValue(
document, fieldName, fieldType, fields, resolvedValue, variableValues);
}
Map<String, dynamic> coerceArgumentValues(GraphQLObjectType objectType,
@ -186,14 +191,14 @@ class GraphQL {
coercedValues[argumentName] = variableValue;
} else if (defaultValue != null || argumentDefinition.defaultsToNull) {
coercedValues[argumentName] = defaultValue;
} else if (!argumentType.isNullable) {
} else if (argumentType is GraphQLNonNullableType) {
throw new GraphQLException(
'Missing value for argument "$argumentName".');
}
} else {
if (defaultValue != null || argumentDefinition.defaultsToNull) {
coercedValues[argumentName] = defaultValue;
} else if (!argumentType.isNullable) {
} else if (argumentType is GraphQLNonNullableType) {
throw new GraphQLException(
'Missing value for argument "$argumentName".');
}
@ -209,6 +214,64 @@ class GraphQL {
return await field.resolve(objectValue, argumentValues) as T;
}
Future completeValue(
DocumentContext document,
String fieldName,
GraphQLType fieldType,
List<SelectionContext> fields,
result,
Map<String, dynamic> variableValues) async {
if (fieldType is GraphQLNonNullableType) {
var innerType = fieldType.innerType;
var completedResult = completeValue(
document, fieldName, innerType, fields, result, variableValues);
if (completedResult == null) {
throw new GraphQLException(
'Null value provided for non-nullable field "$fieldName".');
} else {
return completedResult;
}
}
if (result == null) {
return null;
}
if (fieldType is GraphQLListType) {
if (result is! Iterable) {
throw new GraphQLException(
'Value of field "$fieldName" must be a list or iterable.');
}
var innerType = fieldType.innerType;
return (result as Iterable)
.map((resultItem) => completeValue(document, '(item in "$fieldName")',
innerType, fields, resultItem, variableValues))
.toList();
}
if (fieldType is GraphQLScalarType) {
var validation = fieldType.validate(fieldName, result);
if (!validation.successful) {
return null;
} else {
return validation.value;
}
}
if (fieldType is GraphQLObjectType) {
var objectType = fieldType;
var subSelectionSet = new SelectionSetContext.merged(fields);
return await executeSelectionSet(
document, subSelectionSet, objectType, result, variableValues);
}
// TODO: Interface/union type
throw new UnsupportedError('Unsupported type: $fieldType');
}
Map<String, List<SelectionContext>> collectFields(
DocumentContext document,
GraphQLObjectType objectType,