trevorwang / retrofit.dart

retrofit.dart is an dio client generator using source_gen and inspired by Chopper and Retrofit.
https://mings.in/retrofit.dart/
MIT License
1.08k stars 246 forks source link

feat(generator): Add ParseErrorLogger #680

Closed Sadhorsephile closed 3 months ago

Sadhorsephile commented 4 months ago

Problem

Sometimes, the back-end changes the response structure unexpectedly. For example, our app expects this:

 {
    "id": "0",
    "name": "Name"
 }

but we get this instead:

{
    "id": 0,
    "name": "Name"
 }

Logging these discrepancies would be really useful. To make the logs as helpful as possible, we need to capture:

The most logical place for such logging is within the client.

Solution

Let's introduce a new entity - ParseErrorLogger :

import 'package:dio/dio.dart';

/// Base class for logging errors that occur during parsing of response data.
abstract class ParseErrorLogger {
  /// Logs an error that occurred during parsing of response data.
  /// 
  /// - [error] is the error that occurred.
  /// - [stackTrace] is the stack trace of the error.
  /// - [options] are the options that were used to make the request.
  void logError(Object error, StackTrace stackTrace, RequestOptions options);
}

This entity can be injected into our client:

import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';

part 'rest_client.g.dart';

@RestApi()
abstract class RestClient {
  /// API creation factory using [Dio].
  factory RestClient(
    Dio dio, {
    String baseUrl,
    ParseErrorLogger? errorLogger,
  }) = _RestClient;

  @GET('')
  Future<SomeDto> someRequest();
}

And this entity will be called if parsing fails.

For example, this is rest_client.g.dart. As shown in the After example, errorLogger is called if there are any issues with parsing:

Before After
```dart @override Future someRequest() async { const _extra = {}; final queryParameters = {}; final _headers = {}; final Map? _data = null; final _result = await _dio.fetch>(_setStreamType(Options( method: 'GET', headers: _headers, extra: _extra, ) .compose( _dio.options, '', queryParameters: queryParameters, data: _data, ) .copyWith( baseUrl: _combineBaseUrls( _dio.options.baseUrl, baseUrl, )))); final value = SomeDto.fromJson(_result.data!); return value; } ``` ```dart @override Future someRequest() async { final _extra = {}; final queryParameters = {}; final _headers = {}; const Map? _data = null; final options = _setStreamType(Options( method: 'GET', headers: _headers, extra: _extra, ) .compose( _dio.options, '', queryParameters: queryParameters, data: _data, ) .copyWith( baseUrl: _combineBaseUrls( _dio.options.baseUrl, baseUrl, ))); final _result = await _dio.fetch>(options); late SomeDto value; try { value = SomeDto.fromJson(_result.data!); } on Object catch (e, s) { errorLogger?.logError(e, s, options); rethrow; } return value; } ```
trevorwang commented 3 months ago

please fix the unit test failure

trevorwang commented 3 months ago

/reopen