retrofit.dart icon indicating copy to clipboard operation
retrofit.dart copied to clipboard

Add support for generic nullable return and inner return types

Open Dennis-Mwea opened this issue 1 year ago • 2 comments

Dennis-Mwea avatar Apr 26 '23 10:04 Dennis-Mwea

Scenario- With this class:

part 'authentication_service.g.dart';

@RestApi()
abstract class AuthenticationService {
  factory AuthenticationService(Dio dio, {String baseUrl}) = _AuthenticationService;

  @POST('/set-phone-number')
  Future<BaseResponse<UserResponse?>> setPhoneNumber(@Body() Map<String, String> data);
}

I'd expect the output to be:

class _AuthenticationService implements AuthenticationService {
  _AuthenticationService(
    this._dio, {
    this.baseUrl,
  });

  final Dio _dio;

  String? baseUrl;

  @override
  Future<BaseResponse<UserResponse?>> setPhoneNumber(
      Map<String, String> data) async {
    const _extra = <String, dynamic>{};
    final queryParameters = <String, dynamic>{};
    final _headers = <String, dynamic>{};
    final _data = <String, dynamic>{};
    _data.addAll(data);
    final _result = await _dio.fetch<Map<String, dynamic>>(
        _setStreamType<BaseResponse<UserResponse?>>(Options(
      method: 'POST',
      headers: _headers,
      extra: _extra,
    )
            .compose(
              _dio.options,
              '/set-phone-number',
              queryParameters: queryParameters,
              data: _data,
            )
            .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl)));
    final value = BaseResponse<UserResponse?>.fromJson(_result.data!);
    return value;
  }

  RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
    if (T != dynamic &&
        !(requestOptions.responseType == ResponseType.bytes ||
            requestOptions.responseType == ResponseType.stream)) {
      if (T == String) {
        requestOptions.responseType = ResponseType.plain;
      } else {
        requestOptions.responseType = ResponseType.json;
      }
    }
    return requestOptions;
  }
}

Instead the generated file does not add the nullable UserResponse type and gives me

  @override
  Future<BaseResponse<UserResponse?>> setPhoneNumber(
      Map<String, String> data) async {
    const _extra = <String, dynamic>{};
    final queryParameters = <String, dynamic>{};
    final _headers = <String, dynamic>{};
    final _data = <String, dynamic>{};
    _data.addAll(data);
    final _result = await _dio.fetch<Map<String, dynamic>>(
        _setStreamType<BaseResponse<UserResponse>>(Options(
      method: 'POST',
      headers: _headers,
      extra: _extra,
    )
            .compose(
              _dio.options,
              '/set-phone-number',
              queryParameters: queryParameters,
              data: _data,
            )
            .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl)));
    final value = BaseResponse<UserResponse>.fromJson(_result.data!);
    return value;
  }

This PR adds support to generate the expected nullable return types.

Dennis-Mwea avatar Apr 26 '23 10:04 Dennis-Mwea

please make sure all the tests are passed.

trevorwang avatar Apr 28 '23 07:04 trevorwang