diff --git a/.gitignore b/.gitignore index 8ec982a..02fb48a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ graphql.config.json # IDE .idea + +# Dart tool +.dart_tool diff --git a/README.md b/README.md index 5222d83..7e1bd7f 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,10 @@ -# graphql_client [![Build Status](https://travis-ci.org/hourliert/graphql_client.svg?branch=master)](https://travis-ci.org/hourliert/graphql_client) +**This package and repo is no longer maintained, please follow [graphql_client](https://github.com/zino-app/graphql-flutter) instead, which is a mono repo for following packages** -UPDATE: ---- -**I am not actively working on the project at the moment. I would be more than happy to receive contributions and/or grant release permission to someone else.** I like the dart ecosystem and I'd love to see a mature GraphQL library for it. I unfortunately don't have enough time to work on the project at the moment â˜šī¸. +| Package | Pub | +| :-------------------------------------------- | :----------------------------------------------- | +| [graphql/client.dart]([./packages/graphql](https://github.com/zino-app/graphql-flutter/tree/master/packages/graphql)) | [![version][version-badge]][package-link-client] | +| [graphql_flutter]([./packages/graphql](https://github.com/zino-app/graphql-flutter/tree/master/packages/graphql)_flutter) | [![version][version-badge]][package-link] | -GraphQL Client written in Dart đŸŽ¯. - -## Packages - -This repository is the home of a set of packages for graphql_client: - -### [graphql_client](graphql_client/README.md) - -The GraphQL client library. - -### [graphql_client_generator](graphql_client_generator/README.md) - -A generator that transforms GQL query into the `graphql_client.dsl`. +[version-badge]: https://img.shields.io/pub/v/graphql_flutter.svg +[package-link]: https://pub.dartlang.org/packages/graphql_flutter +[package-link-client]: https://pub.dartlang.org/packages/graphql/versions/1.0.1 diff --git a/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index 39eb4d3..0000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,101 +0,0 @@ -analyzer: - strong-mode: true - -# Lint rules and documentation, see http://dart-lang.github.io/linter/lints -linter: - rules: - - always_declare_return_types - - always_put_control_body_on_new_line - - always_put_required_named_parameters_first - - always_require_non_null_named_parameters - - annotate_overrides - - avoid_annotating_with_dynamic - - avoid_as - - avoid_catches_without_on_clauses - - avoid_catching_errors - - avoid_classes_with_only_static_members - - avoid_empty_else - - avoid_function_literals_in_foreach_calls - - avoid_init_to_null - - avoid_null_checks_in_equality_operators - - avoid_positional_boolean_parameters - - avoid_return_types_on_setters - - avoid_returning_null - - avoid_returning_this - - avoid_setters_without_getters - - avoid_slow_async_io - - avoid_types_on_closure_parameters - - await_only_futures - - camel_case_types - - cancel_subscriptions - - cascade_invocations - - close_sinks - - comment_references - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_catches - - empty_constructor_bodies - - empty_statements - - hash_and_equals - - implementation_imports - - invariant_booleans - - iterable_contains_unrelated_type - - join_return_with_assignment - - library_names - - library_prefixes - - list_remove_unrelated_type - - literal_only_boolean_expressions - - no_adjacent_strings_in_list - - no_duplicate_case_values - - non_constant_identifier_names - - omit_local_variable_types - - one_member_abstracts - - only_throw_errors - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - parameter_assignments - - prefer_adjacent_string_concatenation - - prefer_asserts_in_initializer_lists - - prefer_collection_literals - - prefer_conditional_assignment - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_constructors_over_static_methods - - prefer_contains - - prefer_expression_function_bodies - - prefer_final_fields - - prefer_final_locals - - prefer_foreach - - prefer_function_declarations_over_variables - - prefer_initializing_formals - - prefer_interpolation_to_compose_strings - - prefer_is_empty - - prefer_is_not_empty - - prefer_single_quotes - - public_member_api_docs - - recursive_getters - - slash_for_doc_comments - - sort_constructors_first - - sort_unnamed_constructors_first - - super_goes_last - - test_types_in_equals - - throw_in_finally - - type_annotate_public_apis - - type_init_formals - - unawaited_futures - - unnecessary_brace_in_string_interps - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_null_aware_assignments - - unnecessary_null_in_if_null_operators - - unnecessary_overrides - - unnecessary_this - - unrelated_type_equality_checks - - use_rethrow_when_possible - - use_setters_to_change_properties - - use_string_buffers - - use_to_and_as_if_applicable - - valid_regexps \ No newline at end of file diff --git a/graphql_client/CHANGELOG.md b/graphql_client/CHANGELOG.md index b7b5642..0bc03f5 100644 --- a/graphql_client/CHANGELOG.md +++ b/graphql_client/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.0.4 +- Dart 2 support + ## 0.0.3 ### Features diff --git a/graphql_client/LICENSE b/graphql_client/LICENSE new file mode 100644 index 0000000..bd17f34 --- /dev/null +++ b/graphql_client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Thomas Hourlier, 2019 TruongSinh-Tran-Nguyen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/graphql_client/README.md b/graphql_client/README.md index 13e2820..7e1bd7f 100644 --- a/graphql_client/README.md +++ b/graphql_client/README.md @@ -1,117 +1,10 @@ -# graphql_client [![Build Status](https://travis-ci.org/hourliert/graphql_client.svg?branch=master)](https://travis-ci.org/hourliert/graphql_client) +**This package and repo is no longer maintained, please follow [graphql_client](https://github.com/zino-app/graphql-flutter) instead, which is a mono repo for following packages** -GraphQL Client written in Dart đŸŽ¯. +| Package | Pub | +| :-------------------------------------------- | :----------------------------------------------- | +| [graphql/client.dart]([./packages/graphql](https://github.com/zino-app/graphql-flutter/tree/master/packages/graphql)) | [![version][version-badge]][package-link-client] | +| [graphql_flutter]([./packages/graphql](https://github.com/zino-app/graphql-flutter/tree/master/packages/graphql)_flutter) | [![version][version-badge]][package-link] | -It relies on the [dart http client][http] to send GQL queries. As the **http** client is platform-independent, -and can be used on the command-line, browser and [Flutter](https://flutter.io). -It has a custom DSL to write GQL queries. - -## Usage - -For now, you have to write your GQL queries with the `graphql_client` DSL. You will be able to -convert GQL queries into this DSL soon using a dart transformer. - -The following code sample allows you to retrieve the current github user bio using the Github -GraphQL API v4. - -```dart -import 'dart:async'; -import 'dart:io'; // Optional because I am reading env variables. - -import 'package:http/http.dart'; -import 'package:logging/logging.dart'; // Optional -import 'package:graphql_client/graphql_client.dart'; -import 'package:graphql_client/graphql_dsl.dart'; - -/** - * Define a custom GQL query. - * - * The corresponding GQL is : - * query ViewerBioQuery { - * viewer { - * bio - * } - * } - */ - -class ViewerBioQuery extends Object with Fields implements GQLOperation { - ViewerResolver viewer = new ViewerResolver(); - - @override - String get type => queryType; - - @override - String get name => 'ViewerBioQuery'; - - @override - List get fields => [viewer]; - - @override - ViewerBioQuery clone() => new ViewerBioQuery()..viewer = viewer.clone(); -} - -class ViewerResolver extends Object with Fields implements GQLField { - BioResolver bio = new BioResolver(); - - @override - String get name => 'viewer'; - - @override - List get fields => [bio]; - - @override - ViewerResolver clone() => new ViewerResolver()..bio = bio.clone(); -} - -class BioResolver extends Object with Scalar implements GQLField { - @override - String get name => 'bio'; - - @override - BioResolver clone() => new BioResolver(); -} - -Future main() async { - Logger.root // Optional - ..level = Level.ALL - ..onRecord.listen((rec) { - print('${rec.level.name}: ${rec.time}: ${rec.message}'); - }); - - const endPoint = 'https://api.github.com/graphql'; - final apiToken = Platform.environment['GITHUBQL_TOKEN']; - - final client = new Client(); - final logger = new Logger('GQLClient'); // Optional. - final graphQLClient = new GQLClient( - client: client, - logger: logger, - endPoint: endPoint, - ); - - final query = new ViewerBioQuery(); - - try { - final queryRes = await graphQLClient.execute( - query, - variables: {}, - headers: { - 'Authorization': 'bearer $apiToken', - }, - ); - - print(queryRes.viewer.bio.value); // => 'My awesome Github Bio!' - } on GQLException catch (e) { - print(e.message); - print(e.gqlErrors); - } -} - -``` - -## Roadmap - -You can find it [here](ROADMAP.md). - -[roadmap]: (ROADMAP.md) -[http]: https://pub.dartlang.org/packages/http +[version-badge]: https://img.shields.io/pub/v/graphql_flutter.svg +[package-link]: https://pub.dartlang.org/packages/graphql_flutter +[package-link-client]: https://pub.dartlang.org/packages/graphql/versions/1.0.1 diff --git a/graphql_client/analysis_options.yaml b/graphql_client/analysis_options.yaml new file mode 100644 index 0000000..3ad7840 --- /dev/null +++ b/graphql_client/analysis_options.yaml @@ -0,0 +1 @@ +include: package:perfectionist/analysis_options.yaml diff --git a/graphql_client/example/graphql_client_example.dart b/graphql_client/example/graphql_client_example.dart index 61f1c5b..bc7bd57 100644 --- a/graphql_client/example/graphql_client_example.dart +++ b/graphql_client/example/graphql_client_example.dart @@ -24,24 +24,24 @@ Future main() async { const endPoint = 'https://api.github.com/graphql'; final apiToken = Platform.environment['GQL_GITHUB_TOKEN']; - final client = new Client(); - final logger = new Logger('GQLClient'); - final graphQLClient = new GQLClient( + final client = Client(); + final logger = Logger('GQLClient'); + final graphQLClient = GQLClient( client: client, logger: logger, endPoint: endPoint, ); - final query = new LoginQuery(); - final mutation = new AddTestCommentMutation(); + final query = LoginQuery(); + final mutation = AddTestCommentMutation(); try { print('\n\n===================== TEST 1 ====================='); final queryRes = await graphQLClient.execute( query, - variables: {'issueId': 'MDU6SXNzdWUyNDQzNjk1NTI', 'body': 'Test issue 2'}, - headers: { + variables: {'issueId': 'MDU6SXNzdWUyNDQzNjk1NTI', 'body': 'Test issue 2'}, + headers: { 'Authorization': 'bearer $apiToken', }, ); @@ -61,7 +61,8 @@ Future main() async { print(queryRes.viewer.gist.description.value); print('=== .repositories ==='); - for (var n in queryRes.viewer.repositories.nodes) { + // @todo better if we don't have to cast here + for (var n in queryRes.viewer.repositories.nodes.cast()) { print(n.repoName.value); } } on GQLException catch (e) { @@ -76,8 +77,8 @@ Future main() async { final mutationRes = await graphQLClient.execute( mutation, - variables: {'issueId': 'MDU6SXNzdWUyNDQzNjk1NTI', 'body': 'Test issue '}, - headers: { + variables: {'issueId': 'MDU6SXNzdWUyNDQzNjk1NTI', 'body': 'Test issue '}, + headers: { 'Authorization': 'bearer $apiToken', }, ); @@ -96,8 +97,8 @@ Future main() async { await graphQLClient.execute( mutation, - variables: {'issueId': 'efwef', 'body': 'Test issue'}, - headers: { + variables: {'issueId': 'efwef', 'body': 'Test issue'}, + headers: { 'Authorization': 'bearer $apiToken', }, ); diff --git a/graphql_client/example/queries_examples.dart b/graphql_client/example/queries_examples.dart index 896e2be..1a0a615 100644 --- a/graphql_client/example/queries_examples.dart +++ b/graphql_client/example/queries_examples.dart @@ -18,7 +18,7 @@ import 'package:graphql_client/graphql_dsl.dart'; */ class LoginQuery extends Object with Fields implements GQLOperation { - ViewerResolver viewer = new ViewerResolver(); + ViewerResolver viewer = ViewerResolver(); @override String get type => queryType; @@ -30,13 +30,13 @@ class LoginQuery extends Object with Fields implements GQLOperation { List get fields => [viewer]; @override - LoginQuery clone() => new LoginQuery()..viewer = viewer.clone(); + LoginQuery clone() => LoginQuery()..viewer = viewer.clone(); } class AddTestCommentMutation extends Object with Arguments, Fields implements GQLOperation { - AddCommentMutation addComment = new AddCommentMutation(); + AddCommentMutation addComment = AddCommentMutation(); @override String get type => mutationType; @@ -52,7 +52,7 @@ class AddTestCommentMutation extends Object @override AddTestCommentMutation clone() => - new AddTestCommentMutation()..addComment = addComment.clone(); + AddTestCommentMutation()..addComment = addComment.clone(); } /** @@ -96,7 +96,7 @@ class GistDescriptiveFragment extends GistDescriptiveFragmentResolver class AddCommentMutation extends Object with Arguments, Fields implements GQLField { - CommentEdgeResolver commentEdge = new CommentEdgeResolver(); + CommentEdgeResolver commentEdge = CommentEdgeResolver(); @override String get name => 'addComment'; @@ -109,13 +109,13 @@ class AddCommentMutation extends Object @override AddCommentMutation clone() => - new AddCommentMutation()..commentEdge = commentEdge.clone(); + AddCommentMutation()..commentEdge = commentEdge.clone(); } class CommentEdgeResolver extends Object with Alias, Fields implements GQLField { - NodeResolver node = new NodeResolver(); + NodeResolver node = NodeResolver(); @override String get name => 'commentEdge'; @@ -124,13 +124,13 @@ class CommentEdgeResolver extends Object List get fields => [node]; @override - CommentEdgeResolver clone() => new CommentEdgeResolver() + CommentEdgeResolver clone() => CommentEdgeResolver() ..aliasId = aliasId ..node = node.clone(); } class NodeResolver extends Object with Alias, Fields implements GQLField { - BodyResolver body = new BodyResolver(); + BodyResolver body = BodyResolver(); @override String get name => 'node'; @@ -139,18 +139,18 @@ class NodeResolver extends Object with Alias, Fields implements GQLField { List get fields => [body]; @override - NodeResolver clone() => new NodeResolver() + NodeResolver clone() => NodeResolver() ..aliasId = aliasId ..body = body.clone(); } class ViewerResolver extends Object with Alias, Fields implements GQLField { - GistResolver gist = new GistResolver(); - RepositoryResolver repository = new RepositoryResolver(); - RepositoriesResolver repositories = new RepositoriesResolver(); - LoginResolver login = new LoginResolver(); - BioResolver bio = new BioResolver(); - BioResolver bio2 = new BioResolver(); + GistResolver gist = GistResolver(); + RepositoryResolver repository = RepositoryResolver(); + RepositoriesResolver repositories = RepositoriesResolver(); + LoginResolver login = LoginResolver(); + BioResolver bio = BioResolver(); + BioResolver bio2 = BioResolver(); @override String get name => 'viewer'; @@ -160,7 +160,7 @@ class ViewerResolver extends Object with Alias, Fields implements GQLField { [repositories, gist, repository, login, bio, bio2]; @override - ViewerResolver clone() => new ViewerResolver() + ViewerResolver clone() => ViewerResolver() ..aliasId = aliasId ..gist = gist.clone() ..repository = repository.clone() @@ -170,17 +170,19 @@ class ViewerResolver extends Object with Alias, Fields implements GQLField { ..bio2 = bio2.clone(); } -class NodesResolver extends Object with Fields implements GQLField { - NameResolver repoName = new NameResolver(); +// @todo cannot `with Fields` +class NodesResolver extends GQLField /* with Fields */{ + NameResolver repoName = NameResolver(); @override String get name => 'nodes'; + // @todo cannot `with Fields` @override List get fields => [repoName]; @override - NodesResolver clone() => new NodesResolver()..repoName = repoName.clone(); + NodesResolver clone() => NodesResolver()..repoName = repoName.clone(); } class RepositoryResolver extends Object @@ -190,10 +192,10 @@ class RepositoryResolver extends Object RepositoryIdFragmentResolver, GQLField { final RepositoryDescriptiveFragment _descriptiveRepositoryFragment = - new RepositoryDescriptiveFragment(); - final RepositoryIdFragment _idRepositoryFragment = new RepositoryIdFragment(); + RepositoryDescriptiveFragment(); + final RepositoryIdFragment _idRepositoryFragment = RepositoryIdFragment(); - CreatedAtResolver createdAt = new CreatedAtResolver(); + CreatedAtResolver createdAt = CreatedAtResolver(); @override DescriptionResolver description; @@ -223,7 +225,7 @@ class RepositoryResolver extends Object List get fragments => [_descriptiveRepositoryFragment, _idRepositoryFragment]; - RepositoryResolver clone() => new RepositoryResolver() + RepositoryResolver clone() => RepositoryResolver() ..aliasId = aliasId ..description = description.clone() ..repoName = repoName.clone() @@ -235,7 +237,7 @@ class GistResolver extends Object with Arguments, Alias, Fields, Fragments implements GistDescriptiveFragmentResolver, GQLField { final GistDescriptiveFragment _descriptiveGistFragment = - new GistDescriptiveFragment(); + GistDescriptiveFragment(); @override DescriptionResolver description; @@ -254,7 +256,7 @@ class GistResolver extends Object List get fragments => [_descriptiveGistFragment]; @override - GistResolver clone() => new GistResolver() + GistResolver clone() => GistResolver() ..aliasId = aliasId ..description = description.clone(); } @@ -262,7 +264,7 @@ class GistResolver extends Object class GistDescriptiveFragmentResolver extends Object with Fields implements GQLField { - DescriptionResolver description = new DescriptionResolver(); + DescriptionResolver description = DescriptionResolver(); @override String get name => 'GistDescriptiveFragment'; @@ -272,14 +274,14 @@ class GistDescriptiveFragmentResolver extends Object @override GistDescriptiveFragmentResolver clone() => - new GistDescriptiveFragmentResolver()..description = description.clone(); + GistDescriptiveFragmentResolver()..description = description.clone(); } class RepositoryDescriptiveFragmentResolver extends Object with Fields implements GQLField { - DescriptionResolver description = new DescriptionResolver(); - NameResolver repoName = new NameResolver(); + DescriptionResolver description = DescriptionResolver(); + NameResolver repoName = NameResolver(); @override String get name => 'RepositoryDescriptiveFragment'; @@ -289,7 +291,7 @@ class RepositoryDescriptiveFragmentResolver extends Object @override RepositoryDescriptiveFragmentResolver clone() => - new RepositoryDescriptiveFragmentResolver() + RepositoryDescriptiveFragmentResolver() ..description = description.clone() ..repoName = repoName.clone(); } @@ -297,7 +299,7 @@ class RepositoryDescriptiveFragmentResolver extends Object class RepositoryIdFragmentResolver extends Object with Fields implements GQLField { - IdResolver id = new IdResolver(); + IdResolver id = IdResolver(); @override String get name => 'RepositoryIdFragment'; @@ -307,7 +309,7 @@ class RepositoryIdFragmentResolver extends Object @override RepositoryIdFragmentResolver clone() => - new RepositoryIdFragmentResolver()..id = id.clone(); + RepositoryIdFragmentResolver()..id = id.clone(); } /** @@ -324,7 +326,7 @@ class RepositoriesResolver extends Object with Arguments, Alias, ScalarCollection, Fields implements GQLField { @override - NodesResolver nodesResolver = new NodesResolver(); + NodesResolver nodesResolver = NodesResolver(); @override String get name => 'repositories'; @@ -336,7 +338,7 @@ class RepositoriesResolver extends Object List get fields => [nodesResolver]; @override - RepositoriesResolver clone() => new RepositoriesResolver() + RepositoriesResolver clone() => RepositoriesResolver() ..aliasId = aliasId ..nodesResolver = nodesResolver.clone(); } @@ -364,7 +366,7 @@ class LoginResolver extends Object String get directiveValue => 'false'; @override - LoginResolver clone() => new LoginResolver()..aliasId = aliasId; + LoginResolver clone() => LoginResolver()..aliasId = aliasId; } class BioResolver extends Object @@ -374,7 +376,7 @@ class BioResolver extends Object String get name => 'bio'; @override - BioResolver clone() => new BioResolver()..aliasId = aliasId; + BioResolver clone() => BioResolver()..aliasId = aliasId; } class DescriptionResolver extends Object @@ -384,7 +386,7 @@ class DescriptionResolver extends Object String get name => 'description'; @override - DescriptionResolver clone() => new DescriptionResolver()..aliasId = aliasId; + DescriptionResolver clone() => DescriptionResolver()..aliasId = aliasId; } class NameResolver extends Object @@ -394,7 +396,7 @@ class NameResolver extends Object String get name => 'name'; @override - NameResolver clone() => new NameResolver()..aliasId = aliasId; + NameResolver clone() => NameResolver()..aliasId = aliasId; } class CreatedAtResolver extends Object @@ -404,7 +406,7 @@ class CreatedAtResolver extends Object String get name => 'createdAt'; @override - CreatedAtResolver clone() => new CreatedAtResolver()..aliasId = aliasId; + CreatedAtResolver clone() => CreatedAtResolver()..aliasId = aliasId; } class IdResolver extends Object with Scalar, Alias implements GQLField { @@ -412,7 +414,7 @@ class IdResolver extends Object with Scalar, Alias implements GQLField { String get name => 'id'; @override - IdResolver clone() => new IdResolver()..aliasId = aliasId; + IdResolver clone() => IdResolver()..aliasId = aliasId; } class BodyResolver extends Object @@ -422,5 +424,5 @@ class BodyResolver extends Object String get name => 'body'; @override - BodyResolver clone() => new BodyResolver()..aliasId = aliasId; + BodyResolver clone() => BodyResolver()..aliasId = aliasId; } diff --git a/graphql_client/lib/src/client.dart b/graphql_client/lib/src/client.dart index 8f082ef..62a7f35 100644 --- a/graphql_client/lib/src/client.dart +++ b/graphql_client/lib/src/client.dart @@ -6,8 +6,10 @@ library graphql_client.client; import 'dart:async'; import 'dart:convert'; +import 'dart:typed_data'; import 'package:http/http.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; @@ -42,12 +44,13 @@ class GQLClient { /// It waits for the response and throw a [GQLException] if the query has errors. /// Else, it resolves the query and return it. Future execute(T operation, - {Map variables = const {}, Map headers}) async { + {Map variables = const {}, + Map headers}) async { try { - final requestBody = { - 'query': GRAPHQL.encode(operation), - 'variables': variables, + final requestBody = { 'operationName': operation.name, + 'variables': variables, + 'query': GRAPHQL.encode(operation), }; logMessage( @@ -56,13 +59,19 @@ class GQLClient { logger); logMessage(Level.FINE, 'with body GQL request to $requestBody', logger); - final res = await client.post( - endPoint, - headers: headers, - body: JSON.encode(requestBody), - ); - final data = _parseResponse(res); + final Request httpReq = Request('post', Uri.parse(endPoint)); + httpReq.headers.addAll({ + 'accept': '*/*', + 'content-type': 'application/json; charset=utf-8', + }); + httpReq.headers.addAll(headers); + httpReq.body = json.encode(requestBody); + + final StreamedResponse res = await client.send(httpReq); + + final data = await _parseResponse(res); + assert(data != null); logMessage(Level.INFO, 'Receive response', logger); logMessage(Level.FINE, 'with body $data', logger); @@ -87,36 +96,59 @@ class GQLClient { /// The [operationName] will determine which query to execute. Future executeOperations( Map operations, String operationName, - {Map variables = const {}, Map headers}) async { - final operation = operations[operationName]; + {Map variables = const {}, + Map headers}) async { + final T operation = operations[operationName] as T; - return execute(operation, headers: headers, variables: variables); + return execute(operation, headers: headers, variables: variables); } - Map _parseResponse(Response response) { + Future _parseResponse(StreamedResponse response) async { final statusCode = response.statusCode; final reasonPhrase = response.reasonPhrase; if (statusCode < 200 || statusCode >= 400) { - throw new ClientException('Network Error: $statusCode $reasonPhrase'); + throw ClientException('Network Error: $statusCode $reasonPhrase'); } - final jsonResponse = JSON.decode(response.body); + // @todo limit bodyBytes + final Encoding encoding = _determineEncodingFromResponse(response); + final Uint8List responseByte = await response.stream.toBytes(); + final String decodedBody = encoding.decode(responseByte); + final dynamic jsonResponse = json.decode(decodedBody); + if (jsonResponse is! Map) { + // @todo more debug data + throw Exception('Malformed response from server'); + } if (jsonResponse['errors'] != null) { - throw new GQLException( - 'Error returned by the server in the query', jsonResponse['errors']); + final dynamic errors = jsonResponse['errors']; + if (errors is List) { + throw GQLException( + 'Error returned by the server in the query', errors.cast()); + } + // @todo more debug data + throw Exception('Malformed response from server'); } - return jsonResponse['data']; + if (jsonResponse['data'] != null) { + final dynamic data = jsonResponse['data']; + if (data is Map) { + return data; + } + } + // @todo more debug data + throw Exception('Malformed response from server'); } void _resolveQuery(GQLField operation, Map data) { + assert(data != null); _resolveFields(operation, data); _resolveFragments(operation, data); } void _resolveFields(GQLField operation, Map data) { + assert(data != null); if (operation is Fields) { for (var field in operation.fields) { _resolve(field, data); @@ -125,6 +157,7 @@ class GQLClient { } void _resolveFragments(GQLField operation, Map data) { + assert(data != null); if (operation is Fragments) { for (var fragment in operation.fragments) { _resolveFields(fragment, data); @@ -134,38 +167,71 @@ class GQLClient { void _resolve(GQLField resolver, Map data) { final key = (resolver is Alias) ? resolver.alias : resolver.name; - final fieldData = data[key]; + final dynamic fieldData = data[key]; if (resolver is Scalar) { resolver.value = fieldData; - } else if (resolver is ScalarCollection) { + } else if (fieldData != null && resolver is ScalarCollection) { final nodeResolver = resolver.nodesResolver; final edgeResolver = resolver.edgesResolver; - final nodesData = fieldData['nodes']; - final edgesData = fieldData['edges']; - final totalCountData = fieldData['totalCount']; + final nodesData = (fieldData['nodes'] as List); + final edgesData = (fieldData['edges'] as List); + final totalCountData = fieldData['totalCount'] as int; resolver.totalCount = totalCountData; if (nodeResolver != null) { resolver.nodes = - new List.generate(nodesData.length, (_) => nodeResolver.clone()); + List.generate(nodesData.length, (_) => nodeResolver.clone()); - for (var i = 0; i < nodesData.length; i++) { - _resolveQuery(resolver.nodes[i], nodesData[i]); + for (int i = 0; i < nodesData.length; i++) { + final data = nodesData[i] as Map; + assert(data != null); + final rNodes = resolver.nodes[i]; + _resolveQuery(rNodes, data); } } if (edgeResolver != null) { resolver.edges = - new List.generate(edgesData.length, (_) => edgeResolver.clone()); + List.generate(edgesData.length, (_) => edgeResolver.clone()); for (var i = 0; i < edgesData.length; i++) { - _resolveQuery(resolver.edges[i], edgesData[i]); + final data = edgesData[i] as Map; + assert(data != null); + _resolveQuery(resolver.edges[i], data); } } + } else if (fieldData != null && fieldData is Map && resolver is Fields) { + _resolveQuery(resolver, fieldData); } else { - _resolveQuery(resolver, fieldData); + throw Exception('nooooo'); + // @todo what else } } } + +/// Returns the charset encoding for the given response. +/// +/// The default fallback encoding is set to UTF-8 according to the IETF RFC4627 standard +/// which specifies the application/json media type: +/// "JSON text SHALL be encoded in Unicode. The default encoding is UTF-8." +Encoding _determineEncodingFromResponse(BaseResponse response, + [Encoding fallback = utf8]) { + final String contentType = response.headers['content-type']; + + if (contentType == null) { + return fallback; + } + + final MediaType mediaType = MediaType.parse(contentType); + final String charset = mediaType.parameters['charset']; + + if (charset == null) { + return fallback; + } + + final Encoding encoding = Encoding.getByName(charset); + + return encoding == null ? fallback : encoding; +} diff --git a/graphql_client/lib/src/converter.dart b/graphql_client/lib/src/converter.dart index bc42ed9..037d98a 100644 --- a/graphql_client/lib/src/converter.dart +++ b/graphql_client/lib/src/converter.dart @@ -24,10 +24,10 @@ class GQLConverter extends Codec { /// Throws a [StateError] as this is not supported. @override Converter get decoder => - throw new StateError("Not supported. You can't decode a GraphQL query."); + throw StateError("Not supported. You can't decode a GraphQL query."); } /// An immutable [GQLConverter] instance. /// /// ignore: constant_identifier_names -const GQLConverter GRAPHQL = const GQLConverter(); +const GQLConverter GRAPHQL = GQLConverter(); diff --git a/graphql_client/lib/src/encoder.dart b/graphql_client/lib/src/encoder.dart index a781974..269035c 100644 --- a/graphql_client/lib/src/encoder.dart +++ b/graphql_client/lib/src/encoder.dart @@ -13,9 +13,9 @@ class GQLEncoder extends Converter { const GQLEncoder(); @override - String convert(GQLOperation operation) { - final operationGQL = _encodeOperation(operation); - final fragmentsGQL = _encodeNestedOperationFragments(operation); + String convert(GQLOperation input) { + final operationGQL = _encodeOperation(input); + final fragmentsGQL = _encodeNestedOperationFragments(input); return '$operationGQL\n$fragmentsGQL'; } diff --git a/graphql_client/lib/src/exceptions.dart b/graphql_client/lib/src/exceptions.dart index 42583db..4bbb5f6 100644 --- a/graphql_client/lib/src/exceptions.dart +++ b/graphql_client/lib/src/exceptions.dart @@ -14,8 +14,8 @@ class Location { /// Constructs a [Location] from a JSON map. Location.fromJSON(Map data) - : line = data['line'], - column = data['column']; + : line = data['line'] as int, + column = data['column'] as int; @override String toString() => '{ line: $line, column: $column }'; @@ -34,10 +34,11 @@ class GQLError { /// Constructs a [GQLError] from a JSON map. GQLError.fromJSON(Map data) - : message = data['message'], - locations = new List.from( - (data['locations']).map((d) => new Location.fromJSON(d))), - path = data['path']; + : message = data['message'] as String, + locations = List.from( + (data['locations'] as List).cast().map((d) => Location.fromJSON(d)), + ), + path = (data['path'] as List).cast(); @override String toString() => @@ -52,13 +53,13 @@ class GQLException implements Exception { /// The list of [GQLError] in the response. final List gqlErrors; - /// Creates a new [GQLException]. + /// Creates a [GQLException]. /// /// It requires a message and a JSON list from a GQL response /// (returned by a GQL server). - GQLException(this.message, List rawGQLError) + GQLException(this.message, List> rawGQLError) : gqlErrors = - new List.from(rawGQLError.map((d) => new GQLError.fromJSON(d))); + List.from(rawGQLError.map((d) => GQLError.fromJSON(d))); @override String toString() => diff --git a/graphql_client/lib/src/graphql_fields.dart b/graphql_client/lib/src/graphql_fields.dart index 5ade52a..0233fda 100644 --- a/graphql_client/lib/src/graphql_fields.dart +++ b/graphql_client/lib/src/graphql_fields.dart @@ -13,7 +13,7 @@ part of graphql_client.definitions; /// generate the following GQL query. /// ``` /// class GistResolver extends Object with Fields implements GQLField { -/// DescriptionResolver description = new DescriptionResolver(); +/// DescriptionResolver description = DescriptionResolver(); /// @override /// String get name => 'gist'; /// diff --git a/graphql_client/lib/src/graphql_fragments.dart b/graphql_client/lib/src/graphql_fragments.dart index 896c40f..f920afc 100644 --- a/graphql_client/lib/src/graphql_fragments.dart +++ b/graphql_client/lib/src/graphql_fragments.dart @@ -13,7 +13,7 @@ part of graphql_client.definitions; /// generate the following GQL query. /// ``` /// class GistResolver extends Object with Fragments implements GQLField { -/// DescriptionFragment descriptionFragment = new DescriptionFragment(); +/// DescriptionFragment descriptionFragment = DescriptionFragment(); /// @override /// String get name => 'gist'; /// diff --git a/graphql_client/lib/src/graphql_operations.dart b/graphql_client/lib/src/graphql_operations.dart index ad747ae..651ce7c 100644 --- a/graphql_client/lib/src/graphql_operations.dart +++ b/graphql_client/lib/src/graphql_operations.dart @@ -50,7 +50,7 @@ abstract class GQLOperation extends GQLField { /// /// class DescriptionFragmentResolver extends Object /// with Fields /// implements GQLField { -/// DescriptionResolver description = new DescriptionResolver(); +/// DescriptionResolver description = DescriptionResolver(); /// /// @override /// String get name => 'GistDescriptionFragment'; diff --git a/graphql_client/lib/src/graphql_scalars.dart b/graphql_client/lib/src/graphql_scalars.dart index 4c0ce33..db3dcf4 100644 --- a/graphql_client/lib/src/graphql_scalars.dart +++ b/graphql_client/lib/src/graphql_scalars.dart @@ -20,7 +20,9 @@ abstract class Scalar implements GQLField { /// (ie. has the GQL List type). abstract class ScalarCollection implements GQLField { /// The nodes collection of [GQLField]. - List nodes; + // List nodes; + // @todo try to have List nodes; back + List nodes; /// The edges collection of [GQLField]. List edges; diff --git a/graphql_client/lib/src/utils.dart b/graphql_client/lib/src/utils.dart index c82bd60..759a385 100644 --- a/graphql_client/lib/src/utils.dart +++ b/graphql_client/lib/src/utils.dart @@ -8,7 +8,7 @@ import 'dart:math'; import 'package:logging/logging.dart'; -final _rng = new Random(); +final _rng = Random(); /// Returns a random number. /// diff --git a/graphql_client/pubspec.yaml b/graphql_client/pubspec.yaml index ff7f37e..e538bbd 100644 --- a/graphql_client/pubspec.yaml +++ b/graphql_client/pubspec.yaml @@ -1,11 +1,13 @@ name: graphql_client description: A GraphQL client. -version: 0.0.3 -homepage: https://github.com/hourliert/graphql_client -author: Thomas Hourlier +version: 0.0.4 +homepage: https://github.com/dart-graphql/graphql_client +authors: + - Thomas Hourlier + - TruongSinh Tran-Nguyen environment: - sdk: '>=1.20.1 <2.0.0' + sdk: '>=2.2.0 <3.0.0' dependencies: http: ^0.11.3 @@ -13,4 +15,6 @@ dependencies: meta: ^1.1.1 dev_dependencies: - test: ^0.12.0 + mockito: ^4.0.0 + test: ^1.5.3 + perfectionist: ^2.0.0 diff --git a/graphql_client/test/graphql_client_test.dart b/graphql_client/test/graphql_client_test.dart index facb8e4..6472ad3 100644 --- a/graphql_client/test/graphql_client_test.dart +++ b/graphql_client/test/graphql_client_test.dart @@ -1,11 +1,327 @@ -// Copyright Thomas Hourlier. All rights reserved. -// Use of this source code is governed by a MIT-style license -// that can be found in the LICENSE file. +import 'dart:convert'; +import 'dart:io' show File, Platform; +import 'dart:typed_data' show Uint8List; +import 'package:graphql_client/graphql_dsl.dart'; +import 'package:path/path.dart' show dirname, join; import 'package:test/test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:http/http.dart' as http; +import 'package:graphql_client/graphql_client.dart'; +import 'queries_examples.dart'; + +class MockHttpClient extends Mock implements http.Client {} void main() { - test('Placeholder', () { - expect(true, equals(true)); + GQLClient graphQLClient; + MockHttpClient mockHttpClient; + const String apiToken = 'my-special-bearer-token'; + group('simple json', () { + setUp(() { + mockHttpClient = MockHttpClient(); + graphQLClient = GQLClient( + client: mockHttpClient, + endPoint: 'https://api.github.com/graphql', + // logger: null, + ); + }); + group('query', () { + test('successful query', () async { + when( + mockHttpClient.send(any), + ).thenAnswer((Invocation a) async { + const String body = ''' +{ + "data": { + "viewer": { + "repositories": { + "nodes": [ + { + "__typename": "Repository", + "id": "MDEwOlJlcG9zaXRvcnkyNDgzOTQ3NA==", + "name": "pq", + "viewerHasStarred": false + }, + { + "__typename": "Repository", + "id": "MDEwOlJlcG9zaXRvcnkzMjkyNDQ0Mw==", + "name": "go-evercookie", + "viewerHasStarred": false + }, + { + "__typename": "Repository", + "id": "MDEwOlJlcG9zaXRvcnkzNTA0NjgyNA==", + "name": "watchbot", + "viewerHasStarred": false + } + ] + } + } + } +} + '''; + + final List bytes = utf8.encode(body); + final Stream> stream = + Stream>.fromIterable([bytes]); + + final http.StreamedResponse r = http.StreamedResponse(stream, 200); + + return r; + }); + + final ReadRepositoriesQuery queryRes = await graphQLClient.execute( + ReadRepositoriesQuery(), + // @todo type-sfe + variables: { + 'nRepositories': 42, + }, + headers: { + 'Authorization': 'bearer $apiToken', + }, + ); + + final dynamic captured = verify(mockHttpClient.send(captureAny)) + .captured + .first; + expect(captured, isA()); + http.Request request = captured as http.Request; + expect(request.method, 'post'); + expect(request.url.toString(), 'https://api.github.com/graphql'); + expect( + request.headers, + { + 'accept': '*/*', + 'content-type': 'application/json; charset=utf-8', + 'Authorization': 'bearer my-special-bearer-token', + }, + ); + expect(await request.finalize().bytesToString(), + r'{"operationName":"ReadRepositories","variables":{"nRepositories":42},"query":"query ReadRepositories ($nRepositories: Int!) { viewer { repositories (last: $nRepositories) { nodes { __typename id name viewerHasStarred } } } }\n"}'); + + // @todo errors should be value, not excpetion + // expect(queryRes.errors, isNull); + // expect(queryRes.data, isNotNull); + expect(queryRes.viewer, isNotNull); + // @todo better if we don't have to cast here + final List nodes = queryRes.viewer.repositories.nodes.cast(); + expect(nodes, hasLength(3)); + expect(nodes[0].repoId.value, 'MDEwOlJlcG9zaXRvcnkyNDgzOTQ3NA=='); + expect(nodes[1].repoName.value, 'go-evercookie'); + expect(nodes[2].viewerHasStarred.value, false); + return; + }); +// test('failed query because of network', {}); +// test('failed query because of because of error response', {}); +// test('failed query because of because of invalid response', {}); + }); + group('mutation', () { + test('successful mutation', () async { + when(mockHttpClient.send(any)).thenAnswer((Invocation a) async { + const String body = + '{"data":{"action":{"starrable":{"viewerHasStarred":true}}}}'; + + final List bytes = utf8.encode(body); + final Stream> stream = + Stream>.fromIterable(>[bytes]); + + final http.StreamedResponse r = http.StreamedResponse(stream, 200); + return r; + }); + + final AddStarMutation queryRes = await graphQLClient.execute( + AddStarMutation(), + // @todo type-sfe + variables: { + 'nRepositories': 38, + }, + headers: { + 'Authorization': 'bearer $apiToken', + }, + ); + + final http.Request request = verify(mockHttpClient.send(captureAny)) + .captured + .first as http.Request; + expect(request.method, 'post'); + expect(request.url.toString(), 'https://api.github.com/graphql'); + expect( + request.headers, + { + 'accept': '*/*', + 'content-type': 'application/json; charset=utf-8', + 'Authorization': 'bearer my-special-bearer-token', + }, + ); + + expect(await request.finalize().bytesToString(), + r'{"operationName":"AddStar","variables":{"nRepositories":38},"query":"mutation AddStar ($starrableId: ID!) { action: addStar(input: {starrableId: $starrableId}) {\n starrable {\n viewerHasStarred\n }\n }\n }\n"}'); + +// expect(response.errors, isNull); +// expect(response.data, isNotNull); +// final bool viewerHasStarred = +// response.data['action']['starrable']['viewerHasStarred'] as bool; +// expect(viewerHasStarred, true); + }); + }); +// }); +// +// group('upload', () { +// const String uploadMutation = r''' +// mutation($files: [Upload!]!) { +// multipleUpload(files: $files) { +// id +// filename +// mimetype +// path +// } +// } +// +// '''; +// +// setUp(() { +// mockHttpClient = MockHttpClient(); +// +// httpLink = HttpLink( +// uri: 'http://localhost:3001/graphql', httpClient: mockHttpClient); +// +// authLink = AuthLink( +// getToken: () async => 'Bearer my-special-bearer-token', +// ); +// +// link = authLink.concat(httpLink as Link); +// +// graphQLClientClient = GraphQLClient( +// cache: NormalizedInMemoryCache( +// dataIdFromObject: typenameDataIdFromObject, +// ), +// link: link, +// ); +// }); +// Future expectUploadBody(http.ByteStream bodyBytesStream, +// String boundary, List files) async { +// final Uint8List bodyBytes = await bodyBytesStream.toBytes(); +// int i = 0; +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 2)), '--'); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 70)), boundary); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 55)), +// '\r\ncontent-disposition: form-data; name="operations"\r\n\r\n'); +// expect( +// String.fromCharCodes(bodyBytes.sublist(i, i += 226)), +// r''' +//{"operationName":null,"variables":{"files":[null,null]},"query":" mutation($files: [Upload!]!) {\n multipleUpload(files: $files) {\n id\n filename\n mimetype\n path\n }\n }\n\n "} +// ''' +// .trim()); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 4)), '\r\n--'); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 70)), boundary); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 101)), +// '\r\ncontent-disposition: form-data; name="map"\r\n\r\n{"0":["variables.files.0"],"1":["variables.files.1"]}'); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 4)), '\r\n--'); +// // then random 51 chars for boundary +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 70)), boundary); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 102)), +// '\r\ncontent-type: image/jpeg\r\ncontent-disposition: form-data; name="0"; filename="sample_upload.jpg"\r\n\r\n'); +// // binary starts +// expect(await files[0].readAsBytes(), bodyBytes.sublist(i, i += 256)); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 4)), '\r\n--'); +// // then random 51 chars for boundary +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 70)), boundary); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 107)), +// '\r\ncontent-type: video/quicktime\r\ncontent-disposition: form-data; name="1"; filename="sample_upload.mov"\r\n\r\n'); +// // binary starts +// expect(await files[1].readAsBytes(), bodyBytes.sublist(i, i += 271)); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 4)), '\r\n--'); +// // then random 51 chars for boundary +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 70)), boundary); +// expect(String.fromCharCodes(bodyBytes.sublist(i, i += 4)), '--\r\n'); +// expect(bodyBytes.lengthInBytes, i); +// } +// +// test('upload success', () async { +// final List files = +// ['sample_upload.jpg', 'sample_upload.mov'] +// .map((String fileName) => join( +// dirname(Platform.script.path), +// 'test', +// fileName, +// )) +// .map((String filePath) => File(filePath)) +// .toList(); +// +// final MutationOptions _options = MutationOptions( +// document: uploadMutation, +// variables: { +// 'files': files, +// }, +// ); +// +// http.ByteStream bodyBytes; +// when(mockHttpClient.send(any)).thenAnswer((Invocation a) async { +// bodyBytes = (a.positionalArguments[0] as http.BaseRequest).finalize(); +// const String body = r''' +//{ +// "data": { +// "multipleUpload": [ +// { +// "id": "r1odc4PAz", +// "filename": "sample_upload.jpg", +// "mimetype": "image/jpeg", +// "path": "./uploads/r1odc4PAz-sample_upload.jpg" +// }, +// { +// "id": "5Ea18qlMur", +// "filename": "sample_upload.mov", +// "mimetype": "video/quicktime", +// "path": "./uploads/5Ea18qlMur-sample_upload.mov" +// } +// ] +// } +//} +// '''; +// +// final List bytes = utf8.encode(body); +// final Stream> stream = +// Stream>.fromIterable(>[bytes]); +// +// final http.StreamedResponse r = http.StreamedResponse(stream, 200); +// return r; +// }); +// +// final QueryResult r = await graphQLClientClient.mutate(_options); +// +// final http.MultipartRequest request = +// verify(mockHttpClient.send(captureAny)).captured.first +// as http.MultipartRequest; +// expect(request.method, 'post'); +// expect(request.url.toString(), 'http://localhost:3001/graphql'); +// expect(request.headers['accept'], '*/*'); +// expect( +// request.headers['Authorization'], 'Bearer my-special-bearer-token'); +// final List contentTypeStringSplit = +// request.headers['content-type'].split('; boundary='); +// expect(contentTypeStringSplit[0], 'multipart/form-data'); +// await expectUploadBody(bodyBytes, contentTypeStringSplit[1], files); +// +// expect(r.errors, isNull); +// expect(r.data, isNotNull); +// final List> multipleUpload = +// (r.data['multipleUpload'] as List) +// .cast>(); +// +// expect(multipleUpload, >[ +// { +// 'id': 'r1odc4PAz', +// 'filename': 'sample_upload.jpg', +// 'mimetype': 'image/jpeg', +// 'path': './uploads/r1odc4PAz-sample_upload.jpg' +// }, +// { +// 'id': '5Ea18qlMur', +// 'filename': 'sample_upload.mov', +// 'mimetype': 'video/quicktime', +// 'path': './uploads/5Ea18qlMur-sample_upload.mov' +// }, +// ]); +// }); }); } diff --git a/graphql_client/test/queries_examples.dart b/graphql_client/test/queries_examples.dart new file mode 100644 index 0000000..806a41e --- /dev/null +++ b/graphql_client/test/queries_examples.dart @@ -0,0 +1,499 @@ +// Copyright Thomas Hourlier. All rights reserved. +// Use of this source code is governed by a MIT-style license +// that can be found in the LICENSE file. + +import 'package:graphql_client/graphql_dsl.dart'; + +// ignore_for_file: public_member_api_docs +// ignore_for_file: slash_for_doc_comments + +const String readRepositories = r''' + query ReadRepositories($nRepositories: Int!) { + viewer { + repositories(last: $nRepositories) { + nodes { + __typename + id + name + viewerHasStarred + } + } + } + } +'''; +/** + * ************************ + * ************************ + * ************************ + * **** GQL OPERATIONS **** + * ************************ + * ************************ + * ************************ + */ + +class ReadRepositoriesQuery extends Object + with Arguments, Fields + implements GQLOperation { + ViewerResolver viewer = ViewerResolver(); + + @override + String get type => queryType; + + @override + String get name => 'ReadRepositories'; + + @override + List get fields => [viewer]; + + @override + ReadRepositoriesQuery clone() => + ReadRepositoriesQuery()..viewer = viewer.clone(); + + @override + String get args => '\$nRepositories: Int!'; +} + +const String addStar = r''' + mutation AddStar($starrableId: ID!) { + action: addStar(input: {starrableId: $starrableId}) { + starrable { + viewerHasStarred + } + } + } +'''; + +class AddStarMutation extends Object + with Arguments, Fields + implements GQLOperation { + ActionMutation action = ActionMutation(); + + @override + String get type => mutationType; + + @override + String get name => 'AddStar'; + + @override + String get args => '\$starrableId: ID!'; + + @override + List get fields => [action]; + + @override + AddStarMutation clone() => + AddStarMutation()..action = action.clone(); +} + +class AddTestCommentMutation extends Object + with Arguments, Fields + implements GQLOperation { + ActionMutation addComment = ActionMutation(); + + @override + String get type => mutationType; + + @override + String get name => 'AddTestCommentMutation'; + + @override + String get args => '\$issueId: ID!, \$body: String!'; + + @override + List get fields => [addComment]; + + @override + AddTestCommentMutation clone() => + AddTestCommentMutation()..addComment = addComment.clone(); +} + +/** + * ************************ + * ************************ + * ************************ + * ****** FRAGMENTS ******* + * ************************ + * ************************ + * ************************ + */ + +class RepositoryDescriptiveFragment + extends RepositoryDescriptiveFragmentResolver implements GQLFragment { + @override + String get onType => 'Repository'; +} + +class RepositoryIdFragment extends RepositoryIdFragmentResolver + implements GQLFragment { + @override + String get onType => 'Repository'; +} + +class GistDescriptiveFragment extends GistDescriptiveFragmentResolver + implements GQLFragment { + @override + String get onType => 'Gist'; +} + +/** + * ************************ + * ************************ + * ************************ + * ******** FIELDS ******** + * ************************ + * ************************ + * ************************ + */ + +class ActionMutation extends Object + with Fields + implements GQLField { + CommentEdgeResolver commentEdge = CommentEdgeResolver(); + + @override + String get name => 'action'; + + @override + List get fields => [commentEdge]; + + @override + ActionMutation clone() => + ActionMutation()..commentEdge = commentEdge.clone(); +} + +class CommentEdgeResolver extends Object + with Arguments, Fields + implements GQLField { + ViewerHasStarredResolver viewerHasStarred = ViewerHasStarredResolver(); + + @override + String get args => r'input: {starrableId: $starrableId}'; + + @override + String get name => 'starrable'; + + @override + List get fields => [viewerHasStarred]; + + @override + CommentEdgeResolver clone() => CommentEdgeResolver() + ..viewerHasStarred = viewerHasStarred.clone(); +} + +class NodeResolver extends Object with Fields, Alias implements GQLField { + BodyResolver body = BodyResolver(); + + @override + String get name => 'node'; + + @override + List get fields => [body]; + + @override + NodeResolver clone() => NodeResolver() + ..aliasId = aliasId + ..body = body.clone(); +} + +class ViewerResolver extends Object with Fields implements GQLField { + RepositoriesResolver repositories = RepositoriesResolver(); + + @override + String get name => 'viewer'; + + @override + List get fields => [repositories]; + + @override + ViewerResolver clone() => + ViewerResolver()..repositories = repositories.clone(); +} + +class NodesResolver extends Object with Fields implements GQLField { + RepoTypeNameResolver repoTypeName = RepoTypeNameResolver(); + RepoNameResolver repoName = RepoNameResolver(); + RepoIdResolver repoId = RepoIdResolver(); + RepoViewerHasStarredResolver viewerHasStarred = + RepoViewerHasStarredResolver(); + + @override + String get name => 'nodes'; + + @override + List get fields => + [repoTypeName, repoId, repoName, viewerHasStarred]; + + @override + NodesResolver clone() => NodesResolver()..repoName = repoName.clone(); +} + +class RepositoryResolver extends Object + with Arguments, Fields + implements + RepositoryDescriptiveFragmentResolver, + RepositoryIdFragmentResolver, + GQLField { + final RepositoryDescriptiveFragment _descriptiveRepositoryFragment = + RepositoryDescriptiveFragment(); + final RepositoryIdFragment _idRepositoryFragment = RepositoryIdFragment(); + + CreatedAtResolver createdAt = CreatedAtResolver(); + + @override + DescriptionResolver description; + + @override + RepoNameResolver repoName; + + @override + IdResolver id; + + RepositoryResolver() { + description = _descriptiveRepositoryFragment.description; + repoName = _descriptiveRepositoryFragment.repoName; + id = _idRepositoryFragment.id; + } + + @override + String get name => 'repository'; + + @override + String get args => 'name: "graphql_client"'; + + @override + List get fields => [createdAt]; + + RepositoryResolver clone() => RepositoryResolver() + ..description = description.clone() + ..repoName = repoName.clone() + ..id = id.clone() + ..createdAt = createdAt.clone(); +} + +class GistResolver extends Object + with Arguments, Alias, Fields, Fragments + implements GistDescriptiveFragmentResolver, GQLField { + final GistDescriptiveFragment _descriptiveGistFragment = + GistDescriptiveFragment(); + + @override + DescriptionResolver description; + + GistResolver() { + description = _descriptiveGistFragment.description; + } + + @override + String get name => 'gist'; + + @override + String get args => 'name: "e675723fc16a5b9bd4d1"'; + + @override + List get fragments => [_descriptiveGistFragment]; + + @override + GistResolver clone() => GistResolver() + ..aliasId = aliasId + ..description = description.clone(); +} + +class GistDescriptiveFragmentResolver extends Object + with Fields + implements GQLField { + DescriptionResolver description = DescriptionResolver(); + + @override + String get name => 'GistDescriptiveFragment'; + + @override + List get fields => [description]; + + @override + GistDescriptiveFragmentResolver clone() => + GistDescriptiveFragmentResolver()..description = description.clone(); +} + +class RepositoryDescriptiveFragmentResolver extends Object + with Fields + implements GQLField { + DescriptionResolver description = DescriptionResolver(); + RepoNameResolver repoName = RepoNameResolver(); + + @override + String get name => 'RepositoryDescriptiveFragment'; + + @override + List get fields => [description, repoName]; + + @override + RepositoryDescriptiveFragmentResolver clone() => + RepositoryDescriptiveFragmentResolver() + ..description = description.clone() + ..repoName = repoName.clone(); +} + +class RepositoryIdFragmentResolver extends Object + with Fields + implements GQLField { + IdResolver id = IdResolver(); + + @override + String get name => 'RepositoryIdFragment'; + + @override + List get fields => [id]; + + @override + RepositoryIdFragmentResolver clone() => + RepositoryIdFragmentResolver()..id = id.clone(); +} + +/** + * ***************************** + * ***************************** + * ***************************** + * *** COLLECTIONS RESOLVERS *** + * ***************************** + * ***************************** + * ***************************** + */ + +class RepositoriesResolver extends Object + with Arguments, ScalarCollection, Fields + implements GQLField { + @override + NodesResolver nodesResolver = NodesResolver(); + + @override + String get name => 'repositories'; + + @override + String get args => r'last: $nRepositories'; + + @override + List get fields => [nodesResolver]; + + @override + RepositoriesResolver clone() => + RepositoriesResolver()..nodesResolver = nodesResolver.clone(); +} + +/** + * ************************ + * ************************ + * ************************ + * *** SCALAR RESOLVERS *** + * ************************ + * ************************ + * ************************ + */ + +class LoginResolver extends Object + with Scalar, Alias, Directives + implements GQLField { + @override + String get name => 'login'; + + @override + String get directive => includeDirective; + + @override + String get directiveValue => 'false'; + + @override + LoginResolver clone() => LoginResolver()..aliasId = aliasId; +} + +class ViewerHasStarredResolver extends Object + with Scalar + implements GQLField { + @override + String get name => 'viewerHasStarred'; + + @override + ViewerHasStarredResolver clone() => ViewerHasStarredResolver(); +} +class BioResolver extends Object + with Scalar, Alias + implements GQLField { + @override + String get name => 'bio'; + + @override + BioResolver clone() => BioResolver()..aliasId = aliasId; +} + +class DescriptionResolver extends Object + with Scalar, Alias + implements GQLField { + @override + String get name => 'description'; + + @override + DescriptionResolver clone() => DescriptionResolver()..aliasId = aliasId; +} + +class RepoViewerHasStarredResolver extends Object + with Scalar + implements GQLField { + @override + String get name => 'viewerHasStarred'; + + @override + RepoViewerHasStarredResolver clone() => RepoViewerHasStarredResolver(); +} + +class RepoIdResolver extends Object with Scalar implements GQLField { + @override + String get name => 'id'; + + @override + RepoIdResolver clone() => RepoIdResolver(); +} + +class RepoTypeNameResolver extends Object + with Scalar + implements GQLField { + @override + String get name => '__typename'; + + @override + RepoTypeNameResolver clone() => RepoTypeNameResolver(); +} + +class RepoNameResolver extends Object with Scalar implements GQLField { + @override + String get name => 'name'; + + @override + RepoNameResolver clone() => RepoNameResolver(); +} + +class CreatedAtResolver extends Object + with Scalar, Alias + implements GQLField { + @override + String get name => 'createdAt'; + + @override + CreatedAtResolver clone() => CreatedAtResolver()..aliasId = aliasId; +} + +class IdResolver extends Object with Scalar, Alias implements GQLField { + @override + String get name => 'id'; + + @override + IdResolver clone() => IdResolver()..aliasId = aliasId; +} + +class BodyResolver extends Object + with Scalar, Alias + implements GQLField { + @override + String get name => 'body'; + + @override + BodyResolver clone() => BodyResolver()..aliasId = aliasId; +} diff --git a/graphql_client_generator/CHANGELOG.md b/graphql_client_generator/CHANGELOG.md new file mode 100644 index 0000000..2a2d63c --- /dev/null +++ b/graphql_client_generator/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 0.0.1 + +- Initial version diff --git a/graphql_client_generator/LICENSE b/graphql_client_generator/LICENSE new file mode 100644 index 0000000..fe0c6a6 --- /dev/null +++ b/graphql_client_generator/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Thomas Hourlier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/graphql_client_generator/README.md b/graphql_client_generator/README.md index 112cd20..7e1bd7f 100644 --- a/graphql_client_generator/README.md +++ b/graphql_client_generator/README.md @@ -1,5 +1,10 @@ -# graphql_client_generator +**This package and repo is no longer maintained, please follow [graphql_client](https://github.com/zino-app/graphql-flutter) instead, which is a mono repo for following packages** -GraphQL Client generator. +| Package | Pub | +| :-------------------------------------------- | :----------------------------------------------- | +| [graphql/client.dart]([./packages/graphql](https://github.com/zino-app/graphql-flutter/tree/master/packages/graphql)) | [![version][version-badge]][package-link-client] | +| [graphql_flutter]([./packages/graphql](https://github.com/zino-app/graphql-flutter/tree/master/packages/graphql)_flutter) | [![version][version-badge]][package-link] | -Stay tuned 🎤! \ No newline at end of file +[version-badge]: https://img.shields.io/pub/v/graphql_flutter.svg +[package-link]: https://pub.dartlang.org/packages/graphql_flutter +[package-link-client]: https://pub.dartlang.org/packages/graphql/versions/1.0.1 diff --git a/graphql_client_generator/analysis_options.yaml b/graphql_client_generator/analysis_options.yaml new file mode 100644 index 0000000..3ad7840 --- /dev/null +++ b/graphql_client_generator/analysis_options.yaml @@ -0,0 +1 @@ +include: package:perfectionist/analysis_options.yaml diff --git a/graphql_client_generator/pubspec.yaml b/graphql_client_generator/pubspec.yaml index 3d7a1a2..080ba5a 100644 --- a/graphql_client_generator/pubspec.yaml +++ b/graphql_client_generator/pubspec.yaml @@ -1,14 +1,17 @@ name: graphql_client_generator description: The GraphQL client generator. -version: 0.0.0 -homepage: https://github.com/hourliert/graphql_client/graphql_client_generator -author: Thomas Hourlier +version: 0.0.1 +homepage: https://github.com/dart-graphql/graphql_client +authors: + - Thomas Hourlier + - TruongSinh Tran-Nguyen environment: - sdk: '>=1.20.1 <2.0.0' + sdk: '>=2.2.0 <3.0.0' dependencies: logging: ^0.11.3 dev_dependencies: - test: ^0.12.0 + test: ^1.5.3 + perfectionist: ^2.0.0