diff --git a/graphql_schema/lib/src/argument.dart b/graphql_schema/lib/src/argument.dart index 31e1ce89..24214c74 100644 --- a/graphql_schema/lib/src/argument.dart +++ b/graphql_schema/lib/src/argument.dart @@ -4,10 +4,11 @@ class GraphQLFieldArgument { final String name; final GraphQLType type; final Value defaultValue; + final String description; /// 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}); + {this.defaultValue, this.defaultsToNull: false, this.description}); } diff --git a/graphql_schema/lib/src/scalar.dart b/graphql_schema/lib/src/scalar.dart index a43f3606..2d5941fb 100644 --- a/graphql_schema/lib/src/scalar.dart +++ b/graphql_schema/lib/src/scalar.dart @@ -30,7 +30,9 @@ final GraphQLScalarType graphQLFloat = abstract class GraphQLScalarType extends GraphQLType - with _NonNullableMixin {} + with _NonNullableMixin { + Type get valueType => Value; +} typedef bool _NumVerifier(x); diff --git a/graphql_server/lib/graphql_server.dart b/graphql_server/lib/graphql_server.dart index 2c420326..412d44cc 100644 --- a/graphql_server/lib/graphql_server.dart +++ b/graphql_server/lib/graphql_server.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:graphql_parser/graphql_parser.dart'; import 'package:graphql_schema/graphql_schema.dart'; + import 'introspection.dart'; class GraphQL { @@ -305,12 +306,17 @@ class GraphQL { } if (fieldType is GraphQLScalarType) { - var validation = fieldType.validate(fieldName, result); + try { + var validation = fieldType.validate(fieldName, result); - if (!validation.successful) { - return null; - } else { - return validation.value; + if (!validation.successful) { + return null; + } else { + return validation.value; + } + } on TypeError { + throw new GraphQLException( + 'Value of field "$fieldName" must be ${fieldType.valueType}, got $result instead.'); } } diff --git a/graphql_server/lib/introspection.dart b/graphql_server/lib/introspection.dart index 5c75b665..ccb9a5bf 100644 --- a/graphql_server/lib/introspection.dart +++ b/graphql_server/lib/introspection.dart @@ -36,6 +36,8 @@ GraphQLSchema reflectSchema(GraphQLSchema schema, List allTypes) { typeType, schemaType, _reflectFields(), + _reflectDirectiveType(), + _reflectInputValueType(), ]); var fields = [ @@ -86,6 +88,7 @@ GraphQLObjectType _reflectSchemaTypes() { ); var fieldType = _reflectFields(); + var inputValueType = _reflectInputValueType(); var typeField = fieldType.fields .firstWhere((f) => f.name == 'type', orElse: () => null); @@ -98,6 +101,19 @@ GraphQLObjectType _reflectSchemaTypes() { ), ); } + + typeField = fieldType.fields + .firstWhere((f) => f.name == 'type', orElse: () => null); + + if (typeField == null) { + inputValueType.fields.add( + field( + 'type', + type: _reflectSchemaTypes(), + resolve: (f, _) => (f as GraphQLFieldArgument).type, + ), + ); + } } return _typeType; @@ -155,6 +171,8 @@ GraphQLObjectType _reflectFields() { } GraphQLObjectType _createFieldType() { + var inputValueType = _reflectInputValueType(); + return objectType('__Field', fields: [ field( 'name', @@ -163,7 +181,7 @@ GraphQLObjectType _createFieldType() { ), field( 'isDeprecated', - type: graphQLString, + type: graphQLBoolean, resolve: (f, _) => (f as GraphQLField).isDeprecated, ), field( @@ -173,13 +191,38 @@ GraphQLObjectType _createFieldType() { ), field( 'args', - type: listType(graphQLString.nonNullable()).nonNullable(), // TODO: Input value type + type: listType(inputValueType.nonNullable()).nonNullable(), resolve: (f, _) => (f as GraphQLField).arguments, ), ]); } +GraphQLObjectType _inputValueType; + +GraphQLObjectType _reflectInputValueType() { + return _inputValueType ??= objectType('__InputValue', fields: [ + field( + 'name', + type: graphQLString, + resolve: (obj, _) => (obj as GraphQLFieldArgument).name, + ), + field( + 'description', + type: graphQLString, + resolve: (obj, _) => (obj as GraphQLFieldArgument).description, + ), + field( + 'defaultValue', + type: graphQLString, + resolve: (obj, _) => + (obj as GraphQLFieldArgument).defaultValue?.toString(), + ), + ]); +} + GraphQLObjectType _reflectDirectiveType() { + var inputValueType = _reflectInputValueType(); + // TODO: What actually is this??? return objectType('__Directive', fields: [ field( @@ -194,12 +237,13 @@ GraphQLObjectType _reflectDirectiveType() { ), field( 'locations', - type: listType(graphQLString.nonNullable()).nonNullable(), // TODO: Enum directiveLocation + type: listType(graphQLString.nonNullable()).nonNullable(), + // TODO: Enum directiveLocation resolve: (obj, _) => [], ), field( 'args', - type: listType(graphQLString.nonNullable()).nonNullable(), + type: listType(inputValueType.nonNullable()).nonNullable(), resolve: (obj, _) => [], ), ]);