diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index dacfcb0..2060041 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -23,7 +23,7 @@ jobs: # https://github.com/dart-lang/setup-dart/blob/main/README.md - uses: dart-lang/setup-dart@v1 with: - sdk: "3.6.1" + sdk: "3.8.3" # Graphql Schema - id: graphql_schema2_upgrade diff --git a/packages/angel_graphql/example/main.dart b/packages/angel_graphql/example/main.dart index 51c9c71..c123446 100644 --- a/packages/angel_graphql/example/main.dart +++ b/packages/angel_graphql/example/main.dart @@ -12,13 +12,14 @@ import 'package:logging/logging.dart'; void main() async { var logger = Logger('angel3_graphql'); var app = Angel( - reflector: MirrorsReflector(), - logger: logger - ..onRecord.listen((rec) { - print(rec); - if (rec.error != null) print(rec.error); - if (rec.stackTrace != null) print(rec.stackTrace); - })); + reflector: MirrorsReflector(), + logger: logger + ..onRecord.listen((rec) { + print(rec); + if (rec.error != null) print(rec.error); + if (rec.stackTrace != null) print(rec.stackTrace); + }), + ); var http = AngelHttp(app); var todoService = app.use('api/todos', MapService()); @@ -36,9 +37,7 @@ void main() async { 'todo', convertDartType(Todo)!, resolve: resolveViaServiceRead(todoService), - inputs: [ - GraphQLFieldInput('id', graphQLId.nonNullable()), - ], + inputs: [GraphQLFieldInput('id', graphQLId.nonNullable())], ), ], ); @@ -52,33 +51,39 @@ void main() async { convertDartType(Todo)!, inputs: [ GraphQLFieldInput( - 'data', convertDartType(Todo)!.coerceToInputObject()), + 'data', + convertDartType(Todo)!.coerceToInputObject(), + ), ], resolve: resolveViaServiceCreate(todoService), ), ], ); - var schema = graphQLSchema( - queryType: queryType, - mutationType: mutationType, - ); + var schema = graphQLSchema(queryType: queryType, mutationType: mutationType); app.all('/graphql', graphQLHttp(GraphQL(schema))); app.get('/graphiql', graphiQL()); - await todoService - .create({'text': 'Clean your room!', 'completion_status': 'COMPLETE'}); - await todoService.create( - {'text': 'Take out the trash', 'completion_status': 'INCOMPLETE'}); + await todoService.create({ + 'text': 'Clean your room!', + 'completion_status': 'COMPLETE', + }); + await todoService.create({ + 'text': 'Take out the trash', + 'completion_status': 'INCOMPLETE', + }); await todoService.create({ 'text': 'Become a billionaire at the age of 5', - 'completion_status': 'INCOMPLETE' + 'completion_status': 'INCOMPLETE', }); var server = await http.startServer('127.0.0.1', 3000); - var uri = - Uri(scheme: 'http', host: server.address.address, port: server.port); + var uri = Uri( + scheme: 'http', + host: server.address.address, + port: server.port, + ); var graphiqlUri = uri.replace(path: 'graphiql'); print('Listening at $uri'); print('Access graphiql at $graphiqlUri'); @@ -91,7 +96,8 @@ abstract class HasText { @serializable @GraphQLDocumentation( - description: 'A task that might not be completed yet. **Yay! Markdown!**') + description: 'A task that might not be completed yet. **Yay! Markdown!**', +) class Todo extends Model implements HasText { @override String? text; diff --git a/packages/angel_graphql/example/subscription.dart b/packages/angel_graphql/example/subscription.dart index 703832e..b137a62 100644 --- a/packages/angel_graphql/example/subscription.dart +++ b/packages/angel_graphql/example/subscription.dart @@ -23,8 +23,10 @@ void main() async { // Create an in-memory service. var fs = LocalFileSystem(); - var postService = - app.use('/api/posts', JsonFileService(fs.file('posts.json'))); + var postService = app.use( + '/api/posts', + JsonFileService(fs.file('posts.json')), + ); // Also get a [Stream] of item creation events. var postAdded = postService.afterCreated @@ -33,10 +35,10 @@ void main() async { .asBroadcastStream(); // GraphQL setup. - var postType = objectType('Post', fields: [ - field('author', graphQLString), - field('comment', graphQLString), - ]); + var postType = objectType( + 'Post', + fields: [field('author', graphQLString), field('comment', graphQLString)], + ); var schema = graphQLSchema( // Hooked up to the postService: @@ -64,7 +66,9 @@ void main() async { postType, inputs: [ GraphQLFieldInput( - 'data', postType.toInputObject('PostInput').nonNullable()), + 'data', + postType.toInputObject('PostInput').nonNullable(), + ), ], resolve: resolveViaServiceCreate(postService), ), @@ -75,22 +79,27 @@ void main() async { // type Subscription { postAdded: Post } subscriptionType: objectType( 'Subscription', - fields: [ - field('postAdded', postType, resolve: (_, __) => postAdded), - ], + fields: [field('postAdded', postType, resolve: (_, __) => postAdded)], ), ); // Mount GraphQL routes; we'll support HTTP and WebSockets transports. app.all('/graphql', graphQLHttp(GraphQL(schema))); - app.get('/subscriptions', - graphQLWS(GraphQL(schema), keepAliveInterval: Duration(seconds: 3))); - app.get('/graphiql', - graphiQL(subscriptionsEndpoint: 'ws://localhost:3000/subscriptions')); + app.get( + '/subscriptions', + graphQLWS(GraphQL(schema), keepAliveInterval: Duration(seconds: 3)), + ); + app.get( + '/graphiql', + graphiQL(subscriptionsEndpoint: 'ws://localhost:3000/subscriptions'), + ); var server = await http.startServer('127.0.0.1', 3000); - var uri = - Uri(scheme: 'http', host: server.address.address, port: server.port); + var uri = Uri( + scheme: 'http', + host: server.address.address, + port: server.port, + ); var graphiqlUri = uri.replace(path: 'graphiql'); var postsUri = uri.replace(pathSegments: ['api', 'posts']); print('Listening at $uri'); diff --git a/packages/angel_graphql/lib/src/graphiql.dart b/packages/angel_graphql/lib/src/graphiql.dart index 976e47c..6cdcaf1 100644 --- a/packages/angel_graphql/lib/src/graphiql.dart +++ b/packages/angel_graphql/lib/src/graphiql.dart @@ -5,20 +5,27 @@ import 'package:http_parser/http_parser.dart'; /// /// By default, the interface expects your backend to be mounted at `/graphql`; this is configurable /// via [graphQLEndpoint]. -RequestHandler graphiQL( - {String graphQLEndpoint = '/graphql', String? subscriptionsEndpoint}) { +RequestHandler graphiQL({ + String graphQLEndpoint = '/graphql', + String? subscriptionsEndpoint, +}) { return (req, res) { res ..contentType = MediaType('text', 'html') - ..write(renderGraphiql( + ..write( + renderGraphiql( graphqlEndpoint: graphQLEndpoint, - subscriptionsEndpoint: subscriptionsEndpoint)) + subscriptionsEndpoint: subscriptionsEndpoint, + ), + ) ..close(); }; } -String renderGraphiql( - {String graphqlEndpoint = '/graphql', String? subscriptionsEndpoint}) { +String renderGraphiql({ + String graphqlEndpoint = '/graphql', + String? subscriptionsEndpoint, +}) { var subscriptionsScripts = '', subscriptionsFetcher = '', fetcherName = 'graphQLFetcher'; @@ -29,7 +36,8 @@ String renderGraphiql( '''; - subscriptionsFetcher = ''' + subscriptionsFetcher = + ''' let subscriptionsClient = window.SubscriptionsTransportWs.SubscriptionClient('$subscriptionsEndpoint', { reconnect: true }); diff --git a/packages/angel_graphql/lib/src/graphql_http.dart b/packages/angel_graphql/lib/src/graphql_http.dart index f2b4a2b..4bbde1a 100644 --- a/packages/angel_graphql/lib/src/graphql_http.dart +++ b/packages/angel_graphql/lib/src/graphql_http.dart @@ -22,9 +22,11 @@ final RegExp _num = RegExp(r'^[0-9]+$'); /// /// Follows the guidelines listed here: /// https://graphql.org/learn/serving-over-http/ -RequestHandler graphQLHttp(GraphQL graphQL, - {Function(RequestContext, ResponseContext, Stream>)? - onSubscription}) { +RequestHandler graphQLHttp( + GraphQL graphQL, { + Function(RequestContext, ResponseContext, Stream>)? + onSubscription, +}) { return (req, res) async { var globalVariables = { '__requestctx': req, @@ -35,15 +37,14 @@ RequestHandler graphQLHttp(GraphQL graphQL, if (result is Stream>) { if (onSubscription == null) { throw StateError( - 'The GraphQL backend returned a Stream, but no `onSubscription` callback was provided.'); + 'The GraphQL backend returned a Stream, but no `onSubscription` callback was provided.', + ); } else { return await onSubscription(req, res, result); } } - return { - 'data': result, - }; + return {'data': result}; } Future executeMap(Map map) async { @@ -56,13 +57,15 @@ RequestHandler graphQLHttp(GraphQL graphQL, variables = json.decode(variables); } - return await sendGraphQLResponse(await graphQL.parseAndExecute( - text, - sourceUrl: 'input', - operationName: operationName, - variableValues: foldToStringDynamic(variables as Map?), - globalVariables: globalVariables, - )); + return await sendGraphQLResponse( + await graphQL.parseAndExecute( + text, + sourceUrl: 'input', + operationName: operationName, + variableValues: foldToStringDynamic(variables as Map?), + globalVariables: globalVariables, + ), + ); } try { @@ -73,11 +76,13 @@ RequestHandler graphQLHttp(GraphQL graphQL, } else if (req.method == 'POST') { if (req.headers!.contentType?.mimeType == graphQlContentType.mimeType) { var text = await req.body!.transform(utf8.decoder).join(); - return sendGraphQLResponse(await graphQL.parseAndExecute( - text, - sourceUrl: 'input', - globalVariables: globalVariables, - )); + return sendGraphQLResponse( + await graphQL.parseAndExecute( + text, + sourceUrl: 'input', + globalVariables: globalVariables, + ), + ); } else if (req.headers!.contentType?.mimeType == 'application/json') { if (await validate(graphQlPostBody)(req, res) as bool) { return await executeMap(req.bodyAsMap); @@ -88,29 +93,34 @@ RequestHandler graphQLHttp(GraphQL graphQL, var operations = fields['operations'] as String?; if (operations == null) { throw AngelHttpException.badRequest( - message: 'Missing "operations" field.'); + message: 'Missing "operations" field.', + ); } var map = fields.containsKey('map') ? json.decode(fields['map'] as String) : null; if (map is! Map) { throw AngelHttpException.badRequest( - message: '"map" field must decode to a JSON object.'); + message: '"map" field must decode to a JSON object.', + ); } var variables = Map.from(globalVariables); for (var entry in map.entries) { - var file = - req.uploadedFiles!.firstWhereOrNull((f) => f.name == entry.key); + var file = req.uploadedFiles!.firstWhereOrNull( + (f) => f.name == entry.key, + ); if (file == null) { throw AngelHttpException.badRequest( - message: - '"map" contained key "${entry.key}", but no uploaded file ' - 'has that name.'); + message: + '"map" contained key "${entry.key}", but no uploaded file ' + 'has that name.', + ); } if (entry.value is! List) { throw AngelHttpException.badRequest( - message: - 'The value for "${entry.key}" in the "map" field was not a JSON array.'); + message: + 'The value for "${entry.key}" in the "map" field was not a JSON array.', + ); } var objectPaths = entry.value as List; for (var objectPath in objectPaths) { @@ -123,33 +133,38 @@ RequestHandler graphQLHttp(GraphQL graphQL, if (_num.hasMatch(name)) { if (current is! List) { throw AngelHttpException.badRequest( - message: - 'Object "$parent" is not a JSON array, but the ' - '"map" field contained a mapping to $parent.$name.'); + message: + 'Object "$parent" is not a JSON array, but the ' + '"map" field contained a mapping to $parent.$name.', + ); } current[int.parse(name)] = file; } else { if (current is! Map) { throw AngelHttpException.badRequest( - message: - 'Object "$parent" is not a JSON object, but the ' - '"map" field contained a mapping to $parent.$name.'); + message: + 'Object "$parent" is not a JSON object, but the ' + '"map" field contained a mapping to $parent.$name.', + ); } current[name] = file; } } } else { throw AngelHttpException.badRequest( - message: - 'All array values in the "map" field must begin with "variables.".'); + message: + 'All array values in the "map" field must begin with "variables.".', + ); } } } - return await sendGraphQLResponse(await graphQL.parseAndExecute( - operations, - sourceUrl: 'input', - globalVariables: variables, - )); + return await sendGraphQLResponse( + await graphQL.parseAndExecute( + operations, + sourceUrl: 'input', + globalVariables: variables, + ), + ); } else { throw AngelHttpException.badRequest(); } @@ -173,9 +188,10 @@ RequestHandler graphQLHttp(GraphQL graphQL, } catch (e, st) { if (req.app?.logger != null) { req.app!.logger.severe( - 'An error occurred while processing GraphQL query at ${req.uri}.', - e, - st); + 'An error occurred while processing GraphQL query at ${req.uri}.', + e, + st, + ); } return GraphQLException.fromMessage(e.toString()).toJson(); diff --git a/packages/angel_graphql/lib/src/graphql_ws.dart b/packages/angel_graphql/lib/src/graphql_ws.dart index 3b25bac..964a482 100644 --- a/packages/angel_graphql/lib/src/graphql_ws.dart +++ b/packages/angel_graphql/lib/src/graphql_ws.dart @@ -20,27 +20,37 @@ RequestHandler graphQLWS(GraphQL graphQL, {Duration? keepAliveInterval}) { if (req is HttpRequestContext) { if (WebSocketTransformer.isUpgradeRequest(req.rawRequest!)) { await res.detach(); - var socket = await WebSocketTransformer.upgrade(req.rawRequest!, - protocolSelector: (protocols) { - if (protocols.contains('graphql-ws')) { - return 'graphql-ws'; - } else { - throw AngelHttpException.badRequest( - message: 'Only the "graphql-ws" protocol is allowed.'); - } - }); + var socket = await WebSocketTransformer.upgrade( + req.rawRequest!, + protocolSelector: (protocols) { + if (protocols.contains('graphql-ws')) { + return 'graphql-ws'; + } else { + throw AngelHttpException.badRequest( + message: 'Only the "graphql-ws" protocol is allowed.', + ); + } + }, + ); var channel = IOWebSocketChannel(socket); var client = stw.RemoteClient(channel.cast()); - var server = - _GraphQLWSServer(client, graphQL, req, res, keepAliveInterval); + var server = _GraphQLWSServer( + client, + graphQL, + req, + res, + keepAliveInterval, + ); await server.done; } else { throw AngelHttpException.badRequest( - message: 'The `graphQLWS` endpoint only accepts WebSockets.'); + message: 'The `graphQLWS` endpoint only accepts WebSockets.', + ); } } else { throw AngelHttpException.badRequest( - message: 'The `graphQLWS` endpoint only accepts HTTP/1.1 requests.'); + message: 'The `graphQLWS` endpoint only accepts HTTP/1.1 requests.', + ); } }; } @@ -50,16 +60,24 @@ class _GraphQLWSServer extends stw.Server { final RequestContext req; final ResponseContext res; - _GraphQLWSServer(super.client, this.graphQL, this.req, this.res, - Duration? keepAliveInterval) - : super(keepAliveInterval: keepAliveInterval); + _GraphQLWSServer( + super.client, + this.graphQL, + this.req, + this.res, + Duration? keepAliveInterval, + ) : super(keepAliveInterval: keepAliveInterval); @override bool onConnect(stw.RemoteClient client, [Map? connectionParams]) => true; @override - Future onOperation(String? id, String query, - [Map? variables, String? operationName]) async { + Future onOperation( + String? id, + String query, [ + Map? variables, + String? operationName, + ]) async { try { var globalVariables = { '__requestctx': req, diff --git a/packages/angel_graphql/lib/src/resolvers.dart b/packages/angel_graphql/lib/src/resolvers.dart index d4df180..f927edb 100644 --- a/packages/angel_graphql/lib/src/resolvers.dart +++ b/packages/angel_graphql/lib/src/resolvers.dart @@ -20,7 +20,7 @@ Map? _getQuery(Map arguments) { /// The arguments passed to the resolver will be forwarded to the service, and the /// service will receive [Providers.graphql]. GraphQLFieldResolver, Serialized> - resolveViaServiceIndex(Service service) { +resolveViaServiceIndex(Service service) { return (_, arguments) async { var requestInfo = _fetchRequestInfo(arguments); var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} @@ -35,8 +35,7 @@ GraphQLFieldResolver, Serialized> /// The arguments passed to the resolver will be forwarded to the service, and the /// service will receive [Providers.graphql]. GraphQLFieldResolver - resolveViaServiceFindOne( - Service service) { +resolveViaServiceFindOne(Service service) { return (_, arguments) async { var requestInfo = _fetchRequestInfo(arguments); var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} @@ -52,9 +51,10 @@ GraphQLFieldResolver /// /// The arguments passed to the resolver will be forwarded to the service, and the /// service will receive [Providers.graphql]. -GraphQLFieldResolver - resolveViaServiceRead(Service service, - {String idField = 'id'}) { +GraphQLFieldResolver resolveViaServiceRead< + Value, + Serialized +>(Service service, {String idField = 'id'}) { return (_, arguments) async { var requestInfo = _fetchRequestInfo(arguments); var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} @@ -72,8 +72,7 @@ GraphQLFieldResolver /// The arguments passed to the resolver will be forwarded to the service, and the /// service will receive [Providers.graphql]. GraphQLFieldResolver - resolveViaServiceCreate( - Service service) { +resolveViaServiceCreate(Service service) { return (_, arguments) async { var requestInfo = _fetchRequestInfo(arguments); var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} @@ -90,9 +89,10 @@ GraphQLFieldResolver /// /// The arguments passed to the resolver will be forwarded to the service, and the /// service will receive [Providers.graphql]. -GraphQLFieldResolver - resolveViaServiceModify(Service service, - {String idField = 'id'}) { +GraphQLFieldResolver resolveViaServiceModify< + Value, + Serialized +>(Service service, {String idField = 'id'}) { return (_, arguments) async { var requestInfo = _fetchRequestInfo(arguments); var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} @@ -113,9 +113,10 @@ GraphQLFieldResolver /// /// Keep in mind that `update` **overwrites** existing contents. /// To avoid this, use [resolveViaServiceModify] instead. -GraphQLFieldResolver - resolveViaServiceUpdate(Service service, - {String idField = 'id'}) { +GraphQLFieldResolver resolveViaServiceUpdate< + Value, + Serialized +>(Service service, {String idField = 'id'}) { return (_, arguments) async { var requestInfo = _fetchRequestInfo(arguments); var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} @@ -132,9 +133,10 @@ GraphQLFieldResolver /// /// The arguments passed to the resolver will be forwarded to the service, and the /// service will receive [Providers.graphql]. -GraphQLFieldResolver - resolveViaServiceRemove(Service service, - {String idField = 'id'}) { +GraphQLFieldResolver resolveViaServiceRemove< + Value, + Serialized +>(Service service, {String idField = 'id'}) { return (_, arguments) async { var requestInfo = _fetchRequestInfo(arguments); var params = {'query': _getQuery(arguments), 'provider': Providers.graphQL} diff --git a/packages/angel_graphql/pubspec.yaml b/packages/angel_graphql/pubspec.yaml index fd84edc..07af1e3 100644 --- a/packages/angel_graphql/pubspec.yaml +++ b/packages/angel_graphql/pubspec.yaml @@ -4,7 +4,7 @@ description: The fastest and easiest way to get a GraphQL backend in Dart, using homepage: https://angel3-framework.web.app/ repository: https://github.com/dart-backend/graphql_dart/tree/master/packages/angel_graphql environment: - sdk: '>=3.8.0 <5.0.0' + sdk: '>=3.8.0 <4.0.0' dependencies: angel3_file_service: ^8.0.0 angel3_framework: ^8.4.0 diff --git a/packages/graphql_data_loader/lib/graphql_data_loader2.dart b/packages/graphql_data_loader/lib/graphql_data_loader2.dart index ed7a323..bdedc88 100644 --- a/packages/graphql_data_loader/lib/graphql_data_loader2.dart +++ b/packages/graphql_data_loader/lib/graphql_data_loader2.dart @@ -27,9 +27,7 @@ class DataLoader { var loadIds = current.map((i) => i.id).toSet().toList(growable: false); - var data = await loadMany( - loadIds, - ); + var data = await loadMany(loadIds); for (var i = 0; i < loadIds.length; i++) { var id = loadIds[i]; @@ -63,7 +61,8 @@ class DataLoader { void close() { while (_queue.isNotEmpty) { _queue.removeFirst().completer.completeError( - StateError('The DataLoader was closed before the item was loaded.')); + StateError('The DataLoader was closed before the item was loaded.'), + ); } _queue.clear(); diff --git a/packages/graphql_data_loader/pubspec.yaml b/packages/graphql_data_loader/pubspec.yaml index 45cb03f..7b3fd08 100644 --- a/packages/graphql_data_loader/pubspec.yaml +++ b/packages/graphql_data_loader/pubspec.yaml @@ -4,7 +4,7 @@ description: Batch and cache database lookups. Works well with GraphQL. Ported f homepage: https://angel3-framework.web.app/ repository: https://github.com/dart-backend/graphql_dart/tree/master/packages/graphql_data_loader environment: - sdk: '>=3.8.0 <5.0.0' + sdk: '>=3.8.0 <4.0.0' dev_dependencies: graphql_schema2: ^6.0.0 lints: ^6.0.0 diff --git a/packages/graphql_data_loader/test/all_test.dart b/packages/graphql_data_loader/test/all_test.dart index 2fcac49..2a29897 100644 --- a/packages/graphql_data_loader/test/all_test.dart +++ b/packages/graphql_data_loader/test/all_test.dart @@ -21,9 +21,7 @@ void main() { test('dedupe', () async { var loader = DataLoader>>((ids) { - return ids.map( - (i) => {i: ids.toList()}, - ); + return ids.map((i) => {i: ids.toList()}); }); var zero = loader.load(0); @@ -32,23 +30,20 @@ void main() { var anotherZero = loader.load(0); var batch = await Future.wait([zero, one, two, anotherZero]); - expect( - batch, - [ - { - 0: [0, 1, 2] - }, - { - 1: [0, 1, 2] - }, - { - 2: [0, 1, 2] - }, - { - 0: [0, 1, 2] - }, - ], - ); + expect(batch, [ + { + 0: [0, 1, 2], + }, + { + 1: [0, 1, 2], + }, + { + 2: [0, 1, 2], + }, + { + 0: [0, 1, 2], + }, + ]); }); group('cache', () { diff --git a/packages/graphql_generator/pubspec.yaml b/packages/graphql_generator/pubspec.yaml index f51a954..9ea17af 100644 --- a/packages/graphql_generator/pubspec.yaml +++ b/packages/graphql_generator/pubspec.yaml @@ -4,7 +4,7 @@ description: Generates GraphQL schemas from Dart classes, for use with pkg:graph homepage: https://angel3-framework.web.app/ repository: https://github.com/dart-backend/graphql_dart/tree/master/packages/graphql_generator environment: - sdk: '>=3.8.0 <5.0.0' + sdk: '>=3.8.0 <4.0.0' dependencies: analyzer: ^7.7.0 angel3_model: ^8.0.0 diff --git a/packages/graphql_parser/example/example.dart b/packages/graphql_parser/example/example.dart index a68da47..c83a533 100644 --- a/packages/graphql_parser/example/example.dart +++ b/packages/graphql_parser/example/example.dart @@ -1,13 +1,14 @@ import 'package:graphql_parser2/graphql_parser2.dart'; -final String text = ''' +final String text = + ''' { project(name: "GraphQL") { tagline } } ''' - .trim(); + .trim(); void main() { var tokens = scan(text); diff --git a/packages/graphql_parser/lib/src/language/ast/array_value.dart b/packages/graphql_parser/lib/src/language/ast/array_value.dart index bc318da..74636b7 100644 --- a/packages/graphql_parser/lib/src/language/ast/array_value.dart +++ b/packages/graphql_parser/lib/src/language/ast/array_value.dart @@ -23,7 +23,9 @@ class ListValueContext extends InputValueContext { @override FileSpan get span { var out = values.fold( - lBracketToken.span, (o, v) => o!.expand(v.span!))!; + lBracketToken.span, + (o, v) => o!.expand(v.span!), + )!; return out.expand(rBracketToken.span!); } diff --git a/packages/graphql_parser/lib/src/language/ast/directive.dart b/packages/graphql_parser/lib/src/language/ast/directive.dart index 0f3437a..c20d65e 100644 --- a/packages/graphql_parser/lib/src/language/ast/directive.dart +++ b/packages/graphql_parser/lib/src/language/ast/directive.dart @@ -15,8 +15,15 @@ class DirectiveContext extends Node { /// The (optional) value being passed with the directive. final InputValueContext? value; - DirectiveContext(this.arrobaToken, this.nameToken, this.colonToken, - this.lParenToken, this.rParenToken, this.argument, this.value) { + DirectiveContext( + this.arrobaToken, + this.nameToken, + this.colonToken, + this.lParenToken, + this.rParenToken, + this.argument, + this.value, + ) { assert(nameToken != null); } diff --git a/packages/graphql_parser/lib/src/language/ast/field.dart b/packages/graphql_parser/lib/src/language/ast/field.dart index a8f12a7..eb6a8ee 100644 --- a/packages/graphql_parser/lib/src/language/ast/field.dart +++ b/packages/graphql_parser/lib/src/language/ast/field.dart @@ -27,11 +27,15 @@ class FieldContext extends Node with Directives { return fieldName.span?.expand(otherSpan); } else if (directives.isNotEmpty) { return directives.fold( - fieldName.span, (out, d) => out?.expand(d.span)); + fieldName.span, + (out, d) => out?.expand(d.span), + ); } if (arguments.isNotEmpty) { return arguments.fold( - fieldName.span, (out, a) => out?.expand(a.span)); + fieldName.span, + (out, a) => out?.expand(a.span), + ); } else { return fieldName.span; } diff --git a/packages/graphql_parser/lib/src/language/ast/fragment_definition.dart b/packages/graphql_parser/lib/src/language/ast/fragment_definition.dart index 4f96455..8733142 100644 --- a/packages/graphql_parser/lib/src/language/ast/fragment_definition.dart +++ b/packages/graphql_parser/lib/src/language/ast/fragment_definition.dart @@ -22,8 +22,13 @@ class FragmentDefinitionContext extends ExecutableDefinitionContext { /// The [String] value of the [nameToken]. String? get name => nameToken!.text; - FragmentDefinitionContext(this.fragmentToken, this.nameToken, this.onToken, - this.typeCondition, this.selectionSet); + FragmentDefinitionContext( + this.fragmentToken, + this.nameToken, + this.onToken, + this.typeCondition, + this.selectionSet, + ); /// Use [fragmentToken] instead. @Deprecated("Use [fragmentToken]") diff --git a/packages/graphql_parser/lib/src/language/ast/inline_fragment.dart b/packages/graphql_parser/lib/src/language/ast/inline_fragment.dart index 8471c88..d52fa2a 100644 --- a/packages/graphql_parser/lib/src/language/ast/inline_fragment.dart +++ b/packages/graphql_parser/lib/src/language/ast/inline_fragment.dart @@ -20,12 +20,17 @@ class InlineFragmentContext extends Node { final SelectionSetContext selectionSet; InlineFragmentContext( - this.ellipsisToken, this.onToken, this.typeCondition, this.selectionSet); + this.ellipsisToken, + this.onToken, + this.typeCondition, + this.selectionSet, + ); @override FileSpan get span { - var out = - ellipsisToken.span!.expand(onToken.span!).expand(typeCondition.span!); + var out = ellipsisToken.span! + .expand(onToken.span!) + .expand(typeCondition.span!); out = directives.fold(out, (o, d) => o.expand(d.span)); return out.expand(selectionSet.span!); diff --git a/packages/graphql_parser/lib/src/language/ast/misc_value.dart b/packages/graphql_parser/lib/src/language/ast/misc_value.dart index 134f788..e82a73b 100644 --- a/packages/graphql_parser/lib/src/language/ast/misc_value.dart +++ b/packages/graphql_parser/lib/src/language/ast/misc_value.dart @@ -64,8 +64,10 @@ class ObjectValueContext extends InputValueContext> { if (fields.isEmpty) { return {}; } else { - return fields.fold>({}, - (map, field) { + return fields.fold>({}, ( + map, + field, + ) { return map ..[field.nameToken.text] = field.value.computeValue(variables); }); diff --git a/packages/graphql_parser/lib/src/language/ast/number_value.dart b/packages/graphql_parser/lib/src/language/ast/number_value.dart index 02126c3..92b5900 100644 --- a/packages/graphql_parser/lib/src/language/ast/number_value.dart +++ b/packages/graphql_parser/lib/src/language/ast/number_value.dart @@ -23,9 +23,9 @@ class NumberValueContext extends InputValueContext { } } -// Use [numberToken] instead. -// @deprecated -// Token get NUMBER => numberToken; + // Use [numberToken] instead. + // @deprecated + // Token get NUMBER => numberToken; @override FileSpan? get span => numberToken.span; diff --git a/packages/graphql_parser/lib/src/language/ast/operation_definition.dart b/packages/graphql_parser/lib/src/language/ast/operation_definition.dart index 7496e9e..d5c98ef 100644 --- a/packages/graphql_parser/lib/src/language/ast/operation_definition.dart +++ b/packages/graphql_parser/lib/src/language/ast/operation_definition.dart @@ -31,12 +31,18 @@ class OperationDefinitionContext extends ExecutableDefinitionContext { /// The [String] value of the [nameToken]. String? get name => nameToken?.text; - OperationDefinitionContext(this.typeToken, this.nameToken, - this.variableDefinitions, this.selectionSet) { - assert(typeToken == null || - typeToken!.text == 'query' || - typeToken!.text == 'mutation' || - typeToken!.text == 'subscription'); + OperationDefinitionContext( + this.typeToken, + this.nameToken, + this.variableDefinitions, + this.selectionSet, + ) { + assert( + typeToken == null || + typeToken!.text == 'query' || + typeToken!.text == 'mutation' || + typeToken!.text == 'subscription', + ); } @override diff --git a/packages/graphql_parser/lib/src/language/ast/selection_set.dart b/packages/graphql_parser/lib/src/language/ast/selection_set.dart index 10828c2..ff00bf2 100644 --- a/packages/graphql_parser/lib/src/language/ast/selection_set.dart +++ b/packages/graphql_parser/lib/src/language/ast/selection_set.dart @@ -21,7 +21,9 @@ class SelectionSetContext extends Node { @override FileSpan? get span { var out = selections.fold( - lBraceToken!.span, (out, s) => out!.expand(s.span!))!; + lBraceToken!.span, + (out, s) => out!.expand(s.span!), + )!; return out.expand(rBraceToken!.span!); } } @@ -31,7 +33,7 @@ class _MergedSelectionSetContext extends SelectionSetContext { //final List selections; _MergedSelectionSetContext(List selections) - : super(null, null) { + : super(null, null) { super.selections = selections; } diff --git a/packages/graphql_parser/lib/src/language/ast/variable_definition.dart b/packages/graphql_parser/lib/src/language/ast/variable_definition.dart index 137805a..2cdfe65 100644 --- a/packages/graphql_parser/lib/src/language/ast/variable_definition.dart +++ b/packages/graphql_parser/lib/src/language/ast/variable_definition.dart @@ -15,8 +15,12 @@ class VariableDefinitionContext extends Node with Directives { /// The default value of the variable. final DefaultValueContext? defaultValue; - VariableDefinitionContext(this.variable, this.colonToken, this.type, - [this.defaultValue]); + VariableDefinitionContext( + this.variable, + this.colonToken, + this.type, [ + this.defaultValue, + ]); /// Use [colonToken] instead. @Deprecated('Use [colonToken] instead.') diff --git a/packages/graphql_parser/lib/src/language/ast/variable_definitions.dart b/packages/graphql_parser/lib/src/language/ast/variable_definitions.dart index 9970cd7..5bbb430 100644 --- a/packages/graphql_parser/lib/src/language/ast/variable_definitions.dart +++ b/packages/graphql_parser/lib/src/language/ast/variable_definitions.dart @@ -16,7 +16,9 @@ class VariableDefinitionsContext extends Node { @override FileSpan get span { var out = variableDefinitions.fold( - lParenToken!.span, (o, v) => o!.expand(v.span))!; + lParenToken!.span, + (o, v) => o!.expand(v.span), + )!; return out.expand(rParenToken!.span!); } } diff --git a/packages/graphql_parser/lib/src/language/lexer.dart b/packages/graphql_parser/lib/src/language/lexer.dart index 902d093..adc5fbf 100644 --- a/packages/graphql_parser/lib/src/language/lexer.dart +++ b/packages/graphql_parser/lib/src/language/lexer.dart @@ -9,7 +9,8 @@ final RegExp _whitespace = RegExp('[ \t\n\r]+'); // final RegExp _boolean = RegExp(r'true|false'); final RegExp _number = RegExp(r'-?[0-9]+(\.[0-9]+)?(E|e(\+|-)?[0-9]+)?'); final RegExp _string = RegExp( - r'"((\\(["\\/bfnrt]|(u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))|([^"\\]))*"'); + r'"((\\(["\\/bfnrt]|(u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])))|([^"\\]))*"', +); final RegExp _blockString = RegExp(r'"""(([^"])|(\\"""))*"""'); final RegExp _name = RegExp(r'[_A-Za-z][_0-9A-Za-z]*'); @@ -37,7 +38,7 @@ final Map _patterns = { _number: TokenType.NUMBER, _string: TokenType.STRING, _blockString: TokenType.BLOCK_STRING, - _name: TokenType.NAME + _name: TokenType.NAME, }; List scan(String text, {sourceUrl}) { diff --git a/packages/graphql_parser/lib/src/language/parser.dart b/packages/graphql_parser/lib/src/language/parser.dart index 7643a5e..f15b24e 100644 --- a/packages/graphql_parser/lib/src/language/parser.dart +++ b/packages/graphql_parser/lib/src/language/parser.dart @@ -83,9 +83,12 @@ class Parser { return OperationDefinitionContext(TYPE, NAME, variables, selectionSet) ..directives.addAll(dirs); } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Missing selection set in fragment definition.', - NAME?.span ?? TYPE?.span)); + NAME?.span ?? TYPE?.span, + ), + ); return null; } } else { @@ -107,30 +110,46 @@ class Parser { var selectionSet = parseSelectionSet(); if (selectionSet != null) { return FragmentDefinitionContext( - FRAGMENT, NAME, ON, typeCondition, selectionSet) - ..directives.addAll(dirs); + FRAGMENT, + NAME, + ON, + typeCondition, + selectionSet, + )..directives.addAll(dirs); } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Expected selection set in fragment definition.', - typeCondition.span)); + typeCondition.span, + ), + ); return null; } } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Expected type condition after "on" in fragment definition.', - ON?.span)); + ON?.span, + ), + ); return null; } } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Expected "on" after name "${NAME?.text}" in fragment definition.', - NAME?.span)); + NAME?.span, + ), + ); return null; } } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Expected name after "fragment" in fragment definition.', - FRAGMENT.span)); + FRAGMENT.span, + ), + ); return null; } } else { @@ -165,25 +184,36 @@ class Parser { var selectionSet = parseSelectionSet(); if (selectionSet != null) { return InlineFragmentContext( - ELLIPSIS, ON, typeCondition, selectionSet) - ..directives.addAll(directives); + ELLIPSIS, + ON, + typeCondition, + selectionSet, + )..directives.addAll(directives); } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Missing selection set in inline fragment.', - directives.isEmpty - ? typeCondition.span - : directives.last.span)); + directives.isEmpty ? typeCondition.span : directives.last.span, + ), + ); return null; } } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Missing type condition after "on" in inline fragment.', - ON.span)); + ON.span, + ), + ); return null; } } else { - errors.add(SyntaxError( - 'Missing "on" after "..." in inline fragment.', ELLIPSIS.span)); + errors.add( + SyntaxError( + 'Missing "on" after "..." in inline fragment.', + ELLIPSIS.span, + ), + ); return null; } } else { @@ -208,8 +238,12 @@ class Parser { return SelectionSetContext(LBRACE, current) ..selections.addAll(selections); } else { - errors.add(SyntaxError('Missing "}" after selection set.', - selections.isEmpty ? LBRACE.span : selections.last.span)); + errors.add( + SyntaxError( + 'Missing "}" after selection set.', + selections.isEmpty ? LBRACE.span : selections.last.span, + ), + ); return null; } } else { @@ -253,7 +287,8 @@ class Parser { return FieldNameContext(null, AliasContext(NAME1, COLON, current!)); } else { errors.add( - SyntaxError('Missing name after colon in alias.', COLON.span)); + SyntaxError('Missing name after colon in alias.', COLON.span), + ); return null; } } else { @@ -280,8 +315,9 @@ class Parser { return VariableDefinitionsContext(LPAREN, current) ..variableDefinitions.addAll(defs); } else { - errors.add(SyntaxError( - 'Missing ")" after variable definitions.', LPAREN.span)); + errors.add( + SyntaxError('Missing ")" after variable definitions.', LPAREN.span), + ); return null; } } else { @@ -301,12 +337,14 @@ class Parser { ..directives.addAll(parseDirectives()); } else { errors.add( - SyntaxError('Missing type in variable definition.', COLON.span)); + SyntaxError('Missing type in variable definition.', COLON.span), + ); return null; } } else { errors.add( - SyntaxError('Missing ":" in variable definition.', variable.span)); + SyntaxError('Missing ":" in variable definition.', variable.span), + ); return null; } } else { @@ -371,9 +409,12 @@ class Parser { if (val != null) { return DirectiveContext(ARROBA, NAME, COLON, null, null, null, val); } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Missing value or variable in directive after colon.', - COLON.span)); + COLON.span, + ), + ); return null; } } else if (next(TokenType.LPAREN)) { @@ -382,14 +423,22 @@ class Parser { if (arg != null) { if (next(TokenType.RPAREN)) { return DirectiveContext( - ARROBA, NAME, null, LPAREN, current, arg, null); + ARROBA, + NAME, + null, + LPAREN, + current, + arg, + null, + ); } else { errors.add(SyntaxError('Missing \')\'', arg.value.span)); return null; } } else { errors.add( - SyntaxError('Missing argument in directive.', LPAREN.span)); + SyntaxError('Missing argument in directive.', LPAREN.span), + ); return null; } } else { @@ -436,13 +485,15 @@ class Parser { if (val != null) { return ArgumentContext(NAME, COLON, val); } else { - errors.add(SyntaxError( - 'Missing value or variable in argument.', COLON.span)); + errors.add( + SyntaxError('Missing value or variable in argument.', COLON.span), + ); return null; } } else { errors.add( - SyntaxError('Missing colon after name in argument.', NAME.span)); + SyntaxError('Missing colon after name in argument.', NAME.span), + ); return null; } } else { @@ -456,9 +507,12 @@ class Parser { if (next(TokenType.NAME)) { return VariableContext(DOLLAR, current!); } else { - errors.add(SyntaxError( + errors.add( + SyntaxError( 'Missing name for variable; found a lone "\$" instead.', - DOLLAR.span)); + DOLLAR.span, + ), + ); return null; } } else { @@ -500,31 +554,32 @@ class Parser { InputValueContext? parseInputValue() { return (parseVariable() ?? - parseNumberValue() ?? - parseStringValue() ?? - parseBooleanValue() ?? - parseNullValue() ?? - parseEnumValue() ?? - parseListValue() ?? - parseObjectValue()) as InputValueContext?; + parseNumberValue() ?? + parseStringValue() ?? + parseBooleanValue() ?? + parseNullValue() ?? + parseEnumValue() ?? + parseListValue() ?? + parseObjectValue()) + as InputValueContext?; } StringValueContext? parseStringValue() => next(TokenType.STRING) && current != null - ? StringValueContext(current!) - : (next(TokenType.BLOCK_STRING) && current != null - ? StringValueContext(current!, isBlockString: true) - : null); + ? StringValueContext(current!) + : (next(TokenType.BLOCK_STRING) && current != null + ? StringValueContext(current!, isBlockString: true) + : null); NumberValueContext? parseNumberValue() => next(TokenType.NUMBER) && current != null - ? NumberValueContext(current!) - : null; + ? NumberValueContext(current!) + : null; BooleanValueContext? parseBooleanValue() => (nextName('true') || nextName('false') && current != null) - ? BooleanValueContext(current!) - : null; + ? BooleanValueContext(current!) + : null; EnumValueContext? parseEnumValue() => next(TokenType.NAME) && current != null ? EnumValueContext(current!) @@ -601,8 +656,12 @@ class Parser { return null; } } else { - errors.add(SyntaxError( - 'Missing ":" after name "${NAME.span!.text}".', NAME.span)); + errors.add( + SyntaxError( + 'Missing ":" after name "${NAME.span!.text}".', + NAME.span, + ), + ); return null; } } else { diff --git a/packages/graphql_parser/lib/src/language/token_type.dart b/packages/graphql_parser/lib/src/language/token_type.dart index 8eb50aa..caa339a 100644 --- a/packages/graphql_parser/lib/src/language/token_type.dart +++ b/packages/graphql_parser/lib/src/language/token_type.dart @@ -21,7 +21,6 @@ enum TokenType { // QUERY, // NULL // BOOLEAN, - NUMBER, STRING, BLOCK_STRING, diff --git a/packages/graphql_parser/pubspec.yaml b/packages/graphql_parser/pubspec.yaml index 0b715f5..53f9619 100644 --- a/packages/graphql_parser/pubspec.yaml +++ b/packages/graphql_parser/pubspec.yaml @@ -4,7 +4,7 @@ description: Parses GraphQL queries and schemas. Also includes classes for the G homepage: https://angel3-framework.web.app/ repository: https://github.com/dart-backend/graphql_dart/tree/master/packages/graphql_parser environment: - sdk: '>=3.8.0 <5.0.0' + sdk: '>=3.8.0 <4.0.0' dependencies: charcode: ^1.3.1 source_span: ^1.10.0 diff --git a/packages/graphql_parser/test/directive_test.dart b/packages/graphql_parser/test/directive_test.dart index 03b757b..3d715e0 100644 --- a/packages/graphql_parser/test/directive_test.dart +++ b/packages/graphql_parser/test/directive_test.dart @@ -15,8 +15,10 @@ void main() { test('with argument', () { expect('@foo (bar: 2)', isDirective('foo', argument: isArgument('bar', 2))); - expect(r'@foo (bar: $baz)', - isDirective('foo', argument: isArgument('bar', r'baz'))); + expect( + r'@foo (bar: $baz)', + isDirective('foo', argument: isArgument('bar', r'baz')), + ); }); test('exceptions', () { @@ -35,9 +37,11 @@ void main() { DirectiveContext? parseDirective(String text) => parse(text).parseDirective(); -Matcher isDirective(String name, - {Matcher? valueOrVariable, Matcher? argument}) => - _IsDirective(name, valueOrVariable: valueOrVariable, argument: argument); +Matcher isDirective( + String name, { + Matcher? valueOrVariable, + Matcher? argument, +}) => _IsDirective(name, valueOrVariable: valueOrVariable, argument: argument); Matcher isDirectiveList(List directives) => _IsDirectiveList(directives); @@ -63,8 +67,9 @@ class _IsDirective extends Matcher { @override bool matches(item, Map matchState) { - var directive = - item is DirectiveContext ? item : parseDirective(item.toString()); + var directive = item is DirectiveContext + ? item + : parseDirective(item.toString()); if (directive == null) return false; if (valueOrVariable != null) { if (directive.value == null) { @@ -74,8 +79,10 @@ class _IsDirective extends Matcher { if (v is VariableContext) { return valueOrVariable!.matches(v.name, matchState); } else { - return valueOrVariable! - .matches(directive.value!.computeValue({}), matchState); + return valueOrVariable!.matches( + directive.value!.computeValue({}), + matchState, + ); } } } else if (argument != null) { diff --git a/packages/graphql_parser/test/document_test.dart b/packages/graphql_parser/test/document_test.dart index f7c56a2..fc752be 100644 --- a/packages/graphql_parser/test/document_test.dart +++ b/packages/graphql_parser/test/document_test.dart @@ -24,14 +24,15 @@ void main() { expect(fragment.name, 'PostInfo'); expect(fragment.typeCondition.typeName.name, 'Post'); expect( - fragment.selectionSet, - isSelectionSet([ - isField(fieldName: isFieldName('description')), - isField( - fieldName: isFieldName('comments'), - selectionSet: - isSelectionSet([isField(fieldName: isFieldName('id'))])), - ])); + fragment.selectionSet, + isSelectionSet([ + isField(fieldName: isFieldName('description')), + isField( + fieldName: isFieldName('comments'), + selectionSet: isSelectionSet([isField(fieldName: isFieldName('id'))]), + ), + ]), + ); }); test('fragment exceptions', () { @@ -54,11 +55,12 @@ void main() { expect(op.isMutation, isFalse); expect(op.name, isNull); expect( - op.selectionSet, - isSelectionSet([ - isField(fieldName: isFieldName('foo')), - isField(fieldName: isFieldName('bar', alias: 'baz')) - ])); + op.selectionSet, + isSelectionSet([ + isField(fieldName: isFieldName('foo')), + isField(fieldName: isFieldName('bar', alias: 'baz')), + ]), + ); }); test('mutation', () { @@ -68,17 +70,18 @@ void main() { expect(op.isMutation, isTrue); expect(op.name, isNull); expect( - op.selectionSet, - isSelectionSet([ - isField(fieldName: isFieldName('foo')), - isField(fieldName: isFieldName('bar', alias: 'baz')) - ])); + op.selectionSet, + isSelectionSet([ + isField(fieldName: isFieldName('foo')), + isField(fieldName: isFieldName('bar', alias: 'baz')), + ]), + ); }); test('with operation type', () { - var doc = - parse(r'query foo ($one: [int] = 2) @foo @bar: 2 {foo, bar: baz}') - .parseDocument(); + var doc = parse( + r'query foo ($one: [int] = 2) @foo @bar: 2 {foo, bar: baz}', + ).parseDocument(); print(doc.span!.highlight()); expect(doc.definitions, hasLength(1)); expect(doc.definitions.first is OperationDefinitionContext, isTrue); @@ -88,10 +91,13 @@ void main() { expect(op.variableDefinitions!.variableDefinitions, hasLength(1)); expect( - op.variableDefinitions!.variableDefinitions.first, - isVariableDefinition('one', - type: isListType(isType('int'), isNullable: true), - defaultValue: isValue(2))); + op.variableDefinitions!.variableDefinitions.first, + isVariableDefinition( + 'one', + type: isListType(isType('int'), isNullable: true), + defaultValue: isValue(2), + ), + ); expect(op.directives, hasLength(2)); expect(op.directives[0], isDirective('foo')); @@ -99,11 +105,12 @@ void main() { expect(op.selectionSet, isNotNull); expect( - op.selectionSet, - isSelectionSet([ - isField(fieldName: isFieldName('foo')), - isField(fieldName: isFieldName('bar', alias: 'baz')) - ])); + op.selectionSet, + isSelectionSet([ + isField(fieldName: isFieldName('foo')), + isField(fieldName: isFieldName('bar', alias: 'baz')), + ]), + ); }); test('exceptions', () { diff --git a/packages/graphql_parser/test/field_test.dart b/packages/graphql_parser/test/field_test.dart index 6c6fa97..f9a7da5 100644 --- a/packages/graphql_parser/test/field_test.dart +++ b/packages/graphql_parser/test/field_test.dart @@ -29,8 +29,10 @@ void main() { test('arguments', () { expect('()', isArgumentList([])); expect(r'(a: 2)', isArgumentList([isArgument('a', 2)])); - expect(r'(a: 2, b: $c)', - isArgumentList([isArgument('a', 2), isArgument('b', 'c')])); + expect( + r'(a: 2, b: $c)', + isArgumentList([isArgument('a', 2), isArgument('b', 'c')]), + ); }); group('field tests', () { @@ -44,35 +46,40 @@ void main() { test('with arguments', () { expect( - r'foo (a: 2, b: $c)', - isField( - fieldName: isFieldName('foo'), - arguments: - isArgumentList([isArgument('a', 2), isArgument('b', 'c')]))); + r'foo (a: 2, b: $c)', + isField( + fieldName: isFieldName('foo'), + arguments: isArgumentList([isArgument('a', 2), isArgument('b', 'c')]), + ), + ); }); test('with directives', () { expect( - 'foo: bar (a: 2) @bar @baz: 2 @quux (one: 1)', - isField( - fieldName: isFieldName('foo', alias: 'bar'), - arguments: isArgumentList([isArgument('a', 2)]), - directives: isDirectiveList([ - isDirective('bar'), - isDirective('baz', valueOrVariable: isValue(2)), - isDirective('quux', argument: isArgument('one', 1)) - ]))); + 'foo: bar (a: 2) @bar @baz: 2 @quux (one: 1)', + isField( + fieldName: isFieldName('foo', alias: 'bar'), + arguments: isArgumentList([isArgument('a', 2)]), + directives: isDirectiveList([ + isDirective('bar'), + isDirective('baz', valueOrVariable: isValue(2)), + isDirective('quux', argument: isArgument('one', 1)), + ]), + ), + ); }); test('with selection set', () { expect( - 'foo: bar {baz, ...quux}', - isField( - fieldName: isFieldName('foo', alias: 'bar'), - selectionSet: isSelectionSet([ - isField(fieldName: isFieldName('baz')), - isFragmentSpread('quux') - ]))); + 'foo: bar {baz, ...quux}', + isField( + fieldName: isFieldName('foo', alias: 'bar'), + selectionSet: isSelectionSet([ + isField(fieldName: isFieldName('baz')), + isFragmentSpread('quux'), + ]), + ), + ); }); }); } @@ -81,12 +88,12 @@ FieldContext? parseField(String text) => parse(text).parseField(); FieldNameContext? parseFieldName(String text) => parse(text).parseFieldName(); -Matcher isField( - {Matcher? fieldName, - Matcher? arguments, - Matcher? directives, - Matcher? selectionSet}) => - _IsField(fieldName, arguments, directives, selectionSet); +Matcher isField({ + Matcher? fieldName, + Matcher? arguments, + Matcher? directives, + Matcher? selectionSet, +}) => _IsField(fieldName, arguments, directives, selectionSet); Matcher isFieldName(String name, {String? alias}) => _IsFieldName(name, alias); @@ -123,16 +130,18 @@ class _IsFieldName extends Matcher { @override Description describe(Description description) { if (realName != null) { - return description - .add('is field with name "$name" and alias "$realName"'); + return description.add( + 'is field with name "$name" and alias "$realName"', + ); } return description.add('is field with name "$name"'); } @override bool matches(item, Map matchState) { - var fieldName = - item is FieldNameContext ? item : parseFieldName(item.toString()); + var fieldName = item is FieldNameContext + ? item + : parseFieldName(item.toString()); if (realName != null) { return fieldName!.alias?.alias == name && fieldName.alias?.name == realName; diff --git a/packages/graphql_parser/test/fragment_spread_test.dart b/packages/graphql_parser/test/fragment_spread_test.dart index b130f87..e60b5bb 100644 --- a/packages/graphql_parser/test/fragment_spread_test.dart +++ b/packages/graphql_parser/test/fragment_spread_test.dart @@ -11,13 +11,16 @@ void main() { test('with directives', () { expect( - '... foo @bar @baz: 2 @quux(one: 1)', - isFragmentSpread('foo', - directives: isDirectiveList([ - isDirective('bar'), - isDirective('baz', valueOrVariable: equals(2)), - isDirective('quux', argument: isArgument('one', 1)) - ]))); + '... foo @bar @baz: 2 @quux(one: 1)', + isFragmentSpread( + 'foo', + directives: isDirectiveList([ + isDirective('bar'), + isDirective('baz', valueOrVariable: equals(2)), + isDirective('quux', argument: isArgument('one', 1)), + ]), + ), + ); }); } @@ -37,7 +40,8 @@ class _IsFragmentSpread extends Matcher { Description describe(Description description) { if (directives != null) { return directives!.describe( - description.add('is a fragment spread named "$name" that also ')); + description.add('is a fragment spread named "$name" that also '), + ); } return description.add('is a fragment spread named "$name"'); } diff --git a/packages/graphql_parser/test/inline_fragment_test.dart b/packages/graphql_parser/test/inline_fragment_test.dart index 34c9aff..c3a3846 100644 --- a/packages/graphql_parser/test/inline_fragment_test.dart +++ b/packages/graphql_parser/test/inline_fragment_test.dart @@ -10,24 +10,30 @@ import 'selection_set_test.dart'; void main() { test('no directives', () { expect( - '... on foo {bar, baz: quux}', - isInlineFragment('foo', - selectionSet: isSelectionSet([ - isField(fieldName: isFieldName('bar')), - isField(fieldName: isFieldName('baz', alias: 'quux')) - ]))); + '... on foo {bar, baz: quux}', + isInlineFragment( + 'foo', + selectionSet: isSelectionSet([ + isField(fieldName: isFieldName('bar')), + isField(fieldName: isFieldName('baz', alias: 'quux')), + ]), + ), + ); }); test('with directives', () { expect( - '... on foo @bar @baz: 2 @quux(one: 1) {... bar}', - isInlineFragment('foo', - directives: isDirectiveList([ - isDirective('bar'), - isDirective('baz', valueOrVariable: equals(2)), - isDirective('quux', argument: isArgument('one', 1)) - ]), - selectionSet: isSelectionSet([isFragmentSpread('bar')]))); + '... on foo @bar @baz: 2 @quux(one: 1) {... bar}', + isInlineFragment( + 'foo', + directives: isDirectiveList([ + isDirective('bar'), + isDirective('baz', valueOrVariable: equals(2)), + isDirective('quux', argument: isArgument('one', 1)), + ]), + selectionSet: isSelectionSet([isFragmentSpread('bar')]), + ), + ); }); test('exceptions', () { @@ -45,9 +51,11 @@ void main() { InlineFragmentContext? parseInlineFragment(String text) => parse(text).parseInlineFragment(); -Matcher isInlineFragment(String name, - {Matcher? directives, Matcher? selectionSet}) => - _IsInlineFragment(name, directives, selectionSet); +Matcher isInlineFragment( + String name, { + Matcher? directives, + Matcher? selectionSet, +}) => _IsInlineFragment(name, directives, selectionSet); class _IsInlineFragment extends Matcher { final String name; diff --git a/packages/graphql_parser/test/issue23_test.dart b/packages/graphql_parser/test/issue23_test.dart index 31e92de..6a4c8b5 100644 --- a/packages/graphql_parser/test/issue23_test.dart +++ b/packages/graphql_parser/test/issue23_test.dart @@ -36,20 +36,27 @@ void main() { var customerMemberAttributesByCustomerId = customerByCustomerId.field!.selectionSet!.selections[0]; var nodes0 = customerMemberAttributesByCustomerId - .field!.selectionSet!.selections[0]; + .field! + .selectionSet! + .selections[0]; var customerMemberAttributeId = nodes0.field!.selectionSet!.selections[0]; - expect(customerMemberAttributeId.field!.fieldName.name, - 'customerMemberAttributeId'); + expect( + customerMemberAttributeId.field!.fieldName.name, + 'customerMemberAttributeId', + ); var memberAttr = nodes0.field!.selectionSet!.selections[1]; - expect(memberAttr.field!.fieldName.name, - 'memberAttributesByCustomerMemberAttributeId'); + expect( + memberAttr.field!.fieldName.name, + 'memberAttributesByCustomerMemberAttributeId', + ); expect(memberAttr.field!.arguments, hasLength(1)); var condition = memberAttr.field!.arguments[0]; expect(condition.name, 'condition'); expect(condition.value, TypeMatcher()); var conditionValue = condition.value as ObjectValueContext; - var memberId = conditionValue.fields - .singleWhere((f) => f.nameToken.text == 'memberId'); + var memberId = conditionValue.fields.singleWhere( + (f) => f.nameToken.text == 'memberId', + ); expect(memberId.value, TypeMatcher()); print('Found \$memberId: Instance of $T'); }); diff --git a/packages/graphql_parser/test/selection_set_test.dart b/packages/graphql_parser/test/selection_set_test.dart index 3449190..924fee4 100644 --- a/packages/graphql_parser/test/selection_set_test.dart +++ b/packages/graphql_parser/test/selection_set_test.dart @@ -12,34 +12,38 @@ void main() { test('with commas', () { expect( - '{foo, bar: baz}', - isSelectionSet([ - isField(fieldName: isFieldName('foo')), - isField(fieldName: isFieldName('bar', alias: 'baz')) - ])); + '{foo, bar: baz}', + isSelectionSet([ + isField(fieldName: isFieldName('foo')), + isField(fieldName: isFieldName('bar', alias: 'baz')), + ]), + ); }); test('no commas', () { expect( - ''' + ''' { foo bar: baz ...quux ... on foo {bar, baz} }''' - .split('\n') - .map((s) => s.trim()) - .join(' '), - isSelectionSet([ - isField(fieldName: isFieldName('foo')), - isField(fieldName: isFieldName('bar', alias: 'baz')), - isFragmentSpread('quux'), - isInlineFragment('foo', - selectionSet: isSelectionSet([ - isField(fieldName: isFieldName('bar')), - isField(fieldName: isFieldName('baz')), - ])) - ])); + .split('\n') + .map((s) => s.trim()) + .join(' '), + isSelectionSet([ + isField(fieldName: isFieldName('foo')), + isField(fieldName: isFieldName('bar', alias: 'baz')), + isFragmentSpread('quux'), + isInlineFragment( + 'foo', + selectionSet: isSelectionSet([ + isField(fieldName: isFieldName('bar')), + isField(fieldName: isFieldName('baz')), + ]), + ), + ]), + ); }); test('exceptions', () { @@ -64,14 +68,16 @@ class _IsSelectionSet extends Matcher { @override Description describe(Description description) { - return description - .add('is selection set with ${selections.length} selection(s)'); + return description.add( + 'is selection set with ${selections.length} selection(s)', + ); } @override bool matches(item, Map matchState) { - var set = - item is SelectionSetContext ? item : parseSelectionSet(item.toString()); + var set = item is SelectionSetContext + ? item + : parseSelectionSet(item.toString()); // if (set != null) { // print('Item: $set has ${set.selections.length} selection(s):'); @@ -86,7 +92,9 @@ class _IsSelectionSet extends Matcher { for (var i = 0; i < set.selections.length; i++) { var sel = set.selections[i]; if (!selections[i].matches( - sel.field ?? sel.fragmentSpread ?? sel.inlineFragment, matchState)) { + sel.field ?? sel.fragmentSpread ?? sel.inlineFragment, + matchState, + )) { return false; } } diff --git a/packages/graphql_parser/test/type_test.dart b/packages/graphql_parser/test/type_test.dart index cc19f98..84b6b49 100644 --- a/packages/graphql_parser/test/type_test.dart +++ b/packages/graphql_parser/test/type_test.dart @@ -24,13 +24,17 @@ void main() { group('non-nullable list type', () { test('with nullable', () { - expect('[foo]!', - isListType(isType('foo', isNullable: true), isNullable: false)); + expect( + '[foo]!', + isListType(isType('foo', isNullable: true), isNullable: false), + ); }); test('with non-nullable', () { - expect('[foo!]!', - isListType(isType('foo', isNullable: false), isNullable: false)); + expect( + '[foo!]!', + isListType(isType('foo', isNullable: false), isNullable: false), + ); }); }); diff --git a/packages/graphql_parser/test/variable_definition_test.dart b/packages/graphql_parser/test/variable_definition_test.dart index 8be20d2..5d16b5b 100644 --- a/packages/graphql_parser/test/variable_definition_test.dart +++ b/packages/graphql_parser/test/variable_definition_test.dart @@ -7,15 +7,21 @@ import 'value_test.dart'; void main() { test('no default value', () { - expect(r'$foo: bar', - isVariableDefinition('foo', type: isType('bar', isNullable: true))); + expect( + r'$foo: bar', + isVariableDefinition('foo', type: isType('bar', isNullable: true)), + ); }); test('default value', () { expect( - r'$foo: int! = 2', - isVariableDefinition('foo', - type: isType('int', isNullable: false), defaultValue: isValue(2))); + r'$foo: int! = 2', + isVariableDefinition( + 'foo', + type: isType('int', isNullable: false), + defaultValue: isValue(2), + ), + ); }); test('exceptions', () { @@ -40,9 +46,11 @@ void main() { VariableDefinitionContext? parseVariableDefinition(String text) => parse(text).parseVariableDefinition(); -Matcher isVariableDefinition(String name, - {Matcher? type, Matcher? defaultValue}) => - _IsVariableDefinition(name, type, defaultValue); +Matcher isVariableDefinition( + String name, { + Matcher? type, + Matcher? defaultValue, +}) => _IsVariableDefinition(name, type, defaultValue); class _IsVariableDefinition extends Matcher { final String name; diff --git a/packages/graphql_schema/example/example.dart b/packages/graphql_schema/example/example.dart index b1804dc..9e2cb13 100644 --- a/packages/graphql_schema/example/example.dart +++ b/packages/graphql_schema/example/example.dart @@ -1,30 +1,22 @@ import 'package:graphql_schema2/graphql_schema2.dart'; final GraphQLSchema todoSchema = GraphQLSchema( - queryType: objectType('Todo', fields: [ - field( - 'text', - graphQLString.nonNullable(), - resolve: resolveToNull, - ), - field( - 'created_at', - graphQLDate, - resolve: resolveToNull, - ), - ]), + queryType: objectType( + 'Todo', + fields: [ + field('text', graphQLString.nonNullable(), resolve: resolveToNull), + field('created_at', graphQLDate, resolve: resolveToNull), + ], + ), ); void main() { // Validation - var validation = todoSchema.queryType!.validate( - '@root', - { - 'foo': 'bar', - 'text': null, - 'created_at': 24, - }, - ); + var validation = todoSchema.queryType!.validate('@root', { + 'foo': 'bar', + 'text': null, + 'created_at': 24, + }); if (validation.successful) { print('This is valid data!!!'); @@ -36,8 +28,10 @@ void main() { } // Serialization - print(todoSchema.queryType!.serialize({ - 'text': 'Clean your room!', - 'created_at': DateTime.now().subtract(Duration(days: 10)) - })); + print( + todoSchema.queryType!.serialize({ + 'text': 'Clean your room!', + 'created_at': DateTime.now().subtract(Duration(days: 10)), + }), + ); } diff --git a/packages/graphql_schema/lib/src/argument.dart b/packages/graphql_schema/lib/src/argument.dart index 4af4428..53fedf2 100644 --- a/packages/graphql_schema/lib/src/argument.dart +++ b/packages/graphql_schema/lib/src/argument.dart @@ -35,10 +35,17 @@ class GraphQLFieldInput { } } - GraphQLFieldInput(this.name, this.type, - {this.defaultValue, this.defaultsToNull = false, this.description}) { - assert(_isInputTypeOrScalar(type), - 'All inputs to a GraphQL field must either be scalar types, or explicitly marked as INPUT_OBJECT. Call `GraphQLObjectType.asInputObject()` on any object types you are passing as inputs to a field.'); + GraphQLFieldInput( + this.name, + this.type, { + this.defaultValue, + this.defaultsToNull = false, + this.description, + }) { + assert( + _isInputTypeOrScalar(type), + 'All inputs to a GraphQL field must either be scalar types, or explicitly marked as INPUT_OBJECT. Call `GraphQLObjectType.asInputObject()` on any object types you are passing as inputs to a field.', + ); } @override diff --git a/packages/graphql_schema/lib/src/enum.dart b/packages/graphql_schema/lib/src/enum.dart index 3c5aed5..e153295 100644 --- a/packages/graphql_schema/lib/src/enum.dart +++ b/packages/graphql_schema/lib/src/enum.dart @@ -1,27 +1,31 @@ part of 'schema.dart'; /// Shorthand for building a [GraphQLEnumType]. -GraphQLEnumType enumType(String name, Map values, - {String? description}) { +GraphQLEnumType enumType( + String name, + Map values, { + String? description, +}) { final len = values.keys.length; - return GraphQLEnumType( - name, - [ - for (var i = 0; i < len; i++) - GraphQLEnumValue( - values.keys.elementAt(i), values.values.elementAt(i)), - ], - description: description); + return GraphQLEnumType(name, [ + for (var i = 0; i < len; i++) + GraphQLEnumValue(values.keys.elementAt(i), values.values.elementAt(i)), + ], description: description); } /// Shorthand for building a [GraphQLEnumType] where all the possible values /// are mapped to Dart strings. -GraphQLEnumType enumTypeFromStrings(String name, List values, - {String? description}) { +GraphQLEnumType enumTypeFromStrings( + String name, + List values, { + String? description, +}) { return GraphQLEnumType( - name, values.map((s) => GraphQLEnumValue(s, s)).toList(), - description: description); + name, + values.map((s) => GraphQLEnumValue(s, s)).toList(), + description: description, + ); } /// A [GraphQLType] with only a predetermined number of possible values. @@ -66,8 +70,9 @@ class GraphQLEnumType extends GraphQLScalarType // ['The enum "$name" does not accept null values.']); //} - return ValidationResult._failure( - ['"$input" is not a valid value for the enum "$name".']); + return ValidationResult._failure([ + '"$input" is not a valid value for the enum "$name".', + ]); } return ValidationResult._ok(input); @@ -103,8 +108,12 @@ class GraphQLEnumValue { /// The reason, if any, that this value was deprecated, if it indeed is deprecated. final String? deprecationReason; - GraphQLEnumValue(this.name, this.value, - {this.description, this.deprecationReason}); + GraphQLEnumValue( + this.name, + this.value, { + this.description, + this.deprecationReason, + }); /// Returns `true` if this value has a [deprecationReason]. bool get isDeprecated => deprecationReason != null; diff --git a/packages/graphql_schema/lib/src/field.dart b/packages/graphql_schema/lib/src/field.dart index 353f516..9ac21c1 100644 --- a/packages/graphql_schema/lib/src/field.dart +++ b/packages/graphql_schema/lib/src/field.dart @@ -1,8 +1,11 @@ part of 'schema.dart'; /// Typedef for a function that resolves the value of a [GraphQLObjectField], whether asynchronously or not. -typedef GraphQLFieldResolver = FutureOr Function( - Serialized serialized, Map argumentValues); +typedef GraphQLFieldResolver = + FutureOr Function( + Serialized serialized, + Map argumentValues, + ); /// A field on a [GraphQLObjectType]. /// @@ -27,15 +30,18 @@ class GraphQLObjectField { /// The reason that this field, if it is deprecated, was deprecated. final String? deprecationReason; - GraphQLObjectField(this.name, this.type, - {Iterable arguments = const [], - required this.resolve, - this.deprecationReason, - this.description}) { -// assert(type != null, 'GraphQL fields must specify a `type`.'); -// assert( -// resolve != null, 'GraphQL fields must specify a `resolve` callback.'); -// this.inputs.addAll(arguments ?? []); + GraphQLObjectField( + this.name, + this.type, { + Iterable arguments = const [], + required this.resolve, + this.deprecationReason, + this.description, + }) { + // assert(type != null, 'GraphQL fields must specify a `type`.'); + // assert( + // resolve != null, 'GraphQL fields must specify a `resolve` callback.'); + // this.inputs.addAll(arguments ?? []); inputs.addAll(arguments); } @@ -46,8 +52,10 @@ class GraphQLObjectField { return type.serialize(value); } - FutureOr? deserialize(Serialized serialized, - [Map argumentValues = const {}]) { + FutureOr? deserialize( + Serialized serialized, [ + Map argumentValues = const {}, + ]) { if (resolve != null) return resolve!(serialized, argumentValues); return type.deserialize(serialized); } diff --git a/packages/graphql_schema/lib/src/gen.dart b/packages/graphql_schema/lib/src/gen.dart index 7ac6e0d..5164fc6 100644 --- a/packages/graphql_schema/lib/src/gen.dart +++ b/packages/graphql_schema/lib/src/gen.dart @@ -1,16 +1,21 @@ part of 'schema.dart'; /// Shorthand for generating a [GraphQLObjectType]. -GraphQLObjectType objectType(String name, - {String? description, - bool isInterface = false, - Iterable fields = const [], - Iterable interfaces = const [], - Iterable subs = const [], - String? polymorphicName}) { - var obj = GraphQLObjectType(name, description, - isInterface: isInterface, polymorphicName: polymorphicName) - ..fields.addAll(fields); +GraphQLObjectType objectType( + String name, { + String? description, + bool isInterface = false, + Iterable fields = const [], + Iterable interfaces = const [], + Iterable subs = const [], + String? polymorphicName, +}) { + var obj = GraphQLObjectType( + name, + description, + isInterface: isInterface, + polymorphicName: polymorphicName, + )..fields.addAll(fields); if (interfaces.isNotEmpty == true) { for (var i in interfaces) { @@ -29,30 +34,47 @@ GraphQLObjectType objectType(String name, /// Shorthand for generating a [GraphQLObjectField]. GraphQLObjectField field( - String name, GraphQLType type, - {Iterable> inputs = const [], - GraphQLFieldResolver? resolve, - String? deprecationReason, - String? description}) { - return GraphQLObjectField(name, type, - arguments: inputs, - resolve: resolve, - description: description, - deprecationReason: deprecationReason); + String name, + GraphQLType type, { + Iterable> inputs = const [], + GraphQLFieldResolver? resolve, + String? deprecationReason, + String? description, +}) { + return GraphQLObjectField( + name, + type, + arguments: inputs, + resolve: resolve, + description: description, + deprecationReason: deprecationReason, + ); } /// Shorthand for generating a [GraphQLInputObjectType]. -GraphQLInputObjectType inputObjectType(String name, - {String? description, - Iterable inputFields = const []}) { - return GraphQLInputObjectType(name, - description: description, inputFields: inputFields); +GraphQLInputObjectType inputObjectType( + String name, { + String? description, + Iterable inputFields = const [], +}) { + return GraphQLInputObjectType( + name, + description: description, + inputFields: inputFields, + ); } /// Shorthand for generating a [GraphQLInputObjectField]. GraphQLInputObjectField inputField( - String name, GraphQLType type, - {String? description, T? defaultValue}) { - return GraphQLInputObjectField(name, type, - description: description, defaultValue: defaultValue); + String name, + GraphQLType type, { + String? description, + T? defaultValue, +}) { + return GraphQLInputObjectField( + name, + type, + description: description, + defaultValue: defaultValue, + ); } diff --git a/packages/graphql_schema/lib/src/object_type.dart b/packages/graphql_schema/lib/src/object_type.dart index c44238d..14c6716 100644 --- a/packages/graphql_schema/lib/src/object_type.dart +++ b/packages/graphql_schema/lib/src/object_type.dart @@ -34,22 +34,32 @@ class GraphQLObjectType List get possibleTypes => List.unmodifiable(_possibleTypes); - GraphQLObjectType(this.name, this.description, - {this.isInterface = false, this.polymorphicName}); + GraphQLObjectType( + this.name, + this.description, { + this.isInterface = false, + this.polymorphicName, + }); @override GraphQLType, Map> - coerceToInputObject() { + coerceToInputObject() { return toInputObject('${name}Input', description: description); } /// Converts [this] into a [GraphQLInputObjectType]. GraphQLInputObjectType toInputObject(String name, {String? description}) { - return GraphQLInputObjectType(name, - description: description ?? this.description, - inputFields: fields.map((f) => GraphQLInputObjectField( - f.name, f.type.coerceToInputObject(), - description: f.description))); + return GraphQLInputObjectType( + name, + description: description ?? this.description, + inputFields: fields.map( + (f) => GraphQLInputObjectField( + f.name, + f.type.coerceToInputObject(), + description: f.description, + ), + ), + ); } /// Declares that this type inherits from another parent type. @@ -92,7 +102,8 @@ class GraphQLObjectType if (field.type is GraphQLNonNullableType) { if (!input.containsKey(field.name) || input[field.name] == null) { errors.add( - 'Field "${field.name}, of type ${field.type} cannot be null."'); + 'Field "${field.name}, of type ${field.type} cannot be null."', + ); } } } @@ -102,7 +113,8 @@ class GraphQLObjectType if (field == null) { errors.add( - 'Unexpected field "$k" encountered in $key. Accepted values on type $name: ${fields.map((f) => f.name).toList()}'); + 'Unexpected field "$k" encountered in $key. Accepted values on type $name: ${fields.map((f) => f.name).toList()}', + ); } else { var v = input[k]; var result = field.type.validate(k.toString(), field.type.convert(v)); @@ -128,7 +140,8 @@ class GraphQLObjectType var field = fields.firstWhereOrNull((f) => f.name == k); if (field == null) { throw UnsupportedError( - 'Cannot serialize field "$k", which was not defined in the schema.'); + 'Cannot serialize field "$k", which was not defined in the schema.', + ); } return out..[k.toString()] = field.serialize(value[k]); }); @@ -165,10 +178,12 @@ class GraphQLObjectType other.description == description && other.isInterface == isInterface && const ListEquality().equals(other.fields, fields) && -// const ListEquality() Removed, as it causes a stack overflow :( -// .equals(other.interfaces, interfaces) && - const ListEquality() - .equals(other.possibleTypes, possibleTypes); + // const ListEquality() Removed, as it causes a stack overflow :( + // .equals(other.interfaces, interfaces) && + const ListEquality().equals( + other.possibleTypes, + possibleTypes, + ); } @override @@ -177,7 +192,9 @@ class GraphQLObjectType Map _foldToStringDynamic(Map map) { return map.keys.fold>( - {}, (out, k) => out..[k.toString()] = map[k]); + {}, + (out, k) => out..[k.toString()] = map[k], + ); } enum DirectiveLocation { @@ -188,12 +205,16 @@ enum DirectiveLocation { fragmentDefinition, fragmentSpread, inlineFragment, - variableDefinition + variableDefinition, } class GraphQLDirectiveType extends GraphQLInputObjectType { - GraphQLDirectiveType(super.name, - {super.description, required this.locations, super.inputFields}); + GraphQLDirectiveType( + super.name, { + super.description, + required this.locations, + super.inputFields, + }); final Set locations; } @@ -217,9 +238,11 @@ class GraphQLInputObjectType /// A list of the fields that an input object of this type is expected to have. final List inputFields = []; - GraphQLInputObjectType(this.name, - {this.description, - Iterable inputFields = const []}) { + GraphQLInputObjectType( + this.name, { + this.description, + Iterable inputFields = const [], + }) { this.inputFields.addAll(inputFields); } @@ -236,7 +259,8 @@ class GraphQLInputObjectType if (field.type is GraphQLNonNullableType) { if (!input.containsKey(field.name) || input[field.name] == null) { errors.add( - 'Field "${field.name}, of type ${field.type} cannot be null."'); + 'Field "${field.name}, of type ${field.type} cannot be null."', + ); } } } @@ -246,7 +270,8 @@ class GraphQLInputObjectType if (field == null) { errors.add( - 'Unexpected field "$k" encountered in $key. Accepted values on type $name: ${inputFields.map((f) => f.name).toList()}'); + 'Unexpected field "$k" encountered in $key. Accepted values on type $name: ${inputFields.map((f) => f.name).toList()}', + ); } else { var v = input[k]; var result = field.type.validate(k.toString(), v); @@ -272,7 +297,8 @@ class GraphQLInputObjectType var field = inputFields.firstWhereOrNull((f) => f.name == k); if (field == null) { throw UnsupportedError( - 'Cannot serialize field "$k", which was not defined in the schema.'); + 'Cannot serialize field "$k", which was not defined in the schema.', + ); } return out..[k.toString()] = field.type.serialize(value[k]); }); @@ -294,8 +320,10 @@ class GraphQLInputObjectType return other is GraphQLInputObjectType && other.name == name && other.description == description && - const ListEquality() - .equals(other.inputFields, inputFields); + const ListEquality().equals( + other.inputFields, + inputFields, + ); } @override @@ -303,7 +331,7 @@ class GraphQLInputObjectType @override GraphQLType, Map> - coerceToInputObject() => this; + coerceToInputObject() => this; } /// A field expected within a [GraphQLInputObjectType]. @@ -320,8 +348,12 @@ class GraphQLInputObjectField { /// An optional default value for this field in an input object. final Value? defaultValue; - GraphQLInputObjectField(this.name, this.type, - {this.description, this.defaultValue}); + GraphQLInputObjectField( + this.name, + this.type, { + this.description, + this.defaultValue, + }); @override bool operator ==(other) => diff --git a/packages/graphql_schema/lib/src/scalar.dart b/packages/graphql_schema/lib/src/scalar.dart index a800217..4056d73 100644 --- a/packages/graphql_schema/lib/src/scalar.dart +++ b/packages/graphql_schema/lib/src/scalar.dart @@ -9,11 +9,14 @@ final GraphQLScalarType graphQLString = GraphQLStringType(); /// The ID scalar type represents a unique identifier, often used to re-fetch an object or as the key for a cache. /// /// The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable. -final GraphQLScalarType graphQLId = - GraphQLStringType(name: 'ID'); +final GraphQLScalarType graphQLId = GraphQLStringType( + name: 'ID', +); -final graphQLNonEmptyString = - GraphQLStringMinType(1, description: 'Non empty String'); +final graphQLNonEmptyString = GraphQLStringMinType( + 1, + description: 'Non empty String', +); GraphQLStringType graphQLStringMin(int min) => GraphQLStringMinType(min); @@ -28,17 +31,29 @@ final GraphQLScalarType graphQLDate = _GraphQLDateType._(); /// A signed 32‐bit integer. final graphQLInt = GraphQLNumType('Int'); -final graphQLPositiveInt = - GraphQLNumMinType('Int', 1, description: 'Positive integer (>= 1)'); +final graphQLPositiveInt = GraphQLNumMinType( + 'Int', + 1, + description: 'Positive integer (>= 1)', +); -final graphQLNonPositiveInt = GraphQLNumMaxType('Int', 0, - description: 'Non positive integer (<= 0)'); +final graphQLNonPositiveInt = GraphQLNumMaxType( + 'Int', + 0, + description: 'Non positive integer (<= 0)', +); -final graphQLNegativeInt = - GraphQLNumMaxType('Int', -1, description: 'Negative integer (<= -1)'); +final graphQLNegativeInt = GraphQLNumMaxType( + 'Int', + -1, + description: 'Negative integer (<= -1)', +); -final graphQLNonNegativeInt = GraphQLNumMinType('Int', 0, - description: 'Non negative integer (>= 0)'); +final graphQLNonNegativeInt = GraphQLNumMinType( + 'Int', + 0, + description: 'Non negative integer (>= 0)', +); GraphQLNumMinType graphQLIntMin(int min) => GraphQLNumMinType('Int', min); @@ -121,7 +136,7 @@ class GraphQLNumType extends GraphQLScalarType { class GraphQLNumMinType extends GraphQLNumType { GraphQLNumMinType(super.name, this.min, {String? description}) - : super(description: description ?? '$name with minimum of $min'); + : super(description: description ?? '$name with minimum of $min'); final T min; @@ -130,8 +145,9 @@ class GraphQLNumMinType extends GraphQLNumType { var ret = super.validate(key, input); if (ret.successful && input < min) { - ret = ValidationResult._failure( - ['Value ($input) can not be lower than $min']); + ret = ValidationResult._failure([ + 'Value ($input) can not be lower than $min', + ]); } return ret; @@ -140,7 +156,7 @@ class GraphQLNumMinType extends GraphQLNumType { class GraphQLNumMaxType extends GraphQLNumType { GraphQLNumMaxType(super.name, this.max, {String? description}) - : super(description: description ?? '$name with maximum of $max'); + : super(description: description ?? '$name with maximum of $max'); final T max; @@ -149,8 +165,9 @@ class GraphQLNumMaxType extends GraphQLNumType { var ret = super.validate(key, input); if (ret.successful && input > max) { - ret = ValidationResult._failure( - ['Value ($input) can not be greater than $max']); + ret = ValidationResult._failure([ + 'Value ($input) can not be greater than $max', + ]); } return ret; @@ -159,9 +176,10 @@ class GraphQLNumMaxType extends GraphQLNumType { class GraphQLNumRangedType extends GraphQLNumType { GraphQLNumRangedType(super.name, this.min, this.max, {String? description}) - : super( - description: description ?? - '$name between $min and $max. (>= $min && <= $max)'); + : super( + description: + description ?? '$name between $min and $max. (>= $min && <= $max)', + ); final T min; final T max; @@ -172,7 +190,7 @@ class GraphQLNumRangedType extends GraphQLNumType { if (ret.successful && (input < min || input > max)) { ret = ValidationResult._failure([ - 'Value ($input) must be between $min and $max. (>= $min && <= $max)' + 'Value ($input) must be between $min and $max. (>= $min && <= $max)', ]); } @@ -181,8 +199,10 @@ class GraphQLNumRangedType extends GraphQLNumType { } class GraphQLStringType extends GraphQLScalarType { - GraphQLStringType( - {this.name = 'String', this.description = 'A character sequence.'}); + GraphQLStringType({ + this.name = 'String', + this.description = 'A character sequence.', + }); @override final String name; @@ -207,9 +227,9 @@ class GraphQLStringType extends GraphQLScalarType { class GraphQLStringMinType extends GraphQLStringType { GraphQLStringMinType(this.min, {String? description, super.name}) - : super( - description: - description ?? '$name with minimum of $min characters'); + : super( + description: description ?? '$name with minimum of $min characters', + ); final int min; @@ -218,8 +238,9 @@ class GraphQLStringMinType extends GraphQLStringType { var ret = super.validate(key, input); if (ret.successful && input.length < min) { - ret = ValidationResult._failure( - ['Value (${input.length} chars) can not be lower than $min']); + ret = ValidationResult._failure([ + 'Value (${input.length} chars) can not be lower than $min', + ]); } return ret; @@ -228,7 +249,7 @@ class GraphQLStringMinType extends GraphQLStringType { class GraphQLStringMaxType extends GraphQLStringType { GraphQLStringMaxType(this.max, {String? description, super.name}) - : super(description: description ?? '$name with max of $max characters'); + : super(description: description ?? '$name with max of $max characters'); final int max; @@ -237,8 +258,9 @@ class GraphQLStringMaxType extends GraphQLStringType { var ret = super.validate(key, input); if (ret.successful && input.length > max) { - ret = ValidationResult._failure( - ['Value (${input.length} chars) can not be greater than $max']); + ret = ValidationResult._failure([ + 'Value (${input.length} chars) can not be greater than $max', + ]); } return ret; @@ -247,9 +269,10 @@ class GraphQLStringMaxType extends GraphQLStringType { class GraphQLStringRangeType extends GraphQLStringType { GraphQLStringRangeType(this.min, this.max, {String? description, super.name}) - : super( - description: - description ?? '$name with characters between $min and $max'); + : super( + description: + description ?? '$name with characters between $min and $max', + ); final int min; final int max; @@ -260,7 +283,7 @@ class GraphQLStringRangeType extends GraphQLStringType { if (ret.successful && (input.length < min || input.length > max)) { ret = ValidationResult._failure([ - 'Value (${input.length} chars) must have between $min and $max chars' + 'Value (${input.length} chars) must have between $min and $max chars', ]); } @@ -287,8 +310,9 @@ class _GraphQLDateType extends GraphQLScalarType @override ValidationResult validate(String key, input) { if (input is! String) { - return ValidationResult._failure( - ['$key must be an ISO 8601-formatted date string.']); + return ValidationResult._failure([ + '$key must be an ISO 8601-formatted date string.', + ]); } // else if (input == null) return ValidationResult._ok(input); @@ -296,8 +320,9 @@ class _GraphQLDateType extends GraphQLScalarType DateTime.parse(input); return ValidationResult._ok(input); } on FormatException { - return ValidationResult._failure( - ['$key must be an ISO 8601-formatted date string.']); + return ValidationResult._failure([ + '$key must be an ISO 8601-formatted date string.', + ]); } } diff --git a/packages/graphql_schema/lib/src/schema.dart b/packages/graphql_schema/lib/src/schema.dart index 7809e42..286db5e 100644 --- a/packages/graphql_schema/lib/src/schema.dart +++ b/packages/graphql_schema/lib/src/schema.dart @@ -41,24 +41,26 @@ class GraphQLSchema { final Iterable? directiveTypes; - GraphQLSchema( - {this.queryType, - this.mutationType, - this.subscriptionType, - this.directiveTypes}); + GraphQLSchema({ + this.queryType, + this.mutationType, + this.subscriptionType, + this.directiveTypes, + }); } /// A shorthand for creating a [GraphQLSchema]. -GraphQLSchema graphQLSchema( - {required GraphQLObjectType queryType, - GraphQLObjectType? mutationType, - GraphQLObjectType? subscriptionType, - Iterable? directiveTypes}) => - GraphQLSchema( - queryType: queryType, - mutationType: mutationType, - subscriptionType: subscriptionType, - directiveTypes: directiveTypes); +GraphQLSchema graphQLSchema({ + required GraphQLObjectType queryType, + GraphQLObjectType? mutationType, + GraphQLObjectType? subscriptionType, + Iterable? directiveTypes, +}) => GraphQLSchema( + queryType: queryType, + mutationType: mutationType, + subscriptionType: subscriptionType, + directiveTypes: directiveTypes, +); /// A default resolver that always returns `null`. Object? resolveToNull(_, __) => null; @@ -71,26 +73,20 @@ class GraphQLException implements Exception { GraphQLException(this.errors); factory GraphQLException.fromMessage(String message) { - return GraphQLException([ - GraphQLExceptionError(message), - ]); + return GraphQLException([GraphQLExceptionError(message)]); } factory GraphQLException.fromSourceSpan(String message, FileSpan span) { return GraphQLException([ GraphQLExceptionError( message, - locations: [ - GraphExceptionErrorLocation.fromSourceLocation(span.start), - ], + locations: [GraphExceptionErrorLocation.fromSourceLocation(span.start)], ), ]); } Map>> toJson() { - return { - 'errors': errors.map((e) => e.toJson()).toList(), - }; + return {'errors': errors.map((e) => e.toJson()).toList()}; } @override @@ -131,7 +127,8 @@ class GraphExceptionErrorLocation { GraphExceptionErrorLocation(this.line, this.column); factory GraphExceptionErrorLocation.fromSourceLocation( - SourceLocation location) { + SourceLocation location, + ) { return GraphExceptionErrorLocation(location.line, location.column); } @@ -157,8 +154,12 @@ class GraphQLDocumentation { /// The name of an explicit type for the annotated field, rather than having it be assumed. final Symbol? typeName; - const GraphQLDocumentation( - {this.description, this.deprecationReason, this.type, this.typeName}); + const GraphQLDocumentation({ + this.description, + this.deprecationReason, + this.type, + this.typeName, + }); } /// The canonical instance. diff --git a/packages/graphql_schema/lib/src/type.dart b/packages/graphql_schema/lib/src/type.dart index 70dcfd7..118f580 100644 --- a/packages/graphql_schema/lib/src/type.dart +++ b/packages/graphql_schema/lib/src/type.dart @@ -40,8 +40,8 @@ abstract class GraphQLType { /// Shorthand to create a [GraphQLListType]. GraphQLListType listOf( - GraphQLType innerType) => - GraphQLListType(innerType); + GraphQLType innerType, +) => GraphQLListType(innerType); /// A special [GraphQLType] that indicates that input vales should be a list of another type, [ofType]. class GraphQLListType @@ -148,8 +148,9 @@ class GraphQLNonNullableType @override ValidationResult validate(String key, Serialized input) { if (input == null) { - return ValidationResult._failure( - ['Expected "$key" to be a non-null value.']); + return ValidationResult._failure([ + 'Expected "$key" to be a non-null value.', + ]); } return ofType.validate(key, input); } diff --git a/packages/graphql_schema/lib/src/union.dart b/packages/graphql_schema/lib/src/union.dart index fa2bac4..4e08465 100644 --- a/packages/graphql_schema/lib/src/union.dart +++ b/packages/graphql_schema/lib/src/union.dart @@ -14,13 +14,18 @@ class GraphQLUnionType final List possibleTypes = []; GraphQLUnionType( - this.name, - Iterable, Map>> - possibleTypes) { - assert(possibleTypes.every((t) => t is GraphQLObjectType), - 'The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union.'); - assert(possibleTypes.isNotEmpty, - 'A Union type must define one or more member types.'); + this.name, + Iterable, Map>> + possibleTypes, + ) { + assert( + possibleTypes.every((t) => t is GraphQLObjectType), + 'The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union.', + ); + assert( + possibleTypes.isNotEmpty, + 'A Union type must define one or more member types.', + ); for (var t in possibleTypes.toSet()) { this.possibleTypes.add(t as GraphQLObjectType); @@ -32,9 +37,11 @@ class GraphQLUnionType @override GraphQLType, Map> - coerceToInputObject() { + coerceToInputObject() { return GraphQLUnionType( - '${name}Input', possibleTypes.map((t) => t.coerceToInputObject())); + '${name}Input', + possibleTypes.map((t) => t.coerceToInputObject()), + ); } @override @@ -63,7 +70,9 @@ class GraphQLUnionType @override ValidationResult> validate( - String key, Map input) { + String key, + Map input, + ) { var errors = []; for (var type in possibleTypes) { @@ -84,8 +93,10 @@ class GraphQLUnionType other is GraphQLUnionType && other.name == name && other.description == description && - const ListEquality() - .equals(other.possibleTypes, possibleTypes); + const ListEquality().equals( + other.possibleTypes, + possibleTypes, + ); @override int get hashCode => hash3(name, description, possibleTypes); diff --git a/packages/graphql_schema/lib/src/validation_result.dart b/packages/graphql_schema/lib/src/validation_result.dart index 272f332..ae7902e 100644 --- a/packages/graphql_schema/lib/src/validation_result.dart +++ b/packages/graphql_schema/lib/src/validation_result.dart @@ -14,15 +14,11 @@ class ValidationResult { //ValidationResult._(this.successful, this.value, this.errors); - ValidationResult._ok(this.value) - : errors = [], - successful = true; + ValidationResult._ok(this.value) : errors = [], successful = true; - ValidationResult._failure(this.errors) - : value = null, - successful = false; + ValidationResult._failure(this.errors) : value = null, successful = false; -// ValidationResult _asFailure() { -// return new ValidationResult._(false, value, errors); -// } + // ValidationResult _asFailure() { + // return new ValidationResult._(false, value, errors); + // } } diff --git a/packages/graphql_schema/pubspec.yaml b/packages/graphql_schema/pubspec.yaml index 7c953bd..d368c18 100644 --- a/packages/graphql_schema/pubspec.yaml +++ b/packages/graphql_schema/pubspec.yaml @@ -4,7 +4,7 @@ description: An implementation of GraphQL's type system in Dart. Basis of graphq homepage: https://angel3-framework.web.app/ repository: https://github.com/dart-backend/graphql_dart/tree/master/packages/graphql_schema environment: - sdk: '>=3.8.0 <5.0.0' + sdk: '>=3.8.0 <4.0.0' dependencies: collection: ^1.17.0 meta: ^1.9.0 diff --git a/packages/graphql_schema/test/common.dart b/packages/graphql_schema/test/common.dart index d36428a..a2a6b35 100644 --- a/packages/graphql_schema/test/common.dart +++ b/packages/graphql_schema/test/common.dart @@ -1,15 +1,19 @@ import 'package:graphql_schema2/graphql_schema2.dart'; -final GraphQLObjectType pokemonType = objectType('Pokemon', fields: [ - field('species', graphQLString), - field('catch_date', graphQLDate) -]); +final GraphQLObjectType pokemonType = objectType( + 'Pokemon', + fields: [field('species', graphQLString), field('catch_date', graphQLDate)], +); -final GraphQLObjectType trainerType = - objectType('Trainer', fields: [field('name', graphQLString)]); +final GraphQLObjectType trainerType = objectType( + 'Trainer', + fields: [field('name', graphQLString)], +); -final GraphQLObjectType pokemonRegionType = objectType('PokemonRegion', - fields: [ - field('trainer', trainerType), - field('pokemon_species', listOf(pokemonType)) - ]); +final GraphQLObjectType pokemonRegionType = objectType( + 'PokemonRegion', + fields: [ + field('trainer', trainerType), + field('pokemon_species', listOf(pokemonType)), + ], +); diff --git a/packages/graphql_schema/test/equality_test.dart b/packages/graphql_schema/test/equality_test.dart index 88e8dbe..0b76dc7 100644 --- a/packages/graphql_schema/test/equality_test.dart +++ b/packages/graphql_schema/test/equality_test.dart @@ -5,47 +5,41 @@ import 'package:test/test.dart'; void main() { group('equality', () { test('enums', () { - expect(enumTypeFromStrings('A', ['B', 'C']), - enumTypeFromStrings('A', ['B', 'C'])); - expect(enumTypeFromStrings('A', ['B', 'C']), - isNot(enumTypeFromStrings('B', ['B', 'C']))); + expect( + enumTypeFromStrings('A', ['B', 'C']), + enumTypeFromStrings('A', ['B', 'C']), + ); + expect( + enumTypeFromStrings('A', ['B', 'C']), + isNot(enumTypeFromStrings('B', ['B', 'C'])), + ); }); test('objects', () { expect( - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), ); expect( - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), - isNot(objectType('BD', fields: [ - field('b', graphQLString.nonNullable()), - ])), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + isNot( + objectType('BD', fields: [field('b', graphQLString.nonNullable())]), + ), ); expect( - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), - isNot(objectType('B', fields: [ - field('ba', graphQLString.nonNullable()), - ])), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + isNot( + objectType('B', fields: [field('ba', graphQLString.nonNullable())]), + ), ); expect( - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), - isNot(objectType('B', fields: [ - field('a', graphQLFloat.nonNullable()), - ])), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + isNot( + objectType('B', fields: [field('a', graphQLFloat.nonNullable())]), + ), ); }); @@ -54,59 +48,42 @@ void main() { test('union type', () { expect( GraphQLUnionType('A', [ - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), - objectType('C', fields: [ - field('c', graphQLString.nonNullable()), - ]), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + objectType('C', fields: [field('c', graphQLString.nonNullable())]), ]), GraphQLUnionType('A', [ - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), - objectType('C', fields: [ - field('c', graphQLString.nonNullable()), - ]), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + objectType('C', fields: [field('c', graphQLString.nonNullable())]), ]), ); expect( GraphQLUnionType('A', [ - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), - ]), - objectType('C', fields: [ - field('c', graphQLString.nonNullable()), - ]), + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + objectType('C', fields: [field('c', graphQLString.nonNullable())]), ]), - isNot(GraphQLUnionType('AA', [ - objectType('B', fields: [ - field('b', graphQLString.nonNullable()), + isNot( + GraphQLUnionType('AA', [ + objectType('B', fields: [field('b', graphQLString.nonNullable())]), + objectType('C', fields: [field('c', graphQLString.nonNullable())]), ]), - objectType('C', fields: [ - field('c', graphQLString.nonNullable()), - ]), - ])), + ), ); expect( GraphQLUnionType('A', [ - objectType('BB', fields: [ - field('b', graphQLString.nonNullable()), - ]), - objectType('C', fields: [ - field('c', graphQLString.nonNullable()), - ]), + objectType('BB', fields: [field('b', graphQLString.nonNullable())]), + objectType('C', fields: [field('c', graphQLString.nonNullable())]), ]), - isNot(GraphQLUnionType('AA', [ - objectType('BDD', fields: [ - field('b', graphQLString.nonNullable()), - ]), - objectType('C', fields: [ - field('c', graphQLString.nonNullable()), + isNot( + GraphQLUnionType('AA', [ + objectType( + 'BDD', + fields: [field('b', graphQLString.nonNullable())], + ), + objectType('C', fields: [field('c', graphQLString.nonNullable())]), ]), - ])), + ), ); }); }); diff --git a/packages/graphql_schema/test/inheritance_test.dart b/packages/graphql_schema/test/inheritance_test.dart index 65ba8c8..85d793e 100644 --- a/packages/graphql_schema/test/inheritance_test.dart +++ b/packages/graphql_schema/test/inheritance_test.dart @@ -6,27 +6,21 @@ void main() { var a = objectType( 'A', isInterface: true, - fields: [ - field('text', graphQLString.nonNullable()), - ], + fields: [field('text', graphQLString.nonNullable())], ); var b = objectType( 'B', isInterface: true, interfaces: [a], - fields: [ - field('text', graphQLString.nonNullable()), - ], + fields: [field('text', graphQLString.nonNullable())], ); var c = objectType( 'C', isInterface: true, interfaces: [b], - fields: [ - field('text', graphQLString.nonNullable()), - ], + fields: [field('text', graphQLString.nonNullable())], ); test('child implements parent', () { diff --git a/packages/graphql_schema/test/serialize_test.dart b/packages/graphql_schema/test/serialize_test.dart index a778c2f..98ea2f2 100644 --- a/packages/graphql_schema/test/serialize_test.dart +++ b/packages/graphql_schema/test/serialize_test.dart @@ -40,8 +40,10 @@ void main() { var today = DateTime.now(); var tomorrow = today.add(Duration(days: 1)); - expect(listOf(graphQLDate).serialize([today, tomorrow]), - [today.toIso8601String(), tomorrow.toIso8601String()]); + expect(listOf(graphQLDate).serialize([today, tomorrow]), [ + today.toIso8601String(), + tomorrow.toIso8601String(), + ]); }); group('input object', () { @@ -54,8 +56,10 @@ void main() { ); test('serializes valid input', () { - expect( - type.serialize({'bar': 'a', 'baz': 2.0}), {'bar': 'a', 'baz': 2.0}); + expect(type.serialize({'bar': 'a', 'baz': 2.0}), { + 'bar': 'a', + 'baz': 2.0, + }); }); }); @@ -64,40 +68,35 @@ void main() { var pikachu = {'species': 'Pikachu', 'catch_date': catchDate}; - expect(pokemonType.serialize(pikachu), - {'species': 'Pikachu', 'catch_date': catchDate.toIso8601String()}); + expect(pokemonType.serialize(pikachu), { + 'species': 'Pikachu', + 'catch_date': catchDate.toIso8601String(), + }); }); test('union type lets any of its types serialize', () { - var typeType = enumTypeFromStrings('Type', [ - 'FIRE', - 'WATER', - 'GRASS', - ]); + var typeType = enumTypeFromStrings('Type', ['FIRE', 'WATER', 'GRASS']); - var pokemonType = objectType('Pokémon', fields: [ - field( - 'name', - graphQLString.nonNullable(), - ), - field( - 'type', - typeType, - ), - ]); + var pokemonType = objectType( + 'Pokémon', + fields: [ + field('name', graphQLString.nonNullable()), + field('type', typeType), + ], + ); var digimonType = objectType( 'Digimon', - fields: [ - field('size', graphQLFloat.nonNullable()), - ], + fields: [field('size', graphQLFloat.nonNullable())], ); var u = GraphQLUnionType('Monster', [pokemonType, digimonType]); expect(u.serialize({'size': 10.0}), {'size': 10.0}); - expect(u.serialize({'name': 'Charmander', 'type': 'FIRE'}), - {'name': 'Charmander', 'type': 'FIRE'}); + expect(u.serialize({'name': 'Charmander', 'type': 'FIRE'}), { + 'name': 'Charmander', + 'type': 'FIRE', + }); }); test('nested object', () { @@ -111,7 +110,7 @@ void main() { var region = pokemonRegionType.serialize({ 'trainer': trainer, - 'pokemon_species': [pikachu, charizard] + 'pokemon_species': [pikachu, charizard], }); print(region); @@ -119,15 +118,16 @@ void main() { 'trainer': trainer, 'pokemon_species': [ {'species': 'Pikachu', 'catch_date': pikachuDate.toIso8601String()}, - {'species': 'Charizard', 'catch_date': charizardDate.toIso8601String()} - ] + {'species': 'Charizard', 'catch_date': charizardDate.toIso8601String()}, + ], }); expect( - () => pokemonRegionType.serialize({ - 'trainer': trainer, - 'DIGIMON_species': [pikachu, charizard] - }), - throwsUnsupportedError); + () => pokemonRegionType.serialize({ + 'trainer': trainer, + 'DIGIMON_species': [pikachu, charizard], + }), + throwsUnsupportedError, + ); }); } diff --git a/packages/graphql_schema/test/validation_test.dart b/packages/graphql_schema/test/validation_test.dart index 0176a9e..389afe9 100644 --- a/packages/graphql_schema/test/validation_test.dart +++ b/packages/graphql_schema/test/validation_test.dart @@ -2,30 +2,25 @@ import 'package:graphql_schema2/graphql_schema2.dart'; import 'package:test/test.dart'; void main() { - var typeType = enumTypeFromStrings('Type', [ - 'FIRE', - 'WATER', - 'GRASS', - ]); - - var pokemonType = objectType('Pokémon', fields: [ - field( - 'name', - graphQLString.nonNullable(), - ), - field( - 'type', - typeType, - ), - ]); + var typeType = enumTypeFromStrings('Type', ['FIRE', 'WATER', 'GRASS']); + + var pokemonType = objectType( + 'Pokémon', + fields: [ + field('name', graphQLString.nonNullable()), + field('type', typeType), + ], + ); var isValidPokemon = predicate( - (dynamic x) => - pokemonType.validate('@root', x as Map).successful, - 'is a valid Pokémon'); + (dynamic x) => + pokemonType.validate('@root', x as Map).successful, + 'is a valid Pokémon', + ); var throwsATypeError = throwsA( - predicate((dynamic x) => x is TypeError, 'is a type or cast error')); + predicate((dynamic x) => x is TypeError, 'is a type or cast error'), + ); test('object accepts valid input', () { expect({'name': 'Charmander', 'type': 'FIRE'}, isValidPokemon); @@ -60,9 +55,7 @@ void main() { group('union type', () { var digimonType = objectType( 'Digimon', - fields: [ - field('size', graphQLFloat.nonNullable()), - ], + fields: [field('size', graphQLFloat.nonNullable())], ); var u = GraphQLUnionType('Monster', [pokemonType, digimonType]); @@ -70,9 +63,9 @@ void main() { test('any of its types returns valid', () { expect(u.validate('@root', {'size': 32.0}).successful, true); expect( - u.validate( - '@root', {'name': 'Charmander', 'type': 'FIRE'}).successful, - true); + u.validate('@root', {'name': 'Charmander', 'type': 'FIRE'}).successful, + true, + ); }); }); @@ -86,8 +79,10 @@ void main() { ); test('accept valid input', () { - expect(type.validate('@root', {'bar': 'a', 'baz': 2.0}).value, - {'bar': 'a', 'baz': 2.0}); + expect(type.validate('@root', {'bar': 'a', 'baz': 2.0}).value, { + 'bar': 'a', + 'baz': 2.0, + }); }); test('error on missing non-null fields', () { @@ -96,9 +91,13 @@ void main() { test('error on unrecognized fields', () { expect( - type.validate( - '@root', {'bar': 'a', 'baz': 2.0, 'franken': 'stein'}).successful, - false); + type.validate('@root', { + 'bar': 'a', + 'baz': 2.0, + 'franken': 'stein', + }).successful, + false, + ); }); }); } diff --git a/packages/graphql_server/example/main.dart b/packages/graphql_server/example/main.dart index 9209cc4..7379956 100644 --- a/packages/graphql_server/example/main.dart +++ b/packages/graphql_server/example/main.dart @@ -4,32 +4,31 @@ import 'package:test/test.dart'; void main() { test('single element', () async { - var todoType = objectType('todo', fields: [ - field( - 'text', - graphQLString, - resolve: (obj, args) => obj.text, - ), - field( - 'completed', - graphQLBoolean, - resolve: (obj, args) => obj.completed, - ), - ]); - - var schema = graphQLSchema( - queryType: objectType('api', fields: [ + var todoType = objectType( + 'todo', + fields: [ + field('text', graphQLString, resolve: (obj, args) => obj.text), field( - 'todos', - listOf(todoType), - resolve: (_, __) => [ - Todo( - text: 'Clean your room!', - completed: false, - ) - ], + 'completed', + graphQLBoolean, + resolve: (obj, args) => obj.completed, ), - ]), + ], + ); + + var schema = graphQLSchema( + queryType: objectType( + 'api', + fields: [ + field( + 'todos', + listOf(todoType), + resolve: (_, __) => [ + Todo(text: 'Clean your room!', completed: false), + ], + ), + ], + ), ); var graphql = GraphQL(schema); @@ -38,8 +37,8 @@ void main() { print(result); expect(result, { 'todos': [ - {'text': 'Clean your room!'} - ] + {'text': 'Clean your room!'}, + ], }); }); } diff --git a/packages/graphql_server/lib/graphql_server2.dart b/packages/graphql_server/lib/graphql_server2.dart index 40e8526..576a82b 100644 --- a/packages/graphql_server/lib/graphql_server2.dart +++ b/packages/graphql_server/lib/graphql_server2.dart @@ -10,13 +10,18 @@ Map foldToStringDynamic(Map? map) { return {}; } return map.keys.fold>( - {}, (out, k) => out..[k.toString()] = map[k]); + {}, + (out, k) => out..[k.toString()] = map[k], + ); } class JsonPathArgument { JsonPathArgument( - this.path, this.definition, this.defaultValue, this.variableValues) - : _spl = path.split('.') { + this.path, + this.definition, + this.defaultValue, + this.variableValues, + ) : _spl = path.split('.') { if (_spl.isEmpty || _spl.length < 2 || _spl.first != r'$') { throw 'Bad json path $path'; } @@ -44,15 +49,16 @@ class GraphQL { /// An optional callback that can be used to resolve fields from objects that are not [Map]s, /// when the related field has no resolver. final FutureOr Function(T, String?, Map)? - defaultFieldResolver; + defaultFieldResolver; GraphQLSchema _schema; - GraphQL(GraphQLSchema schema, - {bool introspect = true, - this.defaultFieldResolver, - List customTypes = const []}) - : _schema = schema { + GraphQL( + GraphQLSchema schema, { + bool introspect = true, + this.defaultFieldResolver, + List customTypes = const [], + }) : _schema = schema { if (customTypes.isNotEmpty == true) { this.customTypes.addAll(customTypes); } @@ -82,8 +88,11 @@ class GraphQL { } } - GraphQLType convertType(TypeContext ctx, - {bool usePolymorphicName = false, GraphQLObjectType? parent}) { + GraphQLType convertType( + TypeContext ctx, { + bool usePolymorphicName = false, + GraphQLObjectType? parent, + }) { var listType = ctx.listType; var typeName = ctx.typeName; if (listType != null) { @@ -121,33 +130,43 @@ class GraphQL { } } - return customTypes.firstWhere((t) { - return t.name == name; - }, - orElse: () => - throw ArgumentError('Unknown GraphQL type: "$name"')); + return customTypes.firstWhere( + (t) { + return t.name == name; + }, + orElse: () => throw ArgumentError('Unknown GraphQL type: "$name"'), + ); } } else { throw ArgumentError('Invalid GraphQL type: "${ctx.span.text}"'); } } - Future parseAndExecute(String text, - {String? operationName, - sourceUrl, - Map variableValues = const {}, - initialValue, - Map globalVariables = const {}}) { + Future parseAndExecute( + String text, { + String? operationName, + sourceUrl, + Map variableValues = const {}, + initialValue, + Map globalVariables = const {}, + }) { var tokens = scan(text, sourceUrl: sourceUrl); var parser = Parser(tokens); var document = parser.parseDocument(); if (parser.errors.isNotEmpty) { - throw GraphQLException(parser.errors - .map((e) => GraphQLExceptionError(e.message, locations: [ - GraphExceptionErrorLocation.fromSourceLocation(e.span!.start) - ])) - .toList()); + throw GraphQLException( + parser.errors + .map( + (e) => GraphQLExceptionError( + e.message, + locations: [ + GraphExceptionErrorLocation.fromSourceLocation(e.span!.start), + ], + ), + ) + .toList(), + ); } return executeRequest( @@ -160,45 +179,76 @@ class GraphQL { ); } - Future executeRequest(GraphQLSchema schema, DocumentContext document, - {String? operationName, - Map variableValues = const {}, - initialValue, - Map globalVariables = const {}}) async { + Future executeRequest( + GraphQLSchema schema, + DocumentContext document, { + String? operationName, + Map variableValues = const {}, + initialValue, + Map globalVariables = const {}, + }) async { var operation = getOperation(document, operationName); - var coercedVariableValues = - coerceVariableValues(schema, operation, variableValues); + var coercedVariableValues = coerceVariableValues( + schema, + operation, + variableValues, + ); if (operation.isQuery) { - return await executeQuery(document, operation, schema, - coercedVariableValues, initialValue, globalVariables); + return await executeQuery( + document, + operation, + schema, + coercedVariableValues, + initialValue, + globalVariables, + ); } else if (operation.isSubscription) { - return await subscribe(document, operation, schema, coercedVariableValues, - globalVariables, initialValue); + return await subscribe( + document, + operation, + schema, + coercedVariableValues, + globalVariables, + initialValue, + ); } else { - return executeMutation(document, operation, schema, coercedVariableValues, - initialValue, globalVariables); + return executeMutation( + document, + operation, + schema, + coercedVariableValues, + initialValue, + globalVariables, + ); } } OperationDefinitionContext getOperation( - DocumentContext document, String? operationName) { + DocumentContext document, + String? operationName, + ) { var ops = document.definitions.whereType(); if (operationName == null) { return ops.length == 1 ? ops.first : throw GraphQLException.fromMessage( - 'This document does not define any operations.'); + 'This document does not define any operations.', + ); } else { - return ops.firstWhere((d) => d.name == operationName, - orElse: (() => throw GraphQLException.fromMessage( - 'Missing required operation "$operationName".'))); + return ops.firstWhere( + (d) => d.name == operationName, + orElse: (() => throw GraphQLException.fromMessage( + 'Missing required operation "$operationName".', + )), + ); } } Map coerceVariableValues( - GraphQLSchema schema, - OperationDefinitionContext operation, - Map variableValues) { + GraphQLSchema schema, + OperationDefinitionContext operation, + Map variableValues, + ) { var coercedValues = {}; var variableDefinitions = operation.variableDefinitions?.variableDefinitions ?? []; @@ -215,27 +265,40 @@ class GraphQL { dynamic toSet; final jp = getDirectiveValue( - 'jsonpath', 'path', variableDefinition, variableValues); + 'jsonpath', + 'path', + variableDefinition, + variableValues, + ); if (value == null) { if (defaultValue != null) { toSet = defaultValue.value.computeValue(variableValues); } else if (!variableType.isNullable && jp == null) { throw GraphQLException.fromSourceSpan( - 'Missing required variable "$variableName".', - variableDefinition.span); + 'Missing required variable "$variableName".', + variableDefinition.span, + ); } } else { var type = convertType(variableType); var validation = type.validate(variableName, value); if (!validation.successful) { - throw GraphQLException(validation.errors - .map((e) => GraphQLExceptionError(e, locations: [ - GraphExceptionErrorLocation.fromSourceLocation( - variableDefinition.span.start) - ])) - .toList()); + throw GraphQLException( + validation.errors + .map( + (e) => GraphQLExceptionError( + e, + locations: [ + GraphExceptionErrorLocation.fromSourceLocation( + variableDefinition.span.start, + ), + ], + ), + ) + .toList(), + ); } else { toSet = type.deserialize(value); } @@ -266,81 +329,124 @@ class GraphQL { } Future> executeQuery( - DocumentContext document, - OperationDefinitionContext query, - GraphQLSchema schema, - Map variableValues, - initialValue, - Map globalVariables) async { + DocumentContext document, + OperationDefinitionContext query, + GraphQLSchema schema, + Map variableValues, + initialValue, + Map globalVariables, + ) async { var queryType = schema.queryType; var selectionSet = query.selectionSet; - return await executeSelectionSet(document, selectionSet, queryType, - initialValue, variableValues, globalVariables, - lazy: makeLazy(variableValues)); + return await executeSelectionSet( + document, + selectionSet, + queryType, + initialValue, + variableValues, + globalVariables, + lazy: makeLazy(variableValues), + ); } Future> executeMutation( - DocumentContext document, - OperationDefinitionContext mutation, - GraphQLSchema schema, - Map variableValues, - initialValue, - Map globalVariables) async { + DocumentContext document, + OperationDefinitionContext mutation, + GraphQLSchema schema, + Map variableValues, + initialValue, + Map globalVariables, + ) async { var mutationType = schema.mutationType; if (mutationType == null) { throw GraphQLException.fromMessage( - 'The schema does not define a mutation type.'); + 'The schema does not define a mutation type.', + ); } var selectionSet = mutation.selectionSet; - return await executeSelectionSet(document, selectionSet, mutationType, - initialValue, variableValues, globalVariables, - lazy: makeLazy(variableValues)); + return await executeSelectionSet( + document, + selectionSet, + mutationType, + initialValue, + variableValues, + globalVariables, + lazy: makeLazy(variableValues), + ); } Future>> subscribe( - DocumentContext document, - OperationDefinitionContext subscription, - GraphQLSchema schema, - Map variableValues, - Map globalVariables, - initialValue) async { + DocumentContext document, + OperationDefinitionContext subscription, + GraphQLSchema schema, + Map variableValues, + Map globalVariables, + initialValue, + ) async { var sourceStream = await createSourceEventStream( - document, subscription, schema, variableValues, initialValue); - return mapSourceToResponseEvent(sourceStream, subscription, schema, - document, initialValue, variableValues, globalVariables); + document, + subscription, + schema, + variableValues, + initialValue, + ); + return mapSourceToResponseEvent( + sourceStream, + subscription, + schema, + document, + initialValue, + variableValues, + globalVariables, + ); } Future createSourceEventStream( - DocumentContext document, - OperationDefinitionContext subscription, - GraphQLSchema schema, - Map variableValues, - initialValue) { + DocumentContext document, + OperationDefinitionContext subscription, + GraphQLSchema schema, + Map variableValues, + initialValue, + ) { var selectionSet = subscription.selectionSet; var subscriptionType = schema.subscriptionType; if (subscriptionType == null) { 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 = - collectFields(document, subscriptionType, selectionSet, variableValues); + var groupedFieldSet = collectFields( + document, + subscriptionType, + selectionSet, + variableValues, + ); if (groupedFieldSet.length != 1) { throw GraphQLException.fromSourceSpan( - 'The grouped field set from this query must have exactly one entry.', - selectionSet.span!); + 'The grouped field set from this query must have exactly one entry.', + selectionSet.span!, + ); } 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; var field = fields.first; - var argumentValues = - coerceArgumentValues(subscriptionType, field, variableValues); + var argumentValues = coerceArgumentValues( + subscriptionType, + field, + variableValues, + ); return resolveFieldEventStream( - subscriptionType, initialValue, fieldName, argumentValues); + subscriptionType, + initialValue, + fieldName, + argumentValues, + ); } Stream> mapSourceToResponseEvent( @@ -353,45 +459,66 @@ class GraphQL { Map globalVariables, ) async* { await for (var event in sourceStream) { - yield await executeSubscriptionEvent(document, subscription, schema, - event, variableValues, globalVariables); + yield await executeSubscriptionEvent( + document, + subscription, + schema, + event, + variableValues, + globalVariables, + ); } } Future> executeSubscriptionEvent( - DocumentContext document, - OperationDefinitionContext subscription, - GraphQLSchema schema, - initialValue, - Map variableValues, - Map globalVariables) async { + DocumentContext document, + OperationDefinitionContext subscription, + GraphQLSchema schema, + initialValue, + Map variableValues, + Map globalVariables, + ) async { var selectionSet = subscription.selectionSet; var subscriptionType = schema.subscriptionType; if (subscriptionType == null) { throw GraphQLException.fromSourceSpan( - 'The schema does not define a subscription type.', - subscription.span!); + 'The schema does not define a subscription type.', + subscription.span!, + ); } try { - var data = await executeSelectionSet(document, selectionSet, - subscriptionType, initialValue, variableValues, globalVariables, - lazy: makeLazy(variableValues)); + var data = await executeSelectionSet( + document, + selectionSet, + subscriptionType, + initialValue, + variableValues, + globalVariables, + lazy: makeLazy(variableValues), + ); return {'data': data}; } on GraphQLException catch (e) { return { 'data': null, - 'errors': [e.errors.map((e) => e.toJson()).toList()] + 'errors': [e.errors.map((e) => e.toJson()).toList()], }; } } - Future resolveFieldEventStream(GraphQLObjectType subscriptionType, - rootValue, String? fieldName, Map argumentValues) async { - var field = subscriptionType.fields.firstWhere((f) => f.name == fieldName, - orElse: () { - throw GraphQLException.fromMessage( - 'No subscription field named "$fieldName" is defined.'); - }); + Future resolveFieldEventStream( + GraphQLObjectType subscriptionType, + rootValue, + String? fieldName, + Map argumentValues, + ) async { + var field = subscriptionType.fields.firstWhere( + (f) => f.name == fieldName, + orElse: () { + throw GraphQLException.fromMessage( + 'No subscription field named "$fieldName" is defined.', + ); + }, + ); var resolver = field.resolve!; var result = await resolver(rootValue, argumentValues); if (result is Stream) { @@ -412,8 +539,12 @@ class GraphQL { GraphQLObjectType? parentType, }) async { var groupedFieldSet = collectFields( - document, objectType!, selectionSet, variableValues, - parentType: parentType); + document, + objectType!, + selectionSet, + variableValues, + parentType: parentType, + ); var resultMap = {}; for (var responseKey in groupedFieldSet.keys) { @@ -456,16 +587,16 @@ class GraphQL { } futureResponseValue = executeField( - document, - fieldName, - objectType, - objectValue, - fields, - fieldType, - Map.from(globalVariables) - ..addAll(variableValues), - globalVariables, - lazy: nextLazy.toList()); + document, + fieldName, + objectType, + objectValue, + fields, + fieldType, + Map.from(globalVariables)..addAll(variableValues), + globalVariables, + lazy: nextLazy.toList(), + ); } final val = resultMap[responseKey] = await futureResponseValue; @@ -484,37 +615,55 @@ class GraphQL { } Future executeField( - DocumentContext document, - String? fieldName, - GraphQLObjectType objectType, - dynamic objectValue, - List fields, - GraphQLType fieldType, - Map variableValues, - Map globalVariables, - {List lazy = const []}) async { + DocumentContext document, + String? fieldName, + GraphQLObjectType objectType, + dynamic objectValue, + List fields, + GraphQLType fieldType, + Map variableValues, + Map globalVariables, { + List lazy = const [], + }) async { var field = fields[0]; - var argumentValues = - coerceArgumentValues(objectType, field, variableValues); + var argumentValues = coerceArgumentValues( + objectType, + field, + variableValues, + ); var resolvedValue = await resolveFieldValue( - objectType, - objectValue, - fieldName, - Map.from(globalVariables)..addAll(argumentValues)); - return completeValue(document, fieldName, fieldType, fields, resolvedValue, - variableValues, globalVariables, - lazy: lazy); + objectType, + objectValue, + fieldName, + Map.from(globalVariables)..addAll(argumentValues), + ); + return completeValue( + document, + fieldName, + fieldType, + fields, + resolvedValue, + variableValues, + globalVariables, + lazy: lazy, + ); } - Map coerceArgumentValues(GraphQLObjectType objectType, - SelectionContext field, Map variableValues) { + Map coerceArgumentValues( + GraphQLObjectType objectType, + SelectionContext field, + Map variableValues, + ) { var coercedValues = {}; var argumentValues = field.field?.arguments; var fieldName = field.field?.fieldName.alias?.name ?? field.field?.fieldName.name; - var desiredField = objectType.fields.firstWhere((f) => f.name == fieldName, - orElse: (() => throw FormatException( - '${objectType.name} has no field named "$fieldName".'))); + var desiredField = objectType.fields.firstWhere( + (f) => f.name == fieldName, + orElse: (() => throw FormatException( + '${objectType.name} has no field named "$fieldName".', + )), + ); var argumentDefinitions = desiredField.inputs; for (var argumentDefinition in argumentDefinitions) { @@ -522,21 +671,24 @@ class GraphQL { var argumentType = argumentDefinition.type; var defaultValue = argumentDefinition.defaultValue; - var argumentValue = - argumentValues?.firstWhereOrNull((a) => a.name == argumentName); + var argumentValue = argumentValues?.firstWhereOrNull( + (a) => a.name == argumentName, + ); if (argumentValue == null) { if (defaultValue != null || argumentDefinition.defaultsToNull) { coercedValues[argumentName] = defaultValue; } else if (argumentType is GraphQLNonNullableType) { throw GraphQLException.fromMessage( - 'Missing value for argument "$argumentName" of field "$fieldName".'); + 'Missing value for argument "$argumentName" of field "$fieldName".', + ); } else { continue; } } else { - final inputValue = argumentValue.value - .computeValue(variableValues as Map); + final inputValue = argumentValue.value.computeValue( + variableValues as Map, + ); try { final validation = argumentType.validate(argumentName, inputValue); @@ -547,24 +699,21 @@ class GraphQL { 'Type coercion error for value of argument "$argumentName" of field "$fieldName". ($inputValue)', locations: [ GraphExceptionErrorLocation.fromSourceLocation( - argumentValue.value.span!.start) + argumentValue.value.span!.start, + ), ], - ) + ), ]; for (var error in validation.errors) { var err = argumentValue.value.span?.start; var locations = []; if (err != null) { - locations - .add(GraphExceptionErrorLocation.fromSourceLocation(err)); + locations.add( + GraphExceptionErrorLocation.fromSourceLocation(err), + ); } - errors.add( - GraphQLExceptionError( - error, - locations: locations, - ), - ); + errors.add(GraphQLExceptionError(error, locations: locations)); } throw GraphQLException(errors); @@ -585,10 +734,7 @@ class GraphQL { 'Type coercion error for value of argument "$argumentName" of field "$fieldName". [$inputValue]', locations: locations, ), - GraphQLExceptionError( - e.toString(), - locations: locations, - ), + GraphQLExceptionError(e.toString(), locations: locations), ]); } } @@ -597,8 +743,12 @@ class GraphQL { return coercedValues; } - Future resolveFieldValue(GraphQLObjectType objectType, T objectValue, - String? fieldName, Map argumentValues) async { + Future resolveFieldValue( + GraphQLObjectType objectType, + T objectValue, + String? fieldName, + Map argumentValues, + ) async { final field = objectType.fields.firstWhere((f) => f.name == fieldName); final fieldResolve = field.resolve; @@ -607,7 +757,10 @@ class GraphQL { } else if (fieldResolve == null) { if (defaultFieldResolver != null) { return await defaultFieldResolver!( - objectValue, fieldName, argumentValues); + objectValue, + fieldName, + argumentValues, + ); } return null; } else { @@ -616,22 +769,31 @@ class GraphQL { } Future completeValue( - DocumentContext document, - String? fieldName, - GraphQLType fieldType, - List fields, - dynamic result, - Map variableValues, - Map globalVariables, - {List lazy = const []}) async { + DocumentContext document, + String? fieldName, + GraphQLType fieldType, + List fields, + dynamic result, + Map variableValues, + Map globalVariables, { + List lazy = const [], + }) async { if (fieldType is GraphQLNonNullableType) { var innerType = fieldType.ofType; - var completedResult = await completeValue(document, fieldName, innerType, - fields, result, variableValues, globalVariables); + var completedResult = await completeValue( + document, + fieldName, + innerType, + fields, + result, + variableValues, + globalVariables, + ); if (completedResult == null) { throw GraphQLException.fromMessage( - 'Null value provided for non-nullable field "$fieldName".'); + 'Null value provided for non-nullable field "$fieldName".', + ); } else { return completedResult; } @@ -644,15 +806,25 @@ class GraphQL { if (fieldType is GraphQLListType) { if (result is! Iterable) { 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.', + ); } var innerType = fieldType.ofType; var futureOut = []; for (var resultItem in result) { - futureOut.add(completeValue(document, '(item in "$fieldName")', - innerType, fields, resultItem, variableValues, globalVariables)); + futureOut.add( + completeValue( + document, + '(item in "$fieldName")', + innerType, + fields, + resultItem, + variableValues, + globalVariables, + ), + ); } var out = []; @@ -670,7 +842,8 @@ class GraphQL { return ret; } on TypeError { throw GraphQLException.fromMessage( - 'Value of field "$fieldName" must be ${fieldType.valueType}, got $result (${result.runtimeType}) instead.'); + 'Value of field "$fieldName" must be ${fieldType.valueType}, got $result (${result.runtimeType}) instead.', + ); } } @@ -685,16 +858,26 @@ class GraphQL { //objectType = fieldType as GraphQLObjectType; var subSelectionSet = mergeSelectionSets(fields); - return await executeSelectionSet(document, subSelectionSet, objectType, - result, variableValues, globalVariables, - lazy: lazy, parentType: fieldType as GraphQLObjectType); + return await executeSelectionSet( + document, + subSelectionSet, + objectType, + result, + variableValues, + globalVariables, + lazy: lazy, + parentType: fieldType as GraphQLObjectType, + ); } throw UnsupportedError('Unsupported type: $fieldType'); } GraphQLObjectType resolveAbstractType( - String? fieldName, GraphQLType type, dynamic result) { + String? fieldName, + GraphQLType type, + dynamic result, + ) { List possibleTypes; if (type is GraphQLObjectType) { @@ -714,8 +897,10 @@ class GraphQL { for (var t in possibleTypes) { try { - var validation = - t.validate(fieldName!, foldToStringDynamic(result as Map?)); + var validation = t.validate( + fieldName!, + foldToStringDynamic(result as Map?), + ); if (validation.successful) { types.add(t); @@ -735,8 +920,10 @@ class GraphQL { } } - errors.insert(0, - GraphQLExceptionError('Cannot convert value $result to type $type.')); + errors.insert( + 0, + GraphQLExceptionError('Cannot convert value $result to type $type.'), + ); throw GraphQLException(errors); } @@ -756,12 +943,13 @@ class GraphQL { } Map> collectFields( - DocumentContext document, - GraphQLObjectType? objectType, - SelectionSetContext selectionSet, - Map variableValues, - {List? visitedFragments, - GraphQLObjectType? parentType}) { + DocumentContext document, + GraphQLObjectType? objectType, + SelectionSetContext selectionSet, + Map variableValues, { + List? visitedFragments, + GraphQLObjectType? parentType, + }) { var groupedFields = >{}; visitedFragments ??= []; @@ -779,10 +967,13 @@ class GraphQL { } if (selection.field != null) { - var responseKey = selection.field!.fieldName.alias?.alias ?? + var responseKey = + selection.field!.fieldName.alias?.alias ?? selection.field!.fieldName.name; - var groupForResponseKey = - groupedFields.putIfAbsent(responseKey, () => []); + var groupForResponseKey = groupedFields.putIfAbsent( + responseKey, + () => [], + ); groupForResponseKey.add(selection); } else if (selection.fragmentSpread != null) { var fragmentSpreadName = selection.fragmentSpread!.name; @@ -797,28 +988,43 @@ class GraphQL { if (!doesFragmentTypeApply(objectType, fragmentType)) continue; var fragmentSelectionSet = fragment.selectionSet; var fragmentGroupFieldSet = collectFields( - document, objectType, fragmentSelectionSet, variableValues); + document, + objectType, + fragmentSelectionSet, + variableValues, + ); for (var responseKey in fragmentGroupFieldSet.keys) { var fragmentGroup = fragmentGroupFieldSet[responseKey]!; - var groupForResponseKey = - groupedFields.putIfAbsent(responseKey, () => []); + var groupForResponseKey = groupedFields.putIfAbsent( + responseKey, + () => [], + ); groupForResponseKey.addAll(fragmentGroup); } } else if (selection.inlineFragment != null) { var fragmentType = selection.inlineFragment!.typeCondition; - if (!doesFragmentTypeApply(objectType, fragmentType, - parentType: parentType)) { + if (!doesFragmentTypeApply( + objectType, + fragmentType, + parentType: parentType, + )) { continue; } var fragmentSelectionSet = selection.inlineFragment!.selectionSet; var fragmentGroupFieldSet = collectFields( - document, objectType, fragmentSelectionSet, variableValues); + document, + objectType, + fragmentSelectionSet, + variableValues, + ); for (var responseKey in fragmentGroupFieldSet.keys) { var fragmentGroup = fragmentGroupFieldSet[responseKey]!; - var groupForResponseKey = - groupedFields.putIfAbsent(responseKey, () => []); + var groupForResponseKey = groupedFields.putIfAbsent( + responseKey, + () => [], + ); groupForResponseKey.addAll(fragmentGroup); } } @@ -827,8 +1033,12 @@ class GraphQL { return groupedFields; } - dynamic getDirectiveValue(String name, String argumentName, Directives holder, - Map variableValues) { + dynamic getDirectiveValue( + String name, + String argumentName, + Directives holder, + Map variableValues, + ) { var directive = holder.directives.firstWhereOrNull((d) { var vv = d.value; @@ -849,7 +1059,9 @@ class GraphQL { var vname = vv.name; if (!variableValues.containsKey(vname)) { throw GraphQLException.fromSourceSpan( - 'Unknown variable: "$vname"', vv.span); + 'Unknown variable: "$vname"', + vv.span, + ); } return variableValues[vname]; } @@ -857,10 +1069,15 @@ class GraphQL { } bool doesFragmentTypeApply( - GraphQLObjectType? objectType, TypeConditionContext fragmentType, - {GraphQLObjectType? parentType}) { - var type = convertType(TypeContext(fragmentType.typeName, null), - usePolymorphicName: true, parent: parentType ?? objectType); + GraphQLObjectType? objectType, + TypeConditionContext fragmentType, { + GraphQLObjectType? parentType, + }) { + var type = convertType( + TypeContext(fragmentType.typeName, null), + usePolymorphicName: true, + parent: parentType ?? objectType, + ); if (type is GraphQLObjectType && !type.isInterface) { for (var field in type.fields) { if (!objectType!.fields.any((f) => f.name == field.name)) return false; diff --git a/packages/graphql_server/lib/introspection.dart b/packages/graphql_server/lib/introspection.dart index c4cd06f..4283128 100644 --- a/packages/graphql_server/lib/introspection.dart +++ b/packages/graphql_server/lib/introspection.dart @@ -13,34 +13,29 @@ GraphQLSchema reflectSchema(GraphQLSchema schema, List allTypes) { Set? allTypeSet; - var schemaType = objectType('__Schema', fields: [ - field( - 'types', - listOf(typeType), - resolve: (_, __) => allTypeSet ??= allTypes.toSet(), - ), - field( - 'queryType', - typeType, - resolve: (_, __) => schema.queryType, - ), - field( - 'mutationType', - typeType, - resolve: (_, __) => schema.mutationType, - ), - field( - 'subscriptionType', - typeType, - resolve: (_, __) => schema.subscriptionType, - ), - field( - 'directives', - listOf(directiveType), - resolve: (_, __) => - schema.directiveTypes, // TODO: Actually fetch directives - ), - ]); + var schemaType = objectType( + '__Schema', + fields: [ + field( + 'types', + listOf(typeType), + resolve: (_, __) => allTypeSet ??= allTypes.toSet(), + ), + field('queryType', typeType, resolve: (_, __) => schema.queryType), + field('mutationType', typeType, resolve: (_, __) => schema.mutationType), + field( + 'subscriptionType', + typeType, + resolve: (_, __) => schema.subscriptionType, + ), + field( + 'directives', + listOf(directiveType), + resolve: (_, __) => + schema.directiveTypes, // TODO: Actually fetch directives + ), + ], + ); allTypes.addAll([ graphQLBoolean, @@ -60,20 +55,19 @@ GraphQLSchema reflectSchema(GraphQLSchema schema, List allTypes) { ]); var fields = [ - field( - '__schema', - schemaType, - resolve: (_, __) => schemaType, - ), + field('__schema', schemaType, resolve: (_, __) => schemaType), field( '__type', typeType, inputs: [GraphQLFieldInput('name', graphQLString.nonNullable())], resolve: (_, args) { var name = args['name'] as String?; - return allTypes.firstWhere((t) => t!.name == name, - orElse: () => throw GraphQLException.fromMessage( - 'No type named "$name" exists.')); + return allTypes.firstWhere( + (t) => t!.name == name, + orElse: () => throw GraphQLException.fromMessage( + 'No type named "$name" exists.', + ), + ); }, ), ]; @@ -168,109 +162,116 @@ GraphQLObjectType? _reflectSchemaTypes() { return _typeType; } -final GraphQLEnumType _typeKindType = - enumTypeFromStrings('__TypeKind', [ - 'SCALAR', - 'OBJECT', - 'INTERFACE', - 'UNION', - 'ENUM', - 'INPUT_OBJECT', - 'LIST', - 'NON_NULL' -]); +final GraphQLEnumType _typeKindType = enumTypeFromStrings( + '__TypeKind', + [ + 'SCALAR', + 'OBJECT', + 'INTERFACE', + 'UNION', + 'ENUM', + 'INPUT_OBJECT', + 'LIST', + 'NON_NULL', + ], +); GraphQLObjectType _createTypeType() { var enumValueType = _reflectEnumValueType(); var fieldType = _reflectFields()!; var inputValueType = _reflectInputValueType(); - return objectType('__Type', fields: [ - field( - 'name', - graphQLString, - resolve: (type, _) => (type as GraphQLType).name, - ), - field( - 'description', - graphQLString, - resolve: (type, _) => (type as GraphQLType).description, - ), - field( - 'kind', - _typeKindType, - resolve: (type, _) { - var t = type as GraphQLType; - - if (t is GraphQLEnumType) { - return 'ENUM'; - } else if (t is GraphQLScalarType) { - return 'SCALAR'; - } else if (t is GraphQLInputObjectType) { - return 'INPUT_OBJECT'; - } else if (t is GraphQLObjectType) { - return t.isInterface ? 'INTERFACE' : 'OBJECT'; - } else if (t is GraphQLListType) { - return 'LIST'; - } else if (t is GraphQLNonNullableType) { - return 'NON_NULL'; - } else if (t is GraphQLUnionType) { - return 'UNION'; - } else { - throw UnsupportedError('Cannot get the kind of $t.'); - } - }, - ), - field( - 'fields', - listOf(fieldType), - inputs: [ - GraphQLFieldInput( - 'includeDeprecated', - graphQLBoolean, - defaultValue: false, - ), - ], - resolve: (type, args) => type is GraphQLObjectType - ? type.fields - .where( - (f) => !f.isDeprecated || args['includeDeprecated'] == true) - .toList() - : null, - ), - field( - 'enumValues', - listOf(enumValueType.nonNullable()), - inputs: [ - GraphQLFieldInput( - 'includeDeprecated', - graphQLBoolean, - defaultValue: false, - ), - ], - resolve: (obj, args) { - if (obj is GraphQLEnumType) { - return obj.values - .where( - (f) => !f.isDeprecated || args['includeDeprecated'] == true) - .toList(); - } else { - return null; - } - }, - ), - field( - 'inputFields', - listOf(inputValueType.nonNullable()), - resolve: (obj, _) { - if (obj is GraphQLInputObjectType) { - return obj.inputFields; - } + return objectType( + '__Type', + fields: [ + field( + 'name', + graphQLString, + resolve: (type, _) => (type as GraphQLType).name, + ), + field( + 'description', + graphQLString, + resolve: (type, _) => (type as GraphQLType).description, + ), + field( + 'kind', + _typeKindType, + resolve: (type, _) { + var t = type as GraphQLType; + + if (t is GraphQLEnumType) { + return 'ENUM'; + } else if (t is GraphQLScalarType) { + return 'SCALAR'; + } else if (t is GraphQLInputObjectType) { + return 'INPUT_OBJECT'; + } else if (t is GraphQLObjectType) { + return t.isInterface ? 'INTERFACE' : 'OBJECT'; + } else if (t is GraphQLListType) { + return 'LIST'; + } else if (t is GraphQLNonNullableType) { + return 'NON_NULL'; + } else if (t is GraphQLUnionType) { + return 'UNION'; + } else { + throw UnsupportedError('Cannot get the kind of $t.'); + } + }, + ), + field( + 'fields', + listOf(fieldType), + inputs: [ + GraphQLFieldInput( + 'includeDeprecated', + graphQLBoolean, + defaultValue: false, + ), + ], + resolve: (type, args) => type is GraphQLObjectType + ? type.fields + .where( + (f) => !f.isDeprecated || args['includeDeprecated'] == true, + ) + .toList() + : null, + ), + field( + 'enumValues', + listOf(enumValueType.nonNullable()), + inputs: [ + GraphQLFieldInput( + 'includeDeprecated', + graphQLBoolean, + defaultValue: false, + ), + ], + resolve: (obj, args) { + if (obj is GraphQLEnumType) { + return obj.values + .where( + (f) => !f.isDeprecated || args['includeDeprecated'] == true, + ) + .toList(); + } else { + return null; + } + }, + ), + field( + 'inputFields', + listOf(inputValueType.nonNullable()), + resolve: (obj, _) { + if (obj is GraphQLInputObjectType) { + return obj.inputFields; + } - return null; - }, - ), - ]); + return null; + }, + ), + ], + ); } GraphQLObjectType? _fieldType; @@ -284,39 +285,45 @@ GraphQLObjectType? _reflectFields() { GraphQLObjectType _createFieldType() { var inputValueType = _reflectInputValueType(); - return objectType('__Field', fields: [ - field( - 'name', - graphQLString, - resolve: (f, _) => (f as GraphQLObjectField).name, - ), - field( - 'description', - graphQLString, - resolve: (f, _) => (f as GraphQLObjectField).description, - ), - field( - 'isDeprecated', - graphQLBoolean, - resolve: (f, _) => (f as GraphQLObjectField).isDeprecated, - ), - field( - 'deprecationReason', - graphQLString, - resolve: (f, _) => (f as GraphQLObjectField).deprecationReason, - ), - field( - 'args', - listOf(inputValueType.nonNullable()).nonNullable(), - resolve: (f, _) => (f as GraphQLObjectField).inputs, - ), - ]); + return objectType( + '__Field', + fields: [ + field( + 'name', + graphQLString, + resolve: (f, _) => (f as GraphQLObjectField).name, + ), + field( + 'description', + graphQLString, + resolve: (f, _) => (f as GraphQLObjectField).description, + ), + field( + 'isDeprecated', + graphQLBoolean, + resolve: (f, _) => (f as GraphQLObjectField).isDeprecated, + ), + field( + 'deprecationReason', + graphQLString, + resolve: (f, _) => (f as GraphQLObjectField).deprecationReason, + ), + field( + 'args', + listOf(inputValueType.nonNullable()).nonNullable(), + resolve: (f, _) => (f as GraphQLObjectField).inputs, + ), + ], + ); } GraphQLObjectType? _inputValueType; -T? _fetchFromInputValue(x, T Function(GraphQLFieldInput) ifInput, - T Function(GraphQLInputObjectField) ifObjectField) { +T? _fetchFromInputValue( + x, + T Function(GraphQLFieldInput) ifInput, + T Function(GraphQLInputObjectField) ifObjectField, +) { if (x is GraphQLFieldInput) { return ifInput(x); } else if (x is GraphQLInputObjectField) { @@ -337,64 +344,74 @@ dynamic _def(dynamic f, dynamic Function(dynamic) serializer) { } GraphQLObjectType _reflectInputValueType() { - return _inputValueType ??= objectType('__InputValue', fields: [ - field( - 'name', - graphQLString.nonNullable(), - resolve: (obj, _) => - _fetchFromInputValue(obj, (f) => f.name, (f) => f.name), - ), - field( - 'description', - graphQLString, - resolve: (obj, _) => - _fetchFromInputValue(obj, (f) => f.description, (f) => f.description), - ), - field( - 'defaultValue', - graphQLString, - resolve: (obj, _) => _fetchFromInputValue(obj, - (f) => _def(f, f.type.serialize), (f) => _def(f, f.type.serialize)), - ), - ]); + return _inputValueType ??= objectType( + '__InputValue', + fields: [ + field( + 'name', + graphQLString.nonNullable(), + resolve: (obj, _) => + _fetchFromInputValue(obj, (f) => f.name, (f) => f.name), + ), + field( + 'description', + graphQLString, + resolve: (obj, _) => _fetchFromInputValue( + obj, + (f) => f.description, + (f) => f.description, + ), + ), + field( + 'defaultValue', + graphQLString, + resolve: (obj, _) => _fetchFromInputValue( + obj, + (f) => _def(f, f.type.serialize), + (f) => _def(f, f.type.serialize), + ), + ), + ], + ); } GraphQLObjectType? _directiveType; final GraphQLEnumType _directiveLocationType = enumTypeFromStrings( - '__DirectiveLocation', - DirectiveLocation.values - .map((v) => v.name.snakeCase.toUpperCase()) - .toList()); + '__DirectiveLocation', + DirectiveLocation.values.map((v) => v.name.snakeCase.toUpperCase()).toList(), +); GraphQLObjectType _reflectDirectiveType() { var inputValueType = _reflectInputValueType(); - return _directiveType ??= objectType('__Directive', fields: [ - field( - 'name', - graphQLString.nonNullable(), - resolve: (obj, _) => (obj as GraphQLDirectiveType).name, - ), - field( - 'description', - graphQLString, - resolve: (obj, _) => (obj as GraphQLDirectiveType).description, - ), - field( - 'locations', - listOf(_directiveLocationType.nonNullable()).nonNullable(), - resolve: (obj, _) => (obj as GraphQLDirectiveType) - .locations - .map((v) => v.name.snakeCase.toUpperCase()) - .toList(), - ), - field( - 'args', - listOf(inputValueType.nonNullable()).nonNullable(), - resolve: (obj, _) => (obj as GraphQLDirectiveType).inputFields, - ), - ]); + return _directiveType ??= objectType( + '__Directive', + fields: [ + field( + 'name', + graphQLString.nonNullable(), + resolve: (obj, _) => (obj as GraphQLDirectiveType).name, + ), + field( + 'description', + graphQLString, + resolve: (obj, _) => (obj as GraphQLDirectiveType).description, + ), + field( + 'locations', + listOf(_directiveLocationType.nonNullable()).nonNullable(), + resolve: (obj, _) => (obj as GraphQLDirectiveType).locations + .map((v) => v.name.snakeCase.toUpperCase()) + .toList(), + ), + field( + 'args', + listOf(inputValueType.nonNullable()).nonNullable(), + resolve: (obj, _) => (obj as GraphQLDirectiveType).inputFields, + ), + ], + ); } GraphQLObjectType? _enumValueType; @@ -428,7 +445,9 @@ GraphQLObjectType _reflectEnumValueType() { } List fetchAllTypes( - GraphQLSchema schema, List specifiedTypes) { + GraphQLSchema schema, + List specifiedTypes, +) { var data = {} ..add(schema.queryType) ..addAll(specifiedTypes); diff --git a/packages/graphql_server/lib/mirrors.dart b/packages/graphql_server/lib/mirrors.dart index 1102b5f..8f9764c 100644 --- a/packages/graphql_server/lib/mirrors.dart +++ b/packages/graphql_server/lib/mirrors.dart @@ -5,8 +5,11 @@ import 'package:graphql_schema2/graphql_schema2.dart'; import 'package:recase/recase.dart'; /// Uses `dart:mirrors` to read field names from items. If they are Maps, performs a regular lookup. -T? mirrorsFieldResolver(objectValue, String fieldName, - [Map? objectValues]) { +T? mirrorsFieldResolver( + objectValue, + String fieldName, [ + Map? objectValues, +]) { if (objectValue is Map) { return objectValue[fieldName] as T?; } else { @@ -44,7 +47,8 @@ GraphQLType? _objectTypeFromDartType(Type type, [List? typeArguments]) { return graphQLFloat; } else if (type == num) { 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) { throw UnsupportedError('Cannot convert `Null` to a GraphQL type.'); } else if (type == String) { @@ -54,11 +58,14 @@ GraphQLType? _objectTypeFromDartType(Type type, [List? typeArguments]) { } var mirror = reflectType( - type, typeArguments?.isNotEmpty == true ? typeArguments : null); + type, + typeArguments?.isNotEmpty == true ? typeArguments : null, + ); if (mirror is! ClassMirror) { 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.', + ); } var clazz = mirror; @@ -71,7 +78,8 @@ GraphQLType? _objectTypeFromDartType(Type type, [List? typeArguments]) { } 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.', + ); } if (clazz.isEnum) { @@ -95,7 +103,8 @@ GraphQLObjectType? objectTypeFromClassMirror(ClassMirror mirror) { for (var name in map.keys) { var methodMirror = map[name]!; var exclude = _getExclude(name, methodMirror); - var canAdd = name != #hashCode && + var canAdd = + name != #hashCode && name != #runtimeType && !methodMirror.isPrivate && exclude?.canSerialize != true; @@ -168,7 +177,7 @@ GraphQLObjectType? objectTypeFromClassMirror(ClassMirror mirror) { dynamic, Null, Type, - Symbol + Symbol, ]; void walk(ClassMirror parent) { @@ -233,7 +242,11 @@ GraphQLEnumType enumTypeFromClassMirror(ClassMirror mirror) { @Deprecated('No replacement yet') GraphQLObjectField fieldFromGetter( - Symbol name, MethodMirror mirror, Exclude? exclude, ClassMirror clazz) { + Symbol name, + MethodMirror mirror, + Exclude? exclude, + ClassMirror clazz, +) { var type = _getProvidedType(mirror.metadata); var wasProvided = type != null; @@ -241,8 +254,10 @@ GraphQLObjectField fieldFromGetter( var returnType = mirror.returnType; if (!clazz.isAssignableTo(returnType)) { - type = convertDartType(returnType.reflectedType, - mirror.returnType.typeArguments.map((t) => t.reflectedType).toList()); + type = convertDartType( + returnType.reflectedType, + mirror.returnType.typeArguments.map((t) => t.reflectedType).toList(), + ); } } @@ -281,7 +296,10 @@ Exclude? _getExclude(Symbol name, MethodMirror mirror) { } String? _getSerializedName( - Symbol name, MethodMirror mirror, ClassMirror clazz) { + Symbol name, + MethodMirror mirror, + ClassMirror clazz, +) { // First search for an @Alias() for (var obj in mirror.metadata) { if (obj.reflectee is SerializableField) { diff --git a/packages/graphql_server/lib/src/apollo/remote_client.dart b/packages/graphql_server/lib/src/apollo/remote_client.dart index d8e4053..d57b68f 100644 --- a/packages/graphql_server/lib/src/apollo/remote_client.dart +++ b/packages/graphql_server/lib/src/apollo/remote_client.dart @@ -18,7 +18,7 @@ class RemoteClient extends StreamChannelMixin { } RemoteClient(StreamChannel channel) - : this.withoutJson(jsonDocument.bind(channel).cast()); + : this.withoutJson(jsonDocument.bind(channel).cast()); @override StreamSink get sink => _ctrl.foreign.sink; diff --git a/packages/graphql_server/lib/src/apollo/server.dart b/packages/graphql_server/lib/src/apollo/server.dart index f8884bc..40ae71e 100644 --- a/packages/graphql_server/lib/src/apollo/server.dart +++ b/packages/graphql_server/lib/src/apollo/server.dart @@ -14,120 +14,154 @@ abstract class Server { Server(this.client, {this.keepAliveInterval}) { _sub = client.stream.listen( - (msg) async { - if ((msg.type == OperationMessage.gqlConnectionInit) && !_init) { - try { - Map? connectionParams; - if (msg.payload is Map) { - connectionParams = msg.payload as Map?; - } else if (msg.payload != null) { - throw FormatException( - '${msg.type} payload must be a map (object).'); - } + (msg) async { + if ((msg.type == OperationMessage.gqlConnectionInit) && !_init) { + try { + Map? connectionParams; + if (msg.payload is Map) { + connectionParams = msg.payload as Map?; + } else if (msg.payload != null) { + throw FormatException( + '${msg.type} payload must be a map (object).', + ); + } - var connect = await onConnect(client, connectionParams); - if (!connect) throw false; - _init = true; - client.sink - .add(OperationMessage(OperationMessage.gqlConnectionAck)); + var connect = await onConnect(client, connectionParams); + if (!connect) throw false; + _init = true; + client.sink.add( + OperationMessage(OperationMessage.gqlConnectionAck), + ); - if (keepAliveInterval != null) { + if (keepAliveInterval != null) { + client.sink.add( + OperationMessage(OperationMessage.gqlConnectionKeepAlive), + ); + _timer ??= Timer.periodic(keepAliveInterval!, (timer) { client.sink.add( - OperationMessage(OperationMessage.gqlConnectionKeepAlive)); - _timer ??= Timer.periodic(keepAliveInterval!, (timer) { - client.sink.add(OperationMessage( - OperationMessage.gqlConnectionKeepAlive)); - }); - } - } catch (e) { - if (e == false) { - _reportError('The connection was rejected.'); - } else { - _reportError(e.toString()); - } + OperationMessage(OperationMessage.gqlConnectionKeepAlive), + ); + }); } - } else if (_init) { - if (msg.type == OperationMessage.gqlStart) { - if (msg.id == null) { - throw FormatException('${msg.type} id is required.'); - } - if (msg.payload == null) { - throw FormatException('${msg.type} payload is required.'); - } else if (msg.payload is! Map) { - throw FormatException( - '${msg.type} payload must be a map (object).'); - } - var payload = msg.payload as Map; - var query = payload['query']; - var variables = payload['variables']; - var operationName = payload['operationName']; - if (query == null || query is! String) { - throw FormatException( - '${msg.type} payload must contain a string named "query".'); - } - if (variables != null && variables is! Map) { - throw FormatException( - '${msg.type} payload\'s "variables" field must be a map (object).'); - } - if (operationName != null && operationName is! String) { - throw FormatException( - '${msg.type} payload\'s "operationName" field must be a string.'); - } - var result = await onOperation( - msg.id, - query, - (variables as Map?)?.cast(), - operationName as String?); - var data = result.data; + } catch (e) { + if (e == false) { + _reportError('The connection was rejected.'); + } else { + _reportError(e.toString()); + } + } + } else if (_init) { + if (msg.type == OperationMessage.gqlStart) { + if (msg.id == null) { + throw FormatException('${msg.type} id is required.'); + } + if (msg.payload == null) { + throw FormatException('${msg.type} payload is required.'); + } else if (msg.payload is! Map) { + throw FormatException( + '${msg.type} payload must be a map (object).', + ); + } + var payload = msg.payload as Map; + var query = payload['query']; + var variables = payload['variables']; + var operationName = payload['operationName']; + if (query == null || query is! String) { + throw FormatException( + '${msg.type} payload must contain a string named "query".', + ); + } + if (variables != null && variables is! Map) { + throw FormatException( + '${msg.type} payload\'s "variables" field must be a map (object).', + ); + } + if (operationName != null && operationName is! String) { + throw FormatException( + '${msg.type} payload\'s "operationName" field must be a string.', + ); + } + var result = await onOperation( + msg.id, + query, + (variables as Map?)?.cast(), + operationName as String?, + ); + var data = result.data; - if (result.errors.isNotEmpty) { - client.sink.add(OperationMessage(OperationMessage.gqlData, - id: msg.id, payload: {'errors': result.errors.toList()})); - } else { - if (data is Map && - data.keys.length == 1 && - data.containsKey('data')) { - data = data['data']; - } + if (result.errors.isNotEmpty) { + client.sink.add( + OperationMessage( + OperationMessage.gqlData, + id: msg.id, + payload: {'errors': result.errors.toList()}, + ), + ); + } else { + if (data is Map && + data.keys.length == 1 && + data.containsKey('data')) { + data = data['data']; + } - if (data is Stream) { - await for (var event in data) { - if (event is Map && - event.keys.length == 1 && - event.containsKey('data')) { - event = event['data']; - } - client.sink.add(OperationMessage(OperationMessage.gqlData, - id: msg.id, payload: {'data': event})); + if (data is Stream) { + await for (var event in data) { + if (event is Map && + event.keys.length == 1 && + event.containsKey('data')) { + event = event['data']; } - } else { - client.sink.add(OperationMessage(OperationMessage.gqlData, - id: msg.id, payload: {'data': data})); + client.sink.add( + OperationMessage( + OperationMessage.gqlData, + id: msg.id, + payload: {'data': event}, + ), + ); } + } else { + client.sink.add( + OperationMessage( + OperationMessage.gqlData, + id: msg.id, + payload: {'data': data}, + ), + ); } - - // c.complete(); - client.sink.add( - OperationMessage(OperationMessage.gqlComplete, id: msg.id)); - } else if (msg.type == OperationMessage.gqlConnectionTerminate) { - await _sub?.cancel(); } + + // c.complete(); + client.sink.add( + OperationMessage(OperationMessage.gqlComplete, id: msg.id), + ); + } else if (msg.type == OperationMessage.gqlConnectionTerminate) { + await _sub?.cancel(); } - }, - onError: _done.completeError, - onDone: () { - _done.complete(); - _timer?.cancel(); - }); + } + }, + onError: _done.completeError, + onDone: () { + _done.complete(); + _timer?.cancel(); + }, + ); } void _reportError(String message) { - client.sink.add(OperationMessage(OperationMessage.gqlConnectionError, - payload: {'message': message})); + client.sink.add( + OperationMessage( + OperationMessage.gqlConnectionError, + payload: {'message': message}, + ), + ); } FutureOr onConnect(RemoteClient client, [Map? connectionParams]); - FutureOr onOperation(String? id, String query, - [Map? variables, String? operationName]); + FutureOr onOperation( + String? id, + String query, [ + Map? variables, + String? operationName, + ]); } diff --git a/packages/graphql_server/pubspec.yaml b/packages/graphql_server/pubspec.yaml index 302c9c7..70a33da 100644 --- a/packages/graphql_server/pubspec.yaml +++ b/packages/graphql_server/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://angel3-framework.web.app/ repository: https://github.com/dart-backend/graphql_dart/tree/master/packages/graphql_server environment: - sdk: '>=3.8.0 <5.0.0' + sdk: '>=3.8.0 <4.0.0' dependencies: angel3_serialize: ^8.0.0 diff --git a/packages/graphql_server/test/common.dart b/packages/graphql_server/test/common.dart index 9be5204..d374939 100644 --- a/packages/graphql_server/test/common.dart +++ b/packages/graphql_server/test/common.dart @@ -2,4 +2,5 @@ import 'package:graphql_schema2/graphql_schema2.dart'; import 'package:test/test.dart'; final Matcher throwsAGraphQLException = throwsA( - predicate((dynamic x) => x is GraphQLException, 'is a GraphQL exception')); + predicate((dynamic x) => x is GraphQLException, 'is a GraphQL exception'), +); diff --git a/packages/graphql_server/test/mirrors_test.dart b/packages/graphql_server/test/mirrors_test.dart index 6ba1753..8c26d14 100644 --- a/packages/graphql_server/test/mirrors_test.dart +++ b/packages/graphql_server/test/mirrors_test.dart @@ -47,8 +47,4 @@ void main() { } @graphQLClass -enum RomanceLanguage { - spanish, - france, - italian, -} +enum RomanceLanguage { spanish, france, italian } diff --git a/packages/graphql_server/test/query_test.dart b/packages/graphql_server/test/query_test.dart index 9209cc4..7379956 100644 --- a/packages/graphql_server/test/query_test.dart +++ b/packages/graphql_server/test/query_test.dart @@ -4,32 +4,31 @@ import 'package:test/test.dart'; void main() { test('single element', () async { - var todoType = objectType('todo', fields: [ - field( - 'text', - graphQLString, - resolve: (obj, args) => obj.text, - ), - field( - 'completed', - graphQLBoolean, - resolve: (obj, args) => obj.completed, - ), - ]); - - var schema = graphQLSchema( - queryType: objectType('api', fields: [ + var todoType = objectType( + 'todo', + fields: [ + field('text', graphQLString, resolve: (obj, args) => obj.text), field( - 'todos', - listOf(todoType), - resolve: (_, __) => [ - Todo( - text: 'Clean your room!', - completed: false, - ) - ], + 'completed', + graphQLBoolean, + resolve: (obj, args) => obj.completed, ), - ]), + ], + ); + + var schema = graphQLSchema( + queryType: objectType( + 'api', + fields: [ + field( + 'todos', + listOf(todoType), + resolve: (_, __) => [ + Todo(text: 'Clean your room!', completed: false), + ], + ), + ], + ), ); var graphql = GraphQL(schema); @@ -38,8 +37,8 @@ void main() { print(result); expect(result, { 'todos': [ - {'text': 'Clean your room!'} - ] + {'text': 'Clean your room!'}, + ], }); }); } diff --git a/packages/graphql_server/test/subscription_test.dart b/packages/graphql_server/test/subscription_test.dart index 3299e60..291d66d 100644 --- a/packages/graphql_server/test/subscription_test.dart +++ b/packages/graphql_server/test/subscription_test.dart @@ -7,42 +7,49 @@ void main() { var episodes = [ {'name': 'The Phantom Menace'}, {'name': 'Attack of the Clones'}, - {'name': 'Attack of the Clones'} + {'name': 'Attack of the Clones'}, ]; var episodesAsData = episodes.map((ep) { return { - 'data': {'prequels': ep} + 'data': {'prequels': ep}, }; }); - Stream> resolveEpisodes(_, __) => - Stream.fromIterable(episodes) - .map((ep) => {'prequels': ep, 'not_selected': 1337}); + Stream> resolveEpisodes(_, __) => Stream.fromIterable( + episodes, + ).map((ep) => {'prequels': ep, 'not_selected': 1337}); - var episodeType = objectType('Episode', fields: [ - field('name', graphQLString.nonNullable()), - field('not_selected', graphQLInt), - ]); + var episodeType = objectType( + 'Episode', + fields: [ + field('name', graphQLString.nonNullable()), + field('not_selected', graphQLInt), + ], + ); var schema = graphQLSchema( - queryType: objectType('TestQuery', fields: [ - field('episodes', graphQLInt, resolve: (_, __) => episodes), - ]), - subscriptionType: objectType('TestSubscription', fields: [ - field('prequels', episodeType, resolve: resolveEpisodes), - ]), + queryType: objectType( + 'TestQuery', + fields: [field('episodes', graphQLInt, resolve: (_, __) => episodes)], + ), + subscriptionType: objectType( + 'TestSubscription', + fields: [field('prequels', episodeType, resolve: resolveEpisodes)], + ), ); var graphQL = GraphQL(schema); test('subscribe with selection', () async { - var stream = await graphQL.parseAndExecute(''' + var stream = + await graphQL.parseAndExecute(''' subscription { prequels { name } } - ''') as Stream>; + ''') + as Stream>; var asList = await stream.toList(); print(asList);