Skip to content

Commit a50884d

Browse files
authored
Merge pull request #8 from Instabug/dio4-support
[MOB-5941] - Upgrade Instabug Dio Interceptor to null safety
2 parents 8fcc192 + e876789 commit a50884d

File tree

6 files changed

+160
-62
lines changed

6 files changed

+160
-62
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
.dart_tool/
2525
.flutter-plugins
2626
.packages
27+
.flutter-plugins-dependencies
2728
.pub-cache/
2829
.pub/
2930
build/

analysis_options.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ linter:
5151
- always_specify_types
5252
- annotate_overrides
5353
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
54-
- avoid_as
5554
# - avoid_bool_literals_in_conditional_expressions # not yet tested
5655
# - avoid_catches_without_on_clauses # we do this commonly
5756
# - avoid_catching_errors # we do this commonly

lib/instabug_dio_interceptor.dart

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,58 @@ class InstabugDioInterceptor extends Interceptor {
66
static final Map<int, NetworkData> _requests = <int, NetworkData>{};
77

88
@override
9-
dynamic onRequest(RequestOptions options) {
10-
final NetworkData data = NetworkData();
11-
data.startTime = DateTime.now();
9+
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
10+
final NetworkData data = NetworkData(
11+
startTime: DateTime.now(),
12+
url: options.uri.toString(),
13+
method: options.method);
1214
_requests[options.hashCode] = data;
15+
handler.next(options);
1316
}
1417

1518
@override
16-
dynamic onResponse(Response response) {
17-
NetworkLogger.networkLog(_map(response));
19+
void onResponse(
20+
Response<dynamic> response, ResponseInterceptorHandler handler) {
21+
final NetworkData data = _map(response);
22+
NetworkLogger.networkLog(data);
23+
handler.next(response);
1824
}
1925

2026
@override
21-
dynamic onError(DioError err) {
22-
NetworkLogger.networkLog(_map(err.response));
27+
void onError(DioError err, ErrorInterceptorHandler handler) {
28+
if (err.response != null) {
29+
final NetworkData data = _map(err.response!);
30+
NetworkLogger.networkLog(data);
31+
}
32+
33+
handler.next(err);
2334
}
2435

2536
static NetworkData _getRequestData(int requestHashCode) {
26-
if (_requests[requestHashCode] != null) {
27-
return _requests.remove(requestHashCode);
28-
}
29-
return null;
37+
final NetworkData data = _requests[requestHashCode]!;
38+
_requests.remove(requestHashCode);
39+
return data;
3040
}
3141

32-
NetworkData _map(Response response) {
33-
final NetworkData data = _getRequestData(response.request.hashCode);
34-
data.endTime = DateTime.now();
35-
data.duration = data.endTime.millisecondsSinceEpoch -
36-
data.startTime.millisecondsSinceEpoch;
42+
NetworkData _map(Response<dynamic> response) {
43+
final NetworkData data = _getRequestData(response.requestOptions.hashCode);
3744
final Map<String, dynamic> responseHeaders = <String, dynamic>{};
38-
response.headers.forEach((name, value) => responseHeaders[name] = value);
39-
data.url = response.request.uri.toString();
40-
data.method = response.request.method;
41-
data.requestBody = response.request.data;
42-
data.requestHeaders = response.request.headers;
43-
data.contentType = response.request.contentType.toString();
44-
data.status = response.statusCode;
45-
data.responseBody = response.data;
46-
data.responseHeaders = responseHeaders;
47-
return data;
45+
response.headers
46+
.forEach((String name, dynamic value) => responseHeaders[name] = value);
47+
return data.copyWith(
48+
endTime: DateTime.now(),
49+
duration: data.endTime != null
50+
? data.endTime!.millisecondsSinceEpoch -
51+
data.startTime.millisecondsSinceEpoch
52+
: 0,
53+
url: response.requestOptions.uri.toString(),
54+
method: response.requestOptions.method,
55+
requestBody: response.requestOptions.data.toString(),
56+
requestHeaders: response.requestOptions.headers,
57+
contentType: response.requestOptions.contentType,
58+
status: response.statusCode,
59+
responseBody: response.data.toString(),
60+
responseHeaders: responseHeaders,
61+
);
4862
}
4963
}

pubspec.yaml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
name: instabug_dio_interceptor
22
description: This package is an add on to instabug_flutter. It intercepts any requests performed
33
with Dio Package and sends them to the report that will be sent to the dashboard.
4-
version: 1.0.0
4+
version: 2.0.0
55
author: Instabug <support@instabug.com>
66
homepage: https://github.com/Instabug/Instabug-Flutter#readme
77

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

1111
dependencies:
12+
dio: ^4.0.0
1213
flutter:
1314
sdk: flutter
14-
dio: ^2.1.7
15-
instabug_flutter: ^1.0.0
15+
instabug_flutter: ^10.8.0
1616
dev_dependencies:
1717
flutter_test:
1818
sdk: flutter
19-
mockito:
2019

2120
# For information on the generic Dart part of this file, see the
2221
# following page: https://dart.dev/tools/pub/pubspec

test/instabug_dio_interceptor_test.dart

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,96 @@
11
import 'package:dio/dio.dart';
2-
import 'package:mockito/mockito.dart';
3-
import 'package:instabug_dio_interceptor/instabug_dio_interceptor.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter/services.dart';
44
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:instabug_dio_interceptor/instabug_dio_interceptor.dart';
6+
import 'package:instabug_flutter/Instabug.dart';
7+
8+
import 'mock_adapter.dart';
59

10+
class MyInterceptor extends InstabugDioInterceptor {
11+
int requestCount = 0;
12+
int resposneCount = 0;
13+
int errorCount = 0;
614

7-
class MockInstabugDioInterceptor extends Mock implements InstabugDioInterceptor {}
15+
@override
16+
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
17+
requestCount++;
18+
super.onRequest(options, handler);
19+
}
20+
21+
@override
22+
void onResponse(
23+
Response<dynamic> response, ResponseInterceptorHandler handler) {
24+
resposneCount++;
25+
super.onResponse(response, handler);
26+
}
27+
28+
@override
29+
void onError(DioError err, ErrorInterceptorHandler handler) {
30+
errorCount++;
31+
super.onError(err, handler);
32+
}
33+
}
834

935
void main() {
10-
test('onResponse Test', () async {
11-
final MockInstabugDioInterceptor instabugDioInterceptor = MockInstabugDioInterceptor();
12-
final Dio dio = Dio();
36+
TestWidgetsFlutterBinding.ensureInitialized();
37+
WidgetsFlutterBinding.ensureInitialized();
38+
late Dio dio;
39+
late MyInterceptor instabugDioInterceptor;
40+
const String appToken = '068ba9a8c3615035e163dc5f829c73be';
41+
setUpAll(() async {
42+
const MethodChannel('instabug_flutter')
43+
.setMockMethodCallHandler((MethodCall methodCall) async {
44+
switch (methodCall.method) {
45+
case 'getTags':
46+
return <String>['tag1', 'tag2'];
47+
default:
48+
return null;
49+
}
50+
});
51+
});
52+
setUp(() {
53+
dio = Dio();
54+
dio.options.baseUrl = MockAdapter.mockBase;
55+
dio.httpClientAdapter = MockAdapter();
56+
final List<InvocationEvent> events = <InvocationEvent>[];
57+
instabugDioInterceptor = MyInterceptor();
1358
dio.interceptors.add(instabugDioInterceptor);
14-
when<dynamic>(instabugDioInterceptor.onRequest(any)).thenReturn(null);
15-
when<dynamic>(instabugDioInterceptor.onResponse(any)).thenReturn(null);
59+
Instabug.start(appToken, events);
60+
});
61+
62+
test('onResponse Test', () async {
1663
try {
17-
await dio.get<dynamic>("http://dummy.restapiexample.com/api/v1/employees");
18-
} on DioError { }
19-
verify<dynamic>(instabugDioInterceptor.onRequest(any));
20-
verify<dynamic>(instabugDioInterceptor.onResponse(any));
64+
await dio.get<dynamic>('/test');
65+
} on DioError {
66+
// ignor
67+
}
68+
69+
expect(instabugDioInterceptor.requestCount, 1);
70+
expect(instabugDioInterceptor.resposneCount, 1);
71+
expect(instabugDioInterceptor.errorCount, 0);
2172
});
2273

2374
test('onError Test', () async {
24-
final MockInstabugDioInterceptor instabugDioInterceptor = MockInstabugDioInterceptor();
25-
final Dio dio = Dio();
26-
dio.interceptors.add(instabugDioInterceptor);
27-
when<dynamic>(instabugDioInterceptor.onRequest(any)).thenReturn(null);
28-
when<dynamic>(instabugDioInterceptor.onError(any)).thenReturn(null);
2975
try {
30-
await dio.get<dynamic>("http://dummy.restapiexample.com/api/v1/employee");
31-
} on DioError { }
32-
verify<dynamic>(instabugDioInterceptor.onRequest(any));
33-
verify<dynamic>(instabugDioInterceptor.onError(any));
76+
await dio.get<dynamic>('/test-error');
77+
} on DioError {
78+
// ignor
79+
}
80+
81+
expect(instabugDioInterceptor.requestCount, 1);
82+
expect(instabugDioInterceptor.resposneCount, 0);
83+
expect(instabugDioInterceptor.errorCount, 1);
3484
});
3585

3686
test('Stress Test', () async {
37-
final MockInstabugDioInterceptor instabugDioInterceptor = MockInstabugDioInterceptor();
38-
final Dio dio = Dio();
39-
dio.interceptors.add(instabugDioInterceptor);
40-
when<dynamic>(instabugDioInterceptor.onRequest(any)).thenReturn(null);
41-
when<dynamic>(instabugDioInterceptor.onResponse(any)).thenReturn(null);
4287
for (int i = 0; i < 1000; i++) {
4388
try {
44-
dio.get<dynamic>("http://dummy.restapiexample.com/api/v1/employees");
45-
} on DioError { }
89+
await dio.get<dynamic>('/test');
90+
} on DioError {
91+
// ignor
92+
}
4693
}
47-
verify<dynamic>(instabugDioInterceptor.onRequest(any)).called(1000);
94+
expect(instabugDioInterceptor.requestCount, 1000);
4895
});
4996
}
50-

test/mock_adapter.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import 'dart:async';
2+
import 'dart:convert';
3+
import 'dart:typed_data';
4+
import 'package:dio/dio.dart';
5+
import 'package:dio/adapter.dart';
6+
7+
class MockAdapter extends HttpClientAdapter {
8+
static const String mockHost = 'mockserver';
9+
static const String mockBase = 'http://$mockHost';
10+
final DefaultHttpClientAdapter _adapter = DefaultHttpClientAdapter();
11+
12+
@override
13+
Future<ResponseBody> fetch(RequestOptions options,
14+
Stream<Uint8List>? requestStream, Future<dynamic>? cancelFuture) async {
15+
final Uri uri = options.uri;
16+
17+
if (uri.host == mockHost) {
18+
switch (uri.path) {
19+
case '/test':
20+
return ResponseBody.fromString(
21+
jsonEncode(<String, dynamic>{
22+
'errCode': 0,
23+
'data': <String, dynamic>{'path': uri.path}
24+
}),
25+
200,
26+
);
27+
28+
default:
29+
return ResponseBody.fromString('', 404);
30+
}
31+
}
32+
return _adapter.fetch(options, requestStream, cancelFuture);
33+
}
34+
35+
@override
36+
void close({bool force = false}) {
37+
_adapter.close(force: force);
38+
}
39+
}

0 commit comments

Comments
 (0)