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.06k stars 241 forks source link

Assign data with null safety #665

Closed dickermoshe closed 2 weeks ago

dickermoshe commented 4 months ago

Currently there is no null checking when retrofit works on a request body. https://github.com/trevorwang/retrofit.dart/issues/662

This small refactor separates the process of setting the data variable into 3 parts

  1. Declaration: final data =
  2. Value: body.map((e) => e.toJson()).toList()
  3. Post Assignment data.removeWhere((k, v) => v == null);

This way we can assign the data value using a ternary expression:

final _data = body == null ? null : body.map((e) => e.toJson()).toList();

and also run the Post Assignment in a type safe manner:

if (_data != null) {
  data.removeWhere((k, v) => v == null);
}

This change is completely backwards compatible, using this with a required, non-nullable body results in the exactly same code.

dickermoshe commented 4 months ago

Before

Client

import 'dart:io';

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

part 'client_client.g.dart';

@RestApi()
abstract class ClientClient {
  factory ClientClient(Dio dio, {String? baseUrl}) = _ClientClient;

  /// test upload.
  ///
  /// upload test.
  @POST('/test/upload')
  Future<void> testUpload({
    @Body() File? nullableBody,
  });
}

Generated

  @override
  Future<void> testUpload({File? nullableBody}) async {
    final _extra = <String, dynamic>{};
    final queryParameters = <String, dynamic>{};
    queryParameters.removeWhere((k, v) => v == null);
    final _headers = <String, dynamic>{};
    final _data =
        Stream.fromIterable(nullableBody.readAsBytesSync().map((i) => [i])); // This should be checked for null !!
    await _dio.fetch<void>(_setStreamType<void>(Options(
      method: 'POST',
      headers: _headers,
      extra: _extra,
    )
        .compose(
          _dio.options,
          '/test/upload',
          queryParameters: queryParameters,
          data: _data,
        )
        .copyWith(
            baseUrl: _combineBaseUrls(
          _dio.options.baseUrl,
          baseUrl,
        ))));
  }

After

  @override
  Future<void> testUpload({File? requiredBody}) async {
    final _extra = <String, dynamic>{};
    final queryParameters = <String, dynamic>{};
    queryParameters.removeWhere((k, v) => v == null);
    final _headers = <String, dynamic>{};
    final _data = requiredBody == null
        ? null
        : Stream.fromIterable(requiredBody.readAsBytesSync().map((i) => [i]));
    await _dio.fetch<void>(_setStreamType<void>(Options(
      method: 'POST',
      headers: _headers,
      extra: _extra,
    )
        .compose(
          _dio.options,
          '/test/upload',
          queryParameters: queryParameters,
          data: _data,
        )
        .copyWith(
            baseUrl: _combineBaseUrls(
          _dio.options.baseUrl,
          baseUrl,
        ))));
  }
dickermoshe commented 2 weeks ago

Thanks man!

trevorwang commented 2 weeks ago

@dickermoshe

Can you fix the unit failure?

just pull the latest code and run dart run test in generator

trevorwang commented 2 weeks ago

The PR caused build failure.

dickermoshe commented 2 weeks ago

I haven't looked at this PR in a while. I'll try to look at it again today.