diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/.gitignore b/packages/firebase_app_installations/firebase_app_installations_dart/.gitignore new file mode 100644 index 00000000..65c34dc8 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/.gitignore @@ -0,0 +1,10 @@ +# Files and directories created by pub. +.dart_tool/ +.packages + +# Conventional directory for build outputs. +build/ + +# Omit committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/CHANGELOG.md b/packages/firebase_app_installations/firebase_app_installations_dart/CHANGELOG.md new file mode 100644 index 00000000..effe43c8 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/README.md b/packages/firebase_app_installations/firebase_app_installations_dart/README.md new file mode 100644 index 00000000..8b55e735 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/analysis_options.yaml b/packages/firebase_app_installations/firebase_app_installations_dart/analysis_options.yaml new file mode 100644 index 00000000..dee8927a --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/lib/firebase_app_installations_dart.dart b/packages/firebase_app_installations/firebase_app_installations_dart/lib/firebase_app_installations_dart.dart new file mode 100644 index 00000000..24b134d7 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/lib/firebase_app_installations_dart.dart @@ -0,0 +1,10 @@ +// Copyright 2022 Invertase Limited. All rights reserved. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +/// Support for Firebase installations API in pure Dart. +library firebase_app_installations; + +import 'package:firebase_core_dart/firebase_core_dart.dart'; + +part 'src/firebase_app_installations.dart'; diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/api/api.dart b/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/api/api.dart new file mode 100644 index 00000000..5c4ca0c1 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/api/api.dart @@ -0,0 +1,68 @@ +library api; + +import 'package:firebaseapis/firebaseinstallations/v1.dart'; +import 'package:googleapis_auth/auth_io.dart'; +import 'package:http/http.dart' as http; + +/// Configurations necessary for making all Identity Toolkit requests. +class ApiConfig { + /// Construct [ApiConfig]. + ApiConfig( + this.apiKey, + this.projectId, + this.appId, + ); + + /// The API Key associated with the Firebase project used for initialization. + final String apiKey; + + /// The project Id associated with the Firebase project used for initialization. + final String projectId; + + /// The Firebase app Id of the current app. + final String appId; +} + +class ApiDelegate { + ApiDelegate._(this.apiConfig, {http.Client? client}) { + _client = client ?? clientViaApiKey(apiConfig.apiKey); + } + + /// Construct new or existing [API] instance for a given [APIConfig]. + factory ApiDelegate.instanceOf(ApiConfig apiConfig, {http.Client? client}) { + return _instances.putIfAbsent( + apiConfig, + () => ApiDelegate._(apiConfig, client: client), + ); + } + + /// The Api configurations of this instance. + final ApiConfig apiConfig; + + static final Map _instances = {}; + + late final http.Client _client; + + /// [FirebaseinstallationsApi] initialized with this instance's [ApiConfig]. + FirebaseinstallationsApi get installationsAPi { + return FirebaseinstallationsApi(_client); + } + + Future getId() async { + final response = await installationsAPi.projects.installations.create( + GoogleFirebaseInstallationsV1Installation(appId: apiConfig.appId), + apiConfig.projectId, + ); + + return response.fid; + } + + Future getAuthToken() async { + final response = await installationsAPi.projects.installations.create( + GoogleFirebaseInstallationsV1Installation(appId: apiConfig.appId), + apiConfig.projectId, + ); + + return response.fid; + } +} diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/firebase_app_installations.dart b/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/firebase_app_installations.dart new file mode 100644 index 00000000..7f1d5e96 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/firebase_app_installations.dart @@ -0,0 +1,25 @@ +part of firebase_app_installations; + +class FirebaseAppInstallationsDart { + /// The entry point for the [FirebaseAppInstallationsDart] class. + FirebaseAppInstallationsDart({FirebaseApp? app}); + + /// Stub initializer to allow the [registerWith] to create an instance without + /// registering the web delegates or listeners. + FirebaseAppInstallationsDart._(); + + /// Returns an instance of [FirebaseAppInstallationsDart]. + static FirebaseAppInstallationsDart get instance { + return FirebaseAppInstallationsDart._(); + } + + FirebaseAppInstallationsDart instanceFor({required FirebaseApp app}) { + return FirebaseAppInstallationsDart(app: app); + } + + // Future delete() async {} + + // Future getId() async {} + + // Future getToken(bool forceRefresh) async {} +} diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/helpers/generate_fid.dart b/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/helpers/generate_fid.dart new file mode 100644 index 00000000..9a91f2ae --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/lib/src/helpers/generate_fid.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +final fidPattern = RegExp(r'^[cdef][\w-]{21}$'); +const invalidFid = ''; + +/// Generates a new FID using random values. +/// Returns an empty string if FID generation fails for any reason. +String generateFid([Uint8List? byteArray]) { + try { + // A valid FID has exactly 22 base64 characters, which is 132 bits, or 16.5 + // bytes. This implementation generates a 17 byte array instead. + final fidByteArray = byteArray ?? _randBytes(17); + + // Replace the first 4 random bits with the constant FID header of 0b0111. + fidByteArray[0] = int.parse("01110000", radix: 2) + + (fidByteArray[0] % int.parse("00010000", radix: 2)); + + String fid = _encode(fidByteArray); + + return fidPattern.hasMatch(fid) ? fid : invalidFid; + } catch (_) { + // FID generation errored + return invalidFid; + } +} + +/// Converts a FID Uint8Array to a base64 string representation. +String _encode(Uint8List fidByteArray) { + String b64String = base64UrlEncode(fidByteArray); + + // Remove the 23rd character that was added because of the extra 4 bits at the + // end of our 17 byte array, and the '=' padding. + return b64String.substring(0, 22); +} + +Uint8List _randBytes(int n) { + final Random generator = Random.secure(); + final Uint8List random = Uint8List(n); + for (int i = 0; i < random.length; i++) { + random[i] = generator.nextInt(255); + } + return random; +} diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/pubspec.yaml b/packages/firebase_app_installations/firebase_app_installations_dart/pubspec.yaml new file mode 100644 index 00000000..8b6165bb --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/pubspec.yaml @@ -0,0 +1,20 @@ +name: firebase_app_installations_dart +description: Pure Dart implementation of FlutterFire Installations API +version: 1.0.0 +repository: https://github.com/invertase/flutterfire_desktop + +environment: + sdk: '>=2.17.0 <3.0.0' + +dependencies: + crypto: ^3.0.2 + firebase_core_dart: ^1.0.0 + firebaseapis: ^0.1.1 + googleapis_auth: ^1.1.0 + http: ^0.13.4 + meta: ^1.3.0 + storagebox: ^0.1.0+3 + +dev_dependencies: + lints: ^2.0.0 + test: ^1.16.0 diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/test/firebase_app_installations_dart_test.dart b/packages/firebase_app_installations/firebase_app_installations_dart/test/firebase_app_installations_dart_test.dart new file mode 100644 index 00000000..56a989be --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/test/firebase_app_installations_dart_test.dart @@ -0,0 +1,5 @@ +import 'generate_fid_test.dart'; + +void main() { + generateFidTests(); +} diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/test/generate_fid_test.dart b/packages/firebase_app_installations/firebase_app_installations_dart/test/generate_fid_test.dart new file mode 100644 index 00000000..389be167 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/test/generate_fid_test.dart @@ -0,0 +1,29 @@ +import 'dart:typed_data'; + +import 'package:firebase_app_installations_dart/src/helpers/generate_fid.dart'; +import 'package:test/expect.dart'; +import 'package:test/scaffolding.dart'; + +import 'mock_data.dart'; + +void generateFidTests() { + group( + 'generateFid()', + (() { + test('deterministically generates FIDs from known random byte list', () { + for (var i = 0; i < 12; i++) { + expect( + generateFid(Uint8List.fromList(mockRandomValues[i])), + equals(expectedFids[i]), + ); + } + }); + test('generate valid FIDs', () { + for (var i = 0; i < 1000; i++) { + final fid = generateFid(); + expect(fidPattern.hasMatch(fid), isTrue); + } + }); + }), + ); +} diff --git a/packages/firebase_app_installations/firebase_app_installations_dart/test/mock_data.dart b/packages/firebase_app_installations/firebase_app_installations_dart/test/mock_data.dart new file mode 100644 index 00000000..cb6a6934 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_dart/test/mock_data.dart @@ -0,0 +1,211 @@ +/// A few random values to generate a FID from. +const mockRandomValues = [ + [ + 14, + 107, + 44, + 183, + 190, + 84, + 253, + 45, + 219, + 233, + 43, + 190, + 240, + 152, + 195, + 222, + 237 + ], + [ + 184, + 251, + 91, + 157, + 125, + 225, + 209, + 15, + 116, + 66, + 46, + 113, + 194, + 126, + 16, + 13, + 226 + ], + [ + 197, + 123, + 13, + 142, + 239, + 129, + 252, + 139, + 156, + 36, + 219, + 192, + 153, + 52, + 182, + 231, + 177 + ], + [ + 69, + 154, + 197, + 91, + 156, + 196, + 125, + 111, + 3, + 67, + 212, + 132, + 169, + 11, + 14, + 254, + 125 + ], + [ + 193, + 102, + 58, + 19, + 244, + 69, + 36, + 135, + 170, + 106, + 98, + 216, + 246, + 209, + 24, + 155, + 149 + ], + [ + 252, + 59, + 222, + 160, + 82, + 160, + 82, + 186, + 14, + 172, + 196, + 114, + 146, + 191, + 196, + 194, + 146 + ], + [ + 64, + 147, + 153, + 236, + 225, + 142, + 235, + 109, + 184, + 249, + 174, + 127, + 33, + 238, + 227, + 172, + 111 + ], + [ + 129, + 137, + 136, + 120, + 248, + 206, + 253, + 78, + 159, + 201, + 216, + 15, + 246, + 80, + 118, + 185, + 211 + ], + [ + 117, + 150, + 2, + 180, + 116, + 230, + 45, + 188, + 183, + 43, + 152, + 100, + 50, + 255, + 101, + 175, + 190 + ], + [156, 129, 30, 101, 58, 137, 217, 249, 12, 227, 235, 80, 248, 81, 191, 2, 5], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255 + ], +]; + +/// The FIDs that should be generated based on MOCK_RANDOM_VALUES. +const expectedFids = [ + 'fmsst75U_S3b6Su-8JjD3u', + 'ePtbnX3h0Q90Qi5xwn4QDe', + 'dXsNju-B_IucJNvAmTS257', + 'dZrFW5zEfW8DQ9SEqQsO_n', + 'cWY6E_RFJIeqamLY9tEYm5', + 'fDveoFKgUroOrMRykr_Ewp', + 'cJOZ7OGO6224-a5_Ie7jrG', + 'cYmIePjO_U6fydgP9lB2ud', + 'dZYCtHTmLby3K5hkMv9lr7', + 'fIEeZTqJ2fkM4-tQ-FG_Ag', + 'cAAAAAAAAAAAAAAAAAAAAA', + 'f_____________________' +]; diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/.gitignore b/packages/firebase_app_installations/firebase_app_installations_desktop/.gitignore new file mode 100644 index 00000000..96486fd9 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/.metadata b/packages/firebase_app_installations/firebase_app_installations_desktop/.metadata new file mode 100644 index 00000000..c79dee43 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + channel: stable + +project_type: package diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/CHANGELOG.md b/packages/firebase_app_installations/firebase_app_installations_desktop/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/LICENSE b/packages/firebase_app_installations/firebase_app_installations_desktop/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/README.md b/packages/firebase_app_installations/firebase_app_installations_desktop/README.md new file mode 100644 index 00000000..8b55e735 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/analysis_options.yaml b/packages/firebase_app_installations/firebase_app_installations_desktop/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/lib/firebase_app_installations_desktop.dart b/packages/firebase_app_installations/firebase_app_installations_desktop/lib/firebase_app_installations_desktop.dart new file mode 100644 index 00000000..5e9785e8 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/lib/firebase_app_installations_desktop.dart @@ -0,0 +1,7 @@ +library firebase_app_installations_desktop; + +/// A Calculator. +class Calculator { + /// Returns [value] plus 1. + int addOne(int value) => value + 1; +} diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/pubspec.yaml b/packages/firebase_app_installations/firebase_app_installations_desktop/pubspec.yaml new file mode 100644 index 00000000..5939cef1 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/pubspec.yaml @@ -0,0 +1,39 @@ +name: firebase_app_installations_desktop +description: Windows and Linux implementation of firebase_app_installations +version: 0.0.1 +homepage: null +repository: https://github.com/invertase/flutterfire_desktop + +environment: + sdk: '>=2.17.0 <3.0.0' + flutter: '>=1.17.0' + +dependencies: + firebase_app_installations: ^0.1.0+13 + firebase_app_installations_dart: + path: ../firebase_app_installations_dart + firebase_app_installations_platform_interface: ^0.1.1+6 + firebase_core: ^1.17.0 + firebase_core_dart: ^1.0.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_lints: ^2.0.0 + flutter_test: + sdk: flutter + +flutter: + plugin: + implements: firebase_app_installations + platforms: + # TODO when doing development on macOS, uncomment this. + # macos: + # dartPluginClass: FirebaseInstallationsDesktop + # pluginClass: none + linux: + dartPluginClass: FirebaseInstallationsDesktop + pluginClass: none + windows: + dartPluginClass: FirebaseInstallationsDesktop + pluginClass: none diff --git a/packages/firebase_app_installations/firebase_app_installations_desktop/test/firebase_app_installations_desktop_test.dart b/packages/firebase_app_installations/firebase_app_installations_desktop/test/firebase_app_installations_desktop_test.dart new file mode 100644 index 00000000..9202bbfa --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_desktop/test/firebase_app_installations_desktop_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:firebase_app_installations_desktop/firebase_app_installations_desktop.dart'; + +void main() { + test('adds one to input values', () { + final calculator = Calculator(); + expect(calculator.addOne(2), 3); + expect(calculator.addOne(-7), -6); + expect(calculator.addOne(0), 1); + }); +}