From 6392c92f564dcb7292ece992464ae7290b1fb134 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Tue, 28 Nov 2023 15:46:25 +0100 Subject: [PATCH 1/9] add connectivity integration --- .../connectivity_integration.dart | 57 +++++++ flutter/pubspec.yaml | 1 + .../connectivity_integration_test.dart | 149 ++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 flutter/lib/src/integrations/connectivity_integration.dart create mode 100644 flutter/test/integrations/connectivity_integration_test.dart diff --git a/flutter/lib/src/integrations/connectivity_integration.dart b/flutter/lib/src/integrations/connectivity_integration.dart new file mode 100644 index 0000000000..02ad0f7664 --- /dev/null +++ b/flutter/lib/src/integrations/connectivity_integration.dart @@ -0,0 +1,57 @@ +import 'dart:async'; + +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:meta/meta.dart'; +import '../../sentry_flutter.dart'; + +class ConnectivityIntegration extends Integration { + Connectivity connectivity = Connectivity(); + Hub? _hub; + StreamSubscription? _subscription; + + @override + void call(Hub hub, SentryFlutterOptions options) { + _hub = hub; + _subscription = connectivity.onConnectivityChanged.listen(addBreadcrumb); + + options.sdk.addIntegration('connectivityIntegration'); + } + + @override + void close() { + _hub = null; + _subscription?.cancel(); + _subscription = null; + } + + @internal + @visibleForTesting + void addBreadcrumb(ConnectivityResult result) { + _hub?.addBreadcrumb( + Breadcrumb( + category: 'device.connectivity', + level: SentryLevel.info, + type: 'connectivity', + data: {'connectivity': result.toSentryConnectivity()}), + ); + } +} + +extension on ConnectivityResult { + String toSentryConnectivity() { + switch (this) { + case ConnectivityResult.bluetooth: + case ConnectivityResult.vpn: + case ConnectivityResult.wifi: + return 'wifi'; + case ConnectivityResult.ethernet: + return 'ethernet'; + case ConnectivityResult.mobile: + return 'cellular'; + case ConnectivityResult.none: + return 'none'; + case ConnectivityResult.other: + return 'other'; + } + } +} diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 5c4d529917..3411ead9ca 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: package_info_plus: '>=1.0.0 <=5.0.1' meta: ^1.3.0 ffi: ^2.0.0 + connectivity_plus: ^5.0.2 dev_dependencies: build_runner: ^2.4.2 diff --git a/flutter/test/integrations/connectivity_integration_test.dart b/flutter/test/integrations/connectivity_integration_test.dart new file mode 100644 index 0000000000..cf9179f451 --- /dev/null +++ b/flutter/test/integrations/connectivity_integration_test.dart @@ -0,0 +1,149 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:sentry/sentry.dart'; +import 'package:sentry_flutter/src/integrations/connectivity_integration.dart'; +import 'package:sentry_flutter/src/sentry_flutter_options.dart'; + +import '../mocks.dart'; +import '../mocks.mocks.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + + late Fixture fixture; + + setUp(() { + fixture = Fixture(); + }); + + verifyBreadcrumb(Breadcrumb crumb, String connectivityData) { + expect(crumb.category, 'device.connectivity'); + expect(crumb.type, 'connectivity'); + expect(crumb.level, SentryLevel.info); + expect(crumb.data?['connectivity'], connectivityData); + } + + test('adds integration', () { + final sut = fixture.getSut(); + sut(fixture.hub, fixture.options); + + expect(fixture.options.sdk.integrations.contains('connectivityIntegration'), + true); + }); + + test( + '$ConnectivityIntegration: connectivity changed `bluetooth` adds `wifi` breadcrumb', + () { + final integration = fixture.getSut(); + integration.call(fixture.hub, fixture.options); + + integration.addBreadcrumb(ConnectivityResult.bluetooth); + + final crumb = verify( + fixture.hub.addBreadcrumb(captureAny), + ).captured.first as Breadcrumb; + + verifyBreadcrumb(crumb, 'wifi'); + }); + + test( + '$ConnectivityIntegration: connectivity changed `wifi` adds `wifi` breadcrumb', + () { + final integration = fixture.getSut(); + integration.call(fixture.hub, fixture.options); + + integration.addBreadcrumb(ConnectivityResult.wifi); + + final crumb = verify( + fixture.hub.addBreadcrumb(captureAny), + ).captured.first as Breadcrumb; + + verifyBreadcrumb(crumb, 'wifi'); + }); + + test( + '$ConnectivityIntegration: connectivity changed `vpn` adds `vpn` breadcrumb', + () { + final integration = fixture.getSut(); + integration.call(fixture.hub, fixture.options); + + integration.addBreadcrumb(ConnectivityResult.vpn); + + final crumb = verify( + fixture.hub.addBreadcrumb(captureAny), + ).captured.first as Breadcrumb; + + verifyBreadcrumb(crumb, 'wifi'); + }); + + test( + '$ConnectivityIntegration: connectivity changed `ethernet` adds `ethernet` breadcrumb', + () { + final integration = fixture.getSut(); + integration.call(fixture.hub, fixture.options); + + integration.addBreadcrumb(ConnectivityResult.ethernet); + + final crumb = verify( + fixture.hub.addBreadcrumb(captureAny), + ).captured.first as Breadcrumb; + + verifyBreadcrumb(crumb, 'ethernet'); + }); + + test( + '$ConnectivityIntegration: connectivity changed `mobile` adds `cellular` breadcrumb', + () { + final integration = fixture.getSut(); + integration.call(fixture.hub, fixture.options); + + integration.addBreadcrumb(ConnectivityResult.mobile); + + final crumb = verify( + fixture.hub.addBreadcrumb(captureAny), + ).captured.first as Breadcrumb; + + verifyBreadcrumb(crumb, 'cellular'); + }); + + test( + '$ConnectivityIntegration: connectivity changed `other` adds `other` breadcrumb', + () { + final integration = fixture.getSut(); + integration.call(fixture.hub, fixture.options); + + integration.addBreadcrumb(ConnectivityResult.other); + + final crumb = verify( + fixture.hub.addBreadcrumb(captureAny), + ).captured.first as Breadcrumb; + + verifyBreadcrumb(crumb, 'other'); + }); + + test( + '$ConnectivityIntegration: connectivity changed `none` adds `none` breadcrumb', + () { + final integration = fixture.getSut(); + integration.call(fixture.hub, fixture.options); + + integration.addBreadcrumb(ConnectivityResult.none); + + final crumb = verify( + fixture.hub.addBreadcrumb(captureAny), + ).captured.first as Breadcrumb; + + verifyBreadcrumb(crumb, 'none'); + }); +} + +class Fixture { + final hub = MockHub(); + final options = SentryFlutterOptions(dsn: fakeDsn); + + ConnectivityIntegration getSut() { + return ConnectivityIntegration(); + } +} From 2b3acd0d4e0783afd51c162250329334a5f32f4b Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Tue, 28 Nov 2023 15:53:08 +0100 Subject: [PATCH 2/9] Add connectivity integration in options --- flutter/lib/src/sentry_flutter.dart | 5 ++++ flutter/test/sentry_flutter_options_test.dart | 2 +- flutter/test/sentry_flutter_test.dart | 29 ++++++++++++++++--- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index cef52ee564..ee22a61a7d 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -9,6 +9,7 @@ import '../sentry_flutter.dart'; import 'event_processor/android_platform_exception_event_processor.dart'; import 'event_processor/flutter_exception_event_processor.dart'; import 'event_processor/platform_exception_event_processor.dart'; +import 'integrations/connectivity_integration.dart'; import 'integrations/screenshot_integration.dart'; import 'native/factory.dart'; import 'native/native_scope_observer.dart'; @@ -171,6 +172,10 @@ mixin SentryFlutter { integrations.add(ScreenshotIntegration()); } + if (!platform.isIOS && !platform.isMacOS || !platform.isAndroid) { + integrations.add(ConnectivityIntegration()); + } + // works with Skia, CanvasKit and HTML renderer integrations.add(SentryViewHierarchyIntegration()); diff --git a/flutter/test/sentry_flutter_options_test.dart b/flutter/test/sentry_flutter_options_test.dart index a6d871318b..ed4a3ea7f7 100644 --- a/flutter/test/sentry_flutter_options_test.dart +++ b/flutter/test/sentry_flutter_options_test.dart @@ -32,7 +32,7 @@ void main() { expect(options.enableAutoNativeBreadcrumbs, isFalse); }); - testWidgets('useFlutterBreadcrumbTracking', (WidgetTester tester) async { + testWidgets('useNativeBreadcrumbTracking', (WidgetTester tester) async { final options = SentryFlutterOptions(); options.useNativeBreadcrumbTracking(); diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 87e4f46e84..8c80feb8b9 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:sentry_flutter/src/integrations/connectivity_integration.dart'; import 'package:sentry_flutter/src/integrations/integrations.dart'; import 'package:sentry_flutter/src/integrations/screenshot_integration.dart'; import 'package:sentry_flutter/src/profiling.dart'; @@ -43,6 +44,11 @@ final nativeIntegrations = [ NativeSdkIntegration, ]; +// These should be added to every platform except Android & iOS/macOS. +final nonMobileIntegrations = [ + ConnectivityIntegration, +]; + void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -88,7 +94,11 @@ void main() { ...platformAgnosticIntegrations, ...nonWebIntegrations, ], - shouldNotHaveIntegrations: iOsAndMacOsIntegrations); + shouldNotHaveIntegrations: [ + ...iOsAndMacOsIntegrations, + ...nonWebIntegrations, + ], + ); integrations .indexWhere((element) => element is WidgetsFlutterBindingIntegration); @@ -138,7 +148,10 @@ void main() { ...platformAgnosticIntegrations, ...nonWebIntegrations, ], - shouldNotHaveIntegrations: androidIntegrations, + shouldNotHaveIntegrations: [ + ...androidIntegrations, + ...nonWebIntegrations, + ], ); testBefore( @@ -187,7 +200,10 @@ void main() { ...platformAgnosticIntegrations, ...nonWebIntegrations, ], - shouldNotHaveIntegrations: androidIntegrations, + shouldNotHaveIntegrations: [ + ...androidIntegrations, + ...nonWebIntegrations, + ] ); testBefore( @@ -234,6 +250,7 @@ void main() { shouldHaveIntegrations: [ ...platformAgnosticIntegrations, ...nonWebIntegrations, + ...nonMobileIntegrations, ], shouldNotHaveIntegrations: [ ...androidIntegrations, @@ -285,6 +302,7 @@ void main() { shouldHaveIntegrations: [ ...platformAgnosticIntegrations, ...nonWebIntegrations, + ...nonMobileIntegrations, ], shouldNotHaveIntegrations: [ ...androidIntegrations, @@ -336,7 +354,10 @@ void main() { testConfiguration( integrations: integrations, - shouldHaveIntegrations: platformAgnosticIntegrations, + shouldHaveIntegrations: [ + ...platformAgnosticIntegrations, + ...nonMobileIntegrations, + ], shouldNotHaveIntegrations: [ ...androidIntegrations, ...iOsAndMacOsIntegrations, From 71562541541fc453ae7278e369c4bf3a942c4db7 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 11 Dec 2023 16:23:22 +0100 Subject: [PATCH 3/9] only check web apis --- .../connectivity_integration.dart | 100 ++++++++++++++---- flutter/lib/src/sentry_flutter.dart | 2 +- .../connectivity_integration_test.dart | 96 +---------------- 3 files changed, 81 insertions(+), 117 deletions(-) diff --git a/flutter/lib/src/integrations/connectivity_integration.dart b/flutter/lib/src/integrations/connectivity_integration.dart index 02ad0f7664..14cb49d4a5 100644 --- a/flutter/lib/src/integrations/connectivity_integration.dart +++ b/flutter/lib/src/integrations/connectivity_integration.dart @@ -1,57 +1,111 @@ import 'dart:async'; +import 'dart:html' as html; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:meta/meta.dart'; import '../../sentry_flutter.dart'; class ConnectivityIntegration extends Integration { - Connectivity connectivity = Connectivity(); Hub? _hub; - StreamSubscription? _subscription; + html.NetworkInformation? _networkInformation; + String? _oldResult = 'none'; + + StreamSubscription? _networkInfoSub; + StreamSubscription? _onOnlineSub; + StreamSubscription? _onOfflineSub; @override void call(Hub hub, SentryFlutterOptions options) { _hub = hub; - _subscription = connectivity.onConnectivityChanged.listen(addBreadcrumb); + final supportsNetworkInformation = html.window.navigator.connection != null; + if (supportsNetworkInformation) { + _networkInformation = html.window.navigator.connection; + _oldResult = _networkInformation?.toConnectivityResult(); + + _networkInfoSub = _networkInformation?.onChange.listen((_) { + final newResult = _networkInformation?.toConnectivityResult(); + if (newResult != null && _oldResult != newResult) { + _oldResult = newResult; + addBreadcrumb(newResult); + } + }); + } else { + // Fallback to onLine/onOffline API + _oldResult = (html.window.navigator.onLine ?? false) ? 'wifi' : 'none'; + _onOnlineSub = html.window.onOnline.listen((_) { + addBreadcrumb('wifi'); + }); + _onOfflineSub = html.window.onOffline.listen((_) { + addBreadcrumb('none'); + }); + } options.sdk.addIntegration('connectivityIntegration'); } @override void close() { _hub = null; - _subscription?.cancel(); - _subscription = null; + + _networkInfoSub?.cancel(); + _networkInfoSub = null; + + _onOnlineSub?.cancel(); + _onOnlineSub = null; + + _onOfflineSub?.cancel(); + _onOfflineSub = null; + + _oldResult = 'none'; + _networkInformation = null; } @internal @visibleForTesting - void addBreadcrumb(ConnectivityResult result) { + void addBreadcrumb(String result) { _hub?.addBreadcrumb( Breadcrumb( category: 'device.connectivity', level: SentryLevel.info, type: 'connectivity', - data: {'connectivity': result.toSentryConnectivity()}), + data: {'connectivity': result}), ); } } -extension on ConnectivityResult { - String toSentryConnectivity() { - switch (this) { - case ConnectivityResult.bluetooth: - case ConnectivityResult.vpn: - case ConnectivityResult.wifi: - return 'wifi'; - case ConnectivityResult.ethernet: - return 'ethernet'; - case ConnectivityResult.mobile: - return 'cellular'; - case ConnectivityResult.none: - return 'none'; - case ConnectivityResult.other: - return 'other'; +// Source: https://github.com/fluttercommunity/plus_plugins/blob/258f7b8b461f6d78028354f95d24014b240a80f0/packages/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart#L8 +extension on html.NetworkInformation { + String toConnectivityResult() { + if (downlink == 0 && rtt == 0) { + return 'none'; + } + if (type != null) { + switch (type) { + case 'none': + return 'none'; + case 'bluetooth': + return 'bluetooth'; + case 'cellular': + case 'mixed': + case 'other': + case 'unknown': + return 'mobile'; + case 'ethernet': + return 'ethernet'; + default: + return 'wifi'; + } + } + if (effectiveType != null) { + switch (effectiveType) { + case 'slow-2g': + case '2g': + case '3g': + case '4g': + return 'mobile'; + default: + return 'wifi'; + } } + return 'none'; } } diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index ee22a61a7d..a6d5c42ab3 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -172,7 +172,7 @@ mixin SentryFlutter { integrations.add(ScreenshotIntegration()); } - if (!platform.isIOS && !platform.isMacOS || !platform.isAndroid) { + if (platformChecker.isWeb) { integrations.add(ConnectivityIntegration()); } diff --git a/flutter/test/integrations/connectivity_integration_test.dart b/flutter/test/integrations/connectivity_integration_test.dart index cf9179f451..a0def0fe09 100644 --- a/flutter/test/integrations/connectivity_integration_test.dart +++ b/flutter/test/integrations/connectivity_integration_test.dart @@ -34,108 +34,18 @@ void main() { }); test( - '$ConnectivityIntegration: connectivity changed `bluetooth` adds `wifi` breadcrumb', + '$ConnectivityIntegration: addsBreadcrumb', () { final integration = fixture.getSut(); integration.call(fixture.hub, fixture.options); - integration.addBreadcrumb(ConnectivityResult.bluetooth); + integration.addBreadcrumb('bluetooth'); final crumb = verify( fixture.hub.addBreadcrumb(captureAny), ).captured.first as Breadcrumb; - verifyBreadcrumb(crumb, 'wifi'); - }); - - test( - '$ConnectivityIntegration: connectivity changed `wifi` adds `wifi` breadcrumb', - () { - final integration = fixture.getSut(); - integration.call(fixture.hub, fixture.options); - - integration.addBreadcrumb(ConnectivityResult.wifi); - - final crumb = verify( - fixture.hub.addBreadcrumb(captureAny), - ).captured.first as Breadcrumb; - - verifyBreadcrumb(crumb, 'wifi'); - }); - - test( - '$ConnectivityIntegration: connectivity changed `vpn` adds `vpn` breadcrumb', - () { - final integration = fixture.getSut(); - integration.call(fixture.hub, fixture.options); - - integration.addBreadcrumb(ConnectivityResult.vpn); - - final crumb = verify( - fixture.hub.addBreadcrumb(captureAny), - ).captured.first as Breadcrumb; - - verifyBreadcrumb(crumb, 'wifi'); - }); - - test( - '$ConnectivityIntegration: connectivity changed `ethernet` adds `ethernet` breadcrumb', - () { - final integration = fixture.getSut(); - integration.call(fixture.hub, fixture.options); - - integration.addBreadcrumb(ConnectivityResult.ethernet); - - final crumb = verify( - fixture.hub.addBreadcrumb(captureAny), - ).captured.first as Breadcrumb; - - verifyBreadcrumb(crumb, 'ethernet'); - }); - - test( - '$ConnectivityIntegration: connectivity changed `mobile` adds `cellular` breadcrumb', - () { - final integration = fixture.getSut(); - integration.call(fixture.hub, fixture.options); - - integration.addBreadcrumb(ConnectivityResult.mobile); - - final crumb = verify( - fixture.hub.addBreadcrumb(captureAny), - ).captured.first as Breadcrumb; - - verifyBreadcrumb(crumb, 'cellular'); - }); - - test( - '$ConnectivityIntegration: connectivity changed `other` adds `other` breadcrumb', - () { - final integration = fixture.getSut(); - integration.call(fixture.hub, fixture.options); - - integration.addBreadcrumb(ConnectivityResult.other); - - final crumb = verify( - fixture.hub.addBreadcrumb(captureAny), - ).captured.first as Breadcrumb; - - verifyBreadcrumb(crumb, 'other'); - }); - - test( - '$ConnectivityIntegration: connectivity changed `none` adds `none` breadcrumb', - () { - final integration = fixture.getSut(); - integration.call(fixture.hub, fixture.options); - - integration.addBreadcrumb(ConnectivityResult.none); - - final crumb = verify( - fixture.hub.addBreadcrumb(captureAny), - ).captured.first as Breadcrumb; - - verifyBreadcrumb(crumb, 'none'); + verifyBreadcrumb(crumb, 'bluetooth'); }); } From d6edcbbb5ee6784d01b78bcb2aa8910b5e224ac8 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 18 Dec 2023 11:50:35 +0100 Subject: [PATCH 4/9] separate web and non web components --- .../connectivity_integration.dart | 38 +++++++++++++++++ .../connectivity/connectivity_provider.dart | 10 +++++ .../noop_connectivity_provider.dart | 19 +++++++++ .../web_connectivity_provider.dart} | 42 +++++++------------ flutter/lib/src/sentry_flutter.dart | 2 +- flutter/pubspec.yaml | 1 - .../connectivity_integration_test.dart | 2 +- flutter/test/sentry_flutter_test.dart | 2 +- 8 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 flutter/lib/src/integrations/connectivity/connectivity_integration.dart create mode 100644 flutter/lib/src/integrations/connectivity/connectivity_provider.dart create mode 100644 flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart rename flutter/lib/src/integrations/{connectivity_integration.dart => connectivity/web_connectivity_provider.dart} (74%) diff --git a/flutter/lib/src/integrations/connectivity/connectivity_integration.dart b/flutter/lib/src/integrations/connectivity/connectivity_integration.dart new file mode 100644 index 0000000000..71e425797f --- /dev/null +++ b/flutter/lib/src/integrations/connectivity/connectivity_integration.dart @@ -0,0 +1,38 @@ +import 'package:meta/meta.dart'; +import '../../../sentry_flutter.dart'; +import 'connectivity_provider.dart'; + +class ConnectivityIntegration extends Integration { + Hub? _hub; + ConnectivityProvider? _connectivityProvider; + + @override + void call(Hub hub, SentryFlutterOptions options) { + _hub = hub; + _connectivityProvider = ConnectivityProvider(); + _connectivityProvider?.listen((connectivity) { + addBreadcrumb(connectivity); + }); + options.sdk.addIntegration('connectivityIntegration'); + } + + @override + void close() { + _hub = null; + _connectivityProvider?.cancel(); + } + + @internal + @visibleForTesting + void addBreadcrumb(String connectivity) { + _hub?.addBreadcrumb( + Breadcrumb( + category: 'device.connectivity', + level: SentryLevel.info, + type: 'connectivity', + data: {'connectivity': connectivity}), + ); + } +} + + diff --git a/flutter/lib/src/integrations/connectivity/connectivity_provider.dart b/flutter/lib/src/integrations/connectivity/connectivity_provider.dart new file mode 100644 index 0000000000..15099b8e1e --- /dev/null +++ b/flutter/lib/src/integrations/connectivity/connectivity_provider.dart @@ -0,0 +1,10 @@ + +import 'noop_connectivity_provider.dart' +if (dart.library.html) 'web_connectivity_provider.dart'; + +abstract class ConnectivityProvider { + factory ConnectivityProvider() => connectivityProvider(); + + void listen(void Function(String connectivity) onChange); + void cancel(); +} diff --git a/flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart b/flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart new file mode 100644 index 0000000000..d879957e6c --- /dev/null +++ b/flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart @@ -0,0 +1,19 @@ + +import 'connectivity_provider.dart'; + +ConnectivityProvider connectivityProvider() { + return NoOpConnectivityProvider(); +} + +class NoOpConnectivityProvider implements ConnectivityProvider { + + @override + void listen(void Function(String connectivity) onChange) { + // NoOp + } + + @override + void cancel() { + // NoOp + } +} diff --git a/flutter/lib/src/integrations/connectivity_integration.dart b/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart similarity index 74% rename from flutter/lib/src/integrations/connectivity_integration.dart rename to flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart index 14cb49d4a5..e70a445318 100644 --- a/flutter/lib/src/integrations/connectivity_integration.dart +++ b/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart @@ -1,22 +1,23 @@ import 'dart:async'; import 'dart:html' as html; -import 'package:meta/meta.dart'; -import '../../sentry_flutter.dart'; +import 'connectivity_provider.dart'; + +ConnectivityProvider connectivityProvider() { + return WebConnectivityProvider(); +} + +class WebConnectivityProvider implements ConnectivityProvider { -class ConnectivityIntegration extends Integration { - Hub? _hub; html.NetworkInformation? _networkInformation; - String? _oldResult = 'none'; + String? _oldResult; StreamSubscription? _networkInfoSub; StreamSubscription? _onOnlineSub; StreamSubscription? _onOfflineSub; @override - void call(Hub hub, SentryFlutterOptions options) { - _hub = hub; - + void listen(void Function(String connectivity) onChange) { final supportsNetworkInformation = html.window.navigator.connection != null; if (supportsNetworkInformation) { _networkInformation = html.window.navigator.connection; @@ -26,26 +27,23 @@ class ConnectivityIntegration extends Integration { final newResult = _networkInformation?.toConnectivityResult(); if (newResult != null && _oldResult != newResult) { _oldResult = newResult; - addBreadcrumb(newResult); + onChange(newResult); } }); } else { // Fallback to onLine/onOffline API _oldResult = (html.window.navigator.onLine ?? false) ? 'wifi' : 'none'; _onOnlineSub = html.window.onOnline.listen((_) { - addBreadcrumb('wifi'); + onChange('wifi'); }); _onOfflineSub = html.window.onOffline.listen((_) { - addBreadcrumb('none'); + onChange('none'); }); } - options.sdk.addIntegration('connectivityIntegration'); } @override - void close() { - _hub = null; - + void cancel() { _networkInfoSub?.cancel(); _networkInfoSub = null; @@ -55,21 +53,9 @@ class ConnectivityIntegration extends Integration { _onOfflineSub?.cancel(); _onOfflineSub = null; - _oldResult = 'none'; + _oldResult = null; _networkInformation = null; } - - @internal - @visibleForTesting - void addBreadcrumb(String result) { - _hub?.addBreadcrumb( - Breadcrumb( - category: 'device.connectivity', - level: SentryLevel.info, - type: 'connectivity', - data: {'connectivity': result}), - ); - } } // Source: https://github.com/fluttercommunity/plus_plugins/blob/258f7b8b461f6d78028354f95d24014b240a80f0/packages/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart#L8 diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index a6d5c42ab3..62a9043bc9 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -9,7 +9,7 @@ import '../sentry_flutter.dart'; import 'event_processor/android_platform_exception_event_processor.dart'; import 'event_processor/flutter_exception_event_processor.dart'; import 'event_processor/platform_exception_event_processor.dart'; -import 'integrations/connectivity_integration.dart'; +import 'integrations/connectivity/connectivity_integration.dart'; import 'integrations/screenshot_integration.dart'; import 'native/factory.dart'; import 'native/native_scope_observer.dart'; diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 3411ead9ca..5c4d529917 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -19,7 +19,6 @@ dependencies: package_info_plus: '>=1.0.0 <=5.0.1' meta: ^1.3.0 ffi: ^2.0.0 - connectivity_plus: ^5.0.2 dev_dependencies: build_runner: ^2.4.2 diff --git a/flutter/test/integrations/connectivity_integration_test.dart b/flutter/test/integrations/connectivity_integration_test.dart index a0def0fe09..45cb189002 100644 --- a/flutter/test/integrations/connectivity_integration_test.dart +++ b/flutter/test/integrations/connectivity_integration_test.dart @@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry_flutter/src/integrations/connectivity_integration.dart'; +import 'package:sentry_flutter/src/integrations/connectivity/connectivity_integration.dart'; import 'package:sentry_flutter/src/sentry_flutter_options.dart'; import '../mocks.dart'; diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 8c80feb8b9..2305d1f87c 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:sentry_flutter/src/integrations/connectivity_integration.dart'; +import 'package:sentry_flutter/src/integrations/connectivity/connectivity_integration.dart'; import 'package:sentry_flutter/src/integrations/integrations.dart'; import 'package:sentry_flutter/src/integrations/screenshot_integration.dart'; import 'package:sentry_flutter/src/profiling.dart'; From bf887be0c3b86a83db289cb628042bce6b6d9454 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 18 Dec 2023 12:49:58 +0100 Subject: [PATCH 5/9] remove connectivity api as it is still experimental API --- .../web_connectivity_provider.dart | 76 ++----------------- 1 file changed, 6 insertions(+), 70 deletions(-) diff --git a/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart b/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart index e70a445318..6d414bb02d 100644 --- a/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart +++ b/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart @@ -9,89 +9,25 @@ ConnectivityProvider connectivityProvider() { class WebConnectivityProvider implements ConnectivityProvider { - html.NetworkInformation? _networkInformation; - String? _oldResult; - - StreamSubscription? _networkInfoSub; StreamSubscription? _onOnlineSub; StreamSubscription? _onOfflineSub; @override void listen(void Function(String connectivity) onChange) { - final supportsNetworkInformation = html.window.navigator.connection != null; - if (supportsNetworkInformation) { - _networkInformation = html.window.navigator.connection; - _oldResult = _networkInformation?.toConnectivityResult(); - - _networkInfoSub = _networkInformation?.onChange.listen((_) { - final newResult = _networkInformation?.toConnectivityResult(); - if (newResult != null && _oldResult != newResult) { - _oldResult = newResult; - onChange(newResult); - } - }); - } else { - // Fallback to onLine/onOffline API - _oldResult = (html.window.navigator.onLine ?? false) ? 'wifi' : 'none'; - _onOnlineSub = html.window.onOnline.listen((_) { - onChange('wifi'); - }); - _onOfflineSub = html.window.onOffline.listen((_) { - onChange('none'); - }); - } + _onOnlineSub = html.window.onOnline.listen((_) { + onChange('wifi'); + }); + _onOfflineSub = html.window.onOffline.listen((_) { + onChange('none'); + }); } @override void cancel() { - _networkInfoSub?.cancel(); - _networkInfoSub = null; - _onOnlineSub?.cancel(); _onOnlineSub = null; _onOfflineSub?.cancel(); _onOfflineSub = null; - - _oldResult = null; - _networkInformation = null; - } -} - -// Source: https://github.com/fluttercommunity/plus_plugins/blob/258f7b8b461f6d78028354f95d24014b240a80f0/packages/connectivity_plus/connectivity_plus/lib/src/web/utils/connectivity_result.dart#L8 -extension on html.NetworkInformation { - String toConnectivityResult() { - if (downlink == 0 && rtt == 0) { - return 'none'; - } - if (type != null) { - switch (type) { - case 'none': - return 'none'; - case 'bluetooth': - return 'bluetooth'; - case 'cellular': - case 'mixed': - case 'other': - case 'unknown': - return 'mobile'; - case 'ethernet': - return 'ethernet'; - default: - return 'wifi'; - } - } - if (effectiveType != null) { - switch (effectiveType) { - case 'slow-2g': - case '2g': - case '3g': - case '4g': - return 'mobile'; - default: - return 'wifi'; - } - } - return 'none'; } } From e04bec303532139a196e24d10e8948f7a2a7eac2 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 18 Dec 2023 12:55:57 +0100 Subject: [PATCH 6/9] update test --- flutter/test/integrations/connectivity_integration_test.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flutter/test/integrations/connectivity_integration_test.dart b/flutter/test/integrations/connectivity_integration_test.dart index 45cb189002..bc94a56a27 100644 --- a/flutter/test/integrations/connectivity_integration_test.dart +++ b/flutter/test/integrations/connectivity_integration_test.dart @@ -1,4 +1,3 @@ -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -39,13 +38,13 @@ void main() { final integration = fixture.getSut(); integration.call(fixture.hub, fixture.options); - integration.addBreadcrumb('bluetooth'); + integration.addBreadcrumb('wifi'); final crumb = verify( fixture.hub.addBreadcrumb(captureAny), ).captured.first as Breadcrumb; - verifyBreadcrumb(crumb, 'bluetooth'); + verifyBreadcrumb(crumb, 'wifi'); }); } From 2dce3939ee8263da3c1dfb1c3136ebf3c0bbdb26 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 18 Dec 2023 13:11:04 +0100 Subject: [PATCH 7/9] format --- .../connectivity_integration.dart | 2 - .../connectivity/connectivity_provider.dart | 3 +- .../noop_connectivity_provider.dart | 2 - .../web_connectivity_provider.dart | 1 - .../connectivity_integration_test.dart | 4 +- flutter/test/sentry_flutter_test.dart | 74 ++++++++++--------- 6 files changed, 41 insertions(+), 45 deletions(-) diff --git a/flutter/lib/src/integrations/connectivity/connectivity_integration.dart b/flutter/lib/src/integrations/connectivity/connectivity_integration.dart index 71e425797f..c8d0b80c86 100644 --- a/flutter/lib/src/integrations/connectivity/connectivity_integration.dart +++ b/flutter/lib/src/integrations/connectivity/connectivity_integration.dart @@ -34,5 +34,3 @@ class ConnectivityIntegration extends Integration { ); } } - - diff --git a/flutter/lib/src/integrations/connectivity/connectivity_provider.dart b/flutter/lib/src/integrations/connectivity/connectivity_provider.dart index 15099b8e1e..30095dda0d 100644 --- a/flutter/lib/src/integrations/connectivity/connectivity_provider.dart +++ b/flutter/lib/src/integrations/connectivity/connectivity_provider.dart @@ -1,6 +1,5 @@ - import 'noop_connectivity_provider.dart' -if (dart.library.html) 'web_connectivity_provider.dart'; + if (dart.library.html) 'web_connectivity_provider.dart'; abstract class ConnectivityProvider { factory ConnectivityProvider() => connectivityProvider(); diff --git a/flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart b/flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart index d879957e6c..4276448b7a 100644 --- a/flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart +++ b/flutter/lib/src/integrations/connectivity/noop_connectivity_provider.dart @@ -1,4 +1,3 @@ - import 'connectivity_provider.dart'; ConnectivityProvider connectivityProvider() { @@ -6,7 +5,6 @@ ConnectivityProvider connectivityProvider() { } class NoOpConnectivityProvider implements ConnectivityProvider { - @override void listen(void Function(String connectivity) onChange) { // NoOp diff --git a/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart b/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart index 6d414bb02d..34d0e0ab42 100644 --- a/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart +++ b/flutter/lib/src/integrations/connectivity/web_connectivity_provider.dart @@ -8,7 +8,6 @@ ConnectivityProvider connectivityProvider() { } class WebConnectivityProvider implements ConnectivityProvider { - StreamSubscription? _onOnlineSub; StreamSubscription? _onOfflineSub; diff --git a/flutter/test/integrations/connectivity_integration_test.dart b/flutter/test/integrations/connectivity_integration_test.dart index bc94a56a27..2f0781e9ec 100644 --- a/flutter/test/integrations/connectivity_integration_test.dart +++ b/flutter/test/integrations/connectivity_integration_test.dart @@ -32,9 +32,7 @@ void main() { true); }); - test( - '$ConnectivityIntegration: addsBreadcrumb', - () { + test('$ConnectivityIntegration: addsBreadcrumb', () { final integration = fixture.getSut(); integration.call(fixture.hub, fixture.options); diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 2305d1f87c..f01c9a8d3f 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -24,6 +24,10 @@ final platformAgnosticIntegrations = [ SentryViewHierarchyIntegration, ]; +final webIntegrations = [ + ConnectivityIntegration, +]; + final nonWebIntegrations = [ OnErrorIntegration, ]; @@ -44,11 +48,6 @@ final nativeIntegrations = [ NativeSdkIntegration, ]; -// These should be added to every platform except Android & iOS/macOS. -final nonMobileIntegrations = [ - ConnectivityIntegration, -]; - void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -87,17 +86,17 @@ void main() { options: sentryFlutterOptions!, expectedHasNativeScopeObserver: true); testConfiguration( - integrations: integrations, - shouldHaveIntegrations: [ - ...androidIntegrations, - ...nativeIntegrations, - ...platformAgnosticIntegrations, - ...nonWebIntegrations, - ], - shouldNotHaveIntegrations: [ - ...iOsAndMacOsIntegrations, - ...nonWebIntegrations, - ], + integrations: integrations, + shouldHaveIntegrations: [ + ...androidIntegrations, + ...nativeIntegrations, + ...platformAgnosticIntegrations, + ...nonWebIntegrations, + ], + shouldNotHaveIntegrations: [ + ...iOsAndMacOsIntegrations, + ...nonWebIntegrations, + ], ); integrations @@ -192,19 +191,15 @@ void main() { testScopeObserver( options: sentryFlutterOptions!, expectedHasNativeScopeObserver: true); - testConfiguration( - integrations: integrations, - shouldHaveIntegrations: [ - ...iOsAndMacOsIntegrations, - ...nativeIntegrations, - ...platformAgnosticIntegrations, - ...nonWebIntegrations, - ], - shouldNotHaveIntegrations: [ - ...androidIntegrations, - ...nonWebIntegrations, - ] - ); + testConfiguration(integrations: integrations, shouldHaveIntegrations: [ + ...iOsAndMacOsIntegrations, + ...nativeIntegrations, + ...platformAgnosticIntegrations, + ...nonWebIntegrations, + ], shouldNotHaveIntegrations: [ + ...androidIntegrations, + ...nonWebIntegrations, + ]); testBefore( integrations: integrations, @@ -250,12 +245,12 @@ void main() { shouldHaveIntegrations: [ ...platformAgnosticIntegrations, ...nonWebIntegrations, - ...nonMobileIntegrations, ], shouldNotHaveIntegrations: [ ...androidIntegrations, ...iOsAndMacOsIntegrations, ...nativeIntegrations, + ...webIntegrations, ], ); @@ -302,12 +297,12 @@ void main() { shouldHaveIntegrations: [ ...platformAgnosticIntegrations, ...nonWebIntegrations, - ...nonMobileIntegrations, ], shouldNotHaveIntegrations: [ ...androidIntegrations, ...iOsAndMacOsIntegrations, ...nativeIntegrations, + ...webIntegrations, ], ); @@ -356,7 +351,7 @@ void main() { integrations: integrations, shouldHaveIntegrations: [ ...platformAgnosticIntegrations, - ...nonMobileIntegrations, + ...webIntegrations, ], shouldNotHaveIntegrations: [ ...androidIntegrations, @@ -404,7 +399,10 @@ void main() { testConfiguration( integrations: integrations, - shouldHaveIntegrations: platformAgnosticIntegrations, + shouldHaveIntegrations: [ + ...platformAgnosticIntegrations, + ...webIntegrations, + ], shouldNotHaveIntegrations: [ ...androidIntegrations, ...iOsAndMacOsIntegrations, @@ -448,7 +446,10 @@ void main() { testConfiguration( integrations: integrations, - shouldHaveIntegrations: platformAgnosticIntegrations, + shouldHaveIntegrations: [ + ...platformAgnosticIntegrations, + ...webIntegrations, + ], shouldNotHaveIntegrations: [ ...androidIntegrations, ...iOsAndMacOsIntegrations, @@ -493,7 +494,10 @@ void main() { testConfiguration( integrations: integrations, - shouldHaveIntegrations: platformAgnosticIntegrations, + shouldHaveIntegrations: [ + ...platformAgnosticIntegrations, + ...webIntegrations, + ], shouldNotHaveIntegrations: [ ...androidIntegrations, ...iOsAndMacOsIntegrations, From 307af9b73f2fcd076c90a59b193d45ea9ddc9859 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 18 Dec 2023 13:13:50 +0100 Subject: [PATCH 8/9] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b00adcfff0..28b09a1aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Features - Add option to opt out of fatal level for automatically collected errors ([#1738](https://github.com/getsentry/sentry-dart/pull/1738)) +- Add `ConnectivityIntegration` for web ([#1765](https://github.com/getsentry/sentry-dart/pull/1765)) + - We only get the info if online/offline on web platform. The added breadcrumb is set to either `wifi` or `none`. ### Dependencies From 948a02bcfe88a7369348d26d4a1d85b22cf3f7de Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 18 Dec 2023 13:15:41 +0100 Subject: [PATCH 9/9] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54489c93c4..5dc1514dab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,9 @@ ### Features - Add option to opt out of fatal level for automatically collected errors ([#1738](https://github.com/getsentry/sentry-dart/pull/1738)) +- Add `Hive` breadcrumbs ([#1773](https://github.com/getsentry/sentry-dart/pull/1773)) - Add `ConnectivityIntegration` for web ([#1765](https://github.com/getsentry/sentry-dart/pull/1765)) - We only get the info if online/offline on web platform. The added breadcrumb is set to either `wifi` or `none`. -- Add `Hive` breadcrumbs ([#1773](https://github.com/getsentry/sentry-dart/pull/1773)) ### Dependencies