Skip to content

Commit e8603bb

Browse files
authored
Add override captureFailedRequests option (#1931)
1 parent fb06db4 commit e8603bb

9 files changed

+195
-19
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## Unreleased
44

5+
## Features
6+
7+
- Add override `captureFailedRequests` option ([#1931](https://github.com/getsentry/sentry-dart/pull/1931))
8+
- The `dio` integration and `SentryHttpClient` now take an additional `captureFailedRequests` option.
9+
- This is useful if you want to disable this option on native and only enable it on `dio` for example.
10+
511
### Dependencies
612

713
- Bump Android SDK from v7.5.0 to v7.6.0 ([#1927](https://github.com/getsentry/sentry-dart/pull/1927))

dart/lib/src/http_client/failed_request_client.dart

+6-3
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,18 @@ class FailedRequestClient extends BaseClient {
7373
this.failedRequestTargets = SentryHttpClient.defaultFailedRequestTargets,
7474
Client? client,
7575
Hub? hub,
76+
bool? captureFailedRequests,
7677
}) : _hub = hub ?? HubAdapter(),
77-
_client = client ?? Client() {
78-
if (_hub.options.captureFailedRequests) {
78+
_client = client ?? Client(),
79+
_captureFailedRequests = captureFailedRequests {
80+
if (captureFailedRequests ?? _hub.options.captureFailedRequests) {
7981
_hub.options.sdk.addIntegration('HTTPClientError');
8082
}
8183
}
8284

8385
final Client _client;
8486
final Hub _hub;
87+
final bool? _captureFailedRequests;
8588

8689
/// Describes which HTTP status codes should be considered as a failed
8790
/// requests.
@@ -129,7 +132,7 @@ class FailedRequestClient extends BaseClient {
129132
StackTrace? stackTrace,
130133
StreamedResponse? response,
131134
Duration duration) async {
132-
if (!_hub.options.captureFailedRequests) {
135+
if (!(_captureFailedRequests ?? _hub.options.captureFailedRequests)) {
133136
return;
134137
}
135138

dart/lib/src/http_client/sentry_http_client.dart

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ import 'failed_request_client.dart';
7373
/// Remarks:
7474
/// HTTP traffic can contain PII (personal identifiable information).
7575
/// Read more on data scrubbing [here](https://docs.sentry.io/product/data-management-settings/advanced-datascrubbing/).
76+
///
77+
/// The constructor parameter `captureFailedRequests` will override what you
78+
/// have configured in options.
7679
/// ```
7780
class SentryHttpClient extends BaseClient {
7881
static const defaultFailedRequestStatusCodes = [
@@ -86,6 +89,7 @@ class SentryHttpClient extends BaseClient {
8689
List<SentryStatusCode> failedRequestStatusCodes =
8790
defaultFailedRequestStatusCodes,
8891
List<String> failedRequestTargets = defaultFailedRequestTargets,
92+
bool? captureFailedRequests,
8993
}) {
9094
_hub = hub ?? HubAdapter();
9195

@@ -96,6 +100,7 @@ class SentryHttpClient extends BaseClient {
96100
failedRequestTargets: failedRequestTargets,
97101
hub: _hub,
98102
client: innerClient,
103+
captureFailedRequests: captureFailedRequests,
99104
);
100105

101106
if (_hub.options.isTracingEnabled()) {

dart/test/http_client/failed_request_client_test.dart

+45-9
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@ void main() {
7070
expect(eventCall.contexts.response, isNull);
7171
});
7272

73-
test('event not reported if disabled', () async {
73+
test(
74+
'exception does not gets reported if client throws but override disables capture',
75+
() async {
76+
fixture._hub.options.captureFailedRequests = true;
77+
fixture._hub.options.sendDefaultPii = true;
78+
7479
final sut = fixture.getSut(
7580
client: createThrowingClient(),
7681
captureFailedRequests: false,
@@ -84,10 +89,42 @@ void main() {
8489
expect(fixture.transport.calls, 0);
8590
});
8691

92+
test('event not reported if disabled', () async {
93+
fixture._hub.options.captureFailedRequests = false;
94+
95+
final sut = fixture.getSut(
96+
client: createThrowingClient(),
97+
);
98+
99+
await expectLater(
100+
() async => await sut.get(requestUri, headers: {'Cookie': 'foo=bar'}),
101+
throwsException,
102+
);
103+
104+
expect(fixture.transport.calls, 0);
105+
});
106+
107+
test('event reported if disabled but overridden', () async {
108+
fixture._hub.options.captureFailedRequests = false;
109+
110+
final sut = fixture.getSut(
111+
client: createThrowingClient(),
112+
captureFailedRequests: true,
113+
);
114+
115+
await expectLater(
116+
() async => await sut.get(requestUri, headers: {'Cookie': 'foo=bar'}),
117+
throwsException,
118+
);
119+
120+
expect(fixture.transport.calls, 1);
121+
});
122+
87123
test('event not reported if not within the targets', () async {
124+
fixture._hub.options.captureFailedRequests = true;
125+
88126
final sut = fixture.getSut(
89127
client: fixture.getClient(statusCode: 500),
90-
captureFailedRequests: true,
91128
failedRequestTargets: const ["myapi.com"]);
92129

93130
final response = await sut.get(requestUri);
@@ -335,17 +372,16 @@ class Fixture {
335372
List<SentryStatusCode> failedRequestStatusCodes = const [
336373
SentryStatusCode.defaultRange()
337374
],
338-
bool captureFailedRequests = true,
339375
List<String> failedRequestTargets = const [".*"],
376+
bool? captureFailedRequests,
340377
}) {
341378
final mc = client ?? getClient();
342-
_hub.options.captureFailedRequests = captureFailedRequests;
343379
return FailedRequestClient(
344-
client: mc,
345-
hub: _hub,
346-
failedRequestStatusCodes: failedRequestStatusCodes,
347-
failedRequestTargets: failedRequestTargets,
348-
);
380+
client: mc,
381+
hub: _hub,
382+
failedRequestStatusCodes: failedRequestStatusCodes,
383+
failedRequestTargets: failedRequestTargets,
384+
captureFailedRequests: captureFailedRequests);
349385
}
350386

351387
MockClient getClient(

dart/test/http_client/sentry_http_client_test.dart

+31-3
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ void main() {
3232
});
3333

3434
test('no captured event with default config', () async {
35+
fixture.hub.options.captureFailedRequests = false;
36+
3537
final sut = fixture.getSut(
3638
client: createThrowingClient(),
37-
captureFailedRequests: false,
3839
);
3940

4041
await expectLater(() async => await sut.get(requestUri), throwsException);
@@ -43,6 +44,19 @@ void main() {
4344
expect(fixture.hub.addBreadcrumbCalls.length, 1);
4445
});
4546

47+
test('captured event with override', () async {
48+
fixture.hub.options.captureFailedRequests = false;
49+
50+
final sut = fixture.getSut(
51+
client: createThrowingClient(),
52+
captureFailedRequests: true,
53+
);
54+
55+
await expectLater(() async => await sut.get(requestUri), throwsException);
56+
57+
expect(fixture.hub.captureEventCalls.length, 1);
58+
});
59+
4660
test('one captured event with when enabling $FailedRequestClient',
4761
() async {
4862
fixture.hub.options.captureFailedRequests = true;
@@ -61,6 +75,20 @@ void main() {
6175
expect(fixture.hub.addBreadcrumbCalls.length, 1);
6276
});
6377

78+
test(
79+
'no captured event with when enabling $FailedRequestClient with override',
80+
() async {
81+
fixture.hub.options.captureFailedRequests = true;
82+
final sut = fixture.getSut(
83+
client: createThrowingClient(),
84+
captureFailedRequests: false,
85+
);
86+
87+
await expectLater(() async => await sut.get(requestUri), throwsException);
88+
89+
expect(fixture.hub.captureEventCalls.length, 0);
90+
});
91+
6492
test('close does get called for user defined client', () async {
6593
final mockHub = MockHub();
6694

@@ -116,14 +144,14 @@ class Fixture {
116144
SentryHttpClient getSut({
117145
MockClient? client,
118146
List<SentryStatusCode> badStatusCodes = const [],
119-
bool captureFailedRequests = true,
147+
bool? captureFailedRequests,
120148
}) {
121149
final mc = client ?? getClient();
122-
hub.options.captureFailedRequests = captureFailedRequests;
123150
return SentryHttpClient(
124151
client: mc,
125152
hub: hub,
126153
failedRequestStatusCodes: badStatusCodes,
154+
captureFailedRequests: captureFailedRequests,
127155
);
128156
}
129157

dio/lib/src/failed_request_interceptor.dart

+6-3
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,24 @@ class FailedRequestInterceptor extends Interceptor {
1010
SentryHttpClient.defaultFailedRequestStatusCodes,
1111
List<String> failedRequestTargets =
1212
SentryHttpClient.defaultFailedRequestTargets,
13+
bool? captureFailedRequests,
1314
}) : _hub = hub ?? HubAdapter(),
1415
_failedRequestStatusCodes = failedRequestStatusCodes,
15-
_failedRequestTargets = failedRequestTargets;
16+
_failedRequestTargets = failedRequestTargets,
17+
_captureFailedRequests = captureFailedRequests;
1618

1719
final Hub _hub;
1820
final List<SentryStatusCode> _failedRequestStatusCodes;
1921
final List<String> _failedRequestTargets;
22+
final bool? _captureFailedRequests;
2023

2124
@override
2225
Future<void> onError(
2326
DioError err,
2427
ErrorInterceptorHandler handler,
2528
) async {
2629
// ignore: invalid_use_of_internal_member
27-
final captureFailedRequests = _hub.options.captureFailedRequests;
30+
final cfr = _captureFailedRequests ?? _hub.options.captureFailedRequests;
2831

2932
final containsStatusCode =
3033
_failedRequestStatusCodes.containsStatusCode(err.response?.statusCode);
@@ -33,7 +36,7 @@ class FailedRequestInterceptor extends Interceptor {
3336
err.requestOptions.path,
3437
);
3538

36-
if (captureFailedRequests && containsStatusCode && containsRequestTarget) {
39+
if (cfr && containsStatusCode && containsRequestTarget) {
3740
final mechanism = Mechanism(type: 'SentryDioClientAdapter');
3841
final throwableMechanism = ThrowableMechanism(mechanism, err);
3942

dio/lib/src/sentry_dio_extension.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,15 @@ extension SentryDioExtension on Dio {
4242
/// failedRequestTargets: ['my-api.com'],
4343
/// );
4444
/// ```
45+
///
46+
/// The captureFailedRequests argument will take precedent over options.
4547
void addSentry({
4648
Hub? hub,
4749
List<SentryStatusCode> failedRequestStatusCodes =
4850
SentryHttpClient.defaultFailedRequestStatusCodes,
4951
List<String> failedRequestTargets =
5052
SentryHttpClient.defaultFailedRequestTargets,
53+
bool? captureFailedRequests,
5154
}) {
5255
hub = hub ?? HubAdapter();
5356

@@ -71,7 +74,7 @@ extension SentryDioExtension on Dio {
7174
}
7275
options.sdk.addPackage(packageName, sdkVersion);
7376

74-
if (options.captureFailedRequests) {
77+
if (captureFailedRequests ?? options.captureFailedRequests) {
7578
// Add FailedRequestInterceptor at index 0, so it's the first interceptor.
7679
// This ensures that it is called and not skipped by any previous interceptor.
7780
interceptors.insert(

dio/test/failed_request_interceptor_test.dart

+34
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,38 @@ void main() {
5050
expect(fixture.hub.captureExceptionCalls.length, 0);
5151
});
5252

53+
test('do capture if captureFailedRequests override is true', () async {
54+
final requestOptions = RequestOptions(path: 'https://example.com');
55+
final error = DioError(
56+
requestOptions: requestOptions,
57+
response: Response(statusCode: 500, requestOptions: requestOptions),
58+
);
59+
60+
fixture.hub.options.captureFailedRequests = false;
61+
62+
final sut = fixture.getSut(captureFailedRequests: true);
63+
await sut.onError(error, fixture.errorInterceptorHandler);
64+
65+
expect(fixture.errorInterceptorHandler.nextWasCalled, true);
66+
expect(fixture.hub.captureExceptionCalls.length, 1);
67+
});
68+
69+
test('do not capture if captureFailedRequests override false', () async {
70+
final requestOptions = RequestOptions(path: 'https://example.com');
71+
final error = DioError(
72+
requestOptions: requestOptions,
73+
response: Response(statusCode: 500, requestOptions: requestOptions),
74+
);
75+
76+
fixture.hub.options.captureFailedRequests = true;
77+
78+
final sut = fixture.getSut(captureFailedRequests: false);
79+
await sut.onError(error, fixture.errorInterceptorHandler);
80+
81+
expect(fixture.errorInterceptorHandler.nextWasCalled, true);
82+
expect(fixture.hub.captureExceptionCalls.length, 0);
83+
});
84+
5385
test('capture in range failedRequestStatusCodes', () async {
5486
final requestOptions = RequestOptions(path: 'https://example.com');
5587
final error = DioError(
@@ -116,11 +148,13 @@ class Fixture {
116148
SentryStatusCode.defaultRange(),
117149
],
118150
List<String> failedRequestTargets = const ['.*'],
151+
bool? captureFailedRequests,
119152
}) {
120153
return FailedRequestInterceptor(
121154
hub: hub,
122155
failedRequestStatusCodes: failedRequestStatusCodes,
123156
failedRequestTargets: failedRequestTargets,
157+
captureFailedRequests: captureFailedRequests,
124158
);
125159
}
126160
}

0 commit comments

Comments
 (0)