diff --git a/links/gql_dio_link/lib/src/dio_link.dart b/links/gql_dio_link/lib/src/dio_link.dart index f8c5c480..a7e6c985 100644 --- a/links/gql_dio_link/lib/src/dio_link.dart +++ b/links/gql_dio_link/lib/src/dio_link.dart @@ -39,6 +39,22 @@ class DioLinkResponseContext extends ContextEntry { ]; } +/// Dio link extra context +@immutable +class DioExtraContext extends ContextEntry { + /// Custom field that will be passed to Dio request options and response + final Map<String, dynamic> extra; + + const DioExtraContext({ + this.extra = const {}, + }); + + @override + List<Object> get fieldsForEquality => [ + extra, + ]; +} + extension _CastDioResponse on dio.Response { dio.Response<T> castData<T>() => dio.Response<T>( data: data as T?, @@ -91,6 +107,7 @@ class DioLink extends Link { ...defaultHeaders, ..._getHttpLinkHeaders(request), }, + extra: _getRequestExtra(request), ); if (dioResponse.statusCode! >= 300 || @@ -130,6 +147,7 @@ class DioLink extends Link { Future<dio.Response<Map<String, dynamic>>> _executeDioRequest({ required Map<String, dynamic> body, required Map<String, String> headers, + required Map<String, dynamic>? extra, }) async { try { final res = await client.post<dynamic>( @@ -138,6 +156,7 @@ class DioLink extends Link { options: dio.Options( responseType: dio.ResponseType.json, headers: headers, + extra: extra, ), ); if (res.data is Map<String, dynamic> == false) { @@ -215,6 +234,17 @@ class DioLink extends Link { } } + Map<String, dynamic>? _getRequestExtra(Request request) { + try { + final DioExtraContext? extraContext = request.context.entry(); + return extraContext?.extra; + } catch (e) { + throw ContextReadException( + originalException: e, + ); + } + } + /// Closes the underlining Dio client void close({bool force = false}) { client.close(force: force); diff --git a/links/gql_dio_link/test/gql_dio_link_test.dart b/links/gql_dio_link/test/gql_dio_link_test.dart index bd24ad1a..b6b98c00 100644 --- a/links/gql_dio_link/test/gql_dio_link_test.dart +++ b/links/gql_dio_link/test/gql_dio_link_test.dart @@ -249,6 +249,73 @@ void main() { ).called(1); }); + test("adds extra from context", () async { + when( + client.post<dynamic>( + any, + data: anyNamed("data"), + options: anyNamed("options"), + ), + ).thenAnswer( + (invocation) { + final options = invocation.namedArguments.entries + .firstWhere((element) => element.key == Symbol("options")) + .value as dio.Options; + return Future.value( + dio.Response<Map<String, dynamic>>( + data: <String, dynamic>{ + "data": <String, dynamic>{}, + }, + statusCode: 200, + requestOptions: dio.RequestOptions( + path: path, + headers: options.headers, + extra: options.extra, + ), + ), + ); + }, + ); + + await execute( + Request( + operation: Operation( + document: parseString("query MyQuery {}"), + ), + variables: const <String, dynamic>{"i": 12}, + context: Context.fromList( + const [ + DioExtraContext( + extra: { + "foo": "bar", + }, + ), + ], + ), + ), + ).first; + + verify( + client.post<dynamic>( + any, + data: anyNamed("data"), + options: argThat( + predicate((dio.Options o) => o.extEqual(dio.Options( + responseType: dio.ResponseType.json, + headers: <String, dynamic>{ + dio.Headers.contentTypeHeader: dio.Headers.jsonContentType, + dio.Headers.acceptHeader: "*/*", + }, + extra: <String, dynamic>{ + "foo": "bar", + }, + ))), + named: "options", + ), + ), + ).called(1); + }); + test("adds default headers", () async { final client = MockDio(); final link = DioLink(