Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MultipartFileRecreatable.fromBytes + plugin improvements #29

Merged
merged 6 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .idea/dio_smart_retry.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 6.0.0
- Updated internal libraries.
- Bumped minimum Dart SDK to 3.0.
- Added `MultipartFileRecreatable` documentation.
- Refactors static constructors to factories.
- Adds a new `MultipartFileRecreatable.fromBytes` factory compatible with web.
- Added a new `headers` parameter.
- You can now read the file's content with `MultipartFileRecreatable.data`.
- **Breaking:** `MultipartFileRecreatable.filename` is now a named parameter to match `dio`.
- **Breaking:** Removed `MultipartFileRecreatable.filePath` since it was not being used internally.

## 5.0.0
- Add supporting of the new dio 5.+

Expand Down
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,30 @@ final formData =
data: formData,
);
```
See the full example in the test: https://github.com/rodion-m/dio_smart_retry/blob/63a3bddae8b5a0581c35c4ae5e973996561d9100/test/multipart_retry_tests.dart#L32-L61
See the full example in the test: https://github.com/rodion-m/dio_smart_retry/blob/63a3bddae8b5a0581c35c4ae5e973996561d9100/test/multipart_retry_tests.dart#L32-L61

## Migrating to 6.0

Version 6.0 introduces 2 breaking changes:
- `MultipartFileRecreatable.filename` is now a named parameter
- `MultipartFileRecreatable.filePath` is now removed

To update to the latest version, if you were using the `MultipartFileRecreatable` constructor, remove the `filePath` parameter and change `filename` to a named parameter:

Old:
```dart
return MultipartFileRecreatable(
stream,
length,
filename,
filePath,
);
```
New:
```dart
return MultipartFileRecreatable(
stream,
length,
filename: filename,
);
```
8 changes: 4 additions & 4 deletions lib/src/default_retry_evaluator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ class DefaultRetryEvaluator {
/// Returns true only if the response hasn't been cancelled
/// or got a bad status code.
// ignore: avoid-unused-parameters
FutureOr<bool> evaluate(DioError error, int attempt) {
FutureOr<bool> evaluate(DioException error, int attempt) {
bool shouldRetry;
if (error.type == DioErrorType.badResponse) {
if (error.type == DioExceptionType.badResponse) {
final statusCode = error.response?.statusCode;
if (statusCode != null) {
shouldRetry = isRetryable(statusCode);
} else {
shouldRetry = true;
}
} else {
shouldRetry =
error.type != DioErrorType.cancel && error.error is! FormatException;
shouldRetry = error.type != DioExceptionType.cancel &&
error.error is! FormatException;
}
currentAttempt = attempt;
return shouldRetry;
Expand Down
54 changes: 40 additions & 14 deletions lib/src/multipart_file_recreatable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,39 @@ import 'package:dio/dio.dart';
import 'package:http_parser/http_parser.dart';
import 'package:path/path.dart' as p;

/// Creates an instance of [MultipartFile] that can be recreated and reused.
class MultipartFileRecreatable extends MultipartFile {
/// Default constructor.
MultipartFileRecreatable(
Stream<List<int>> stream,
int length,
super.stream,
super.length, {
super.filename,
super.contentType,
super.headers,
}) : data = stream;

/// Creates a [MultipartFileRecreatable] object with [bytes].
factory MultipartFileRecreatable.fromBytes(
List<int> bytes, {
String? filename,
this.filePath, {
MediaType? contentType,
}) : super(stream, length, filename: filename, contentType: contentType);
final String filePath;
Map<String, List<String>>? headers,
}) {
return MultipartFileRecreatable(
Stream.fromIterable(<List<int>>[bytes]),
bytes.length,
filename: filename,
contentType: contentType,
headers: headers,
);
}

// ignore: prefer_constructors_over_static_methods
static MultipartFileRecreatable fromFileSync(
/// Creates a [MultipartFileRecreatable] object from a [File] in [filePath].
factory MultipartFileRecreatable.fromFileSync(
String filePath, {
String? filename,
MediaType? contentType,
Map<String, List<String>>? headers,
}) {
filename ??= p.basename(filePath);
final file = File(filePath);
Expand All @@ -27,15 +45,23 @@ class MultipartFileRecreatable extends MultipartFile {
return MultipartFileRecreatable(
stream,
length,
filename,
filePath,
filename: filename,
contentType: contentType,
headers: headers,
);
}

MultipartFileRecreatable recreate() => fromFileSync(
filePath,
filename: filename,
contentType: contentType,
);
/// The stream that will emit the file's contents.
final Stream<List<int>> data;

/// Recreates the [MultipartFileRecreatable] object.
MultipartFileRecreatable recreate() {
return MultipartFileRecreatable(
data,
length,
filename: filename,
contentType: contentType,
headers: headers,
);
}
}
21 changes: 14 additions & 7 deletions lib/src/retry_interceptor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import 'package:dio_smart_retry/src/http_status_codes.dart';
import 'package:dio_smart_retry/src/multipart_file_recreatable.dart';
import 'package:dio_smart_retry/src/retry_not_supported_exception.dart';

typedef RetryEvaluator = FutureOr<bool> Function(DioError error, int attempt);
typedef RetryEvaluator = FutureOr<bool> Function(
DioException error,
int attempt,
);

/// An interceptor that will try to send failed request again
class RetryInterceptor extends Interceptor {
Expand Down Expand Up @@ -35,7 +38,7 @@ class RetryInterceptor extends Interceptor {
'retryableExtraStatuses',
);
}
if(retries < 0) {
if (retries < 0) {
throw ArgumentError(
'[retries] cannot be less than 0',
'retries',
Expand Down Expand Up @@ -82,11 +85,11 @@ class RetryInterceptor extends Interceptor {

/// Redirects to [DefaultRetryEvaluator.evaluate]
/// with [defaultRetryableStatuses]
static final FutureOr<bool> Function(DioError error, int attempt)
static final FutureOr<bool> Function(DioException error, int attempt)
defaultRetryEvaluator =
DefaultRetryEvaluator(defaultRetryableStatuses).evaluate;

Future<bool> _shouldRetry(DioError error, int attempt) async {
Future<bool> _shouldRetry(DioException error, int attempt) async {
try {
return await _retryEvaluator(error, attempt);
} catch (e) {
Expand All @@ -105,7 +108,10 @@ class RetryInterceptor extends Interceptor {
}

@override
Future<dynamic> onError(DioError err, ErrorInterceptorHandler handler) async {
Future<dynamic> onError(
DioException err,
ErrorInterceptorHandler handler,
) async {
if (err.requestOptions.disableRetry) {
return super.onError(err, handler);
}
Expand Down Expand Up @@ -135,7 +141,7 @@ class RetryInterceptor extends Interceptor {
requestOptions = _recreateOptions(err.requestOptions);
} on RetryNotSupportedException catch (e) {
return super.onError(
DioError(requestOptions: requestOptions, error: e),
DioException(requestOptions: requestOptions, error: e),
handler,
);
}
Expand All @@ -153,7 +159,7 @@ class RetryInterceptor extends Interceptor {
await dio
.fetch<void>(requestOptions)
.then((value) => handler.resolve(value));
} on DioError catch (e) {
} on DioException catch (e) {
super.onError(e, handler);
}
}
Expand Down Expand Up @@ -191,6 +197,7 @@ class RetryInterceptor extends Interceptor {
}

var _multipartFileChecked = false;

void _printErrorIfRequestHasMultipartFile(RequestOptions options) {
if (_multipartFileChecked) return;
if (options.data is FormData) {
Expand Down
14 changes: 7 additions & 7 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
name: dio_smart_retry
description: Retry library for Dio and Dio package made with love. By default, the request will be retried only for appropriate retryable http statuses.
version: 5.0.0
version: 6.0.0
repository: https://github.com/rodion-m/dio_smart_retry
issue_tracker: https://github.com/rodion-m/dio_smart_retry/issues
homepage: https://github.com/rodion-m/dio_smart_retry
documentation: https://github.com/rodion-m/dio_smart_retry#contents

environment:
sdk: '>=2.12.0 <3.0.0'
sdk: '>=3.0.0 <4.0.0'

dependencies:
dio: ^5.0.0
dio: ^5.3.3
http_parser: ^4.0.2
path: ^1.8.2
path: ^1.8.3

dev_dependencies:
dart_code_metrics: ^5.6.0
test: ^1.23.1
very_good_analysis: ^4.0.0+1
dart_code_metrics: ^5.7.6
test: ^1.24.9
very_good_analysis: ^5.1.0
6 changes: 3 additions & 3 deletions test/multipart_retry_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void main() {
'https://rodion-m.ru/mock/post500.php',
data: formData,
);
} on DioError catch (error) {
} on DioException catch (error) {
exception = error.error;
}

Expand Down Expand Up @@ -51,8 +51,8 @@ void main() {
'https://rodion-m.ru/mock/post500.php',
data: formData,
);
} on DioError catch (error) {
if (error.type != DioErrorType.badResponse) {
} on DioException catch (error) {
if (error.type != DioExceptionType.badResponse) {
rethrow;
}
}
Expand Down
4 changes: 2 additions & 2 deletions test/override_retryable_statuses_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ void main() {

try {
await dio.get<dynamic>('https://mock.codes/400');
} on DioError catch (error) {
if (error.type != DioErrorType.badResponse ||
} on DioException catch (error) {
if (error.type != DioExceptionType.badResponse ||
error.response?.statusCode != 400) {
rethrow;
}
Expand Down