diff --git a/graphql_schema/lib/src/argument.dart b/graphql_schema/lib/src/argument.dart index 36652039..31e1ce89 100644 --- a/graphql_schema/lib/src/argument.dart +++ b/graphql_schema/lib/src/argument.dart @@ -4,5 +4,10 @@ class GraphQLFieldArgument { final String name; final GraphQLType type; final Value defaultValue; - GraphQLFieldArgument(this.name, this.type, {this.defaultValue}); + + /// If [defaultValue] is `null`, and `null` is a valid value for this argument, set this to `true`. + final bool defaultsToNull; + + GraphQLFieldArgument(this.name, this.type, + {this.defaultValue, this.defaultsToNull: false}); } diff --git a/graphql_schema/lib/src/field.dart b/graphql_schema/lib/src/field.dart index 2b19b855..a16833ad 100644 --- a/graphql_schema/lib/src/field.dart +++ b/graphql_schema/lib/src/field.dart @@ -4,12 +4,17 @@ typedef FutureOr GraphQLFieldResolver( Serialized serialized); class GraphQLField { + final List arguments = []; final String name; - final GraphQLFieldArgument argument; final GraphQLFieldResolver resolve; final GraphQLType type; - GraphQLField(this.name, {this.argument, this.resolve, this.type}); + GraphQLField(this.name, + {Iterable arguments: const [], + this.resolve, + this.type}) { + this.arguments.addAll(arguments ?? []); + } FutureOr serialize(Value value) { return type.serialize(value); diff --git a/graphql_schema/lib/src/type.dart b/graphql_schema/lib/src/type.dart index d65b9a06..b6ffa733 100644 --- a/graphql_schema/lib/src/type.dart +++ b/graphql_schema/lib/src/type.dart @@ -5,6 +5,8 @@ abstract class GraphQLType { Value deserialize(Serialized serialized); ValidationResult validate(String key, Serialized input); GraphQLType nonNullable(); + + bool get isNullable => false; } /// Shorthand to create a [GraphQLListType]. @@ -64,6 +66,9 @@ class _GraphQLNonNullableType final GraphQLType type; _GraphQLNonNullableType._(this.type); + @override + bool get isNullable => true; + @override GraphQLType nonNullable() { throw new UnsupportedError( diff --git a/graphql_server/lib/graphql.dart b/graphql_server/lib/graphql.dart index 3f1e85a6..cf2a9b8d 100644 --- a/graphql_server/lib/graphql.dart +++ b/graphql_server/lib/graphql.dart @@ -33,8 +33,7 @@ class GraphQL { if (customTypes.containsKey(ctx.typeName.name)) return customTypes[ctx.typeName.name]; throw new ArgumentError( - 'Unknown GraphQL type: "${ctx.typeName.name}"\n${ctx.span - .highlight()}'); + 'Unknown GraphQL type: "${ctx.typeName.name}"\n${ctx.span.highlight()}'); break; } } else { @@ -48,7 +47,7 @@ class GraphQL { {Map variableValues: const {}, initialValue}) { var operation = getOperation(document, operationName); var coercedVariableValues = - coerceVariableValues(schema, operation, variableValues ?? {}); + coerceVariableValues(schema, operation, variableValues ?? {}); if (operation.isQuery) return executeQuery( document, operation, schema, coercedVariableValues, initialValue); @@ -65,7 +64,7 @@ class GraphQL { return ops.length == 1 ? ops.first : throw new GraphQLException( - 'Missing required operation "$operationName".'); + 'Missing required operation "$operationName".'); } else { return ops.firstWhere((d) => d.name == operationName, orElse: () => throw new GraphQLException( @@ -128,7 +127,7 @@ class GraphQL { objectValue, Map variableValues) { var groupedFieldSet = - collectFields(document, objectType, selectionSet, variableValues); + collectFields(document, objectType, selectionSet, variableValues); var resultMap = {}; for (var responseKey in groupedFieldSet.keys) { @@ -156,7 +155,7 @@ class GraphQL { Map variableValues) { var field = fields[0]; var argumentValues = - coerceArgumentValues(objectType, field, variableValues); + coerceArgumentValues(objectType, field, variableValues); var resolvedValue = resolveFieldValue( objectType, objectValue, field.field.fieldName.name, argumentValues); return completeValue(fieldType, fields, resolvedValue, variableValues); @@ -168,9 +167,36 @@ class GraphQL { var argumentValues = field.field.arguments; var fieldName = field.field.fieldName.name; var desiredField = objectType.fields.firstWhere((f) => f.name == fieldName); + var argumentDefinitions = desiredField.arguments; - // TODO: Multiple arguments? - var argumentDefinitions = desiredField.argument; + for (var argumentDefinition in argumentDefinitions) { + var argumentName = argumentDefinition.name; + var argumentType = argumentDefinition.type; + var defaultValue = argumentDefinition.defaultValue; + var value = argumentValues.firstWhere((a) => a.name == argumentName, + orElse: () => null); + + if (value != null) { + var variableName = value.name; + var variableValue = variableValues[variableName]; + + if (variableValues.containsKey(variableName)) { + coercedValues[argumentName] = variableValue; + } else if (defaultValue != null || argumentDefinition.defaultsToNull) { + coercedValues[argumentName] = defaultValue; + } else if (!argumentType.isNullable) { + throw new GraphQLException( + 'Missing value for argument "$argumentName".'); + } + } else { + if (defaultValue != null || argumentDefinition.defaultsToNull) { + coercedValues[argumentName] = defaultValue; + } else if (!argumentType.isNullable) { + throw new GraphQLException( + 'Missing value for argument "$argumentName".'); + } + } + } return coercedValues; } @@ -192,7 +218,7 @@ class GraphQL { if (selection.field != null) { var responseKey = selection.field.fieldName.name; var groupForResponseKey = - groupedFields.putIfAbsent(responseKey, () => []); + groupedFields.putIfAbsent(responseKey, () => []); groupForResponseKey.add(selection); } else if (selection.fragmentSpread != null) { var fragmentSpreadName = selection.fragmentSpread.name; @@ -201,7 +227,7 @@ class GraphQL { var fragment = document.definitions .whereType() .firstWhere((f) => f.name == fragmentSpreadName, - orElse: () => null); + orElse: () => null); if (fragment == null) continue; var fragmentType = fragment.typeCondition; @@ -213,7 +239,7 @@ class GraphQL { for (var responseKey in fragmentGroupFieldSet.keys) { var fragmentGroup = fragmentGroupFieldSet[responseKey]; var groupForResponseKey = - groupedFields.putIfAbsent(responseKey, () => []); + groupedFields.putIfAbsent(responseKey, () => []); groupForResponseKey.addAll(fragmentGroup); } } else if (selection.inlineFragment != null) { @@ -227,7 +253,7 @@ class GraphQL { for (var responseKey in fragmentGroupFieldSet.keys) { var fragmentGroup = fragmentGroupFieldSet[responseKey]; var groupForResponseKey = - groupedFields.putIfAbsent(responseKey, () => []); + groupedFields.putIfAbsent(responseKey, () => []); groupForResponseKey.addAll(fragmentGroup); } } @@ -278,4 +304,4 @@ class GraphQL { class GraphQLException extends FormatException { GraphQLException(String message) : super(message); -} \ No newline at end of file +}