Skip to content

Commit 845771a

Browse files
authored
Migrate to null safety (#17)
Named arguments are non-nullable with a default instead of a null default. This means that code which was passing null through instead of omitting the argument will be broken, even in unsound mode. I don't expect any code was doing this, and this API is not one that should need to be wrapped with argument forwarding.
1 parent c140142 commit 845771a

File tree

6 files changed

+64
-80
lines changed

6 files changed

+64
-80
lines changed

.github/workflows/test-package.yml

+8-32
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
strategy:
2121
fail-fast: false
2222
matrix:
23-
sdk: [dev]
23+
sdk: [2.12.0, dev]
2424
steps:
2525
- uses: actions/checkout@v2
2626
- uses: dart-lang/[email protected]
@@ -31,14 +31,17 @@ jobs:
3131
run: dart pub get
3232
- name: Check formatting
3333
run: dart format --output=none --set-exit-if-changed .
34-
if: always() && steps.install.outcome == 'success'
34+
if: matrix.sdk == 'dev' && steps.install.outcome == 'success'
3535
- name: Analyze code
3636
run: dart analyze --fatal-infos
37-
if: always() && steps.install.outcome == 'success'
37+
if: matrix.sdk == 'dev' && steps.install.outcome == 'success'
38+
- name: Analyze code
39+
run: dart analyze --fatal-infos
40+
if: matrix.sdk != 'dev' && steps.install.outcome == 'success'
3841

3942
# Run tests on a matrix consisting of two dimensions:
4043
# 1. OS: ubuntu-latest, (macos-latest, windows-latest)
41-
# 2. release channel: dev
44+
# 2. release channel: oldest stable, dev
4245
test:
4346
needs: analyze
4447
runs-on: ${{ matrix.os }}
@@ -47,7 +50,7 @@ jobs:
4750
matrix:
4851
# Add macos-latest and/or windows-latest if relevant for this package.
4952
os: [ubuntu-latest]
50-
sdk: [dev]
53+
sdk: [2.12.0, dev]
5154
steps:
5255
- uses: actions/checkout@v2
5356
- uses: dart-lang/[email protected]
@@ -62,30 +65,3 @@ jobs:
6265
- name: Run Chrome tests
6366
run: dart test --platform chrome
6467
if: always() && steps.install.outcome == 'success'
65-
66-
# Run tests on a matrix consisting of two dimensions:
67-
# 1. OS: ubuntu-latest, (macos-latest, windows-latest)
68-
# 2. release: 2.1.0
69-
test-legacy-sdk:
70-
needs: analyze
71-
runs-on: ${{ matrix.os }}
72-
strategy:
73-
fail-fast: false
74-
matrix:
75-
# Add macos-latest and/or windows-latest if relevant for this package.
76-
os: [ubuntu-latest]
77-
sdk: [2.1.0]
78-
steps:
79-
- uses: actions/checkout@v2
80-
- uses: dart-lang/[email protected]
81-
with:
82-
sdk: ${{ matrix.sdk }}
83-
- id: install
84-
name: Install dependencies
85-
run: pub get
86-
- name: Run VM tests
87-
run: pub run test --platform vm
88-
if: always() && steps.install.outcome == 'success'
89-
- name: Run Chrome tests
90-
run: pub run test --platform chrome
91-
if: always() && steps.install.outcome == 'success'

CHANGELOG.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
## 0.1.2
1+
## 0.2.0-dev
22

3+
* Migrate to null safety.
34
* Fix a number of lints affecting package maintenance score.
4-
* Update minimum Dart SDK to `2.1.0`.
5+
* **BREAKING** `null` may not be passed for most named arguments, instead the
6+
argument must be omitted.
57

68
## 0.1.1+3
79

example/example.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'package:http_retry/http_retry.dart';
88
Future<void> main() async {
99
final client = RetryClient(http.Client());
1010
try {
11-
print(await client.read('http://example.org'));
11+
print(await client.read(Uri.http('example.org', '')));
1212
} finally {
1313
client.close();
1414
}

lib/http_retry.dart

+29-23
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'dart:math' as math;
77

88
import 'package:async/async.dart';
99
import 'package:http/http.dart';
10-
import 'package:pedantic/pedantic.dart';
1110

1211
/// An HTTP client wrapper that automatically retries failing requests.
1312
class RetryClient extends BaseClient {
@@ -21,13 +20,13 @@ class RetryClient extends BaseClient {
2120
final bool Function(BaseResponse) _when;
2221

2322
/// The callback that determines whether a request when an error is thrown.
24-
final bool Function(dynamic, StackTrace) _whenError;
23+
final bool Function(Object, StackTrace) _whenError;
2524

2625
/// The callback that determines how long to wait before retrying a request.
2726
final Duration Function(int) _delay;
2827

2928
/// The callback to call to indicate that a request is being retried.
30-
final void Function(BaseRequest, BaseResponse, int) _onRetry;
29+
final void Function(BaseRequest, BaseResponse?, int)? _onRetry;
3130

3231
/// Creates a client wrapping [_inner] that retries HTTP requests.
3332
///
@@ -50,17 +49,15 @@ class RetryClient extends BaseClient {
5049
/// error for which [whenError] returned `true`.
5150
RetryClient(
5251
this._inner, {
53-
int retries,
54-
bool Function(BaseResponse) when,
55-
bool Function(Object, StackTrace) whenError,
56-
Duration Function(int retryCount) delay,
57-
void Function(BaseRequest, BaseResponse, int retryCount) onRetry,
58-
}) : _retries = retries ?? 3,
59-
_when = when ?? ((response) => response.statusCode == 503),
60-
_whenError = whenError ?? ((_, __) => false),
61-
_delay = delay ??
62-
((retryCount) =>
63-
const Duration(milliseconds: 500) * math.pow(1.5, retryCount)),
52+
int retries = 3,
53+
bool Function(BaseResponse) when = _defaultWhen,
54+
bool Function(Object, StackTrace) whenError = _defaultWhenError,
55+
Duration Function(int retryCount) delay = _defaultDelay,
56+
void Function(BaseRequest, BaseResponse?, int retryCount)? onRetry,
57+
}) : _retries = retries,
58+
_when = when,
59+
_whenError = whenError,
60+
_delay = delay,
6461
_onRetry = onRetry {
6562
RangeError.checkNotNegative(_retries, 'retries');
6663
}
@@ -74,9 +71,9 @@ class RetryClient extends BaseClient {
7471
RetryClient.withDelays(
7572
Client inner,
7673
Iterable<Duration> delays, {
77-
bool Function(BaseResponse) when,
78-
bool Function(Object, StackTrace) whenError,
79-
void Function(BaseRequest, BaseResponse, int retryCount) onRetry,
74+
bool Function(BaseResponse) when = _defaultWhen,
75+
bool Function(Object, StackTrace) whenError = _defaultWhenError,
76+
void Function(BaseRequest, BaseResponse?, int retryCount)? onRetry,
8077
}) : this._withDelays(
8178
inner,
8279
delays.toList(),
@@ -88,9 +85,9 @@ class RetryClient extends BaseClient {
8885
RetryClient._withDelays(
8986
Client inner,
9087
List<Duration> delays, {
91-
bool Function(BaseResponse) when,
92-
bool Function(Object, StackTrace) whenError,
93-
void Function(BaseRequest, BaseResponse, int) onRetry,
88+
required bool Function(BaseResponse) when,
89+
required bool Function(Object, StackTrace) whenError,
90+
required void Function(BaseRequest, BaseResponse?, int)? onRetry,
9491
}) : this(
9592
inner,
9693
retries: delays.length,
@@ -106,7 +103,7 @@ class RetryClient extends BaseClient {
106103

107104
var i = 0;
108105
for (;;) {
109-
StreamedResponse response;
106+
StreamedResponse? response;
110107
try {
111108
response = await _inner.send(_copyRequest(request, splitter.split()));
112109
} catch (error, stackTrace) {
@@ -118,11 +115,11 @@ class RetryClient extends BaseClient {
118115

119116
// Make sure the response stream is listened to so that we don't leave
120117
// dangling connections.
121-
unawaited(response.stream.listen((_) {}).cancel()?.catchError((_) {}));
118+
_unawaited(response.stream.listen((_) {}).cancel().catchError((_) {}));
122119
}
123120

124121
await Future.delayed(_delay(i));
125-
if (_onRetry != null) _onRetry(request, response, i);
122+
_onRetry?.call(request, response, i);
126123
i++;
127124
}
128125
}
@@ -147,3 +144,12 @@ class RetryClient extends BaseClient {
147144
@override
148145
void close() => _inner.close();
149146
}
147+
148+
bool _defaultWhen(BaseResponse response) => response.statusCode == 503;
149+
150+
bool _defaultWhenError(Object error, StackTrace stackTrace) => false;
151+
152+
Duration _defaultDelay(int retryCount) =>
153+
const Duration(milliseconds: 500) * math.pow(1.5, retryCount);
154+
155+
void _unawaited(Future<void>? f) {}

pubspec.yaml

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
name: http_retry
2-
version: 0.1.2-dev
2+
version: 0.2.0-dev
33

44
description: >-
55
A wrapper for package:http clients that automatically retries requests
66
homepage: https://github.com/dart-lang/http_retry
77

88
environment:
9-
sdk: '>=2.1.0 <3.0.0'
9+
sdk: '>=2.12.0 <3.0.0'
1010

1111
dependencies:
12-
async: ^2.0.7
13-
http: '>=0.11.0 <0.13.0'
14-
pedantic: ^1.0.0
12+
async: ^2.5.0
13+
http: ^0.13.0
1514

1615
dev_dependencies:
17-
fake_async: ^1.0.0
18-
test: ^1.2.0
16+
fake_async: ^1.2.0
17+
pedantic: ^1.10.0
18+
test: ^1.16.0

test/http_retry_test.dart

+15-15
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,23 @@ void main() {
1313
test('a request has a non-503 error code', () async {
1414
final client = RetryClient(
1515
MockClient(expectAsync1((_) async => Response('', 502), count: 1)));
16-
final response = await client.get('http://example.org');
16+
final response = await client.get(Uri.http('example.org', ''));
1717
expect(response.statusCode, equals(502));
1818
});
1919

2020
test("a request doesn't match when()", () async {
2121
final client = RetryClient(
2222
MockClient(expectAsync1((_) async => Response('', 503), count: 1)),
2323
when: (_) => false);
24-
final response = await client.get('http://example.org');
24+
final response = await client.get(Uri.http('example.org', ''));
2525
expect(response.statusCode, equals(503));
2626
});
2727

2828
test('retries is 0', () async {
2929
final client = RetryClient(
3030
MockClient(expectAsync1((_) async => Response('', 503), count: 1)),
3131
retries: 0);
32-
final response = await client.get('http://example.org');
32+
final response = await client.get(Uri.http('example.org', ''));
3333
expect(response.statusCode, equals(503));
3434
});
3535
});
@@ -43,7 +43,7 @@ void main() {
4343
}, count: 2)),
4444
delay: (_) => Duration.zero);
4545

46-
final response = await client.get('http://example.org');
46+
final response = await client.get(Uri.http('example.org', ''));
4747
expect(response.statusCode, equals(200));
4848
});
4949

@@ -58,7 +58,7 @@ void main() {
5858
when: (response) => response.headers['retry'] == 'true',
5959
delay: (_) => Duration.zero);
6060

61-
final response = await client.get('http://example.org');
61+
final response = await client.get(Uri.http('example.org', ''));
6262
expect(response.headers, containsPair('retry', 'false'));
6363
expect(response.statusCode, equals(503));
6464
});
@@ -75,7 +75,7 @@ void main() {
7575
error is StateError && error.message == 'oh no',
7676
delay: (_) => Duration.zero);
7777

78-
final response = await client.get('http://example.org');
78+
final response = await client.get(Uri.http('example.org', ''));
7979
expect(response.statusCode, equals(200));
8080
});
8181

@@ -85,15 +85,15 @@ void main() {
8585
whenError: (error, _) => error == 'oh yeah',
8686
delay: (_) => Duration.zero);
8787

88-
expect(client.get('http://example.org'),
88+
expect(client.get(Uri.http('example.org', '')),
8989
throwsA(isStateError.having((e) => e.message, 'message', 'oh no')));
9090
});
9191

9292
test('retries three times by default', () async {
9393
final client = RetryClient(
9494
MockClient(expectAsync1((_) async => Response('', 503), count: 4)),
9595
delay: (_) => Duration.zero);
96-
final response = await client.get('http://example.org');
96+
final response = await client.get(Uri.http('example.org', ''));
9797
expect(response.statusCode, equals(503));
9898
});
9999

@@ -102,7 +102,7 @@ void main() {
102102
MockClient(expectAsync1((_) async => Response('', 503), count: 13)),
103103
retries: 12,
104104
delay: (_) => Duration.zero);
105-
final response = await client.get('http://example.org');
105+
final response = await client.get(Uri.http('example.org', ''));
106106
expect(response.statusCode, equals(503));
107107
});
108108

@@ -124,7 +124,7 @@ void main() {
124124
return Response('', 503);
125125
}, count: 4)));
126126

127-
expect(client.get('http://example.org'), completes);
127+
expect(client.get(Uri.http('example.org', '')), completes);
128128
fake.elapse(const Duration(minutes: 10));
129129
});
130130
});
@@ -149,7 +149,7 @@ void main() {
149149
}, count: 4)),
150150
delay: (requestCount) => Duration(seconds: requestCount));
151151

152-
expect(client.get('http://example.org'), completes);
152+
expect(client.get(Uri.http('example.org', '')), completes);
153153
fake.elapse(const Duration(minutes: 10));
154154
});
155155
});
@@ -178,7 +178,7 @@ void main() {
178178
Duration(seconds: 12)
179179
]);
180180

181-
expect(client.get('http://example.org'), completes);
181+
expect(client.get(Uri.http('example.org', '')), completes);
182182
fake.elapse(const Duration(minutes: 10));
183183
});
184184
});
@@ -190,12 +190,12 @@ void main() {
190190
retries: 2,
191191
delay: (_) => Duration.zero,
192192
onRetry: expectAsync3((request, response, retryCount) {
193-
expect(request.url, equals(Uri.parse('http://example.org')));
194-
expect(response.statusCode, equals(503));
193+
expect(request.url, equals(Uri.http('example.org', '')));
194+
expect(response?.statusCode, equals(503));
195195
expect(retryCount, equals(count));
196196
count++;
197197
}, count: 2));
198-
final response = await client.get('http://example.org');
198+
final response = await client.get(Uri.http('example.org', ''));
199199
expect(response.statusCode, equals(503));
200200
});
201201

0 commit comments

Comments
 (0)