Skip to content

Commit

Permalink
feat(client): add error link
Browse files Browse the repository at this point in the history
Fixes #419 and #440.
  • Loading branch information
jayjun authored and micimize committed Nov 5, 2019
1 parent cbfd977 commit de9714a
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 0 deletions.
13 changes: 13 additions & 0 deletions packages/graphql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,21 @@ final MutationOptions options = MutationOptions(
// ...
```

## Links

### `ErrorLink`

Perform custom logic when a GraphQL or network error happens, such as logging or
signing out.

```dart
final ErrorLink errorLink = ErrorLink(errorHandler: (ErrorResponse response) {
Operation operation = response.operation;
FetchResult result = response.fetchResult;
OperationException exception = response.exception;
print(exception.toString());
});
```

[build-status-badge]: https://img.shields.io/circleci/build/github/zino-app/graphql-flutter.svg?style=flat-square
[build-status-link]: https://circleci.com/gh/zino-app/graphql-flutter
Expand Down
1 change: 1 addition & 0 deletions packages/graphql/lib/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export 'package:graphql/src/core/query_result.dart';
export 'package:graphql/src/exceptions/exceptions.dart';

export 'package:graphql/src/link/auth/link_auth.dart';
export 'package:graphql/src/link/error/link_error.dart';
export 'package:graphql/src/link/http/link_http.dart';
export 'package:graphql/src/link/link.dart';
export 'package:graphql/src/link/web_socket/link_web_socket.dart';
Expand Down
70 changes: 70 additions & 0 deletions packages/graphql/lib/src/link/error/link_error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'dart:async';

import 'package:graphql/src/link/link.dart';
import 'package:graphql/src/link/operation.dart';
import 'package:graphql/src/link/fetch_result.dart';
import 'package:graphql/src/exceptions/exceptions.dart';
import 'package:graphql/src/exceptions/graphql_error.dart';
import 'package:graphql/src/exceptions/operation_exception.dart';

typedef ErrorHandler = void Function(ErrorResponse);

class ErrorResponse {
ErrorResponse({
this.operation,
this.fetchResult,
this.exception,
});

Operation operation;
FetchResult fetchResult;
OperationException exception;
}

class ErrorLink extends Link {
ErrorLink({
this.errorHandler,
}) : super(
request: (Operation operation, [NextLink forward]) {
StreamController<FetchResult> controller;

Future<void> onListen() async {
Stream stream = forward(operation).map((FetchResult fetchResult) {
if (fetchResult.errors != null) {
List<GraphQLError> errors = fetchResult.errors
.map((json) => GraphQLError.fromJSON(json))
.toList();

ErrorResponse response = ErrorResponse(
operation: operation,
fetchResult: fetchResult,
exception: OperationException(graphqlErrors: errors),
);

errorHandler(response);
}
return fetchResult;
}).handleError((error) {
ErrorResponse response = ErrorResponse(
operation: operation,
exception: OperationException(
clientException: translateFailure(error),
),
);

errorHandler(response);
throw error;
});

await controller.addStream(stream);
await controller.close();
}

controller = StreamController<FetchResult>(onListen: onListen);

return controller.stream;
},
);

ErrorHandler errorHandler;
}
109 changes: 109 additions & 0 deletions packages/graphql/test/link/error/link_error_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import "dart:async";
import "dart:convert";

import 'package:graphql/src/exceptions/exceptions.dart';
import 'package:graphql/src/link/error/link_error.dart';
import 'package:graphql/src/link/http/link_http.dart';
import 'package:graphql/src/link/link.dart';
import 'package:graphql/src/link/operation.dart';
import "package:http/http.dart" as http;
import "package:mockito/mockito.dart";
import "package:test/test.dart";

class MockClient extends Mock implements http.Client {}

void main() {
group('error link', () {
MockClient client;
Operation query;
HttpLink httpLink;

setUp(() {
client = MockClient();
query = Operation(
document: 'query Operation {}',
operationName: 'Operation',
);
httpLink = HttpLink(
uri: '/graphql-test',
httpClient: client,
);
});

test('network error', () async {
bool called = false;

when(
client.send(any),
).thenAnswer(
(_) => Future.value(
http.StreamedResponse(
Stream.fromIterable(
[utf8.encode('{}')],
),
400,
),
),
);

final errorLink = ErrorLink(errorHandler: (response) {
if (response.exception.clientException != null) {
called = true;
}
});

Exception exception;

try {
await execute(
link: errorLink.concat(httpLink),
operation: query,
).first;
} on Exception catch (e) {
exception = e;
}

expect(
exception,
const TypeMatcher<ClientException>(),
);
expect(
called,
true,
);
});

test('graphql error', () async {
bool called = false;

when(
client.send(any),
).thenAnswer(
(_) => Future.value(
http.StreamedResponse(
Stream.fromIterable(
[utf8.encode('{"errors":[{"message":"error"}]}')],
),
200,
),
),
);

final errorLink = ErrorLink(errorHandler: (response) {
if (response.exception.graphqlErrors != null) {
called = true;
}
});

await execute(
link: errorLink.concat(httpLink),
operation: query,
).first;

expect(
called,
true,
);
});
});
}

0 comments on commit de9714a

Please sign in to comment.