From 9119dc3ac8a1addfc66ec60c076fc32d99a39e32 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Mon, 25 Mar 2024 08:17:17 -0600 Subject: [PATCH 01/20] feat: create abstract layer for deep_link_client to simplify different implementations --- .../lib/main/main_development.dart | 5 +- .../lib/main/main_production.dart | 5 +- .../analysis_options.yaml | 0 .../lib/deep_link_client.dart | 2 + .../lib/src/deep_link_client.dart | 26 ++-- .../lib/src/deep_link_service.dart | 11 ++ .../deep_link_client/pubspec.yaml | 15 ++ .../test/deep_link_client_test.dart | 91 ++++++++++++ .../analysis_options.yaml | 1 + .../lib/firebase_deep_link_service.dart | 1 + .../lib/src/firebase_deep_link_service.dart | 26 ++++ .../pubspec.yaml | 4 +- .../test/firebase_deep_link_service_test.dart | 107 ++++++++++++++ .../lib/deep_link_client.dart | 1 - .../test/deep_link_client_test.dart | 136 ------------------ .../packages/user_repository/pubspec.yaml | 2 +- flutter_news_example/pubspec.lock | 9 +- flutter_news_example/pubspec.yaml | 4 +- 18 files changed, 289 insertions(+), 157 deletions(-) rename flutter_news_example/packages/deep_link_client/{ => deep_link_client}/analysis_options.yaml (100%) create mode 100644 flutter_news_example/packages/deep_link_client/deep_link_client/lib/deep_link_client.dart rename flutter_news_example/packages/deep_link_client/{ => deep_link_client}/lib/src/deep_link_client.dart (62%) create mode 100644 flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart create mode 100644 flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml create mode 100644 flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart create mode 100644 flutter_news_example/packages/deep_link_client/firebase_deep_link_service/analysis_options.yaml create mode 100644 flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart create mode 100644 flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart rename flutter_news_example/packages/deep_link_client/{ => firebase_deep_link_service}/pubspec.yaml (83%) create mode 100644 flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart delete mode 100644 flutter_news_example/packages/deep_link_client/lib/deep_link_client.dart delete mode 100644 flutter_news_example/packages/deep_link_client/test/deep_link_client_test.dart diff --git a/flutter_news_example/lib/main/main_development.dart b/flutter_news_example/lib/main/main_development.dart index b77df1277..0795cd0f0 100644 --- a/flutter_news_example/lib/main/main_development.dart +++ b/flutter_news_example/lib/main/main_development.dart @@ -2,6 +2,7 @@ import 'package:ads_consent_client/ads_consent_client.dart'; import 'package:article_repository/article_repository.dart'; import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_authentication_client/firebase_authentication_client.dart'; +import 'package:firebase_deep_link_service/firebase_deep_link_service.dart'; import 'package:firebase_notifications_client/firebase_notifications_client.dart'; import 'package:flutter_news_example/app/app.dart'; import 'package:flutter_news_example/main/bootstrap/bootstrap.dart'; @@ -44,7 +45,9 @@ void main() { ); final deepLinkClient = DeepLinkClient( - firebaseDynamicLinks: firebaseDynamicLinks, + deepLinkService: FirebaseDeepLinkService( + firebaseDynamicLinks: firebaseDynamicLinks, + ), ); final userStorage = UserStorage(storage: persistentStorage); diff --git a/flutter_news_example/lib/main/main_production.dart b/flutter_news_example/lib/main/main_production.dart index d3d404dac..60cab1e67 100644 --- a/flutter_news_example/lib/main/main_production.dart +++ b/flutter_news_example/lib/main/main_production.dart @@ -2,6 +2,7 @@ import 'package:ads_consent_client/ads_consent_client.dart'; import 'package:article_repository/article_repository.dart'; import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_authentication_client/firebase_authentication_client.dart'; +import 'package:firebase_deep_link_service/firebase_deep_link_service.dart'; import 'package:firebase_notifications_client/firebase_notifications_client.dart'; import 'package:flutter_news_example/app/app.dart'; import 'package:flutter_news_example/main/bootstrap/bootstrap.dart'; @@ -44,7 +45,9 @@ void main() { ); final deepLinkClient = DeepLinkClient( - firebaseDynamicLinks: firebaseDynamicLinks, + deepLinkService: FirebaseDeepLinkService( + firebaseDynamicLinks: firebaseDynamicLinks, + ), ); final userStorage = UserStorage(storage: persistentStorage); diff --git a/flutter_news_example/packages/deep_link_client/analysis_options.yaml b/flutter_news_example/packages/deep_link_client/deep_link_client/analysis_options.yaml similarity index 100% rename from flutter_news_example/packages/deep_link_client/analysis_options.yaml rename to flutter_news_example/packages/deep_link_client/deep_link_client/analysis_options.yaml diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/deep_link_client.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/deep_link_client.dart new file mode 100644 index 000000000..083f7826c --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/deep_link_client.dart @@ -0,0 +1,2 @@ +export 'src/deep_link_client.dart'; +export 'src/deep_link_service.dart'; diff --git a/flutter_news_example/packages/deep_link_client/lib/src/deep_link_client.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart similarity index 62% rename from flutter_news_example/packages/deep_link_client/lib/src/deep_link_client.dart rename to flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart index 2192561a0..7ccaf01a8 100644 --- a/flutter_news_example/packages/deep_link_client/lib/src/deep_link_client.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart @@ -1,8 +1,7 @@ import 'dart:async'; +import 'package:deep_link_client/deep_link_client.dart'; import 'package:equatable/equatable.dart'; -import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; - import 'package:rxdart/rxdart.dart'; /// {@template deep_link_client_failure} @@ -20,20 +19,19 @@ class DeepLinkClientFailure with EquatableMixin implements Exception { } /// {@template deep_link_client} -/// A client that exposes a stream of deep link URIs redirected to the app. +/// A generic deep link client interface. /// {@endtemplate} class DeepLinkClient { - /// {@macro deep_link_client} - DeepLinkClient({FirebaseDynamicLinks? firebaseDynamicLinks}) - : _deepLinkSubject = BehaviorSubject() { - _firebaseDynamicLinks = - firebaseDynamicLinks ?? FirebaseDynamicLinks.instance; - + /// {@macro firebase_deep_link_client} + DeepLinkClient({ + required DeepLinkService deepLinkService, + }) : _deepLinkService = deepLinkService, + _deepLinkSubject = BehaviorSubject() { unawaited(_getInitialLink()); - _firebaseDynamicLinks.onLink.listen(_onAppLink).onError(_handleError); + _deepLinkService.deepLinkStream.listen(_onAppLink).onError(_handleError); } - late final FirebaseDynamicLinks _firebaseDynamicLinks; + final DeepLinkService _deepLinkService; final BehaviorSubject _deepLinkSubject; /// Provides a stream of URIs intercepted by the app. Will emit the latest @@ -42,7 +40,7 @@ class DeepLinkClient { Future _getInitialLink() async { try { - final deepLink = await _firebaseDynamicLinks.getInitialLink(); + final deepLink = await _deepLinkService.getInitialLink(); if (deepLink != null) { _onAppLink(deepLink); } @@ -51,8 +49,8 @@ class DeepLinkClient { } } - void _onAppLink(PendingDynamicLinkData dynamicLinkData) { - _deepLinkSubject.add(dynamicLinkData.link); + void _onAppLink(Uri deepLink) { + _deepLinkSubject.add(deepLink); } void _handleError(Object error, StackTrace stackTrace) { diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart new file mode 100644 index 000000000..c18410241 --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart @@ -0,0 +1,11 @@ +/// {@template deep_link_service} +/// A generic deep link service interface. +/// {@endtemplate} +abstract class DeepLinkService { + /// Provides a stream of URIs intercepted by the app. Will emit the latest + /// received value (if any) as first. + Stream get deepLinkStream; + + /// Retrieves the initial deep link if present. + Future getInitialLink(); +} diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml new file mode 100644 index 000000000..6787df628 --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml @@ -0,0 +1,15 @@ +name: deep_link_client +description: A deep link client interface +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + +dependencies: + equatable: ^2.0.3 + mocktail: ^1.0.3 + rxdart: ^0.27.3 + +dev_dependencies: + test: ^1.21.4 + very_good_analysis: ^5.1.0 diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart new file mode 100644 index 000000000..db7c8c68a --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart @@ -0,0 +1,91 @@ +import 'dart:async'; + +import 'package:deep_link_client/deep_link_client.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; + +typedef OnAppLinkFunction = void Function(Uri uri, String stringUri); + +class MockDeepLinkService extends Mock implements DeepLinkService {} + +void main() { + late DeepLinkService deepLinkServiceMock; + late StreamController onDeepLinkStreamController; + + setUp(() { + deepLinkServiceMock = MockDeepLinkService(); + onDeepLinkStreamController = StreamController(); + when(() => deepLinkServiceMock.deepLinkStream) + .thenAnswer((_) => onDeepLinkStreamController.stream); + }); + + tearDown(() { + onDeepLinkStreamController.close(); + }); + + group('DeepLinkClient', () { + test('retrieves and publishes latest link if present', () { + final expectedUri = Uri.https('ham.app.test', '/test/path'); + when(deepLinkServiceMock.getInitialLink).thenAnswer( + (_) => Future.value(expectedUri), + ); + + final client = DeepLinkClient(deepLinkService: deepLinkServiceMock); + expect(client.deepLinkStream, emits(expectedUri)); + + // Testing also the replay of the latest value. + expect(client.deepLinkStream, emits(expectedUri)); + }); + + test('publishes DeepLinkClientFailure to stream if upstream throws', () { + final expectedError = Error(); + final expectedStackTrace = StackTrace.current; + + when(deepLinkServiceMock.getInitialLink).thenAnswer((_) { + return Future.error(expectedError, expectedStackTrace); + }); + + final client = DeepLinkClient(deepLinkService: deepLinkServiceMock); + expect( + client.deepLinkStream, + emitsError( + isA() + .having((failure) => failure.error, 'error', expectedError), + ), + ); + }); + + test('publishes values received through onAppLink callback', () { + final expectedUri1 = Uri.https('ham.app.test', '/test/1'); + final expectedUri2 = Uri.https('ham.app.test', '/test/2'); + + when(deepLinkServiceMock.getInitialLink).thenAnswer((_) async => null); + + final client = DeepLinkClient(deepLinkService: deepLinkServiceMock); + + expect( + client.deepLinkStream, + emitsInOrder( + [expectedUri1, expectedUri1, expectedUri2, expectedUri1], + ), + ); + + onDeepLinkStreamController + ..add(expectedUri1) + ..add(expectedUri1) + ..add(expectedUri2) + ..add(expectedUri1); + }); + }); + + group('DeepLinkClientFailure', () { + final error = Exception('errorMessage'); + + test('has correct props', () { + expect( + DeepLinkClientFailure(error).props, + [error], + ); + }); + }); +} diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/analysis_options.yaml b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/analysis_options.yaml new file mode 100644 index 000000000..799268d3e --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/analysis_options.yaml @@ -0,0 +1 @@ +include: package:very_good_analysis/analysis_options.5.1.0.yaml diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart new file mode 100644 index 000000000..da23c01dc --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart @@ -0,0 +1 @@ +export 'src/firebase_deep_link_service.dart'; diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart new file mode 100644 index 000000000..abbaeeee0 --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart @@ -0,0 +1,26 @@ +import 'dart:async'; + +import 'package:deep_link_client/deep_link_client.dart'; +import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; + +/// {@template firebase_deep_link_client} +/// A client that exposes a stream of deep link URIs redirected to the app. +/// {@endtemplate} +class FirebaseDeepLinkService extends DeepLinkService { + /// {@macro firebase_deep_link_client} + FirebaseDeepLinkService({FirebaseDynamicLinks? firebaseDynamicLinks}) + : _firebaseDynamicLinks = + firebaseDynamicLinks ?? FirebaseDynamicLinks.instance; + + late final FirebaseDynamicLinks _firebaseDynamicLinks; + + @override + Stream get deepLinkStream => + _firebaseDynamicLinks.onLink.map((event) => event.link); + + @override + Future getInitialLink() async { + final deepLink = await _firebaseDynamicLinks.getInitialLink(); + return deepLink?.link; + } +} diff --git a/flutter_news_example/packages/deep_link_client/pubspec.yaml b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/pubspec.yaml similarity index 83% rename from flutter_news_example/packages/deep_link_client/pubspec.yaml rename to flutter_news_example/packages/deep_link_client/firebase_deep_link_service/pubspec.yaml index 25aedb84d..bf31be63c 100644 --- a/flutter_news_example/packages/deep_link_client/pubspec.yaml +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/pubspec.yaml @@ -1,4 +1,4 @@ -name: deep_link_client +name: firebase_deep_link_service description: A Dart package which provides a deep link stream publish_to: none @@ -6,6 +6,8 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: + deep_link_client: + path: ../deep_link_client equatable: ^2.0.3 firebase_core: ^2.24.2 firebase_dynamic_links: ^5.0.3 diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart new file mode 100644 index 000000000..1879841a0 --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart @@ -0,0 +1,107 @@ +import 'dart:async'; + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; +import 'package:firebase_deep_link_service/firebase_deep_link_service.dart'; +import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockAppLinks extends Mock implements FirebaseDynamicLinks {} + +class MockFirebaseCore extends Mock + with MockPlatformInterfaceMixin + implements FirebasePlatform {} + +void main() { + late MockAppLinks appLinksMock; + late StreamController onLinkStreamController; + + setUp(() { + appLinksMock = MockAppLinks(); + onLinkStreamController = StreamController(); + when(() => appLinksMock.onLink) + .thenAnswer((_) => onLinkStreamController.stream); + }); + + tearDown(() { + onLinkStreamController.close(); + }); + + group('FirebaseDeepLinkService', () { + group('getInitialLink', () { + test('retrieves the latest link if present', () async { + final expectedUri = Uri.https('ham.app.test', '/test/path'); + when(appLinksMock.getInitialLink).thenAnswer( + (_) => Future.value(PendingDynamicLinkData(link: expectedUri)), + ); + + final service = + FirebaseDeepLinkService(firebaseDynamicLinks: appLinksMock); + final link = await service.getInitialLink(); + expect(link, expectedUri); + }); + }); + + group('deepLinkStream', () { + test('publishes values received through onLink stream', () { + final expectedUri1 = Uri.https('ham.app.test', '/test/1'); + final expectedUri2 = Uri.https('ham.app.test', '/test/2'); + + final service = + FirebaseDeepLinkService(firebaseDynamicLinks: appLinksMock); + + expect( + service.deepLinkStream, + emitsInOrder( + [expectedUri1, expectedUri1, expectedUri2, expectedUri1], + ), + ); + + onLinkStreamController + ..add(PendingDynamicLinkData(link: expectedUri1)) + ..add(PendingDynamicLinkData(link: expectedUri1)) + ..add(PendingDynamicLinkData(link: expectedUri2)) + ..add(PendingDynamicLinkData(link: expectedUri1)); + }); + }); + + group('with default FirebaseDynamicLinks', () { + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final mock = MockFirebaseCore(); + Firebase.delegatePackingProperty = mock; + + final platformApp = FirebaseAppPlatform( + 'testAppName', + const FirebaseOptions( + apiKey: 'apiKey', + appId: 'appId', + messagingSenderId: 'messagingSenderId', + projectId: 'projectId', + ), + ); + + when(() => mock.apps).thenReturn([platformApp]); + when(() => mock.app(any())).thenReturn(platformApp); + when( + () => mock.initializeApp( + name: any(named: 'name'), + options: any(named: 'options'), + ), + ).thenAnswer((_) => Future.value(platformApp)); + }); + + tearDown(() { + Firebase.delegatePackingProperty = null; + }); + + test('can be instantiated', () async { + await Firebase.initializeApp(); + expect(FirebaseDeepLinkService.new, returnsNormally); + }); + }); + }); +} diff --git a/flutter_news_example/packages/deep_link_client/lib/deep_link_client.dart b/flutter_news_example/packages/deep_link_client/lib/deep_link_client.dart deleted file mode 100644 index d85be935a..000000000 --- a/flutter_news_example/packages/deep_link_client/lib/deep_link_client.dart +++ /dev/null @@ -1 +0,0 @@ -export 'src/deep_link_client.dart'; diff --git a/flutter_news_example/packages/deep_link_client/test/deep_link_client_test.dart b/flutter_news_example/packages/deep_link_client/test/deep_link_client_test.dart deleted file mode 100644 index 52eb84851..000000000 --- a/flutter_news_example/packages/deep_link_client/test/deep_link_client_test.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'dart:async'; - -import 'package:deep_link_client/deep_link_client.dart'; -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; -import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -typedef OnAppLinkFunction = void Function(Uri uri, String stringUri); - -class MockAppLinks extends Mock implements FirebaseDynamicLinks {} - -class MockFirebaseCore extends Mock - with MockPlatformInterfaceMixin - implements FirebasePlatform {} - -void main() { - late MockAppLinks appLinksMock; - late StreamController onLinkStreamController; - - setUp(() { - appLinksMock = MockAppLinks(); - onLinkStreamController = StreamController(); - when(() => appLinksMock.onLink) - .thenAnswer((_) => onLinkStreamController.stream); - }); - - tearDown(() { - onLinkStreamController.close(); - }); - - group('DeepLinkClient', () { - test('retrieves and publishes latest link if present', () { - final expectedUri = Uri.https('ham.app.test', '/test/path'); - when(appLinksMock.getInitialLink).thenAnswer( - (_) => Future.value(PendingDynamicLinkData(link: expectedUri)), - ); - - final client = DeepLinkClient(firebaseDynamicLinks: appLinksMock); - expect(client.deepLinkStream, emits(expectedUri)); - - // Testing also the replay of the latest value. - expect(client.deepLinkStream, emits(expectedUri)); - }); - - test('publishes DeepLinkClientFailure to stream if upstream throws', () { - final expectedError = Error(); - final expectedStackTrace = StackTrace.current; - - when(appLinksMock.getInitialLink).thenAnswer((_) { - return Future.error(expectedError, expectedStackTrace); - }); - - final client = DeepLinkClient(firebaseDynamicLinks: appLinksMock); - expect( - client.deepLinkStream, - emitsError( - isA() - .having((failure) => failure.error, 'error', expectedError), - ), - ); - }); - - test('publishes values received through onAppLink callback', () { - final expectedUri1 = Uri.https('ham.app.test', '/test/1'); - final expectedUri2 = Uri.https('ham.app.test', '/test/2'); - - when(appLinksMock.getInitialLink).thenAnswer((_) async => null); - - final client = DeepLinkClient(firebaseDynamicLinks: appLinksMock); - - expect( - client.deepLinkStream, - emitsInOrder( - [expectedUri1, expectedUri1, expectedUri2, expectedUri1], - ), - ); - - onLinkStreamController - ..add(PendingDynamicLinkData(link: expectedUri1)) - ..add(PendingDynamicLinkData(link: expectedUri1)) - ..add(PendingDynamicLinkData(link: expectedUri2)) - ..add(PendingDynamicLinkData(link: expectedUri1)); - }); - - group('with default FirebaseDynamicLinks', () { - setUp(() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final mock = MockFirebaseCore(); - Firebase.delegatePackingProperty = mock; - - final platformApp = FirebaseAppPlatform( - 'testAppName', - const FirebaseOptions( - apiKey: 'apiKey', - appId: 'appId', - messagingSenderId: 'messagingSenderId', - projectId: 'projectId', - ), - ); - - when(() => mock.apps).thenReturn([platformApp]); - when(() => mock.app(any())).thenReturn(platformApp); - when( - () => mock.initializeApp( - name: any(named: 'name'), - options: any(named: 'options'), - ), - ).thenAnswer((_) => Future.value(platformApp)); - }); - - tearDown(() { - Firebase.delegatePackingProperty = null; - }); - - test('can be instantiated', () async { - await Firebase.initializeApp(); - expect(DeepLinkClient.new, returnsNormally); - }); - }); - }); - - group('DeepLinkClientFailure', () { - final error = Exception('errorMessage'); - - test('has correct props', () { - expect( - DeepLinkClientFailure(error).props, - [error], - ); - }); - }); -} diff --git a/flutter_news_example/packages/user_repository/pubspec.yaml b/flutter_news_example/packages/user_repository/pubspec.yaml index 16755c210..339fe9f68 100644 --- a/flutter_news_example/packages/user_repository/pubspec.yaml +++ b/flutter_news_example/packages/user_repository/pubspec.yaml @@ -9,7 +9,7 @@ dependencies: authentication_client: path: ../authentication_client/authentication_client deep_link_client: - path: ../deep_link_client + path: ../deep_link_client/deep_link_client equatable: ^2.0.3 flutter_news_example_api: path: ../../api diff --git a/flutter_news_example/pubspec.lock b/flutter_news_example/pubspec.lock index e7099505e..83680a63c 100644 --- a/flutter_news_example/pubspec.lock +++ b/flutter_news_example/pubspec.lock @@ -319,7 +319,7 @@ packages: deep_link_client: dependency: "direct main" description: - path: "packages/deep_link_client" + path: "packages/deep_link_client/deep_link_client" relative: true source: path version: "0.0.0" @@ -473,6 +473,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.6.16" + firebase_deep_link_service: + dependency: "direct main" + description: + path: "packages/deep_link_client/firebase_deep_link_service" + relative: true + source: path + version: "0.0.0" firebase_dynamic_links: dependency: "direct main" description: diff --git a/flutter_news_example/pubspec.yaml b/flutter_news_example/pubspec.yaml index 25148a72e..dda1cb9f6 100644 --- a/flutter_news_example/pubspec.yaml +++ b/flutter_news_example/pubspec.yaml @@ -21,7 +21,7 @@ dependencies: clock: ^1.1.0 collection: ^1.16.0 deep_link_client: - path: packages/deep_link_client + path: packages/deep_link_client/deep_link_client email_launcher: path: packages/email_launcher equatable: ^2.0.3 @@ -31,6 +31,8 @@ dependencies: path: packages/authentication_client/firebase_authentication_client firebase_core: ^2.24.2 firebase_crashlytics: ^3.0.3 + firebase_deep_link_service: + path: packages/deep_link_client/firebase_deep_link_service firebase_dynamic_links: ^5.0.3 firebase_messaging: ^14.0.3 firebase_notifications_client: From 9d0ddc80b4c023ac2ffa3992af62ba86a7075e8d Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Mon, 25 Mar 2024 08:20:28 -0600 Subject: [PATCH 02/20] ci: add workflow files for new deep link client --- .github/workflows/deep_link_client.yaml | 8 ++++---- .../workflows/firebase_deep_link_service.yaml | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/firebase_deep_link_service.yaml diff --git a/.github/workflows/deep_link_client.yaml b/.github/workflows/deep_link_client.yaml index d8192f68d..ffed38ead 100644 --- a/.github/workflows/deep_link_client.yaml +++ b/.github/workflows/deep_link_client.yaml @@ -7,14 +7,14 @@ concurrency: on: pull_request: paths: - - "flutter_news_example/packages/deep_link_client/**" + - "flutter_news_example/packages/deep_link_client/deep_link_client/**" - ".github/workflows/deep_link_client.yaml" branches: - main jobs: build: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - flutter_version: 3.10.2 - working_directory: flutter_news_example/packages/deep_link_client + flutter_version: 3.0.2 + working_directory: flutter_news_example/packages/deep_link_client/deep_link_client diff --git a/.github/workflows/firebase_deep_link_service.yaml b/.github/workflows/firebase_deep_link_service.yaml new file mode 100644 index 000000000..5a1a33a83 --- /dev/null +++ b/.github/workflows/firebase_deep_link_service.yaml @@ -0,0 +1,20 @@ +name: firebase_deep_link_service + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + pull_request: + paths: + - "flutter_news_example/packages/deep_link_client/firebase_deep_link_service/**" + - ".github/workflows/firebase_deep_link_service.yaml" + branches: + - main + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + flutter_version: 3.10.2 + working_directory: flutter_news_example/packages/deep_link_client/firebase_deep_link_service From 3c8ee78f3bca41c8f1cce168a2be555e57b0802d Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Mon, 25 Mar 2024 08:34:17 -0600 Subject: [PATCH 03/20] docs: update doc --- .../lib/src/firebase_deep_link_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart index abbaeeee0..e60c0949d 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart @@ -4,7 +4,7 @@ import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; /// {@template firebase_deep_link_client} -/// A client that exposes a stream of deep link URIs redirected to the app. +/// A FirebaseDynamicLinks implementation of [DeepLinkService]. /// {@endtemplate} class FirebaseDeepLinkService extends DeepLinkService { /// {@macro firebase_deep_link_client} From fa66ca0fb48aa7f64308438c820b96886c6f4ff0 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Mon, 25 Mar 2024 08:35:21 -0600 Subject: [PATCH 04/20] ci: fix file syntax --- .github/workflows/deep_link_client.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deep_link_client.yaml b/.github/workflows/deep_link_client.yaml index ffed38ead..da07d7477 100644 --- a/.github/workflows/deep_link_client.yaml +++ b/.github/workflows/deep_link_client.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 with: - flutter_version: 3.0.2 + dart_sdk: 3.0.2 working_directory: flutter_news_example/packages/deep_link_client/deep_link_client From 784c95d3aa9e29b8d18ac03dafd73629d1852f3d Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 28 Mar 2024 09:50:55 -0600 Subject: [PATCH 05/20] docs: fix doc for DeepLinkClient --- .../deep_link_client/lib/src/deep_link_client.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart index 7ccaf01a8..4cdadec37 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart @@ -19,10 +19,10 @@ class DeepLinkClientFailure with EquatableMixin implements Exception { } /// {@template deep_link_client} -/// A generic deep link client interface. +/// A DeepLinkClient that provides access to deep links intercepted by the app. /// {@endtemplate} class DeepLinkClient { - /// {@macro firebase_deep_link_client} + /// {@macro deep_link_client} DeepLinkClient({ required DeepLinkService deepLinkService, }) : _deepLinkService = deepLinkService, From 0a0d7bcd77e4c8047ff11c0b733042b6d41fea3a Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 28 Mar 2024 09:55:10 -0600 Subject: [PATCH 06/20] chore(deep_link_client): move mocktail to dev dependencies --- .../packages/deep_link_client/deep_link_client/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml index 6787df628..64ef2f1e4 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml @@ -7,9 +7,9 @@ environment: dependencies: equatable: ^2.0.3 - mocktail: ^1.0.3 rxdart: ^0.27.3 dev_dependencies: + mocktail: ^1.0.3 test: ^1.21.4 very_good_analysis: ^5.1.0 From 2105d71991f98491427a7ceaa08b2a30e59732d9 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 28 Mar 2024 09:56:38 -0600 Subject: [PATCH 07/20] chore: rename to deepLinkService --- .../test/deep_link_client_test.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart index db7c8c68a..9ec3cc2f4 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart @@ -9,13 +9,13 @@ typedef OnAppLinkFunction = void Function(Uri uri, String stringUri); class MockDeepLinkService extends Mock implements DeepLinkService {} void main() { - late DeepLinkService deepLinkServiceMock; + late DeepLinkService deepLinkService; late StreamController onDeepLinkStreamController; setUp(() { - deepLinkServiceMock = MockDeepLinkService(); + deepLinkService = MockDeepLinkService(); onDeepLinkStreamController = StreamController(); - when(() => deepLinkServiceMock.deepLinkStream) + when(() => deepLinkService.deepLinkStream) .thenAnswer((_) => onDeepLinkStreamController.stream); }); @@ -26,11 +26,11 @@ void main() { group('DeepLinkClient', () { test('retrieves and publishes latest link if present', () { final expectedUri = Uri.https('ham.app.test', '/test/path'); - when(deepLinkServiceMock.getInitialLink).thenAnswer( + when(deepLinkService.getInitialLink).thenAnswer( (_) => Future.value(expectedUri), ); - final client = DeepLinkClient(deepLinkService: deepLinkServiceMock); + final client = DeepLinkClient(deepLinkService: deepLinkService); expect(client.deepLinkStream, emits(expectedUri)); // Testing also the replay of the latest value. @@ -41,11 +41,11 @@ void main() { final expectedError = Error(); final expectedStackTrace = StackTrace.current; - when(deepLinkServiceMock.getInitialLink).thenAnswer((_) { + when(deepLinkService.getInitialLink).thenAnswer((_) { return Future.error(expectedError, expectedStackTrace); }); - final client = DeepLinkClient(deepLinkService: deepLinkServiceMock); + final client = DeepLinkClient(deepLinkService: deepLinkService); expect( client.deepLinkStream, emitsError( @@ -59,9 +59,9 @@ void main() { final expectedUri1 = Uri.https('ham.app.test', '/test/1'); final expectedUri2 = Uri.https('ham.app.test', '/test/2'); - when(deepLinkServiceMock.getInitialLink).thenAnswer((_) async => null); + when(deepLinkService.getInitialLink).thenAnswer((_) async => null); - final client = DeepLinkClient(deepLinkService: deepLinkServiceMock); + final client = DeepLinkClient(deepLinkService: deepLinkService); expect( client.deepLinkStream, From d0afded0a41ed0afeb2b4ba495095e413912f076 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 28 Mar 2024 09:58:11 -0600 Subject: [PATCH 08/20] chore: remove late from final variable --- .../lib/src/firebase_deep_link_service.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart index e60c0949d..d6ec9e658 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart @@ -3,16 +3,16 @@ import 'dart:async'; import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; -/// {@template firebase_deep_link_client} +/// {@template firebase_deep_link_service} /// A FirebaseDynamicLinks implementation of [DeepLinkService]. /// {@endtemplate} -class FirebaseDeepLinkService extends DeepLinkService { - /// {@macro firebase_deep_link_client} +class FirebaseDeepLinkService implements DeepLinkService { + /// {@macro firebase_deep_link_service} FirebaseDeepLinkService({FirebaseDynamicLinks? firebaseDynamicLinks}) : _firebaseDynamicLinks = firebaseDynamicLinks ?? FirebaseDynamicLinks.instance; - late final FirebaseDynamicLinks _firebaseDynamicLinks; + final FirebaseDynamicLinks _firebaseDynamicLinks; @override Stream get deepLinkStream => From bdec65733199b3355e648210dc98eeb213a55224 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 28 Mar 2024 09:58:35 -0600 Subject: [PATCH 09/20] refactor: rename appLinks --- .../test/firebase_deep_link_service_test.dart | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart index 1879841a0..ae578f545 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart @@ -15,13 +15,13 @@ class MockFirebaseCore extends Mock implements FirebasePlatform {} void main() { - late MockAppLinks appLinksMock; + late MockAppLinks appLinks; late StreamController onLinkStreamController; setUp(() { - appLinksMock = MockAppLinks(); + appLinks = MockAppLinks(); onLinkStreamController = StreamController(); - when(() => appLinksMock.onLink) + when(() => appLinks.onLink) .thenAnswer((_) => onLinkStreamController.stream); }); @@ -33,12 +33,11 @@ void main() { group('getInitialLink', () { test('retrieves the latest link if present', () async { final expectedUri = Uri.https('ham.app.test', '/test/path'); - when(appLinksMock.getInitialLink).thenAnswer( + when(appLinks.getInitialLink).thenAnswer( (_) => Future.value(PendingDynamicLinkData(link: expectedUri)), ); - final service = - FirebaseDeepLinkService(firebaseDynamicLinks: appLinksMock); + final service = FirebaseDeepLinkService(firebaseDynamicLinks: appLinks); final link = await service.getInitialLink(); expect(link, expectedUri); }); @@ -49,8 +48,7 @@ void main() { final expectedUri1 = Uri.https('ham.app.test', '/test/1'); final expectedUri2 = Uri.https('ham.app.test', '/test/2'); - final service = - FirebaseDeepLinkService(firebaseDynamicLinks: appLinksMock); + final service = FirebaseDeepLinkService(firebaseDynamicLinks: appLinks); expect( service.deepLinkStream, From 8c74a0dcf2199c277c5fbe59d2f941d3809b0170 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 4 Apr 2024 07:05:59 -0600 Subject: [PATCH 10/20] refactor: exchange names between the service and the client --- .../lib/main/main_development.dart | 8 ++--- .../lib/main/main_production.dart | 8 ++--- .../lib/src/deep_link_client.dart | 20 ++++++------- .../lib/src/deep_link_service.dart | 6 ++-- ..._test.dart => deep_link_service_test.dart} | 30 +++++++++---------- .../analysis_options.yaml | 0 .../lib/firebase_deep_link_client.dart | 1 + .../lib/src/firebase_deep_link_client.dart} | 8 ++--- .../pubspec.yaml | 2 +- .../test/firebase_deep_link_client_test.dart} | 15 +++++----- .../lib/firebase_deep_link_service.dart | 1 - .../lib/src/user_repository.dart | 8 ++--- .../test/src/user_repository_test.dart | 18 +++++------ flutter_news_example/pubspec.lock | 8 ++--- flutter_news_example/pubspec.yaml | 4 +-- 15 files changed, 68 insertions(+), 69 deletions(-) rename flutter_news_example/packages/deep_link_client/deep_link_client/test/{deep_link_client_test.dart => deep_link_service_test.dart} (69%) rename flutter_news_example/packages/deep_link_client/{firebase_deep_link_service => firebase_deep_link_client}/analysis_options.yaml (100%) create mode 100644 flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/firebase_deep_link_client.dart rename flutter_news_example/packages/deep_link_client/{firebase_deep_link_service/lib/src/firebase_deep_link_service.dart => firebase_deep_link_client/lib/src/firebase_deep_link_client.dart} (75%) rename flutter_news_example/packages/deep_link_client/{firebase_deep_link_service => firebase_deep_link_client}/pubspec.yaml (93%) rename flutter_news_example/packages/deep_link_client/{firebase_deep_link_service/test/firebase_deep_link_service_test.dart => firebase_deep_link_client/test/firebase_deep_link_client_test.dart} (87%) delete mode 100644 flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart diff --git a/flutter_news_example/lib/main/main_development.dart b/flutter_news_example/lib/main/main_development.dart index 0795cd0f0..1f7aaa182 100644 --- a/flutter_news_example/lib/main/main_development.dart +++ b/flutter_news_example/lib/main/main_development.dart @@ -2,7 +2,7 @@ import 'package:ads_consent_client/ads_consent_client.dart'; import 'package:article_repository/article_repository.dart'; import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_authentication_client/firebase_authentication_client.dart'; -import 'package:firebase_deep_link_service/firebase_deep_link_service.dart'; +import 'package:firebase_deep_link_client/firebase_deep_link_client.dart'; import 'package:firebase_notifications_client/firebase_notifications_client.dart'; import 'package:flutter_news_example/app/app.dart'; import 'package:flutter_news_example/main/bootstrap/bootstrap.dart'; @@ -44,8 +44,8 @@ void main() { packageVersion: packageVersion, ); - final deepLinkClient = DeepLinkClient( - deepLinkService: FirebaseDeepLinkService( + final deepLinkService = DeepLinkService( + deepLinkClient: FirebaseDeepLinkClient( firebaseDynamicLinks: firebaseDynamicLinks, ), ); @@ -64,7 +64,7 @@ void main() { apiClient: apiClient, authenticationClient: authenticationClient, packageInfoClient: packageInfoClient, - deepLinkClient: deepLinkClient, + deepLinkService: deepLinkService, storage: userStorage, ); diff --git a/flutter_news_example/lib/main/main_production.dart b/flutter_news_example/lib/main/main_production.dart index 60cab1e67..e7acf001c 100644 --- a/flutter_news_example/lib/main/main_production.dart +++ b/flutter_news_example/lib/main/main_production.dart @@ -2,7 +2,7 @@ import 'package:ads_consent_client/ads_consent_client.dart'; import 'package:article_repository/article_repository.dart'; import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_authentication_client/firebase_authentication_client.dart'; -import 'package:firebase_deep_link_service/firebase_deep_link_service.dart'; +import 'package:firebase_deep_link_client/firebase_deep_link_client.dart'; import 'package:firebase_notifications_client/firebase_notifications_client.dart'; import 'package:flutter_news_example/app/app.dart'; import 'package:flutter_news_example/main/bootstrap/bootstrap.dart'; @@ -44,8 +44,8 @@ void main() { packageVersion: packageVersion, ); - final deepLinkClient = DeepLinkClient( - deepLinkService: FirebaseDeepLinkService( + final deepLinkService = DeepLinkService( + deepLinkClient: FirebaseDeepLinkClient( firebaseDynamicLinks: firebaseDynamicLinks, ), ); @@ -64,7 +64,7 @@ void main() { apiClient: apiClient, authenticationClient: authenticationClient, packageInfoClient: packageInfoClient, - deepLinkClient: deepLinkClient, + deepLinkService: deepLinkService, storage: userStorage, ); diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart index 4cdadec37..91c96f771 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart @@ -18,20 +18,20 @@ class DeepLinkClientFailure with EquatableMixin implements Exception { List get props => [error]; } -/// {@template deep_link_client} -/// A DeepLinkClient that provides access to deep links intercepted by the app. +/// {@template deep_link_service} +/// A DeepLinkService that provides access to deep links intercepted by the app. /// {@endtemplate} -class DeepLinkClient { - /// {@macro deep_link_client} - DeepLinkClient({ - required DeepLinkService deepLinkService, - }) : _deepLinkService = deepLinkService, +class DeepLinkService { + /// {@macro deep_link_service} + DeepLinkService({ + required DeepLinkClient deepLinkClient, + }) : _deepLinkClient = deepLinkClient, _deepLinkSubject = BehaviorSubject() { unawaited(_getInitialLink()); - _deepLinkService.deepLinkStream.listen(_onAppLink).onError(_handleError); + _deepLinkClient.deepLinkStream.listen(_onAppLink).onError(_handleError); } - final DeepLinkService _deepLinkService; + final DeepLinkClient _deepLinkClient; final BehaviorSubject _deepLinkSubject; /// Provides a stream of URIs intercepted by the app. Will emit the latest @@ -40,7 +40,7 @@ class DeepLinkClient { Future _getInitialLink() async { try { - final deepLink = await _deepLinkService.getInitialLink(); + final deepLink = await _deepLinkClient.getInitialLink(); if (deepLink != null) { _onAppLink(deepLink); } diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart index c18410241..c5265c179 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart @@ -1,7 +1,7 @@ -/// {@template deep_link_service} -/// A generic deep link service interface. +/// {@template deep_link_client} +/// A generic DeepLinkClient interface. /// {@endtemplate} -abstract class DeepLinkService { +abstract class DeepLinkClient { /// Provides a stream of URIs intercepted by the app. Will emit the latest /// received value (if any) as first. Stream get deepLinkStream; diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_service_test.dart similarity index 69% rename from flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart rename to flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_service_test.dart index 9ec3cc2f4..30ab3c1c7 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_client_test.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/test/deep_link_service_test.dart @@ -6,16 +6,16 @@ import 'package:test/test.dart'; typedef OnAppLinkFunction = void Function(Uri uri, String stringUri); -class MockDeepLinkService extends Mock implements DeepLinkService {} +class MockDeepLinkClient extends Mock implements DeepLinkClient {} void main() { - late DeepLinkService deepLinkService; + late DeepLinkClient deepLinkClient; late StreamController onDeepLinkStreamController; setUp(() { - deepLinkService = MockDeepLinkService(); + deepLinkClient = MockDeepLinkClient(); onDeepLinkStreamController = StreamController(); - when(() => deepLinkService.deepLinkStream) + when(() => deepLinkClient.deepLinkStream) .thenAnswer((_) => onDeepLinkStreamController.stream); }); @@ -23,31 +23,31 @@ void main() { onDeepLinkStreamController.close(); }); - group('DeepLinkClient', () { + group('DeepLinkService', () { test('retrieves and publishes latest link if present', () { final expectedUri = Uri.https('ham.app.test', '/test/path'); - when(deepLinkService.getInitialLink).thenAnswer( + when(deepLinkClient.getInitialLink).thenAnswer( (_) => Future.value(expectedUri), ); - final client = DeepLinkClient(deepLinkService: deepLinkService); - expect(client.deepLinkStream, emits(expectedUri)); + final service = DeepLinkService(deepLinkClient: deepLinkClient); + expect(service.deepLinkStream, emits(expectedUri)); // Testing also the replay of the latest value. - expect(client.deepLinkStream, emits(expectedUri)); + expect(service.deepLinkStream, emits(expectedUri)); }); test('publishes DeepLinkClientFailure to stream if upstream throws', () { final expectedError = Error(); final expectedStackTrace = StackTrace.current; - when(deepLinkService.getInitialLink).thenAnswer((_) { + when(deepLinkClient.getInitialLink).thenAnswer((_) { return Future.error(expectedError, expectedStackTrace); }); - final client = DeepLinkClient(deepLinkService: deepLinkService); + final deepLinkService = DeepLinkService(deepLinkClient: deepLinkClient); expect( - client.deepLinkStream, + deepLinkService.deepLinkStream, emitsError( isA() .having((failure) => failure.error, 'error', expectedError), @@ -59,12 +59,12 @@ void main() { final expectedUri1 = Uri.https('ham.app.test', '/test/1'); final expectedUri2 = Uri.https('ham.app.test', '/test/2'); - when(deepLinkService.getInitialLink).thenAnswer((_) async => null); + when(deepLinkClient.getInitialLink).thenAnswer((_) async => null); - final client = DeepLinkClient(deepLinkService: deepLinkService); + final deepLinkService = DeepLinkService(deepLinkClient: deepLinkClient); expect( - client.deepLinkStream, + deepLinkService.deepLinkStream, emitsInOrder( [expectedUri1, expectedUri1, expectedUri2, expectedUri1], ), diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/analysis_options.yaml b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/analysis_options.yaml similarity index 100% rename from flutter_news_example/packages/deep_link_client/firebase_deep_link_service/analysis_options.yaml rename to flutter_news_example/packages/deep_link_client/firebase_deep_link_client/analysis_options.yaml diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/firebase_deep_link_client.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/firebase_deep_link_client.dart new file mode 100644 index 000000000..527252c3a --- /dev/null +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/firebase_deep_link_client.dart @@ -0,0 +1 @@ +export 'src/firebase_deep_link_client.dart'; diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart similarity index 75% rename from flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart rename to flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart index d6ec9e658..e19b0fe7d 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/src/firebase_deep_link_service.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart @@ -3,12 +3,12 @@ import 'dart:async'; import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; -/// {@template firebase_deep_link_service} +/// {@template firebase_deep_link_client} /// A FirebaseDynamicLinks implementation of [DeepLinkService]. /// {@endtemplate} -class FirebaseDeepLinkService implements DeepLinkService { - /// {@macro firebase_deep_link_service} - FirebaseDeepLinkService({FirebaseDynamicLinks? firebaseDynamicLinks}) +class FirebaseDeepLinkClient implements DeepLinkClient { + /// {@macro firebase_deep_link_client} + FirebaseDeepLinkClient({FirebaseDynamicLinks? firebaseDynamicLinks}) : _firebaseDynamicLinks = firebaseDynamicLinks ?? FirebaseDynamicLinks.instance; diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/pubspec.yaml b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml similarity index 93% rename from flutter_news_example/packages/deep_link_client/firebase_deep_link_service/pubspec.yaml rename to flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml index bf31be63c..fae7543fe 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/pubspec.yaml +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml @@ -1,4 +1,4 @@ -name: firebase_deep_link_service +name: firebase_deep_link_client description: A Dart package which provides a deep link stream publish_to: none diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart similarity index 87% rename from flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart rename to flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart index ae578f545..b86af6713 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/test/firebase_deep_link_service_test.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart @@ -1,8 +1,7 @@ import 'dart:async'; - import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; -import 'package:firebase_deep_link_service/firebase_deep_link_service.dart'; +import 'package:firebase_deep_link_client/firebase_deep_link_client.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -29,7 +28,7 @@ void main() { onLinkStreamController.close(); }); - group('FirebaseDeepLinkService', () { + group('FirebaseDeepLinkClient', () { group('getInitialLink', () { test('retrieves the latest link if present', () async { final expectedUri = Uri.https('ham.app.test', '/test/path'); @@ -37,8 +36,8 @@ void main() { (_) => Future.value(PendingDynamicLinkData(link: expectedUri)), ); - final service = FirebaseDeepLinkService(firebaseDynamicLinks: appLinks); - final link = await service.getInitialLink(); + final client = FirebaseDeepLinkClient(firebaseDynamicLinks: appLinks); + final link = await client.getInitialLink(); expect(link, expectedUri); }); }); @@ -48,10 +47,10 @@ void main() { final expectedUri1 = Uri.https('ham.app.test', '/test/1'); final expectedUri2 = Uri.https('ham.app.test', '/test/2'); - final service = FirebaseDeepLinkService(firebaseDynamicLinks: appLinks); + final client = FirebaseDeepLinkClient(firebaseDynamicLinks: appLinks); expect( - service.deepLinkStream, + client.deepLinkStream, emitsInOrder( [expectedUri1, expectedUri1, expectedUri2, expectedUri1], ), @@ -98,7 +97,7 @@ void main() { test('can be instantiated', () async { await Firebase.initializeApp(); - expect(FirebaseDeepLinkService.new, returnsNormally); + expect(FirebaseDeepLinkClient.new, returnsNormally); }); }); }); diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart deleted file mode 100644 index da23c01dc..000000000 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_service/lib/firebase_deep_link_service.dart +++ /dev/null @@ -1 +0,0 @@ -export 'src/firebase_deep_link_service.dart'; diff --git a/flutter_news_example/packages/user_repository/lib/src/user_repository.dart b/flutter_news_example/packages/user_repository/lib/src/user_repository.dart index cc3228f2d..d9a3debfc 100644 --- a/flutter_news_example/packages/user_repository/lib/src/user_repository.dart +++ b/flutter_news_example/packages/user_repository/lib/src/user_repository.dart @@ -58,18 +58,18 @@ class UserRepository { required FlutterNewsExampleApiClient apiClient, required AuthenticationClient authenticationClient, required PackageInfoClient packageInfoClient, - required DeepLinkClient deepLinkClient, + required DeepLinkService deepLinkService, required UserStorage storage, }) : _apiClient = apiClient, _authenticationClient = authenticationClient, _packageInfoClient = packageInfoClient, - _deepLinkClient = deepLinkClient, + _deepLinkService = deepLinkService, _storage = storage; final FlutterNewsExampleApiClient _apiClient; final AuthenticationClient _authenticationClient; final PackageInfoClient _packageInfoClient; - final DeepLinkClient _deepLinkClient; + final DeepLinkService _deepLinkService; final UserStorage _storage; /// Stream of [User] which will emit the current user when @@ -94,7 +94,7 @@ class UserRepository { /// /// Emits when a new email link is emitted on [DeepLinkClient.deepLinkStream], /// which is validated using [AuthenticationClient.isLogInWithEmailLink]. - Stream get incomingEmailLinks => _deepLinkClient.deepLinkStream.where( + Stream get incomingEmailLinks => _deepLinkService.deepLinkStream.where( (deepLink) => _authenticationClient.isLogInWithEmailLink( emailLink: deepLink.toString(), ), diff --git a/flutter_news_example/packages/user_repository/test/src/user_repository_test.dart b/flutter_news_example/packages/user_repository/test/src/user_repository_test.dart index aa7910933..1f4ea90a3 100644 --- a/flutter_news_example/packages/user_repository/test/src/user_repository_test.dart +++ b/flutter_news_example/packages/user_repository/test/src/user_repository_test.dart @@ -14,7 +14,7 @@ class MockAuthenticationClient extends Mock implements AuthenticationClient {} class MockPackageInfoClient extends Mock implements PackageInfoClient {} -class MockDeepLinkClient extends Mock implements DeepLinkClient {} +class MockDeepLinkService extends Mock implements DeepLinkService {} class MockUserStorage extends Mock implements UserStorage {} @@ -57,7 +57,7 @@ void main() { group('UserRepository', () { late AuthenticationClient authenticationClient; late PackageInfoClient packageInfoClient; - late DeepLinkClient deepLinkClient; + late DeepLinkService deepLinkService; late UserStorage storage; late StreamController deepLinkClientController; late UserRepository userRepository; @@ -66,19 +66,19 @@ void main() { setUp(() { authenticationClient = MockAuthenticationClient(); packageInfoClient = MockPackageInfoClient(); - deepLinkClient = MockDeepLinkClient(); + deepLinkService = MockDeepLinkService(); storage = MockUserStorage(); deepLinkClientController = StreamController.broadcast(); apiClient = MockFlutterNewsExampleApiClient(); - when(() => deepLinkClient.deepLinkStream) + when(() => deepLinkService.deepLinkStream) .thenAnswer((_) => deepLinkClientController.stream); userRepository = UserRepository( apiClient: apiClient, authenticationClient: authenticationClient, packageInfoClient: packageInfoClient, - deepLinkClient: deepLinkClient, + deepLinkService: deepLinkService, storage: storage, ); }); @@ -468,7 +468,7 @@ void main() { apiClient: apiClient, authenticationClient: authenticationClient, packageInfoClient: packageInfoClient, - deepLinkClient: deepLinkClient, + deepLinkService: deepLinkService, storage: storage, ).fetchAppOpenedCount(); expect(result, 1); @@ -484,7 +484,7 @@ void main() { apiClient: apiClient, authenticationClient: authenticationClient, packageInfoClient: packageInfoClient, - deepLinkClient: deepLinkClient, + deepLinkService: deepLinkService, storage: storage, ).fetchAppOpenedCount(), throwsA(isA()), @@ -505,7 +505,7 @@ void main() { apiClient: apiClient, authenticationClient: authenticationClient, packageInfoClient: packageInfoClient, - deepLinkClient: deepLinkClient, + deepLinkService: deepLinkService, storage: storage, ).incrementAppOpenedCount(), completes, @@ -524,7 +524,7 @@ void main() { apiClient: apiClient, authenticationClient: authenticationClient, packageInfoClient: packageInfoClient, - deepLinkClient: deepLinkClient, + deepLinkService: deepLinkService, storage: storage, ).incrementAppOpenedCount(), throwsA(isA()), diff --git a/flutter_news_example/pubspec.lock b/flutter_news_example/pubspec.lock index 83680a63c..d06d9a886 100644 --- a/flutter_news_example/pubspec.lock +++ b/flutter_news_example/pubspec.lock @@ -473,10 +473,10 @@ packages: url: "https://pub.dev" source: hosted version: "3.6.16" - firebase_deep_link_service: + firebase_deep_link_client: dependency: "direct main" description: - path: "packages/deep_link_client/firebase_deep_link_service" + path: "packages/deep_link_client/firebase_deep_link_client" relative: true source: path version: "0.0.0" @@ -980,10 +980,10 @@ packages: dependency: "direct dev" description: name: mocktail - sha256: f603ebd85a576e5914870b02e5839fc5d0243b867bf710651cf239a28ebb365e + sha256: c4b5007d91ca4f67256e720cb1b6d704e79a510183a12fa551021f652577dce6 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.3" mocktail_image_network: dependency: "direct dev" description: diff --git a/flutter_news_example/pubspec.yaml b/flutter_news_example/pubspec.yaml index dda1cb9f6..02ecadd42 100644 --- a/flutter_news_example/pubspec.yaml +++ b/flutter_news_example/pubspec.yaml @@ -31,8 +31,8 @@ dependencies: path: packages/authentication_client/firebase_authentication_client firebase_core: ^2.24.2 firebase_crashlytics: ^3.0.3 - firebase_deep_link_service: - path: packages/deep_link_client/firebase_deep_link_service + firebase_deep_link_client: + path: packages/deep_link_client/firebase_deep_link_client firebase_dynamic_links: ^5.0.3 firebase_messaging: ^14.0.3 firebase_notifications_client: From 06d07524746ae92299d3a966d4f1ec102105f341 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 4 Apr 2024 07:07:55 -0600 Subject: [PATCH 11/20] ci: udpate workflow file for firebase_deep_link_client --- ...p_link_service.yaml => firebase_deep_link_client.yaml} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename .github/workflows/{firebase_deep_link_service.yaml => firebase_deep_link_client.yaml} (73%) diff --git a/.github/workflows/firebase_deep_link_service.yaml b/.github/workflows/firebase_deep_link_client.yaml similarity index 73% rename from .github/workflows/firebase_deep_link_service.yaml rename to .github/workflows/firebase_deep_link_client.yaml index 5a1a33a83..91eb167af 100644 --- a/.github/workflows/firebase_deep_link_service.yaml +++ b/.github/workflows/firebase_deep_link_client.yaml @@ -1,4 +1,4 @@ -name: firebase_deep_link_service +name: firebase_deep_link_client concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -7,8 +7,8 @@ concurrency: on: pull_request: paths: - - "flutter_news_example/packages/deep_link_client/firebase_deep_link_service/**" - - ".github/workflows/firebase_deep_link_service.yaml" + - "flutter_news_example/packages/deep_link_client/firebase_deep_link_client/**" + - ".github/workflows/firebase_deep_link_client.yaml" branches: - main @@ -17,4 +17,4 @@ jobs: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 with: flutter_version: 3.10.2 - working_directory: flutter_news_example/packages/deep_link_client/firebase_deep_link_service + working_directory: flutter_news_example/packages/deep_link_client/firebase_deep_link_client From e0ac93bb98e03863867b0c73aceaaf443b0ebb5f Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 4 Apr 2024 13:30:15 -0600 Subject: [PATCH 12/20] chore: rename files to match class --- .../lib/src/deep_link_client.dart | 60 ++----------------- .../lib/src/deep_link_service.dart | 60 +++++++++++++++++-- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart index 91c96f771..c5265c179 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_client.dart @@ -1,59 +1,11 @@ -import 'dart:async'; - -import 'package:deep_link_client/deep_link_client.dart'; -import 'package:equatable/equatable.dart'; -import 'package:rxdart/rxdart.dart'; - -/// {@template deep_link_client_failure} -/// Indicates a failure during retrieval or processing of a deep link. +/// {@template deep_link_client} +/// A generic DeepLinkClient interface. /// {@endtemplate} -class DeepLinkClientFailure with EquatableMixin implements Exception { - /// {@macro deep_link_client_failure} - DeepLinkClientFailure(this.error); - - /// The error which was caught. - final Object error; - - @override - List get props => [error]; -} - -/// {@template deep_link_service} -/// A DeepLinkService that provides access to deep links intercepted by the app. -/// {@endtemplate} -class DeepLinkService { - /// {@macro deep_link_service} - DeepLinkService({ - required DeepLinkClient deepLinkClient, - }) : _deepLinkClient = deepLinkClient, - _deepLinkSubject = BehaviorSubject() { - unawaited(_getInitialLink()); - _deepLinkClient.deepLinkStream.listen(_onAppLink).onError(_handleError); - } - - final DeepLinkClient _deepLinkClient; - final BehaviorSubject _deepLinkSubject; - +abstract class DeepLinkClient { /// Provides a stream of URIs intercepted by the app. Will emit the latest /// received value (if any) as first. - Stream get deepLinkStream => _deepLinkSubject; - - Future _getInitialLink() async { - try { - final deepLink = await _deepLinkClient.getInitialLink(); - if (deepLink != null) { - _onAppLink(deepLink); - } - } catch (error, stackTrace) { - _handleError(error, stackTrace); - } - } - - void _onAppLink(Uri deepLink) { - _deepLinkSubject.add(deepLink); - } + Stream get deepLinkStream; - void _handleError(Object error, StackTrace stackTrace) { - _deepLinkSubject.addError(DeepLinkClientFailure(error), stackTrace); - } + /// Retrieves the initial deep link if present. + Future getInitialLink(); } diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart index c5265c179..91c96f771 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/lib/src/deep_link_service.dart @@ -1,11 +1,59 @@ -/// {@template deep_link_client} -/// A generic DeepLinkClient interface. +import 'dart:async'; + +import 'package:deep_link_client/deep_link_client.dart'; +import 'package:equatable/equatable.dart'; +import 'package:rxdart/rxdart.dart'; + +/// {@template deep_link_client_failure} +/// Indicates a failure during retrieval or processing of a deep link. /// {@endtemplate} -abstract class DeepLinkClient { +class DeepLinkClientFailure with EquatableMixin implements Exception { + /// {@macro deep_link_client_failure} + DeepLinkClientFailure(this.error); + + /// The error which was caught. + final Object error; + + @override + List get props => [error]; +} + +/// {@template deep_link_service} +/// A DeepLinkService that provides access to deep links intercepted by the app. +/// {@endtemplate} +class DeepLinkService { + /// {@macro deep_link_service} + DeepLinkService({ + required DeepLinkClient deepLinkClient, + }) : _deepLinkClient = deepLinkClient, + _deepLinkSubject = BehaviorSubject() { + unawaited(_getInitialLink()); + _deepLinkClient.deepLinkStream.listen(_onAppLink).onError(_handleError); + } + + final DeepLinkClient _deepLinkClient; + final BehaviorSubject _deepLinkSubject; + /// Provides a stream of URIs intercepted by the app. Will emit the latest /// received value (if any) as first. - Stream get deepLinkStream; + Stream get deepLinkStream => _deepLinkSubject; + + Future _getInitialLink() async { + try { + final deepLink = await _deepLinkClient.getInitialLink(); + if (deepLink != null) { + _onAppLink(deepLink); + } + } catch (error, stackTrace) { + _handleError(error, stackTrace); + } + } + + void _onAppLink(Uri deepLink) { + _deepLinkSubject.add(deepLink); + } - /// Retrieves the initial deep link if present. - Future getInitialLink(); + void _handleError(Object error, StackTrace stackTrace) { + _deepLinkSubject.addError(DeepLinkClientFailure(error), stackTrace); + } } From b82118e67536a69d25c8ad6b8f853f580db4d12d Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Thu, 4 Apr 2024 13:30:59 -0600 Subject: [PATCH 13/20] docs: fix typo --- .../lib/src/firebase_deep_link_client.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart index e19b0fe7d..319444964 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart @@ -4,7 +4,7 @@ import 'package:deep_link_client/deep_link_client.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; /// {@template firebase_deep_link_client} -/// A FirebaseDynamicLinks implementation of [DeepLinkService]. +/// A FirebaseDynamicLinks implementation of [DeepLinkClient]. /// {@endtemplate} class FirebaseDeepLinkClient implements DeepLinkClient { /// {@macro firebase_deep_link_client} From b4a191b8c4bd59abf434524bb39f097b40965507 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Fri, 24 May 2024 14:36:39 -0600 Subject: [PATCH 14/20] ci: update flutter version for firebase deep link client workflow --- .github/workflows/firebase_deep_link_client.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/firebase_deep_link_client.yaml b/.github/workflows/firebase_deep_link_client.yaml index 91eb167af..a3b718648 100644 --- a/.github/workflows/firebase_deep_link_client.yaml +++ b/.github/workflows/firebase_deep_link_client.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 with: - flutter_version: 3.10.2 + flutter_version: 3.19.6 working_directory: flutter_news_example/packages/deep_link_client/firebase_deep_link_client From 3383f19c3af14968a7144d1be80cb41924368a35 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Tue, 3 Sep 2024 13:00:45 -0600 Subject: [PATCH 15/20] build: update dependencies --- .github/workflows/firebase_deep_link_client.yaml | 2 +- .../packages/deep_link_client/deep_link_client/pubspec.yaml | 2 +- .../deep_link_client/firebase_deep_link_client/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/firebase_deep_link_client.yaml b/.github/workflows/firebase_deep_link_client.yaml index a3b718648..a10be4b8c 100644 --- a/.github/workflows/firebase_deep_link_client.yaml +++ b/.github/workflows/firebase_deep_link_client.yaml @@ -16,5 +16,5 @@ jobs: build: uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 with: - flutter_version: 3.19.6 + flutter_version: 3.22.2 working_directory: flutter_news_example/packages/deep_link_client/firebase_deep_link_client diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml index 64ef2f1e4..3cebc18c8 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml @@ -7,7 +7,7 @@ environment: dependencies: equatable: ^2.0.3 - rxdart: ^0.27.3 + rxdart: ^0.27.5 dev_dependencies: mocktail: ^1.0.3 diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml index 11651d6ad..957af20d0 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: firebase_core: ^3.0.0 firebase_dynamic_links: ^6.0.0 plugin_platform_interface: ^2.1.3 - rxdart: ^0.27.3 + rxdart: ^0.27.5 dev_dependencies: firebase_core_platform_interface: ^5.0.0 From 4be0b7418178a99b997d3813c182790f8b1cede2 Mon Sep 17 00:00:00 2001 From: Elian Ortega Date: Tue, 3 Sep 2024 13:13:40 -0600 Subject: [PATCH 16/20] refactor: linter warnings --- .../lib/src/firebase_deep_link_client.dart | 2 ++ .../test/firebase_deep_link_client_test.dart | 22 +++++++++++-------- flutter_news_example/pubspec.lock | 19 +++++++++++----- flutter_news_example/pubspec.yaml | 2 +- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart index 319444964..14b841e55 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use + import 'dart:async'; import 'package:deep_link_client/deep_link_client.dart'; diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart index b86af6713..d80f2e799 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use + import 'dart:async'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; @@ -7,20 +9,20 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -class MockAppLinks extends Mock implements FirebaseDynamicLinks {} +class MockFirebaseDynamicLinks extends Mock implements FirebaseDynamicLinks {} class MockFirebaseCore extends Mock with MockPlatformInterfaceMixin implements FirebasePlatform {} void main() { - late MockAppLinks appLinks; + late MockFirebaseDynamicLinks dynamicLinks; late StreamController onLinkStreamController; setUp(() { - appLinks = MockAppLinks(); + dynamicLinks = MockFirebaseDynamicLinks(); onLinkStreamController = StreamController(); - when(() => appLinks.onLink) + when(() => dynamicLinks.onLink) .thenAnswer((_) => onLinkStreamController.stream); }); @@ -32,11 +34,12 @@ void main() { group('getInitialLink', () { test('retrieves the latest link if present', () async { final expectedUri = Uri.https('ham.app.test', '/test/path'); - when(appLinks.getInitialLink).thenAnswer( + when(dynamicLinks.getInitialLink).thenAnswer( (_) => Future.value(PendingDynamicLinkData(link: expectedUri)), ); - final client = FirebaseDeepLinkClient(firebaseDynamicLinks: appLinks); + final client = + FirebaseDeepLinkClient(firebaseDynamicLinks: dynamicLinks); final link = await client.getInitialLink(); expect(link, expectedUri); }); @@ -44,10 +47,11 @@ void main() { group('deepLinkStream', () { test('publishes values received through onLink stream', () { - final expectedUri1 = Uri.https('ham.app.test', '/test/1'); - final expectedUri2 = Uri.https('ham.app.test', '/test/2'); + final expectedUri1 = Uri.https('news.app.test', '/test/1'); + final expectedUri2 = Uri.https('news.app.test', '/test/2'); - final client = FirebaseDeepLinkClient(firebaseDynamicLinks: appLinks); + final client = + FirebaseDeepLinkClient(firebaseDynamicLinks: dynamicLinks); expect( client.deepLinkStream, diff --git a/flutter_news_example/pubspec.lock b/flutter_news_example/pubspec.lock index 009403340..1c18efb88 100644 --- a/flutter_news_example/pubspec.lock +++ b/flutter_news_example/pubspec.lock @@ -473,6 +473,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.6.36" + firebase_deep_link_client: + dependency: "direct main" + description: + path: "packages/deep_link_client/firebase_deep_link_client" + relative: true + source: path + version: "0.0.0" firebase_dynamic_links: dependency: "direct main" description: @@ -798,10 +805,10 @@ packages: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.2" http_methods: dependency: transitive description: @@ -1299,18 +1306,18 @@ packages: dependency: transitive description: name: share_plus - sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544 + sha256: "59dfd53f497340a0c3a81909b220cfdb9b8973a91055c4e5ab9b9b9ad7c513c0" url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "10.0.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4" + sha256: "6ababf341050edff57da8b6990f11f4e99eaba837865e2e6defe16d039619db5" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" shared_preferences: dependency: "direct main" description: diff --git a/flutter_news_example/pubspec.yaml b/flutter_news_example/pubspec.yaml index f7792c69b..e35b4e766 100644 --- a/flutter_news_example/pubspec.yaml +++ b/flutter_news_example/pubspec.yaml @@ -32,9 +32,9 @@ dependencies: path: packages/authentication_client/firebase_authentication_client firebase_core: ^3.0.0 firebase_crashlytics: ^4.0.0 - firebase_dynamic_links: ^6.0.0 firebase_deep_link_client: path: packages/deep_link_client/firebase_deep_link_client + firebase_dynamic_links: ^6.0.0 firebase_messaging: ^15.0.0 firebase_notifications_client: path: packages/notifications_client/firebase_notifications_client From 7d263f513a6f3a1bcb95579daff0205b9ab7a669 Mon Sep 17 00:00:00 2001 From: elianortega Date: Tue, 24 Sep 2024 22:03:49 -0600 Subject: [PATCH 17/20] chore: update pubspec.lock --- flutter_news_example/pubspec.lock | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/flutter_news_example/pubspec.lock b/flutter_news_example/pubspec.lock index 4bfec3c5a..b96a9c771 100644 --- a/flutter_news_example/pubspec.lock +++ b/flutter_news_example/pubspec.lock @@ -473,6 +473,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.6.42" + firebase_deep_link_client: + dependency: "direct main" + description: + path: "packages/deep_link_client/firebase_deep_link_client" + relative: true + source: path + version: "0.0.0" firebase_dynamic_links: dependency: "direct main" description: @@ -1781,10 +1788,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" watcher: dependency: transitive description: From a9207df98e3ce861873c60622d65a6db42b4ecc8 Mon Sep 17 00:00:00 2001 From: elianortega Date: Tue, 24 Sep 2024 22:07:17 -0600 Subject: [PATCH 18/20] build: pr feedback update dependencies on deep_link_client --- .../deep_link_client/deep_link_client/pubspec.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml index 3cebc18c8..5fc8421cc 100644 --- a/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml +++ b/flutter_news_example/packages/deep_link_client/deep_link_client/pubspec.yaml @@ -6,10 +6,10 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - equatable: ^2.0.3 + equatable: ^2.0.5 rxdart: ^0.27.5 dev_dependencies: - mocktail: ^1.0.3 - test: ^1.21.4 - very_good_analysis: ^5.1.0 + mocktail: ^1.0.4 + test: ^1.25.8 + very_good_analysis: ^6.0.0 From 84f921927ff0c34006f2e0756988f8562ad0ee92 Mon Sep 17 00:00:00 2001 From: elianortega Date: Tue, 24 Sep 2024 22:11:50 -0600 Subject: [PATCH 19/20] refactor: pr feedback on tests for firebase_deep_link client --- .../lib/src/firebase_deep_link_client.dart | 5 +- .../test/firebase_deep_link_client_test.dart | 46 ++----------------- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart index 14b841e55..f371c87a2 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/lib/src/firebase_deep_link_client.dart @@ -10,9 +10,8 @@ import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; /// {@endtemplate} class FirebaseDeepLinkClient implements DeepLinkClient { /// {@macro firebase_deep_link_client} - FirebaseDeepLinkClient({FirebaseDynamicLinks? firebaseDynamicLinks}) - : _firebaseDynamicLinks = - firebaseDynamicLinks ?? FirebaseDynamicLinks.instance; + FirebaseDeepLinkClient({required FirebaseDynamicLinks firebaseDynamicLinks}) + : _firebaseDynamicLinks = firebaseDynamicLinks; final FirebaseDynamicLinks _firebaseDynamicLinks; diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart index d80f2e799..ff8a43e3b 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/test/firebase_deep_link_client_test.dart @@ -1,7 +1,6 @@ // ignore_for_file: deprecated_member_use import 'dart:async'; -import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; import 'package:firebase_deep_link_client/firebase_deep_link_client.dart'; import 'package:firebase_dynamic_links/firebase_dynamic_links.dart'; @@ -53,55 +52,18 @@ void main() { final client = FirebaseDeepLinkClient(firebaseDynamicLinks: dynamicLinks); - expect( - client.deepLinkStream, - emitsInOrder( - [expectedUri1, expectedUri1, expectedUri2, expectedUri1], - ), - ); - onLinkStreamController ..add(PendingDynamicLinkData(link: expectedUri1)) ..add(PendingDynamicLinkData(link: expectedUri1)) ..add(PendingDynamicLinkData(link: expectedUri2)) ..add(PendingDynamicLinkData(link: expectedUri1)); - }); - }); - - group('with default FirebaseDynamicLinks', () { - setUp(() { - TestWidgetsFlutterBinding.ensureInitialized(); - - final mock = MockFirebaseCore(); - Firebase.delegatePackingProperty = mock; - final platformApp = FirebaseAppPlatform( - 'testAppName', - const FirebaseOptions( - apiKey: 'apiKey', - appId: 'appId', - messagingSenderId: 'messagingSenderId', - projectId: 'projectId', + expect( + client.deepLinkStream, + emitsInOrder( + [expectedUri1, expectedUri1, expectedUri2, expectedUri1], ), ); - - when(() => mock.apps).thenReturn([platformApp]); - when(() => mock.app(any())).thenReturn(platformApp); - when( - () => mock.initializeApp( - name: any(named: 'name'), - options: any(named: 'options'), - ), - ).thenAnswer((_) => Future.value(platformApp)); - }); - - tearDown(() { - Firebase.delegatePackingProperty = null; - }); - - test('can be instantiated', () async { - await Firebase.initializeApp(); - expect(FirebaseDeepLinkClient.new, returnsNormally); }); }); }); From b203c46b5e9420a47dd08d40996688ca5fbf3288 Mon Sep 17 00:00:00 2001 From: elianortega Date: Wed, 25 Sep 2024 22:36:55 -0600 Subject: [PATCH 20/20] build: pr feedback remove rxdart from deep_link_client --- .../deep_link_client/firebase_deep_link_client/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml index 025b997a3..4d54fc85e 100644 --- a/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml +++ b/flutter_news_example/packages/deep_link_client/firebase_deep_link_client/pubspec.yaml @@ -12,7 +12,6 @@ dependencies: firebase_core: ^3.4.1 firebase_dynamic_links: ^6.0.6 plugin_platform_interface: ^2.1.3 - rxdart: ^0.27.5 dev_dependencies: firebase_core_platform_interface: ^5.2.1