Skip to content

Commit

Permalink
feat(dio): Allow ResponseDecoder and RequestEncoder to be async (cfug…
Browse files Browse the repository at this point in the history
…#2015)

<!-- Write down your pull request descriptions. -->

### New Pull Request Checklist

- [x] I have read the
[Documentation](https://pub.dev/documentation/dio/latest/)
- [x] I have searched for a similar pull request in the
[project](https://github.com/cfug/dio/pulls) and found none
- [x] I have updated this branch with the latest `main` branch to avoid
conflicts (via merge from master or rebase)
- [x] I have added the required tests to prove the fix/feature I'm
adding
- [ ] I have updated the documentation (if necessary)
- [x] I have run the tests without failures
- [x] I have updated the `CHANGELOG.md` in the corresponding package

### Additional context and info (if any)

Gives more flexibility to the developer if they want to do some async
work in the response decoder. I don't think tests are needed for this. I
don't _think_ this is a breaking change as the return type of
`ResponseDecoder` is now `FutureOr<String?>`.

---------

Signed-off-by: Benjamin <[email protected]>
  • Loading branch information
Reprevise committed Nov 10, 2023
1 parent d427b50 commit 78f3813
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 4 deletions.
1 change: 1 addition & 0 deletions dio/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ See the [Migration Guide][] for the complete breaking changes list.**

- Raise warning for `Map`s other than `Map<String, dynamic>` when encoding request data.
- Improve exception messages
- Allow `ResponseDecoder` and `RequestEncoder` to be async

## 5.3.3

Expand Down
8 changes: 7 additions & 1 deletion dio/lib/src/dio_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,13 @@ abstract class DioMixin implements Dio {
// Call the request transformer.
final transformed = await transformer.transformRequest(options);
if (options.requestEncoder != null) {
bytes = options.requestEncoder!(transformed, options);
final encoded = options.requestEncoder!(transformed, options);

if (encoded is Future) {
bytes = await encoded;
} else {
bytes = encoded;
}
} else {
// Converts the data to UTF-8 by default.
bytes = utf8.encode(transformed);
Expand Down
6 changes: 4 additions & 2 deletions dio/lib/src/options.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:meta/meta.dart';

import 'adapter.dart';
Expand Down Expand Up @@ -76,14 +78,14 @@ enum ListFormat {
typedef ValidateStatus = bool Function(int? status);
/// The type of a response decoding callback.
typedef ResponseDecoder = String? Function(
typedef ResponseDecoder = FutureOr<String?> Function(
List<int> responseBytes,
RequestOptions options,
ResponseBody responseBody,
);
/// The type of a response encoding callback.
typedef RequestEncoder = List<int> Function(
typedef RequestEncoder = FutureOr<List<int>> Function(
String request,
RequestOptions options,
);
Expand Down
8 changes: 7 additions & 1 deletion dio/lib/src/transformers/sync_transformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,17 @@ class SyncTransformer extends Transformer {
);
final String? response;
if (options.responseDecoder != null) {
response = options.responseDecoder!(
final decodeResponse = options.responseDecoder!(
responseBytes,
options,
responseBody..stream = Stream.empty(),
);

if (decodeResponse is Future) {
response = await decodeResponse;
} else {
response = decodeResponse;
}
} else if (!isJsonContent || responseBytes.isNotEmpty) {
response = utf8.decode(responseBytes, allowMalformed: true);
} else {
Expand Down
46 changes: 46 additions & 0 deletions dio/test/options_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
@TestOn('vm')
import 'dart:convert';

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

Expand Down Expand Up @@ -399,6 +401,50 @@ void main() {
expect(response.data, null);
});

test('responseDecoder can return Future<String?>', () async {
final dio = Dio();
dio.options.responseDecoder = (_, __, ___) => Future.value('example');
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.data, 'example');
});

test('responseDecoder can return String?', () async {
final dio = Dio();
dio.options.responseDecoder = (_, __, ___) => 'example';
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.data, 'example');
});

test('requestEncoder can return Future<List<int>>', () async {
final dio = Dio();
dio.options.requestEncoder = (data, _) => Future.value(utf8.encode(data));
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.statusCode, 200);
});

test('requestEncoder can return List<int>', () async {
final dio = Dio();
dio.options.requestEncoder = (data, _) => utf8.encode(data);
dio.options.baseUrl = EchoAdapter.mockBase;
dio.httpClientAdapter = EchoAdapter();

final Response response = await dio.get('');

expect(response.statusCode, 200);
});

test('invalid response type throws exceptions', () async {
final dio = Dio(
BaseOptions(
Expand Down

0 comments on commit 78f3813

Please sign in to comment.