Skip to content

Commit 4ec48a3

Browse files
committed
Improve exceptions on link level errors
Pass original exception also if http request fails with error status. Dio throws DioError if response status is in error range so this makes it possible to have that as original error. GraphQL is agnostic about how transport is handled including errors so many server implementations return always 200 OK even for error responses instead of using status codes like 400. Still, depending on your server solution, authentication errors might be returned with error status 401 with non-graphql formatted body (e.g. plain string). So therefore there might not be any parsedResponse and the default toString of the link error gives you nothing. So this change includes toString override for the DioLinkServerException that should give more useful output out-of-the-box.
1 parent 6379be2 commit 4ec48a3

File tree

3 files changed

+78
-7
lines changed

3 files changed

+78
-7
lines changed

links/gql_dio_link/lib/src/dio_link.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,10 @@ class DioLink extends Link {
164164
? parser.parseResponse(res.data as Map<String, dynamic>)
165165
: null;
166166
throw DioLinkServerException(
167-
response: res, parsedResponse: parsedResponse);
167+
response: res,
168+
parsedResponse: parsedResponse,
169+
originalException: e
170+
);
168171
}
169172
case dio.DioErrorType.DEFAULT:
170173
default:

links/gql_dio_link/lib/src/exceptions.dart

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,24 @@ class DioLinkServerException extends ServerException {
2424
/// Response which caused the exception
2525
final dio.Response response;
2626

27-
const DioLinkServerException({
28-
@required this.response,
29-
@required Response parsedResponse,
30-
}) : super(
31-
parsedResponse: parsedResponse,
32-
);
27+
const DioLinkServerException(
28+
{@required this.response,
29+
@required Response parsedResponse,
30+
dynamic originalException})
31+
: super(
32+
parsedResponse: parsedResponse,
33+
originalException: originalException);
34+
35+
@override
36+
String toString() {
37+
final dynamic data = response.data;
38+
String plainResponse = "...";
39+
if (data is String) {
40+
plainResponse =
41+
data.length > 80 ? data.replaceRange(80, data.length, "...") : data;
42+
}
43+
return "DioLinkServerException(originalException: $originalException, status: ${response.statusCode}, response: ${parsedResponse ?? plainResponse}";
44+
}
3345
}
3446

3547
@immutable

links/gql_dio_link/test/gql_dio_link_test.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,62 @@ void main() {
483483
),
484484
),
485485
);
486+
487+
// Dio returned the failed response instead of throwing, so there is no exception
488+
expect(exception.originalException, null);
489+
});
490+
491+
test("throws DioLinkServerException for dio error response", () async {
492+
final error = dio.DioError(
493+
response:
494+
dio.Response<String>(data: "Not authenticated", statusCode: 401),
495+
type: dio.DioErrorType.RESPONSE);
496+
497+
when(
498+
client.post<dynamic>(
499+
any,
500+
data: anyNamed("data"),
501+
options: anyNamed("options"),
502+
),
503+
).thenThrow(
504+
error,
505+
);
506+
507+
DioLinkServerException exception;
508+
509+
try {
510+
await execute().first;
511+
} catch (e) {
512+
exception = e as DioLinkServerException;
513+
}
514+
515+
expect(
516+
exception,
517+
TypeMatcher<DioLinkServerException>(),
518+
);
519+
expect(
520+
exception.response.data,
521+
error.response.data,
522+
);
523+
expect(
524+
exception.response.statusCode,
525+
error.response.statusCode,
526+
);
527+
// Failed to parse non-grahpql formatted body, so therefore null
528+
expect(
529+
exception.parsedResponse,
530+
null,
531+
);
532+
533+
expect(exception.originalException != null, true);
534+
535+
final dioException = exception.originalException as dio.DioError;
536+
expect(dioException.response.data, "Not authenticated");
537+
expect(dioException.response.statusCode, 401);
538+
expect(dioException.type, dio.DioErrorType.RESPONSE);
539+
540+
expect(exception.toString(),
541+
"DioLinkServerException(originalException: DioError [DioErrorType.RESPONSE]: , status: 401, response: Not authenticated");
486542
});
487543

488544
test("throws HttpLinkServerException when no data and errors", () async {

0 commit comments

Comments
 (0)