felangel / fresh

🍋 A token refresh library for Dart.
https://github.com/felangel/fresh
359 stars 57 forks source link

[fresh_dio] Recommended way to log refresh and retry requests #93

Closed LukasMirbt closed 3 months ago

LukasMirbt commented 1 year ago

What is the recommended way to log refresh and retry requests? Or adding other kinds of interceptors to those requests.

There is currently a pitfall that causes requests to stall indefinitely when passing the same httpClient into the Fresh constructor that later has the Fresh interceptor attached to it.

Could it be a good idea to rename the parameter, adding documentation or to add an assert that the Dio instance passed in should not have a Fresh interceptor?

Kiura commented 9 months ago

@LukasMirbt did you find a way fix this?

felangel commented 3 months ago

This should be fixed by #98

LukasMirbt commented 3 months ago

98 prevents accidentally causing a loop but does not address how to log refresh and retry requests.

I currently have the following setup:

  AppClient({
    required String baseUrl,
    required TokenStorage<Tokens> tokenStorage,
    Dio? client,
    Dio? retryClient,
    TokenApi? tokenApi,
  }) {
    final loggingInterceptor = PrettyDioLogger(
      responseBody: false,
      logPrint: (object) => log(
        object.toString(),
      ),
    );

    http = client ?? Dio();
    _retryClient = retryClient ?? Dio();
    _tokenApi = tokenApi ?? TokenApi(_retryClient, AppLogging.logger);

    _fresh = Fresh(
      httpClient: _retryClient,
      tokenStorage: tokenStorage,
      tokenHeader: (tokens) => {
        'Authorization': 'Bearer ${tokens.access}',
      },
      shouldRefresh: _tokenApi.shouldRefresh,
      refreshToken: (tokens, _) async {
        Tokens refreshedTokens;

        try {
          refreshedTokens = await _tokenApi.refresh(tokens);
        } catch (error, stackTrace) {
          Error.throwWithStackTrace(
            TokenRefreshFailure(error),
            stackTrace,
          );
        }

        return refreshedTokens;
      },
    );

    final baseOptions = BaseOptions(
      validateStatus: (_) => true,
      baseUrl: baseUrl,
    );

    http.options = baseOptions;
    _retryClient.options = baseOptions;

    http
      ..interceptors.add(loggingInterceptor)
      ..interceptors.add(_fresh)
      ..addSentry();

    _retryClient
      ..interceptors.add(loggingInterceptor)
      ..addSentry();
  }

This works but it's a bit ugly. It would be ideal to be able to reuse the same Dio client for refresh/retries so the same BaseOptions and logging is applied. Do you think that makes sense/have you solved this in another way? @felangel