gql-dart / gql

Libraries supporting GraphQL in Dart
MIT License
267 stars 121 forks source link

[Question] Dio interceptor with retry not triggered when timeout() is invoked #442

Open adummy832 opened 7 months ago

adummy832 commented 7 months ago

Hello, @knaeckeKami, i have this very basic app that uses dio and gql. My problem is that the interceptor for retry is not being triggered.

I just wanted to ask if you have any idea of what am i doing wrong? Also, would greatly appreciate if you could point me into some examples on how to properly implement this Retry? Thanks!

Please refer to the code below.

import 'dart:async';

import 'package:dio/dio.dart';
import 'package:dio_smart_retry/dio_smart_retry.dart';
import 'package:ferry/ferry.dart';
import 'package:gql_dio_link/gql_dio_link.dart';

class DioClientManager {
  late final Dio _dio;
  late final Link _dioLink;

  DioClientManager() {
    const baseUrl = 'https://randomuser.me/api/?results=20&gender=female,male';

    final options = BaseOptions(
      baseUrl: baseUrl,
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
    );

    _dio = Dio(options);

    _dio.interceptors.add(
      RetryInterceptor(
        dio: _dio,
        logPrint: print,
        retryEvaluator: (error, attempt) {
          /// Error/exception not being triggered ???
          /// Need help !!!

          print('$error');
          print('${error.message}');

          return true;
        },
      ),
    );

    _dioLink = Link.from([
      DioLink(baseUrl, client: _dio),
    ]);
  }

  Dio get client => _dio;

  Link get link => _dioLink;
}

class FerryClient extends Client {
  FerryClient({
    required Link link,
    required Cache cache,
  }) : super(link: link, cache: cache);

  @override
  Stream<OperationResponse<TData, TVars>> request<TData, TVars>(
    OperationRequest<TData, TVars> request, [
    NextTypedLink<TData, TVars>? forward,
  ]) async* {
    /// Set a faster duration, must handled on Retry Interceptor ???
    /// Need help !!!

    const duration = Duration(milliseconds: 100);

    final stream = super.request(request, forward).timeout(
      duration,
      onTimeout: (EventSink<OperationResponse<TData, TVars>> sink) {
        // Return error on timeout
        final error = DioException(
          requestOptions: RequestOptions(),
          message: 'My beautiful error message',
        );

        sink.addError(error);
        sink.close();
      },
    );

    yield* stream;
  }
}

void main() {
  final dioClientMgr = DioClientManager();
  final ferryClient = FerryClient(
    link: dioClientMgr.link,
    cache: Cache(),
  );

  /// Set some random request object
  final req = OperationRequest(...);
  ferryClient.request(req);
}
knaeckeKami commented 7 months ago

you need to listen to the request to execute it

and you can use the error link for single retries: https://pub.dev/packages/gql_error_link

adummy832 commented 7 months ago

@knaeckeKami, I updated my code by modifying the dioLink and removing the interceptor but sadly it still does not work. Am i missing something here? Really need your guidance. Thanks man.

/// dioLink
_dioLink = Link.from([
  ErrorLink(
    onException: (request, forward, exception) async* {
      /// Still does not print ???
      /// Need help !!!
      print('VERIFY_PRINT: 1');

      if (exception is DioLinkTimeoutException) {
        print('VERIFY_PRINT: 2');
        yield* forward(request);
        return;
      }

      throw exception;
    },
  ),
  DioLink(
    baseUrl,
    client: _dio,
  ),
]);

/// Request onTimeout function body
final requestOptions = RequestOptions();
final error = DioLinkTimeoutException(
  type: DioExceptionType.connectionTimeout,
  originalException: DioException.connectionTimeout(
    timeout: duration,
    requestOptions: requestOptions,
  ),
  originalStackTrace: null,
);

sink.addError(error);
sink.close();

/// Client
final req = OperationRequest(...);
final response = ferryClient.request(req).first;
/// TODO: Check response
knaeckeKami commented 7 months ago

please share a minimal reproducible example of what you're trying to do

adummy832 commented 7 months ago

Hello, @knaeckeKami I created a repository for this alongside with the configs that I have. Hoping you could check this one out. Thanks man.

Repo: https://github.com/adummy832/sample_v2

knaeckeKami commented 7 months ago

Yeah, that's not going to work. Generally, don't extend the client class.

If you add the error in a subclass of the client after the request() method, the Dio links have no chance of handling it, they already ran at this point.

If you want to test timeout, you can replace the diolink with a custom link that just throws a timeout error for every request.