Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create abstract layer for deep_link_client to simplify other implementations #1158

Merged
merged 24 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9119dc3
feat: create abstract layer for deep_link_client to simplify differen…
elianortega Mar 25, 2024
9d0ddc8
ci: add workflow files for new deep link client
elianortega Mar 25, 2024
3c8ee78
docs: update doc
elianortega Mar 25, 2024
fa66ca0
ci: fix file syntax
elianortega Mar 25, 2024
784c95d
docs: fix doc for DeepLinkClient
elianortega Mar 28, 2024
0a0d7bc
chore(deep_link_client): move mocktail to dev dependencies
elianortega Mar 28, 2024
2105d71
chore: rename to deepLinkService
elianortega Mar 28, 2024
d0afded
chore: remove late from final variable
elianortega Mar 28, 2024
bdec657
refactor: rename appLinks
elianortega Mar 28, 2024
8c74a0d
refactor: exchange names between the service and the client
elianortega Apr 4, 2024
06d0752
ci: udpate workflow file for firebase_deep_link_client
elianortega Apr 4, 2024
f67c623
Merge remote-tracking branch 'upstream/main' into feat/deep-link-abst…
elianortega Apr 4, 2024
e0ac93b
chore: rename files to match class
elianortega Apr 4, 2024
b82118e
docs: fix typo
elianortega Apr 4, 2024
c29ee09
Merge remote-tracking branch 'upstream/main' into feat/deep-link-abst…
elianortega May 24, 2024
b4a191b
ci: update flutter version for firebase deep link client workflow
elianortega May 24, 2024
300d5e1
Merge remote-tracking branch 'upstream/main' into feat/deep-link-abst…
elianortega Sep 3, 2024
3383f19
build: update dependencies
elianortega Sep 3, 2024
4be0b74
refactor: linter warnings
elianortega Sep 3, 2024
4a912cf
Merge remote-tracking branch 'upstream/main' into feat/deep-link-abst…
elianortega Sep 25, 2024
7d263f5
chore: update pubspec.lock
elianortega Sep 25, 2024
a9207df
build: pr feedback update dependencies on deep_link_client
elianortega Sep 25, 2024
84f9219
refactor: pr feedback on tests for firebase_deep_link client
elianortega Sep 25, 2024
b203c46
build: pr feedback remove rxdart from deep_link_client
elianortega Sep 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/deep_link_client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.24.2
working_directory: flutter_news_example/packages/deep_link_client
dart_sdk: 3.4.3
working_directory: flutter_news_example/packages/deep_link_client/deep_link_client
20 changes: 20 additions & 0 deletions .github/workflows/firebase_deep_link_client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: firebase_deep_link_client

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

on:
pull_request:
paths:
- "flutter_news_example/packages/deep_link_client/firebase_deep_link_client/**"
- ".github/workflows/firebase_deep_link_client.yaml"
branches:
- main

jobs:
build:
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
with:
flutter_version: 3.22.2
working_directory: flutter_news_example/packages/deep_link_client/firebase_deep_link_client
9 changes: 6 additions & 3 deletions flutter_news_example/lib/main/main_development.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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_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';
Expand Down Expand Up @@ -43,8 +44,10 @@ void main() {
packageVersion: packageVersion,
);

final deepLinkClient = DeepLinkClient(
firebaseDynamicLinks: firebaseDynamicLinks,
final deepLinkService = DeepLinkService(
deepLinkClient: FirebaseDeepLinkClient(
firebaseDynamicLinks: firebaseDynamicLinks,
),
);

final userStorage = UserStorage(storage: persistentStorage);
Expand All @@ -61,7 +64,7 @@ void main() {
apiClient: apiClient,
authenticationClient: authenticationClient,
packageInfoClient: packageInfoClient,
deepLinkClient: deepLinkClient,
deepLinkService: deepLinkService,
storage: userStorage,
);

Expand Down
9 changes: 6 additions & 3 deletions flutter_news_example/lib/main/main_production.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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_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';
Expand Down Expand Up @@ -43,8 +44,10 @@ void main() {
packageVersion: packageVersion,
);

final deepLinkClient = DeepLinkClient(
firebaseDynamicLinks: firebaseDynamicLinks,
final deepLinkService = DeepLinkService(
deepLinkClient: FirebaseDeepLinkClient(
firebaseDynamicLinks: firebaseDynamicLinks,
),
);

final userStorage = UserStorage(storage: persistentStorage);
Expand All @@ -61,7 +64,7 @@ void main() {
apiClient: apiClient,
authenticationClient: authenticationClient,
packageInfoClient: packageInfoClient,
deepLinkClient: deepLinkClient,
deepLinkService: deepLinkService,
storage: userStorage,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:very_good_analysis/analysis_options.5.1.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'src/deep_link_client.dart';
export 'src/deep_link_service.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// {@template deep_link_client}
/// A generic DeepLinkClient interface.
/// {@endtemplate}
abstract class DeepLinkClient {
/// Provides a stream of URIs intercepted by the app. Will emit the latest
/// received value (if any) as first.
Stream<Uri> get deepLinkStream;

/// Retrieves the initial deep link if present.
Future<Uri?> getInitialLink();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

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}
Expand All @@ -21,21 +20,20 @@ class DeepLinkClientFailure with EquatableMixin implements Exception {
List<Object> get props => [error];
}

/// {@template deep_link_client}
/// A client that exposes a stream of deep link URIs redirected to 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({FirebaseDynamicLinks? firebaseDynamicLinks})
: _deepLinkSubject = BehaviorSubject<Uri>() {
_firebaseDynamicLinks =
firebaseDynamicLinks ?? FirebaseDynamicLinks.instance;

class DeepLinkService {
/// {@macro deep_link_service}
DeepLinkService({
required DeepLinkClient deepLinkClient,
}) : _deepLinkClient = deepLinkClient,
_deepLinkSubject = BehaviorSubject<Uri>() {
unawaited(_getInitialLink());
_firebaseDynamicLinks.onLink.listen(_onAppLink).onError(_handleError);
_deepLinkClient.deepLinkStream.listen(_onAppLink).onError(_handleError);
}

late final FirebaseDynamicLinks _firebaseDynamicLinks;
final DeepLinkClient _deepLinkClient;
final BehaviorSubject<Uri> _deepLinkSubject;

/// Provides a stream of URIs intercepted by the app. Will emit the latest
Expand All @@ -44,7 +42,7 @@ class DeepLinkClient {

Future<void> _getInitialLink() async {
try {
final deepLink = await _firebaseDynamicLinks.getInitialLink();
final deepLink = await _deepLinkClient.getInitialLink();
if (deepLink != null) {
_onAppLink(deepLink);
}
Expand All @@ -53,8 +51,8 @@ class DeepLinkClient {
}
}

void _onAppLink(PendingDynamicLinkData dynamicLinkData) {
_deepLinkSubject.add(dynamicLinkData.link);
void _onAppLink(Uri deepLink) {
_deepLinkSubject.add(deepLink);
}

void _handleError(Object error, StackTrace stackTrace) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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.5
rxdart: ^0.27.5

dev_dependencies:
mocktail: ^1.0.4
test: ^1.25.8
very_good_analysis: ^6.0.0
Original file line number Diff line number Diff line change
@@ -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 MockDeepLinkClient extends Mock implements DeepLinkClient {}

void main() {
late DeepLinkClient deepLinkClient;
late StreamController<Uri> onDeepLinkStreamController;

setUp(() {
deepLinkClient = MockDeepLinkClient();
onDeepLinkStreamController = StreamController<Uri>();
when(() => deepLinkClient.deepLinkStream)
.thenAnswer((_) => onDeepLinkStreamController.stream);
});

tearDown(() {
onDeepLinkStreamController.close();
});

group('DeepLinkService', () {
test('retrieves and publishes latest link if present', () {
final expectedUri = Uri.https('ham.app.test', '/test/path');
when(deepLinkClient.getInitialLink).thenAnswer(
(_) => Future.value(expectedUri),
);

final service = DeepLinkService(deepLinkClient: deepLinkClient);
expect(service.deepLinkStream, emits(expectedUri));

// Testing also the replay of the latest value.
expect(service.deepLinkStream, emits(expectedUri));
});

test('publishes DeepLinkClientFailure to stream if upstream throws', () {
final expectedError = Error();
final expectedStackTrace = StackTrace.current;

when(deepLinkClient.getInitialLink).thenAnswer((_) {
return Future.error(expectedError, expectedStackTrace);
});

final deepLinkService = DeepLinkService(deepLinkClient: deepLinkClient);
expect(
deepLinkService.deepLinkStream,
emitsError(
isA<DeepLinkClientFailure>()
.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(deepLinkClient.getInitialLink).thenAnswer((_) async => null);

final deepLinkService = DeepLinkService(deepLinkClient: deepLinkClient);

expect(
deepLinkService.deepLinkStream,
emitsInOrder(
<Uri>[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],
);
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'src/firebase_deep_link_client.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// ignore_for_file: deprecated_member_use

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 FirebaseDynamicLinks implementation of [DeepLinkClient].
/// {@endtemplate}
class FirebaseDeepLinkClient implements DeepLinkClient {
/// {@macro firebase_deep_link_client}
FirebaseDeepLinkClient({required FirebaseDynamicLinks firebaseDynamicLinks})
: _firebaseDynamicLinks = firebaseDynamicLinks;

final FirebaseDynamicLinks _firebaseDynamicLinks;

@override
Stream<Uri> get deepLinkStream =>
_firebaseDynamicLinks.onLink.map((event) => event.link);

@override
Future<Uri?> getInitialLink() async {
final deepLink = await _firebaseDynamicLinks.getInitialLink();
return deepLink?.link;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: deep_link_client
name: firebase_deep_link_client
description: A Dart package which provides a deep link stream
publish_to: none

environment:
sdk: ">=3.0.0 <4.0.0"

dependencies:
deep_link_client:
path: ../deep_link_client
equatable: ^2.0.3
firebase_core: ^3.4.1
firebase_dynamic_links: ^6.0.6
plugin_platform_interface: ^2.1.3
rxdart: ^0.27.3
rxdart: ^0.27.5

matiasleyba marked this conversation as resolved.
Show resolved Hide resolved
dev_dependencies:
firebase_core_platform_interface: ^5.2.1
Expand Down
Loading
Loading