From 91fd4ea7a749c6456f5a83579aa5883c8d559f3c Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 21 Nov 2020 23:05:42 +0100 Subject: [PATCH 01/24] fix Credential.getAccessToken (returns a promise) --- lib/src/bindings.dart | 4 +++- test/admin_test.dart | 9 +++++++++ test/firestore_test.dart | 12 ++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/src/bindings.dart b/lib/src/bindings.dart index ca3204b..2642556 100644 --- a/lib/src/bindings.dart +++ b/lib/src/bindings.dart @@ -116,7 +116,9 @@ abstract class ServiceAccountConfig { abstract class Credential { /// Returns a Google OAuth2 [AccessToken] object used to authenticate with /// Firebase services. - external AccessToken getAccessToken(); + /// + /// Returns Promise. + external Promise getAccessToken(); } /// Google OAuth2 access token object used to authenticate with Firebase diff --git a/test/admin_test.dart b/test/admin_test.dart index 1d65f6f..2b220c1 100644 --- a/test/admin_test.dart +++ b/test/admin_test.dart @@ -3,6 +3,8 @@ @TestOn('node') import 'package:firebase_admin_interop/firebase_admin_interop.dart'; +import 'package:firebase_admin_interop/js.dart' as js; +import 'package:node_interop/util.dart'; import 'package:test/test.dart'; import 'setup.dart'; @@ -22,5 +24,12 @@ void main() { test('app name', () { expect(app.name, '[DEFAULT]'); }); + + test('accessToken', () async { + var accessToken = await promiseToFuture( + js.admin.credential.applicationDefault().getAccessToken()) + as js.AccessToken; + expect(accessToken.access_token, isNotEmpty); + }); }); } diff --git a/test/firestore_test.dart b/test/firestore_test.dart index a075cac..4566ef1 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -903,7 +903,8 @@ void main() { await doc3Ref.setData(new DocumentData()..setInt('value', 3)); await doc4Ref.setData(new DocumentData()..setInt('value', doc4Value)); - await Future.delayed(Duration(seconds: 1)); // to avoid too much contention errors + await Future.delayed( + Duration(seconds: 1)); // to avoid too much contention errors List list = await app.firestore().runTransaction((Transaction tx) async { @@ -955,14 +956,16 @@ void main() { var doc1UpdateTime1 = (await doc1Ref.get()).updateTime; var doc2UpdateTime1 = (await doc2Ref.get()).updateTime; - await Future.delayed(Duration(seconds: 1)); // to avoid too much contention errors + await Future.delayed( + Duration(seconds: 1)); // to avoid too much contention errors await doc1Ref.setData(new DocumentData()..setInt('value', 10)); await doc2Ref.setData(new DocumentData()..setInt('value', 20)); var doc1UpdateTime2 = (await doc1Ref.get()).updateTime; var doc2UpdateTime2 = (await doc2Ref.get()).updateTime; - await Future.delayed(Duration(seconds: 1)); // to avoid too much contention errors + await Future.delayed( + Duration(seconds: 1)); // to avoid too much contention errors Future result = app.firestore().runTransaction((Transaction tx) async { var doc2 = (await tx.get(doc2Ref)).data; @@ -973,7 +976,8 @@ void main() { }); var error = await result.catchError((error) => error); - expect(error.toString(), contains('does not match the required base version')); + expect(error.toString(), + contains('does not match the required base version')); expect((await doc1Ref.get()).data.toMap(), {'value': 10}); expect((await doc2Ref.get()).data.toMap(), {'value': 20}); From 739f29df9584f9213de3ee896a5974d88070b671 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 15 Apr 2021 09:53:00 +0200 Subject: [PATCH 02/24] pre nnbd --- lib/src/firestore.dart | 4 ++-- pubspec.yaml | 10 +++++----- test/firestore_test.dart | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 406cc7d..6d24d56 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -10,7 +10,7 @@ import 'package:meta/meta.dart'; import 'package:node_interop/js.dart'; import 'package:node_interop/node.dart'; import 'package:node_interop/util.dart'; -import 'package:quiver_hashcode/hashcode.dart'; +import 'package:quiver/core.dart'; import 'bindings.dart' as js; @@ -541,7 +541,7 @@ class _FirestoreData { if (data is! List) { throw new StateError('Expected list but got ${data.runtimeType}.'); } - final result = new List(); + final result = []; for (var item in data) { item = _dartify(item); result.add(item); diff --git a/pubspec.yaml b/pubspec.yaml index 733cba1..31987b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,12 +9,12 @@ environment: dependencies: js: ^0.6.1+1 - node_interop: ^1.0.3 + node_interop: ^2.0.0 meta: ^1.1.8 - quiver_hashcode: ^2.0.0 + quiver: ^3.0.0 dev_dependencies: test: ^1.12.0 - build_runner: ^1.7.4 - build_node_compilers: ^0.2.4 - build_test: ^0.10.12+1 + # build_runner: ^1.7.4 + # build_node_compilers: ^0.2.4 + # build_test: ^0.10.12+1 diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 4566ef1..e7381db 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -1001,9 +1001,9 @@ void main() { var doc1Ref = collRef.document('counter'); await doc1Ref.setData(new DocumentData()..setInt('value', 1)); - List> futures = new List(); - List errors = new List(); - List complete = new List(); + List> futures = []; + List errors = []; + List complete = []; var futuresCount = 5; for (int i = 0; i < futuresCount; i++) { From 11774740d70293ba906d01e4b224a60b767dcea4 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 15 Apr 2021 10:04:00 +0200 Subject: [PATCH 03/24] basic nnbd migration --- lib/src/admin.dart | 22 +- lib/src/app.dart | 8 +- lib/src/auth.dart | 10 +- lib/src/bindings.dart | 384 ++++++++++++++++---------------- lib/src/database.dart | 41 ++-- lib/src/firestore.dart | 211 ++++++++---------- lib/src/firestore_bindings.dart | 95 ++++---- lib/src/messaging.dart | 14 +- lib/src/storage_bindings.dart | 14 +- pubspec.yaml | 2 +- test/admin_test.dart | 8 +- test/auth_test.dart | 21 +- test/database_test.dart | 22 +- test/firestore_test.dart | 78 +++---- test/setup.dart | 2 +- 15 files changed, 456 insertions(+), 476 deletions(-) diff --git a/lib/src/admin.dart b/lib/src/admin.dart index f917c7f..bec5e19 100644 --- a/lib/src/admin.dart +++ b/lib/src/admin.dart @@ -1,8 +1,6 @@ // Copyright (c) 2017, Anatoly Pulyaevskiy. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. -import 'package:meta/meta.dart'; - import 'app.dart'; import 'bindings.dart' as js; @@ -11,11 +9,11 @@ import 'bindings.dart' as js; /// To start using Firebase services initialize a Firebase application /// with [initializeApp] method. class FirebaseAdmin { - final js.FirebaseAdmin _admin; + final js.FirebaseAdmin? _admin; final Map _apps = new Map(); static FirebaseAdmin get instance => _instance ??= new FirebaseAdmin._(); - static FirebaseAdmin _instance; + static FirebaseAdmin? _instance; FirebaseAdmin._() : _admin = js.admin; @@ -47,30 +45,30 @@ class FirebaseAdmin { /// * [App] /// * [cert] /// * [certFromPath] - App initializeApp([js.AppOptions options, String name]) { + App? initializeApp([js.AppOptions? options, String? name]) { if (options == null && name == null) { // Special case for default app with Application Default Credentials. name = js.defaultAppName; if (!_apps.containsKey(name)) { - _apps[name] = new App(_admin.initializeApp()); + _apps[name] = new App(_admin!.initializeApp()); } return _apps[name]; } name ??= js.defaultAppName; if (!_apps.containsKey(name)) { - _apps[name] = new App(_admin.initializeApp(options, name)); + _apps[name] = new App(_admin!.initializeApp(options, name)); } return _apps[name]; } /// Creates [App] certificate. js.Credential cert({ - @required String projectId, - @required String clientEmail, - @required String privateKey, + required String? projectId, + required String? clientEmail, + required String? privateKey, }) { - return _admin.credential.cert(new js.ServiceAccountConfig( + return _admin!.credential.cert(new js.ServiceAccountConfig( project_id: projectId, client_email: clientEmail, private_key: privateKey, @@ -79,6 +77,6 @@ class FirebaseAdmin { /// Creates app certificate from service account file at specified [path]. js.Credential certFromPath(String path) { - return _admin.credential.cert(path); + return _admin!.credential.cert(path); } } diff --git a/lib/src/app.dart b/lib/src/app.dart index 0da67a7..9b2372e 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -30,22 +30,22 @@ class App { /// Gets the [Auth] service for this application. Auth auth() => _auth ??= new Auth(nativeInstance.auth()); - Auth _auth; + Auth? _auth; /// Gets Realtime [Database] client for this application. Database database() => _database ??= new Database(this.nativeInstance.database(), this); - Database _database; + Database? _database; /// Gets [Firestore] client for this application. Firestore firestore() => _firestore ??= new Firestore(nativeInstance.firestore()); - Firestore _firestore; + Firestore? _firestore; /// Gets [Messaging] client for this application. Messaging messaging() => _messaging ??= new Messaging(nativeInstance.messaging()); - Messaging _messaging; + Messaging? _messaging; /// Renders this app unusable and frees the resources of all associated /// services. diff --git a/lib/src/auth.dart b/lib/src/auth.dart index 7b77e6d..0ecc734 100644 --- a/lib/src/auth.dart +++ b/lib/src/auth.dart @@ -39,9 +39,9 @@ class Auth { /// Returns a [Future] containing a custom token string for the provided [uid] /// and payload. Future createCustomToken(String uid, - [Map developerClaims]) => - promiseToFuture( - nativeInstance.createCustomToken(uid, jsify(developerClaims))); + [Map? developerClaims]) => + promiseToFuture(nativeInstance.createCustomToken( + uid, jsify(developerClaims ?? {}))); /// Creates a new user. Future createUser(CreateUserRequest properties) => @@ -67,7 +67,7 @@ class Auth { /// and starting from the offset as specified by [pageToken]. /// /// This is used to retrieve all the users of a specified project in batches. - Future listUsers([num maxResults, String pageToken]) { + Future listUsers([num? maxResults, String? pageToken]) { if (pageToken != null && maxResults != null) { return promiseToFuture(nativeInstance.listUsers(maxResults, pageToken)); } else if (maxResults != null) { @@ -120,7 +120,7 @@ class Auth { /// of [DecodedIdToken]; otherwise, the future is completed with an error. /// An optional flag can be passed to additionally check whether the ID token /// was revoked. - Future verifyIdToken(String idToken, [bool checkRevoked]) { + Future verifyIdToken(String idToken, [bool? checkRevoked]) { if (checkRevoked != null) { return promiseToFuture( nativeInstance.verifyIdToken(idToken, checkRevoked)); diff --git a/lib/src/bindings.dart b/lib/src/bindings.dart index 2642556..a8d4a7f 100644 --- a/lib/src/bindings.dart +++ b/lib/src/bindings.dart @@ -15,13 +15,13 @@ export 'firestore_bindings.dart'; const defaultAppName = '[DEFAULT]'; /// Singleton instance of [FirebaseAdmin] module. -final FirebaseAdmin admin = require('firebase-admin'); +final FirebaseAdmin? admin = require('firebase-admin'); @JS() @anonymous abstract class FirebaseAdmin { /// Creates and initializes a Firebase app instance. - external App initializeApp([options, String name]); + external App initializeApp([options, String? name]); /// The current SDK version. external String get SDK_VERSION; @@ -36,10 +36,10 @@ abstract class FirebaseAdmin { /// /// An exception is thrown if the app being retrieved has not yet been /// initialized. - external App app([String name]); + external App app([String? name]); /// Gets the [Auth] service for the default app or a given [app]. - external Auth auth([App app]); + external Auth auth([App? app]); /// Gets the [Database] service for the default app or a given [app]. external DatabaseService get database; @@ -50,7 +50,7 @@ abstract class FirebaseAdmin { external Credentials get credential; /// Gets the [Messaging] service for the default app or a given [app]. - external Messaging messaging([App app]); + external Messaging messaging([App? app]); } @JS() @@ -106,7 +106,7 @@ abstract class ServiceAccountConfig { external String get private_key; external factory ServiceAccountConfig( - {String project_id, String client_email, String private_key}); + {String? project_id, String? client_email, String? private_key}); } /// Interface which provides Google OAuth2 access tokens used to authenticate @@ -190,10 +190,10 @@ abstract class AppOptions { /// Creates new instance of [AppOptions]. external factory AppOptions({ - Credential credential, - String databaseURL, - String projectId, - String storageBucket, + Credential? credential, + String? databaseURL, + String? projectId, + String? storageBucket, }); } @@ -250,7 +250,7 @@ abstract class Auth { /// /// Returns a promise that resolves with the current batch of downloaded users /// and the next page token as an instance of [ListUsersResult]. - external Promise listUsers([num maxResults, String pageToken]); + external Promise listUsers([num? maxResults, String? pageToken]); /// Revokes all refresh tokens for an existing user. /// @@ -291,7 +291,7 @@ abstract class Auth { /// If the token is valid, the returned promise is fulfilled with an instance of /// [DecodedIdToken]; otherwise, the promise is rejected. An optional flag can /// be passed to additionally check whether the ID token was revoked. - external Promise verifyIdToken(String idToken, [bool checkRevoked]); + external Promise verifyIdToken(String idToken, [bool? checkRevoked]); } @JS() @@ -307,14 +307,14 @@ abstract class CreateUserRequest { external String get uid; external factory CreateUserRequest({ - bool disabled, - String displayName, - String email, - bool emailVerified, - String password, - String phoneNumber, - String photoURL, - String uid, + bool? disabled, + String? displayName, + String? email, + bool? emailVerified, + String? password, + String? phoneNumber, + String? photoURL, + String? uid, }); } @@ -330,13 +330,13 @@ abstract class UpdateUserRequest { external String get photoURL; external factory UpdateUserRequest({ - bool disabled, - String displayName, - String email, - bool emailVerified, - String password, - String phoneNumber, - String photoURL, + bool? disabled, + String? displayName, + String? email, + bool? emailVerified, + String? password, + String? phoneNumber, + String? photoURL, }); } @@ -557,27 +557,27 @@ abstract class Messaging { /// /// Returns Promise fulfilled with a unique message ID string after the /// message has been successfully handed off to the FCM service for delivery - external Promise send(FcmMessage message, [bool dryRun]); + external Promise send(FcmMessage message, [bool? dryRun]); /// Sends all the messages in the given array via Firebase Cloud Messaging. /// /// Returns Promise fulfilled with an object representing the /// result of the send operation. - external Promise sendAll(List messages, [bool dryRun]); + external Promise sendAll(List messages, [bool? dryRun]); /// Sends the given multicast message to all the FCM registration tokens /// specified in it. /// /// Returns Promise fulfilled with an object representing the /// result of the send operation. - external Promise sendMulticast(MulticastMessage message, [bool dryRun]); + external Promise sendMulticast(MulticastMessage message, [bool? dryRun]); /// Sends an FCM message to a condition. /// /// Returns Promise fulfilled with the server's /// response after the message has been sent. external Promise sendToCondition(String condition, MessagingPayload payload, - [MessagingOptions options]); + [MessagingOptions? options]); /// Sends an FCM message to a single device corresponding to the provided /// registration token. @@ -586,7 +586,7 @@ abstract class Messaging { /// response after the message has been sent. external Promise sendToDevice( String registrationToken, MessagingPayload payload, - [MessagingOptions options]); + [MessagingOptions? options]); /// Sends an FCM message to a device group corresponding to the provided /// notification key. @@ -595,14 +595,14 @@ abstract class Messaging { /// response after the message has been sent. external Promise sendToDeviceGroup( String notificationKey, MessagingPayload payload, - [MessagingOptions options]); + [MessagingOptions? options]); /// Sends an FCM message to a topic. /// /// Returns Promise fulfilled with the server's /// response after the message has been sent. external Promise sendToTopic(String topic, MessagingPayload payload, - [MessagingOptions options]); + [MessagingOptions? options]); /// Subscribes a device to an FCM topic. /// @@ -626,9 +626,9 @@ abstract class FcmMessage { external String get token; external factory FcmMessage({ - String data, - Notification notification, - String token, + String? data, + Notification? notification, + String? token, }); } @@ -647,14 +647,14 @@ abstract class TopicMessage { external WebpushConfig get webpush; external factory TopicMessage({ - AndroidConfig android, - ApnsConfig apns, + AndroidConfig? android, + ApnsConfig? apns, dynamic data, - String key, - FcmOptions fcmOptions, - Notification notification, - String topic, - WebpushConfig webpush, + String? key, + FcmOptions? fcmOptions, + Notification? notification, + String? topic, + WebpushConfig? webpush, }); } @@ -673,14 +673,14 @@ abstract class TokenMessage { external WebpushConfig get webpush; external factory TokenMessage({ - AndroidConfig android, - ApnsConfig apns, + AndroidConfig? android, + ApnsConfig? apns, dynamic data, - String key, - FcmOptions fcmOptions, - Notification notification, - String token, - WebpushConfig webpush, + String? key, + FcmOptions? fcmOptions, + Notification? notification, + String? token, + WebpushConfig? webpush, }); } @@ -699,14 +699,14 @@ abstract class ConditionMessage { external WebpushConfig get webpush; external factory ConditionMessage({ - AndroidConfig android, - ApnsConfig apns, - String condition, + AndroidConfig? android, + ApnsConfig? apns, + String? condition, dynamic data, - String key, - FcmOptions fcmOptions, - Notification notification, - WebpushConfig webpush, + String? key, + FcmOptions? fcmOptions, + Notification? notification, + WebpushConfig? webpush, }); } @@ -725,14 +725,14 @@ abstract class MulticastMessage { external WebpushConfig get webpush; external factory MulticastMessage({ - AndroidConfig android, - ApnsConfig apns, + AndroidConfig? android, + ApnsConfig? apns, dynamic data, - String key, - FcmOptions fcmOptions, - Notification notification, - List tokens, - WebpushConfig webpush, + String? key, + FcmOptions? fcmOptions, + Notification? notification, + List? tokens, + WebpushConfig? webpush, }); } @@ -750,9 +750,9 @@ abstract class Notification { external String get title; external factory Notification({ - String body, - String imageUrl, - String title, + String? body, + String? imageUrl, + String? title, }); } @@ -818,21 +818,21 @@ abstract class WebpushNotification { external num get vibrate; external factory WebpushNotification({ - List actions, - String badge, - String body, + List? actions, + String? badge, + String? body, dynamic data, - String dir, - String icon, - String image, - String lang, - bool renotify, - bool requireInteraction, - bool silent, - String tag, - num timestamp, - String title, - num vibrate, + String? dir, + String? icon, + String? image, + String? lang, + bool? renotify, + bool? requireInteraction, + bool? silent, + String? tag, + num? timestamp, + String? title, + num? vibrate, }); } @@ -855,9 +855,9 @@ abstract class WebpushConfig { external factory WebpushConfig({ dynamic data, - FcmOptions fcmOptions, + FcmOptions? fcmOptions, dynamic headers, - WebpushNotification notification, + WebpushNotification? notification, }); } @@ -871,7 +871,7 @@ abstract class WebpushFcmOptions { external String get link; external factory WebpushFcmOptions({ - String link, + String? link, }); } @@ -883,7 +883,7 @@ abstract class FcmOptions { external String get analyticsLabel; external factory FcmOptions({ - String analyticsLabel, + String? analyticsLabel, }); } @@ -899,8 +899,8 @@ abstract class MessagingPayload { external NotificationMessagePayload get notification; external factory MessagingPayload({ - DataMessagePayload data, - NotificationMessagePayload notification, + DataMessagePayload? data, + NotificationMessagePayload? notification, }); } @@ -917,7 +917,7 @@ abstract class DataMessagePayload { external dynamic get value; external factory DataMessagePayload({ - String key, + String? key, dynamic value, }); } @@ -981,19 +981,19 @@ abstract class NotificationMessagePayload { external String get titleLocKey; external factory NotificationMessagePayload({ - List actions, - String badge, - String body, - String bodyLocArgs, - String bodyLocKey, - String clickAction, - String color, - String icon, - String sound, - String tag, - String title, - String titleLocArgs, - String titleLocKey, + List? actions, + String? badge, + String? body, + String? bodyLocArgs, + String? bodyLocKey, + String? clickAction, + String? color, + String? icon, + String? sound, + String? tag, + String? title, + String? titleLocArgs, + String? titleLocKey, }); } @@ -1045,13 +1045,13 @@ abstract class MessagingOptions { external num get timeToLive; external factory MessagingOptions({ - String collapseKey, - bool contentAvailable, - bool dryRun, - bool mutableContent, - String priority, - String restrictedPackageName, - num timeToLive, + String? collapseKey, + bool? contentAvailable, + bool? dryRun, + bool? mutableContent, + String? priority, + String? restrictedPackageName, + num? timeToLive, }); } @@ -1091,13 +1091,13 @@ abstract class AndroidConfig { external num get ttl; external factory AndroidConfig({ - String collapseKey, + String? collapseKey, dynamic data, - AndroidFcmOptions fcmOptions, - AndroidNotification notification, - String priority, - String restrictedPackageName, - num ttl, + AndroidFcmOptions? fcmOptions, + AndroidNotification? notification, + String? priority, + String? restrictedPackageName, + num? ttl, }); } @@ -1109,7 +1109,7 @@ abstract class AndroidFcmOptions { external String get analyticsLabel; external factory AndroidFcmOptions({ - String analyticsLabel, + String? analyticsLabel, }); } @@ -1172,19 +1172,19 @@ abstract class AndroidNotification { external String get titleLocKey; external factory AndroidNotification({ - String body, - List bodyLocArgs, - String bodyLocKey, - String channelId, - String clickAction, - String color, - String icon, - String imageUrl, - String sound, - String tag, - String title, - List titleLocArgs, - String titleLocKey, + String? body, + List? bodyLocArgs, + String? bodyLocKey, + String? channelId, + String? clickAction, + String? color, + String? icon, + String? imageUrl, + String? sound, + String? tag, + String? title, + List? titleLocArgs, + String? titleLocKey, }); } @@ -1203,9 +1203,9 @@ abstract class ApnsConfig { external ApnsPayload get payload; external factory ApnsConfig({ - ApnsFcmOptions fcmOptions, + ApnsFcmOptions? fcmOptions, dynamic headers, - ApnsPayload payload, + ApnsPayload? payload, }); } @@ -1219,7 +1219,7 @@ abstract class ApnsFcmOptions { /// URL of an image to be displayed in the notification. external String get imageUrl; - external factory ApnsFcmOptions({String analyticsLabel, String imageUrl}); + external factory ApnsFcmOptions({String? analyticsLabel, String? imageUrl}); } /// Represents options for features provided by the FCM SDK for iOS. @@ -1230,7 +1230,7 @@ abstract class ApnsPayload { external Aps get aps; external factory ApnsPayload({ - Aps aps, + Aps? aps, }); } @@ -1265,12 +1265,12 @@ abstract class Aps { external String get threadId; external factory Aps({ - String alert, - num badge, - String category, - bool contentAvailable, - bool mutableContent, - String sound, + String? alert, + num? badge, + String? category, + bool? contentAvailable, + bool? mutableContent, + String? sound, }); } @@ -1290,17 +1290,17 @@ abstract class ApsAlert { external String get titleLocKey; external factory ApsAlert({ - String actionLocKey, - String body, - String launchImage, - List locArgs, - String locKey, - String subtitle, - List subtitleLocArgs, - String subtitleLocKey, - String title, - List titleLocArgs, - String titleLocKey, + String? actionLocKey, + String? body, + String? launchImage, + List? locArgs, + String? locKey, + String? subtitle, + List? subtitleLocArgs, + String? subtitleLocKey, + String? title, + List? titleLocArgs, + String? titleLocKey, }); } @@ -1324,9 +1324,9 @@ abstract class CriticalSound { external num get volume; external factory CriticalSound({ - bool critical, - String name, - num volume, + bool? critical, + String? name, + num? volume, }); } @@ -1345,9 +1345,9 @@ abstract class BatchResponse { external num get successCount; external factory BatchResponse({ - num failureCount, - List responses, - num successCount, + num? failureCount, + List? responses, + num? successCount, }); } @@ -1371,9 +1371,9 @@ abstract class SendResponse { external bool get success; external factory SendResponse({ - FirebaseError error, - String messageId, - bool success, + FirebaseError? error, + String? messageId, + bool? success, }); } @@ -1387,7 +1387,7 @@ abstract class MessagingConditionResponse { external num get messageId; external factory MessagingConditionResponse({ - num messageId, + num? messageId, }); } @@ -1406,9 +1406,9 @@ abstract class MessagingDeviceGroupResponse { external num get successCount; external factory MessagingDeviceGroupResponse({ - List failedRegistrationTokens, - num failureCount, - num successCount, + List? failedRegistrationTokens, + num? failureCount, + num? successCount, }); } @@ -1431,9 +1431,9 @@ abstract class MessagingDeviceResult { external String get messageId; external factory MessagingDeviceResult({ - String canonicalRegistrationToken, - FirebaseError error, - String messageId, + String? canonicalRegistrationToken, + FirebaseError? error, + String? messageId, }); } @@ -1470,11 +1470,11 @@ abstract class MessagingDevicesResponse { external num get successCount; external factory MessagingDevicesResponse({ - num canonicalRegistrationTokenCount, - num failureCount, - num multicastId, - List results, - num successCount, + num? canonicalRegistrationTokenCount, + num? failureCount, + num? multicastId, + List? results, + num? successCount, }); } @@ -1488,7 +1488,7 @@ abstract class MessagingTopicResponse { external num get messageId; external factory MessagingTopicResponse({ - num messageId, + num? messageId, }); } @@ -1512,9 +1512,9 @@ abstract class MessagingTopicManagementResponse { external num get successCount; external factory MessagingTopicManagementResponse({ - List errors, - num failureCount, - num successCount, + List? errors, + num? failureCount, + num? successCount, }); } @@ -1527,7 +1527,7 @@ abstract class DatabaseService { // external Database call([App app]); /// Logs debugging information to the console. - external enableLogging([dynamic loggerOrBool, bool persistent]); + external enableLogging([dynamic loggerOrBool, bool? persistent]); external ServerValues get ServerValue; } @@ -1559,7 +1559,7 @@ abstract class Database { /// Returns a [Reference] representing the location in the Database /// corresponding to the provided [path]. If no path is provided, the /// Reference will point to the root of the Database. - external Reference ref([String path]); + external Reference ref([String? path]); /// Returns a Reference representing the location in the Database /// corresponding to the provided Firebase URL. @@ -1611,7 +1611,7 @@ abstract class Reference extends Query { /// so the resulting list of items will be chronologically sorted. The keys /// are also designed to be unguessable (they contain 72 random bits of /// entropy). - external ThenableReference push([value, onComplete(JsError error)]); + external ThenableReference push([value, onComplete(JsError error)?]); /// Removes the data at this Database location. /// @@ -1622,7 +1622,7 @@ abstract class Reference extends Query { /// Firebase servers will also be started, and the returned [Promise] will /// resolve when complete. If provided, the [onComplete] callback will be /// called asynchronously after synchronization has finished. - external Promise remove([onComplete(JsError error)]); + external Promise remove([onComplete(JsError error)?]); /// Writes data to this Database location. /// @@ -1646,7 +1646,7 @@ abstract class Reference extends Query { /// /// A single [set] will generate a single "value" event at the location where /// the `set()` was performed. - external Promise set(value, [onComplete(JsError error)]); + external Promise set(value, [onComplete(JsError error)?]); /// Sets a priority for the data at this Database location. /// @@ -1655,7 +1655,7 @@ abstract class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - external Promise setPriority(priority, [onComplete(JsError error)]); + external Promise setPriority(priority, [onComplete(JsError error)?]); /// Writes data the Database location. Like [set] but also specifies the /// [priority] for that data. @@ -1666,7 +1666,7 @@ abstract class Reference extends Query { /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) external Promise setWithPriority(value, priority, - [onComplete(JsError error)]); + [onComplete(JsError error)?]); /// Atomically modifies the data at this location. /// @@ -1722,7 +1722,7 @@ abstract class Reference extends Query { /// [transaction] to modify the same data. /// /// Passing `null` to [update] will remove the data at this location. - external Promise update(values, [onComplete(JsError error)]); + external Promise update(values, [onComplete(JsError error)?]); } @JS() @@ -1766,7 +1766,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise cancel([onComplete(JsError error)]); + external Promise cancel([onComplete(JsError error)?]); /// Ensures the data at this location is deleted when the client is /// disconnected (due to closing the browser, navigating to a new page, or @@ -1775,7 +1775,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise remove([onComplete(JsError error)]); + external Promise remove([onComplete(JsError error)?]); /// Ensures the data at this location is set to the specified [value] when the /// client is disconnected (due to closing the browser, navigating to a new @@ -1791,13 +1791,13 @@ abstract class OnDisconnect { /// /// See also: /// - [Enabling Offline Capabilities in JavaScript](https://firebase.google.com/docs/database/web/offline-capabilities) - external Promise set(value, [onComplete(JsError error)]); + external Promise set(value, [onComplete(JsError error)?]); /// Ensures the data at this location is set to the specified [value] and /// [priority] when the client is disconnected (due to closing the browser, /// navigating to a new page, or network issues). external Promise setWithPriority(value, priority, - [onComplete(JsError error)]); + [onComplete(JsError error)?]); /// Writes multiple [values] at this location when the client is disconnected /// (due to closing the browser, navigating to a new page, or network issues). @@ -1813,7 +1813,7 @@ abstract class OnDisconnect { /// /// See [Reference.update] for examples of using the connected version of /// update. - external Promise update(values, [onComplete(JsError error)]); + external Promise update(values, [onComplete(JsError error)?]); } /// Sorts and filters the data at a [Database] location so only a subset of the @@ -1854,7 +1854,7 @@ abstract class Query { /// Optional [key] is only allowed if ordering by priority and defines the /// child key to end at, among the children with the previously specified /// priority. - external Query endAt(dynamic value, [String key]); + external Query endAt(dynamic value, [String? key]); /// Creates a [Query] that includes children that match the specified value. /// @@ -1869,7 +1869,7 @@ abstract class Query { /// query. If it is specified, then children that have exactly the specified /// value must also have exactly the specified key as their key name. This can /// be used to filter result sets with many matches for the same value. - external Query equalTo(dynamic value, [String key]); + external Query equalTo(dynamic value, [String? key]); /// Returns `true` if this and [other] query are equal. /// @@ -1918,7 +1918,7 @@ abstract class Query { /// If a [callback] is not specified, all callbacks for the specified /// [eventType] will be removed. Similarly, if no [eventType] or [callback] is /// specified, all callbacks for the [Reference] will be removed. - external void off([String eventType, callback, context]); + external void off([String? eventType, callback, context]); /// Listens for data changes at a particular location. /// @@ -1980,7 +1980,7 @@ abstract class Query { /// /// See also: /// - [Filtering data](https://firebase.google.com/docs/database/web/lists-of-data#filtering_data) - external Query startAt(dynamic value, [String key]); + external Query startAt(dynamic value, [String? key]); /// Returns a JSON-serializable representation of this object. external dynamic toJSON(); diff --git a/lib/src/database.dart b/lib/src/database.dart index 0a87d95..e7dd0ce 100644 --- a/lib/src/database.dart +++ b/lib/src/database.dart @@ -32,7 +32,7 @@ class Database { /// Returns a [Reference] representing the location in the Database /// corresponding to the provided [path]. If no path is provided, the /// Reference will point to the root of the Database. - Reference ref([String path]) => new Reference(nativeInstance.ref(path)); + Reference ref([String? path]) => new Reference(nativeInstance.ref(path)); /// Returns a [Reference] representing the location in the Database /// corresponding to the provided Firebase URL. @@ -124,7 +124,7 @@ class Query { /// Returns a [Reference] to the [Query]'s location. Reference get ref => _ref ??= new Reference(nativeInstance.ref); - Reference _ref; + Reference? _ref; /// Creates a [Query] with the specified ending point. /// @@ -143,7 +143,7 @@ class Query { /// Optional [key] is only allowed if ordering by priority and defines the /// child key to end at, among the children with the previously specified /// priority. - Query endAt(value, [String key]) { + Query endAt(value, [String? key]) { if (key == null) { return new Query(nativeInstance.endAt(value)); } @@ -163,7 +163,7 @@ class Query { /// query. If it is specified, then children that have exactly the specified /// value must also have exactly the specified key as their key name. This can /// be used to filter result sets with many matches for the same value. - Query equalTo(value, [String key]) { + Query equalTo(value, [String? key]) { if (key == null) { return new Query(nativeInstance.equalTo(value)); } @@ -225,7 +225,7 @@ class Query { /// specified, [eventType] must be one of [EventType] constants. /// /// To unsubscribe a specific callback, use [QuerySubscription.cancel]. - void off([String eventType]) { + void off([String? eventType]) { if (eventType != null) { nativeInstance.off(eventType); } else { @@ -244,7 +244,7 @@ class Query { /// Returns [QuerySubscription] which can be used to cancel the subscription. QuerySubscription on( String eventType, Function(DataSnapshot snapshot) callback, - [Function() cancelCallback]) { + [Function()? cancelCallback]) { var fn = allowInterop((snapshot) => callback(new DataSnapshot(snapshot))); if (cancelCallback != null) { nativeInstance.on(eventType, fn, allowInterop(cancelCallback)); @@ -299,7 +299,7 @@ class Query { /// /// See also: /// - [Filtering data](https://firebase.google.com/docs/database/web/lists-of-data#filtering_data) - Query startAt(value, [String key]) { + Query startAt(value, [String? key]) { if (key == null) { return new Query(nativeInstance.startAt(value)); } @@ -330,7 +330,7 @@ class Reference extends Query { @override @protected - js.Reference get nativeInstance => super.nativeInstance; + js.Reference get nativeInstance => super.nativeInstance as js.Reference; /// The last part of this Reference's path. /// @@ -342,11 +342,11 @@ class Reference extends Query { /// /// The parent of a root Reference is `null`. Reference get parent => _parent ??= new Reference(nativeInstance.parent); - Reference _parent; + Reference? _parent; /// The root [Reference] of the [Database]. Reference get root => _root ??= new Reference(nativeInstance.root); - Reference _root; + Reference? _root; /// Gets a [Reference] for the location at the specified relative [path]. /// @@ -376,7 +376,7 @@ class Reference extends Query { /// so the resulting list of items will be chronologically sorted. The keys /// are also designed to be unguessable (they contain 72 random bits of /// entropy). - FutureReference push([T value]) { + FutureReference push([T? value]) { if (value != null) { var futureRef = nativeInstance.push(jsify(value)); return new FutureReference(futureRef, promiseToFuture(futureRef)); @@ -421,7 +421,7 @@ class Reference extends Query { /// A single [setValue] will generate a single "value" event at the location /// where the `setValue()` was performed. Future setValue(T value) { - return promiseToFuture(nativeInstance.set(jsify(value))); + return promiseToFuture(nativeInstance.set(jsify(value!))); } /// Sets a priority for the data at this Database location. @@ -444,7 +444,7 @@ class Reference extends Query { /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) Future setWithPriority(T value, priority) { return promiseToFuture( - nativeInstance.setWithPriority(jsify(value), priority)); + nativeInstance.setWithPriority(jsify(value!), priority)); } /// Atomically modifies the data at this location. @@ -489,7 +489,8 @@ class Reference extends Query { DatabaseTransactionHandler handler, [bool applyLocally = true]) { var promise = nativeInstance.transaction( - allowInterop(_createTransactionHandler(handler)), + allowInterop( + _createTransactionHandler(handler) as dynamic Function(dynamic)), allowInterop(_onComplete), applyLocally, ); @@ -510,12 +511,8 @@ class Reference extends Query { return (currentData) { final data = dartify(currentData); final result = handler(data); - assert( - result != null, - 'Transaction handler returned null and this is not allowed. ' - 'Make sure to always return an instance of TransactionResult.'); if (result.aborted) return undefined; - return jsify(result.data); + return jsify(result.data!); }; } @@ -646,12 +643,12 @@ class DataSnapshot { bool hasChild(String path) => nativeInstance.hasChild(path); bool hasChildren() => nativeInstance.hasChildren(); - int numChildren() => nativeInstance.numChildren(); + int numChildren() => nativeInstance.numChildren() as int; - T _value; + T? _value; /// Returns value stored in this data snapshot. - T val() { + T? val() { if (_value != null) return _value; if (!exists()) return null; // Don't attempt to dartify empty snapshot. diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 6d24d56..61eb3b2 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -20,24 +20,24 @@ js.GeoPoint createGeoPoint(num latitude, num longitude) => js.GeoPoint _createJsGeoPoint(num latitude, num longitude) { final proto = new js.GeoPointProto(latitude: latitude, longitude: longitude); - return js.admin.firestore.GeoPoint.fromProto(proto); + return js.admin!.firestore.GeoPoint.fromProto(proto); } -js.Timestamp _createJsTimestamp(Timestamp ts) { +js.Timestamp? _createJsTimestamp(Timestamp ts) { return callConstructor( - js.admin.firestore.Timestamp, jsify([ts.seconds, ts.nanoseconds])); + js.admin!.firestore.Timestamp, jsify([ts.seconds, ts.nanoseconds])); } @Deprecated('This function will be hidden from public API in future versions.') -js.FieldPath createFieldPath(List fieldNames) { - return callConstructor(js.admin.firestore.FieldPath, jsify(fieldNames)); +js.FieldPath? createFieldPath(List fieldNames) { + return callConstructor(js.admin!.firestore.FieldPath, jsify(fieldNames)); } /// Returns a special sentinel [FieldPath] to refer to the ID of a document. /// It can be used in queries to sort or filter by the document ID. @Deprecated('Use "Firestore.documentId" instead.') js.FieldPath documentId() { - final js.FieldPathPrototype proto = js.admin.firestore.FieldPath; + final js.FieldPathPrototype proto = js.admin!.firestore.FieldPath; return proto.documentId(); } @@ -51,7 +51,7 @@ class Firestore { /// Returns a special sentinel [FieldPath] to refer to the ID of a document. /// It can be used in queries to sort or filter by the document ID. static js.FieldPath documentId() { - final js.FieldPathPrototype proto = js.admin.firestore.FieldPath; + final js.FieldPathPrototype proto = js.admin!.firestore.FieldPath; return proto.documentId(); } @@ -72,7 +72,6 @@ class Firestore { /// Gets a [CollectionReference] for the specified Firestore path. CollectionReference collection(String path) { - assert(path != null); return new CollectionReference(nativeInstance.collection(path), this); } @@ -84,14 +83,12 @@ class Firestore { /// or subcollection with this ID as the last segment of its path will be /// included. Cannot contain a slash. DocumentQuery collectionGroup(String collectionId) { - assert(collectionId != null); return new DocumentQuery( nativeInstance.collectionGroup(collectionId), this); } /// Gets a [DocumentReference] for the specified Firestore path. DocumentReference document(String path) { - assert(path != null); return new DocumentReference(nativeInstance.doc(path), this); } @@ -108,8 +105,7 @@ class Firestore { /// with the same error. Future runTransaction( Future updateFunction(Transaction transaction)) { - assert(updateFunction != null); - Function jsUpdateFunction = (js.Transaction transaction) { + var jsUpdateFunction = (js.Transaction transaction) { return futureToPromise(updateFunction(new Transaction(transaction))); }; return promiseToFuture( @@ -151,14 +147,15 @@ class CollectionReference extends DocumentQuery { @override @protected - js.CollectionReference get nativeInstance => super.nativeInstance; + js.CollectionReference? get nativeInstance => + super.nativeInstance as js.CollectionReference?; /// For subcollections, parent returns the containing DocumentReference. /// /// For root collections, null is returned. - DocumentReference get parent { - return (nativeInstance.parent != null) - ? new DocumentReference(nativeInstance.parent, firestore) + DocumentReference? get parent { + return (nativeInstance!.parent != null) + ? new DocumentReference(nativeInstance!.parent!, firestore) : null; } @@ -168,9 +165,9 @@ class CollectionReference extends DocumentQuery { /// /// The unique key generated is prefixed with a client-generated timestamp /// so that the resulting list will be chronologically-sorted. - DocumentReference document([String path]) { + DocumentReference document([String? path]) { final docRef = - (path == null) ? nativeInstance.doc() : nativeInstance.doc(path); + (path == null) ? nativeInstance!.doc() : nativeInstance!.doc(path); return new DocumentReference(docRef, firestore); } @@ -180,16 +177,16 @@ class CollectionReference extends DocumentQuery { /// The unique key generated is prefixed with a client-generated timestamp /// so that the resulting list will be chronologically-sorted. Future add(DocumentData data) { - return promiseToFuture(nativeInstance.add(data.nativeInstance)) + return promiseToFuture(nativeInstance!.add(data.nativeInstance)) .then((jsRef) => new DocumentReference(jsRef, firestore)); } /// The last path element of the referenced collection. - String get id => nativeInstance.id; + String get id => nativeInstance!.id; /// A string representing the path of the referenced collection (relative to /// the root of the database). - String get path => nativeInstance.path; + String get path => nativeInstance!.path; } /// A [DocumentReference] refers to a document location in a Firestore database @@ -218,7 +215,7 @@ class DocumentReference { /// Writes to the document referred to by this [DocumentReference]. If the /// document does not yet exist, it will be created. If you pass [SetOptions], /// the provided data will be merged into an existing document. - Future setData(DocumentData data, [js.SetOptions options]) { + Future setData(DocumentData data, [js.SetOptions? options]) { final docData = data.nativeInstance; if (options != null) { return promiseToFuture(nativeInstance.set(docData, options)); @@ -252,10 +249,10 @@ class DocumentReference { /// Notifies of documents at this location. Stream get snapshots { - Function cancelCallback; + late Function cancelCallback; // It's fine to let the StreamController be garbage collected once all the // subscribers have cancelled; this analyzer warning is safe to ignore. - StreamController controller; // ignore: close_sinks + late StreamController controller; // ignore: close_sinks void _onNextSnapshot(js.DocumentSnapshot jsSnapshot) { controller.add(new DocumentSnapshot(jsSnapshot, firestore)); @@ -309,7 +306,7 @@ class DocumentChange { /// The type of change that occurred (added, modified, or removed). /// /// Can be `null` if this document change was returned from [DocumentQuery.get]. - DocumentChangeType get type { + DocumentChangeType? get type { if (_type != null) return _type; if (nativeInstance.type == 'added') { _type = DocumentChangeType.added; @@ -321,7 +318,7 @@ class DocumentChange { return _type; } - DocumentChangeType _type; + DocumentChangeType? _type; /// The index of the changed document in the result set immediately prior to /// this [DocumentChange] (i.e. supposing that all prior DocumentChange objects @@ -340,7 +337,7 @@ class DocumentChange { /// The document affected by this change. DocumentSnapshot get document => _document ??= new DocumentSnapshot(nativeInstance.doc, firestore); - DocumentSnapshot _document; + DocumentSnapshot? _document; } class DocumentSnapshot { @@ -353,11 +350,11 @@ class DocumentSnapshot { /// The reference that produced this snapshot DocumentReference get reference => _reference ??= new DocumentReference(nativeInstance.ref, firestore); - DocumentReference _reference; + DocumentReference? _reference; /// Contains all the data of this snapshot DocumentData get data => _data ??= new DocumentData(nativeInstance.data()); - DocumentData _data; + DocumentData? _data; /// Returns `true` if the document exists. bool get exists => nativeInstance.exists; @@ -367,7 +364,7 @@ class DocumentSnapshot { /// The time the document was created. Not set for documents that don't /// exist. - Timestamp get createTime { + Timestamp? get createTime { final ts = nativeInstance.createTime; if (ts == null) return null; return new Timestamp(ts.seconds, ts.nanoseconds); @@ -378,7 +375,7 @@ class DocumentSnapshot { /// /// Note that this value includes nanoseconds and can not be represented /// by a [DateTime] object with expected accuracy when used in [Transaction]. - Timestamp get updateTime { + Timestamp? get updateTime { final ts = nativeInstance.updateTime; if (ts == null) return null; return new Timestamp(ts.seconds, ts.nanoseconds); @@ -386,7 +383,7 @@ class DocumentSnapshot { } class _FirestoreData { - _FirestoreData([Object nativeInstance]) + _FirestoreData([Object? nativeInstance]) : nativeInstance = nativeInstance ?? newObject(); @protected final dynamic nativeInstance; @@ -433,25 +430,27 @@ class _FirestoreData { } } - String getString(String key) => (getProperty(nativeInstance, key) as String); + String? getString(String key) => + (getProperty(nativeInstance, key) as String?); void setString(String key, String value) { setProperty(nativeInstance, key, value); } - int getInt(String key) => (getProperty(nativeInstance, key) as int); + int? getInt(String key) => (getProperty(nativeInstance, key) as int?); - void setInt(String key, int value) { + void setInt(String key, int? value) { setProperty(nativeInstance, key, value); } - double getDouble(String key) => (getProperty(nativeInstance, key) as double); + double? getDouble(String key) => + (getProperty(nativeInstance, key) as double?); void setDouble(String key, double value) { setProperty(nativeInstance, key, value); } - bool getBool(String key) => (getProperty(nativeInstance, key) as bool); + bool? getBool(String key) => (getProperty(nativeInstance, key) as bool?); void setBool(String key, bool value) { setProperty(nativeInstance, key, value); @@ -461,15 +460,15 @@ class _FirestoreData { bool has(String key) => hasProperty(nativeInstance, key); @Deprecated('Migrate to using Firestore Timestamps and "getTimestamp()".') - DateTime getDateTime(String key) { - final Date value = getProperty(nativeInstance, key); + DateTime? getDateTime(String key) { + final Date? value = getProperty(nativeInstance, key); if (value == null) return null; assert(_isDate(value), 'Tried to get Date and got $value'); return new DateTime.fromMillisecondsSinceEpoch(value.getTime()); } - Timestamp getTimestamp(String key) { - js.Timestamp ts = getProperty(nativeInstance, key); + Timestamp? getTimestamp(String key) { + js.Timestamp? ts = getProperty(nativeInstance, key); if (ts == null) return null; assert(_isTimestamp(ts), 'Tried to get Timestamp and got $ts.'); return new Timestamp(ts.seconds, ts.nanoseconds); @@ -477,27 +476,24 @@ class _FirestoreData { @Deprecated('Migrate to using Firestore Timestamps and "setTimestamp()".') void setDateTime(String key, DateTime value) { - assert(key != null); - final data = - (value != null) ? new Date(value.millisecondsSinceEpoch) : null; + final data = Date(value.millisecondsSinceEpoch); setProperty(nativeInstance, key, data); } void setTimestamp(String key, Timestamp value) { - assert(key != null); - final ts = (value != null) ? _createJsTimestamp(value) : null; + final ts = _createJsTimestamp(value); setProperty(nativeInstance, key, ts); } - GeoPoint getGeoPoint(String key) { - js.GeoPoint value = getProperty(nativeInstance, key); + GeoPoint? getGeoPoint(String key) { + js.GeoPoint? value = getProperty(nativeInstance, key); if (value == null) return null; assert(_isGeoPoint(value), 'Invalid value provided to $runtimeType.getGeoPoint().'); return new GeoPoint(value.latitude.toDouble(), value.longitude.toDouble()); } - Blob getBlob(String key) { + Blob? getBlob(String key) { var value = getProperty(nativeInstance, key); if (value == null) return null; assert(_isBlob(value), 'Invalid value provided to $runtimeType.getBlob().'); @@ -505,26 +501,21 @@ class _FirestoreData { } void setGeoPoint(String key, GeoPoint value) { - assert(key != null); - final data = (value != null) - ? _createJsGeoPoint(value.latitude, value.longitude) - : null; + final data =_createJsGeoPoint(value.latitude, value.longitude); setProperty(nativeInstance, key, data); } void setBlob(String key, Blob value) { - assert(key != null); - final data = (value != null) ? value.data : null; + final data = value.data; setProperty(nativeInstance, key, data); } void setFieldValue(String key, FieldValue value) { - assert(key != null); - setProperty(nativeInstance, key, value?._jsify()); + + setProperty(nativeInstance, key, value._jsify()); } void setNestedData(String key, DocumentData value) { - assert(key != null); setProperty(nativeInstance, key, value.nativeInstance); } @@ -535,7 +526,7 @@ class _FirestoreData { value is String || value is bool; - List getList(String key) { + List? getList(String key) { final data = getProperty(nativeInstance, key); if (data == null) return null; if (data is! List) { @@ -550,20 +541,14 @@ class _FirestoreData { } void setList(String key, List value) { - assert(key != null); - if (value == null) { - setProperty(nativeInstance, key, value); - return; - } - // The contents remains is js final data = _jsifyList(value); setProperty(nativeInstance, key, data); } - DocumentReference getReference(String key) { - js.DocumentReference ref = getProperty(nativeInstance, key); + DocumentReference? getReference(String key) { + js.DocumentReference? ref = getProperty(nativeInstance, key); if (ref == null) return null; assert(_isReference(ref), 'Invalid value provided to $runtimeType.getReference().'); @@ -573,8 +558,7 @@ class _FirestoreData { } void setReference(String key, DocumentReference value) { - assert(key != null); - final data = (value != null) ? value.nativeInstance : null; + final data = value.nativeInstance; setProperty(nativeInstance, key, data); } @@ -612,8 +596,8 @@ class _FirestoreData { // TODO: figure out how to handle array* field values. For now ignored as they // don't need js to dart conversion bool _isFieldValue(value) { - if (value == js.admin.firestore.FieldValue.delete() || - value == js.admin.firestore.FieldValue.serverTimestamp()) { + if (value == js.admin!.firestore.FieldValue.delete() || + value == js.admin!.firestore.FieldValue.serverTimestamp()) { return true; } return false; @@ -642,7 +626,7 @@ class _FirestoreData { } else if (item is List) { return _jsifyList(item); } else if (item is Map) { - return DocumentData.fromMap(item?.cast()).nativeInstance; + return DocumentData.fromMap(item.cast()).nativeInstance; } else { throw UnsupportedError( 'Value of type ${item.runtimeType} is not supported by Firestore.'); @@ -737,7 +721,7 @@ class _FirestoreData { /// - [UpdateData] which is used to update a part of a document and follows /// different pattern for handling nested fields. class DocumentData extends _FirestoreData { - DocumentData([js.DocumentData nativeInstance]) : super(nativeInstance); + DocumentData([js.DocumentData? nativeInstance]) : super(nativeInstance); factory DocumentData.fromMap(Map data) { final doc = new DocumentData(); @@ -745,7 +729,7 @@ class DocumentData extends _FirestoreData { return doc; } - DocumentData getNestedData(String key) { + DocumentData? getNestedData(String key) { final data = getProperty(nativeInstance, key); if (data == null) return null; return new DocumentData(data); @@ -784,7 +768,7 @@ class DocumentData extends _FirestoreData { /// UpdateData data = new UpdateData(); /// data.setString("profile.name", "John"); class UpdateData extends _FirestoreData { - UpdateData([js.UpdateData nativeInstance]) : super(nativeInstance); + UpdateData([js.UpdateData? nativeInstance]) : super(nativeInstance); factory UpdateData.fromMap(Map data) { final doc = new UpdateData(); @@ -883,7 +867,7 @@ class QuerySnapshot { bool get isNotEmpty => !isEmpty; /// Gets a list of all the documents included in this snapshot - List get documents { + List? get documents { if (isEmpty) return const []; _documents ??= new List.from(nativeInstance.docs) .map((jsDoc) => new DocumentSnapshot(jsDoc, firestore)) @@ -891,16 +875,16 @@ class QuerySnapshot { return _documents; } - List _documents; + List? _documents; /// An array of the documents that changed since the last snapshot. If this /// is the first snapshot, all documents will be in the list as Added changes. - List get documentChanges { + List? get documentChanges { if (_changes == null) { if (nativeInstance.docChanges() == null) { _changes = const []; } else { - _changes = new List.from(nativeInstance.docChanges()) + _changes = new List.from(nativeInstance.docChanges()!) .map((jsChange) => new DocumentChange(jsChange, firestore)) .toList(growable: false); } @@ -908,7 +892,7 @@ class QuerySnapshot { return _changes; } - List _changes; + List? _changes; } /// Represents a query over the data at a particular location. @@ -916,11 +900,11 @@ class DocumentQuery { DocumentQuery(this.nativeInstance, this.firestore); @protected - final js.DocumentQuery nativeInstance; + final js.DocumentQuery? nativeInstance; final Firestore firestore; Future get() { - return promiseToFuture(nativeInstance.get()) + return promiseToFuture(nativeInstance!.get()) .then((jsSnapshot) => new QuerySnapshot(jsSnapshot, firestore)); } @@ -928,7 +912,7 @@ class DocumentQuery { Stream get snapshots { // It's fine to let the StreamController be garbage collected once all the // subscribers have cancelled; this analyzer warning is safe to ignore. - StreamController controller; // ignore: close_sinks + late StreamController controller; // ignore: close_sinks void onSnapshot(js.QuerySnapshot snapshot) { controller.add(new QuerySnapshot(snapshot, firestore)); @@ -938,12 +922,12 @@ class DocumentQuery { controller.addError(error); } - Function unsubscribe; + late Function unsubscribe; controller = new StreamController.broadcast( onListen: () { - unsubscribe = nativeInstance.onSnapshot( - allowInterop(onSnapshot), allowInterop(onError)); + unsubscribe = nativeInstance! + .onSnapshot(allowInterop(onSnapshot), allowInterop(onError)); }, onCancel: () { unsubscribe(); @@ -965,12 +949,12 @@ class DocumentQuery { dynamic isGreaterThan, dynamic isGreaterThanOrEqualTo, dynamic arrayContains, - bool isNull, + bool? isNull, }) { - js.DocumentQuery query = nativeInstance; + js.DocumentQuery? query = nativeInstance; void addCondition(String field, String opStr, dynamic value) { - query = query.where(field, opStr, _FirestoreData._jsify(value)); + query = query!.where(field, opStr, _FirestoreData._jsify(value)); } if (isEqualTo != null) addCondition(field, '==', isEqualTo); @@ -999,7 +983,7 @@ class DocumentQuery { DocumentQuery orderBy(String field, {bool descending: false}) { String direction = descending ? 'desc' : 'asc'; return new DocumentQuery( - nativeInstance.orderBy(field, direction), firestore); + nativeInstance!.orderBy(field, direction), firestore); } /// Takes a [snapshot] or a list of [values], creates and returns a new [DocumentQuery] @@ -1009,7 +993,8 @@ class DocumentQuery { /// The [values] must be in order of [orderBy] filters. /// /// Cannot be used in combination with [startAt]. - DocumentQuery startAfter({DocumentSnapshot snapshot, List values}) { + DocumentQuery startAfter( + {DocumentSnapshot? snapshot, List? values}) { return new DocumentQuery( _wrapPaginatingFunctionCall("startAfter", snapshot, values), firestore); } @@ -1021,7 +1006,7 @@ class DocumentQuery { /// The [values] must be in order of [orderBy] filters. /// /// Cannot be used in combination with [startAfter]. - DocumentQuery startAt({DocumentSnapshot snapshot, List values}) { + DocumentQuery startAt({DocumentSnapshot? snapshot, List? values}) { return new DocumentQuery( _wrapPaginatingFunctionCall("startAt", snapshot, values), firestore); } @@ -1033,7 +1018,7 @@ class DocumentQuery { /// The [values] must be in order of [orderBy] filters. /// /// Cannot be used in combination with [endBefore]. - DocumentQuery endAt({DocumentSnapshot snapshot, List values}) { + DocumentQuery endAt({DocumentSnapshot? snapshot, List? values}) { return new DocumentQuery( _wrapPaginatingFunctionCall("endAt", snapshot, values), firestore); } @@ -1045,7 +1030,7 @@ class DocumentQuery { /// The [values] must be in order of [orderBy] filters. /// /// Cannot be used in combination with [endAt]. - DocumentQuery endBefore({DocumentSnapshot snapshot, List values}) { + DocumentQuery endBefore({DocumentSnapshot? snapshot, List? values}) { return new DocumentQuery( _wrapPaginatingFunctionCall("endBefore", snapshot, values), firestore); } @@ -1053,21 +1038,19 @@ class DocumentQuery { /// Creates and returns a new Query that's additionally limited to only return up /// to the specified number of documents. DocumentQuery limit(int length) { - assert(length != null); - return new DocumentQuery(nativeInstance.limit(length), firestore); + return new DocumentQuery(nativeInstance!.limit(length), firestore); } /// Specifies the offset of the returned results. DocumentQuery offset(int offset) { - assert(offset != null); - return new DocumentQuery(nativeInstance.offset(offset), firestore); + return new DocumentQuery(nativeInstance!.offset(offset), firestore); } /// Calls js paginating [method] with [DocumentSnapshot] or List of [values]. /// We need to call this method in all paginating methods to fix that Dart /// doesn't support varargs - we need to use [List] to call js function. - js.DocumentQuery _wrapPaginatingFunctionCall( - String method, DocumentSnapshot snapshot, List values) { + js.DocumentQuery? _wrapPaginatingFunctionCall( + String method, DocumentSnapshot? snapshot, List? values) { if (snapshot == null && values == null) { throw new ArgumentError( "Please provide either snapshot or values parameter."); @@ -1077,8 +1060,8 @@ class DocumentQuery { } List args = (snapshot != null) ? [snapshot.nativeInstance] - : values.map(_FirestoreData._jsify).toList(); - return callMethod(nativeInstance, method, args); + : values!.map(_FirestoreData._jsify).toList(); + return callMethod(nativeInstance!, method, args); } /// Creates and returns a new Query instance that applies a field mask @@ -1086,10 +1069,9 @@ class DocumentQuery { /// You can specify a list of field paths to return, or use an empty /// list to only return the references of matching documents. DocumentQuery select(List fieldPaths) { - assert(fieldPaths != null); // Dart doesn't support varargs return new DocumentQuery( - callMethod(nativeInstance, "select", fieldPaths), firestore); + callMethod(nativeInstance!, "select", fieldPaths), firestore); } } @@ -1145,7 +1127,7 @@ class Transaction { /// [DocumentSnapshot.updateTime]. The update will be accepted only if /// update time on the server is equal to this value. void update(DocumentReference documentRef, UpdateData data, - {Timestamp lastUpdateTime}) { + {Timestamp? lastUpdateTime}) { final docData = data.nativeInstance; final nativeRef = documentRef.nativeInstance; if (lastUpdateTime != null) { @@ -1162,7 +1144,7 @@ class Transaction { /// delete. This argument, if specified, must contain value of /// [DocumentSnapshot.updateTime]. The delete will be accepted only if /// update time on the server is equal to this value. - void delete(DocumentReference documentRef, {Timestamp lastUpdateTime}) { + void delete(DocumentReference documentRef, {Timestamp? lastUpdateTime}) { final nativeRef = documentRef.nativeInstance; if (lastUpdateTime != null) { nativeInstance.delete(nativeRef, _getNativePrecondition(lastUpdateTime)); @@ -1190,7 +1172,7 @@ class WriteBatch { /// If the document does not exist yet, it will be created. If you pass /// [options], the provided data can be merged into the existing document. void setData(DocumentReference documentRef, DocumentData data, - [js.SetOptions options]) { + [js.SetOptions? options]) { final docData = data.nativeInstance; final nativeRef = documentRef.nativeInstance; if (options != null) { @@ -1220,7 +1202,6 @@ class WriteBatch { /// Using Preconditions, these calls can be restricted to only apply to /// documents that match the specified restrictions. js.Precondition _getNativePrecondition(Timestamp lastUpdateTime) { - assert(lastUpdateTime != null, 'Precontition lastUpdateTime can`t be null'); final ts = _createJsTimestamp(lastUpdateTime); return new js.Precondition(lastUpdateTime: ts); } @@ -1230,14 +1211,14 @@ js.Precondition _getNativePrecondition(Timestamp lastUpdateTime) { /// configured to perform granular merges instead of overwriting the target /// documents in their entirety by providing a [SetOptions] with [merge]: true. js.SetOptions _getNativeSetOptions(bool merge) { - assert(merge != null, 'SetOption merge can`t be null'); + //assert(merge != null, 'SetOption merge can`t be null'); return new js.SetOptions(merge: merge); } class _FieldValueDelete implements FieldValue { @override dynamic _jsify() { - return js.admin.firestore.FieldValue.delete(); + return js.admin!.firestore.FieldValue.delete(); } @override @@ -1247,7 +1228,7 @@ class _FieldValueDelete implements FieldValue { class _FieldValueServerTimestamp implements FieldValue { @override dynamic _jsify() { - return js.admin.firestore.FieldValue.serverTimestamp(); + return js.admin!.firestore.FieldValue.serverTimestamp(); } @override @@ -1265,7 +1246,7 @@ class _FieldValueArrayUnion extends _FieldValueArray { @override _jsify() { - return callMethod(js.admin.firestore.FieldValue, 'arrayUnion', + return callMethod(js.admin!.firestore.FieldValue, 'arrayUnion', _FirestoreData._jsifyList(elements)); } @@ -1278,7 +1259,7 @@ class _FieldValueArrayRemove extends _FieldValueArray { @override _jsify() { - return callMethod(js.admin.firestore.FieldValue, 'arrayRemove', + return callMethod(js.admin!.firestore.FieldValue, 'arrayRemove', _FirestoreData._jsifyList(elements)); } @@ -1290,10 +1271,10 @@ class _FieldValueArrayRemove extends _FieldValueArray { /// or update(). abstract class FieldValue { factory FieldValue._fromJs(dynamic jsFieldValue) { - if (jsFieldValue == js.admin.firestore.FieldValue.delete()) { + if (jsFieldValue == js.admin!.firestore.FieldValue.delete()) { return Firestore.fieldValues.delete(); } else if (jsFieldValue == - js.admin.firestore.FieldValue.serverTimestamp()) { + js.admin!.firestore.FieldValue.serverTimestamp()) { return Firestore.fieldValues.serverTimestamp(); } else { throw ArgumentError.value(jsFieldValue, 'jsFieldValue', diff --git a/lib/src/firestore_bindings.dart b/lib/src/firestore_bindings.dart index 8fe5858..3f1b133 100644 --- a/lib/src/firestore_bindings.dart +++ b/lib/src/firestore_bindings.dart @@ -53,7 +53,7 @@ abstract class GeoPointUtil { @JS() @anonymous abstract class GeoPointProto { - external factory GeoPointProto({num latitude, num longitude}); + external factory GeoPointProto({num? latitude, num? longitude}); external num get latitude; external num get longitude; } @@ -96,11 +96,11 @@ abstract class Firestore { /// Retrieves multiple documents from Firestore. /// snapshots. external Promise getAll( - [DocumentReference documentRef1, - DocumentReference documentRef2, - DocumentReference documentRef3, - DocumentReference documentRef4, - DocumentReference documentRef5]); + [DocumentReference? documentRef1, + DocumentReference? documentRef2, + DocumentReference? documentRef3, + DocumentReference? documentRef4, + DocumentReference? documentRef5]); /// Fetches the root collections that are associated with this Firestore /// database. @@ -165,9 +165,9 @@ abstract class FirestoreSettings { external bool get timestampsInSnapshots; external factory FirestoreSettings({ - String projectId, - String keyFilename, - bool timestampsInSnapshots, + String? projectId, + String? keyFilename, + bool? timestampsInSnapshots, }); } @@ -201,13 +201,14 @@ abstract class Transaction { /// Create the document referred to by the provided `DocumentReference`. /// The operation will fail the transaction if a document exists at the /// specified location. - external Transaction create(DocumentReference documentRef, DocumentData data); + external Transaction create( + DocumentReference documentRef, DocumentData? data); /// Writes to the document referred to by the provided `DocumentReference`. /// If the document does not exist yet, it will be created. If you pass /// `SetOptions`, the provided data can be merged into the existing document. - external Transaction set(DocumentReference documentRef, DocumentData data, - [SetOptions options]); + external Transaction set(DocumentReference documentRef, DocumentData? data, + [SetOptions? options]); /// Updates fields in the document referred to by the provided /// `DocumentReference`. The update will fail if applied to a document that @@ -230,11 +231,11 @@ abstract class Transaction { external Transaction update( DocumentReference documentRef, dynamic /*String|FieldPath*/ data_field, [dynamic /*Precondition|dynamic*/ precondition_value, - List fieldsOrPrecondition]); + List? fieldsOrPrecondition]); /// Deletes the document referred to by the provided `DocumentReference`. external Transaction delete(DocumentReference documentRef, - [Precondition precondition]); + [Precondition? precondition]); } /// A write batch, used to perform multiple writes as a single atomic unit. @@ -257,14 +258,14 @@ abstract class WriteBatch { /// Write to the document referred to by the provided [DocumentReference]. /// If the document does not exist yet, it will be created. If you pass /// [options], the provided data can be merged into the existing document. - external WriteBatch set(DocumentReference documentRef, DocumentData data, - [SetOptions options]); + external WriteBatch set(DocumentReference documentRef, DocumentData? data, + [SetOptions? options]); /// Updates fields in the document referred to by this DocumentReference. /// The update will fail if applied to a document that does not exist. /// /// Nested fields can be updated by providing dot-separated field path strings - external WriteBatch update(DocumentReference documentRef, UpdateData data); + external WriteBatch update(DocumentReference documentRef, UpdateData? data); /// Deletes the document referred to by the provided `DocumentReference`. external WriteBatch delete(DocumentReference documentRef); @@ -284,7 +285,7 @@ abstract class Precondition { /// string). external Timestamp get lastUpdateTime; external set lastUpdateTime(Timestamp v); - external factory Precondition({Timestamp lastUpdateTime}); + external factory Precondition({Timestamp? lastUpdateTime}); } /// An options object that configures the behavior of `set()` calls in @@ -299,7 +300,7 @@ abstract class SetOptions { /// untouched. external bool get merge; external set merge(bool v); - external factory SetOptions({bool merge}); + external factory SetOptions({bool? merge}); } /// A WriteResult wraps the write time set by the Firestore servers on `sets()`, @@ -352,14 +353,14 @@ abstract class DocumentReference { /// Writes to the document referred to by this `DocumentReference`. If the /// document does not yet exist, it will be created. If you pass /// `SetOptions`, the provided data can be merged into an existing document. - external Promise set(DocumentData data, [SetOptions options]); + external Promise set(DocumentData? data, [SetOptions? options]); /// Updates fields in the document referred to by this `DocumentReference`. /// The update will fail if applied to a document that does not exist. /// Nested fields can be updated by providing dot-separated field path /// strings. /// update the document. - external Promise update(UpdateData data, [Precondition precondition]); + external Promise update(UpdateData? data, [Precondition? precondition]); /// Updates fields in the document referred to by this `DocumentReference`. /// The update will fail if applied to a document that does not exist. @@ -375,7 +376,7 @@ abstract class DocumentReference { // List moreFieldsOrPrecondition]); /// Deletes the document referred to by this `DocumentReference`. - external Promise delete([Precondition precondition]); + external Promise delete([Precondition? precondition]); /// Reads the document referred to by this `DocumentReference`. /// current document contents. @@ -386,7 +387,7 @@ abstract class DocumentReference { /// cancelled. No further callbacks will occur. /// the snapshot listener. external Function onSnapshot(void onNext(DocumentSnapshot snapshot), - [void onError(Error error)]); + [void onError(Error error)?]); } /// A `DocumentSnapshot` contains data read from a document in your Firestore @@ -412,13 +413,13 @@ abstract class DocumentSnapshot { /// The time the document was created. Not set for documents that don't /// exist. - external Timestamp get createTime; - external set createTime(Timestamp v); + external Timestamp? get createTime; + external set createTime(Timestamp? v); /// The time the document was last updated (at the time the snapshot was /// generated). Not set for documents that don't exist. - external Timestamp get updateTime; - external set updateTime(Timestamp v); + external Timestamp? get updateTime; + external set updateTime(Timestamp? v); /// The time this snapshot was read. external Timestamp get readTime; @@ -445,13 +446,13 @@ abstract class DocumentSnapshot { @anonymous abstract class QueryDocumentSnapshot extends DocumentSnapshot { /// The time the document was created. - external Timestamp get createTime; - external set createTime(Timestamp v); + external Timestamp? get createTime; + external set createTime(Timestamp? v); /// The time the document was last updated (at the time the snapshot was /// generated). - external Timestamp get updateTime; - external set updateTime(Timestamp v); + external Timestamp? get updateTime; + external set updateTime(Timestamp? v); /// Retrieves all fields in the document as an Object. /// @override @@ -488,7 +489,7 @@ abstract class DocumentQuery { /// than modify the existing instance) to impose the order. /// not specified, order will be ascending. external DocumentQuery orderBy(dynamic /*String|FieldPath*/ fieldPath, - [String /*'desc'|'asc'*/ directionStr]); + [String? /*'desc'|'asc'*/ directionStr]); /// Creates and returns a new Query that's additionally limited to only /// return up to the specified number of documents. @@ -597,7 +598,7 @@ abstract class DocumentQuery { /// cancelled. No further callbacks will occur. /// the snapshot listener. external Function onSnapshot(void onNext(QuerySnapshot snapshot), - [void onError(Error error)]); + [void onError(Error error)?]); } /// A `QuerySnapshot` contains zero or more `QueryDocumentSnapshot` objects @@ -616,7 +617,7 @@ abstract class QuerySnapshot { /// An array of the documents that changed since the last snapshot. If this /// is the first snapshot, all documents will be in the list as added /// changes. - external List docChanges(); + external List? docChanges(); /// An array of all the documents in the QuerySnapshot. external List get docs; @@ -668,10 +669,10 @@ abstract class DocumentChange { external num get newIndex; external set newIndex(num v); external factory DocumentChange( - {String /*'added'|'removed'|'modified'*/ type, - QueryDocumentSnapshot doc, - num oldIndex, - num newIndex}); + {String? /*'added'|'removed'|'modified'*/ type, + QueryDocumentSnapshot? doc, + num? oldIndex, + num? newIndex}); } /// A `CollectionReference` object can be used for adding documents, getting @@ -686,8 +687,8 @@ abstract class CollectionReference extends DocumentQuery { /// A reference to the containing Document if this is a subcollection, else /// null. - external DocumentReference /*DocumentReference|Null*/ get parent; - external set parent(DocumentReference /*DocumentReference|Null*/ v); + external DocumentReference? /*DocumentReference|Null*/ get parent; + external set parent(DocumentReference? /*DocumentReference|Null*/ v); /// A string representing the path of the referenced collection (relative /// to the root of the database). @@ -697,12 +698,12 @@ abstract class CollectionReference extends DocumentQuery { /// Get a `DocumentReference` for the document within the collection at the /// specified path. If no path is specified, an automatically-generated /// unique ID will be used for the returned DocumentReference. - external DocumentReference doc([String documentPath]); + external DocumentReference doc([String? documentPath]); /// Add a new document to this collection with the specified data, assigning /// it a document ID automatically. /// newly created document after it has been written to the backend. - external Promise add(DocumentData data); + external Promise add(DocumentData? data); } /// Sentinel values that can be used when writing document fields with set() @@ -759,9 +760,9 @@ abstract class FieldPath { /// Creates a FieldPath from the provided field names. If more than one field /// name is provided, the path will point to a nested field in a document. external factory FieldPath( - [String fieldNames1, - String fieldNames2, - String fieldNames3, - String fieldNames4, - String fieldNames5]); + [String? fieldNames1, + String? fieldNames2, + String? fieldNames3, + String? fieldNames4, + String? fieldNames5]); } diff --git a/lib/src/messaging.dart b/lib/src/messaging.dart index 7043052..6ceaa61 100644 --- a/lib/src/messaging.dart +++ b/lib/src/messaging.dart @@ -64,7 +64,7 @@ class Messaging { /// /// Returns Future fulfilled with a unique message ID string after the /// message has been successfully handed off to the FCM service for delivery - Future send(FcmMessage message, [bool dryRun]) { + Future send(FcmMessage message, [bool? dryRun]) { if (dryRun != null) return promiseToFuture(nativeInstance.send(message, dryRun)); else @@ -75,7 +75,7 @@ class Messaging { /// /// Returns Future fulfilled with an object representing the /// result of the send operation. - Future sendAll(List messages, [bool dryRun]) { + Future sendAll(List messages, [bool? dryRun]) { if (dryRun != null) return promiseToFuture(nativeInstance.sendAll(messages, dryRun)); else @@ -87,7 +87,7 @@ class Messaging { /// /// Returns Future fulfilled with an object representing the /// result of the send operation. - Future sendMulticast(MulticastMessage message, [bool dryRun]) { + Future sendMulticast(MulticastMessage message, [bool? dryRun]) { if (dryRun != null) return promiseToFuture(nativeInstance.sendMulticast(message, dryRun)); else @@ -100,7 +100,7 @@ class Messaging { /// response after the message has been sent. Future sendToCondition( String condition, MessagingPayload payload, - [MessagingOptions options]) { + [MessagingOptions? options]) { if (options != null) return promiseToFuture( nativeInstance.sendToCondition(condition, payload, options)); @@ -116,7 +116,7 @@ class Messaging { /// response after the message has been sent. Future sendToDevice( String registrationToken, MessagingPayload payload, - [MessagingOptions options]) { + [MessagingOptions? options]) { if (options != null) return promiseToFuture( nativeInstance.sendToDevice(registrationToken, payload, options)); @@ -132,7 +132,7 @@ class Messaging { /// response after the message has been sent. Future sendToDeviceGroup( String notificationKey, MessagingPayload payload, - [MessagingOptions options]) { + [MessagingOptions? options]) { if (options != null) return promiseToFuture( nativeInstance.sendToDeviceGroup(notificationKey, payload, options)); @@ -147,7 +147,7 @@ class Messaging { /// response after the message has been sent. Future sendToTopic( String topic, MessagingPayload payload, - [MessagingOptions options]) { + [MessagingOptions? options]) { if (options != null) return promiseToFuture( nativeInstance.sendToTopic(topic, payload, options)); diff --git a/lib/src/storage_bindings.dart b/lib/src/storage_bindings.dart index 343e1b1..b11db9c 100644 --- a/lib/src/storage_bindings.dart +++ b/lib/src/storage_bindings.dart @@ -20,7 +20,7 @@ abstract class Storage { /// /// [name] of the bucket to be retrieved is optional. If [name] is not /// specified, retrieves a reference to the default bucket. - external Bucket bucket([String name]); + external Bucket bucket([String? name]); } @JS() @@ -40,18 +40,18 @@ abstract class Bucket { /// Create a bucket. /// /// Returns promise containing CreateBucketResponse. - external Promise create([CreateBucketRequest metadata, callback]); + external Promise create([CreateBucketRequest? metadata, callback]); /// Checks if the bucket exists. /// /// Returns promise containing list with following values: /// [0] [boolean] - Whether this bucket exists. - external Promise exists([BucketExistsOptions options, callback]); + external Promise exists([BucketExistsOptions? options, callback]); /// Creates a [StorageFile] object. /// /// See [StorageFile] to see for more details. - external StorageFile file(String name, [StorageFileOptions options]); + external StorageFile file(String name, [StorageFileOptions? options]); /// Upload a file to the bucket. This is a convenience method that wraps /// [StorageFile.createWriteStream]. @@ -81,7 +81,7 @@ abstract class CombineOptions { /// The ID of the project which will be billed for the request. external String get userProject; - external factory CombineOptions({String kmsKeyName, String userProject}); + external factory CombineOptions({String? kmsKeyName, String? userProject}); } @JS() @@ -96,7 +96,7 @@ abstract class BucketExistsOptions { /// The ID of the project which will be billed for the request. external String get userProject; - external factory BucketExistsOptions({String userProject}); + external factory BucketExistsOptions({String? userProject}); } @JS() @@ -115,7 +115,7 @@ abstract class StorageFileOptions { external String get kmsKeyName; external factory StorageFileOptions( - {String generation, String encryptionKey, String kmsKeyName}); + {String? generation, String? encryptionKey, String? kmsKeyName}); } @JS() diff --git a/pubspec.yaml b/pubspec.yaml index 31987b5..6df6db7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/pulyaevskiy/firebase-admin-interop author: Anatoly Pulyaevskiy environment: - sdk: '>=2.7.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: js: ^0.6.1+1 diff --git a/test/admin_test.dart b/test/admin_test.dart index 2b220c1..a377b8f 100644 --- a/test/admin_test.dart +++ b/test/admin_test.dart @@ -11,23 +11,23 @@ import 'setup.dart'; void main() { group('FirebaseAdmin', () { - App app; + App? app; setUpAll(() { app = initFirebaseApp(); }); tearDownAll(() { - return app.delete(); + return app!.delete(); }); test('app name', () { - expect(app.name, '[DEFAULT]'); + expect(app!.name, '[DEFAULT]'); }); test('accessToken', () async { var accessToken = await promiseToFuture( - js.admin.credential.applicationDefault().getAccessToken()) + js.admin!.credential.applicationDefault().getAccessToken()) as js.AccessToken; expect(accessToken.access_token, isNotEmpty); }); diff --git a/test/auth_test.dart b/test/auth_test.dart index 548114e..911859a 100644 --- a/test/auth_test.dart +++ b/test/auth_test.dart @@ -10,39 +10,42 @@ import 'setup.dart'; void main() { group('Auth', () { - App app; + App? app; setUpAll(() async { app = initFirebaseApp(); - var user = - await app.auth().getUser('testuser').catchError((error) => null); + UserRecord? user; + try { + user = await app!.auth().getUser('testuser'); + } catch (_) {} + ; if (user == null) { - await app.auth().createUser(new CreateUserRequest(uid: 'testuser')); + await app!.auth().createUser(new CreateUserRequest(uid: 'testuser')); } }); tearDownAll(() { - return app.delete(); + return app!.delete(); }); test('createCustomToken', () async { var token = - await app.auth().createCustomToken('testuser', {'role': 'admin'}); + await app!.auth().createCustomToken('testuser', {'role': 'admin'}); expect(token, isNotEmpty); }); test('getUser', () async { - var user = await app.auth().getUser('testuser'); + var user = await app!.auth().getUser('testuser'); expect(user.uid, 'testuser'); }); test('getUser which does not exist', () async { - var result = app.auth().getUser('noSuchUser'); + var result = app!.auth().getUser('noSuchUser'); expect(result, throwsA(const TypeMatcher())); }); test('listUsers', () async { - var result = await app.auth().listUsers(); + var result = await app!.auth().listUsers(); expect(result.users, isNotEmpty); }); }); diff --git a/test/database_test.dart b/test/database_test.dart index 8635e8f..bd6b7d1 100644 --- a/test/database_test.dart +++ b/test/database_test.dart @@ -10,15 +10,15 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { - App app = initFirebaseApp(); + App? app = initFirebaseApp(); group('Database', () { tearDownAll(() { - return app.delete(); + return app!.delete(); }); group('Query', () { - var ref = app.database().ref('/app/users/23'); + var ref = app!.database().ref('/app/users/23'); setUp(() async { await ref.setValue('Firebase'); @@ -37,9 +37,9 @@ void main() { }); test('on and off', () async { - var controller = StreamController(); + var controller = StreamController(); final sub = ref.on(EventType.value, (DataSnapshot snapshot) { - controller.add(snapshot.val() as String); + controller.add(snapshot.val() as String?); }); final result = controller.stream.take(3).toList(); @@ -57,7 +57,7 @@ void main() { }); group('Reference', () { - var ref = app.database().ref('/app/users/23'); + var ref = app!.database().ref('/app/users/23'); var refUpdate = app.database().ref('/tests/refUpdate'); setUp(() async { @@ -122,7 +122,7 @@ void main() { }); test('transaction abort', () async { - var result = await refUpdate.transaction((currentData) { + var result = await refUpdate.transaction((dynamic currentData) { return TransactionResult.abort; }); expect(result.committed, isFalse); @@ -131,7 +131,7 @@ void main() { test('transaction commit', () async { await refUpdate.update({'num': 23, 'nested/thing': '1984'}); - var tx = await refUpdate.transaction((currentData) { + var tx = await refUpdate.transaction((dynamic currentData) { // Not sure I fully understand why Firebase sends initial `null` value // here, but this should not have anything to do with our Dart code. if (currentData == null) @@ -147,8 +147,8 @@ void main() { }); group('DataSnapshot', () { - var ref = app.database().ref('/app/users/3/notifications'); - var childKey; + var ref = app!.database().ref('/app/users/3/notifications'); + late var childKey; setUp(() async { await ref.remove(); @@ -210,7 +210,7 @@ void main() { test('val()', () async { var snapshot = await ref.once('value'); - var val = snapshot.val(); + var val = snapshot.val()!; expect(val, isMap); expect(val.length, 2); }); diff --git a/test/firestore_test.dart b/test/firestore_test.dart index e7381db..79c23ba 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { - App app = initFirebaseApp(); + App app = initFirebaseApp()!; app.firestore().settings(FirestoreSettings(timestampsInSnapshots: true)); group('$Firestore', () { @@ -51,7 +51,7 @@ void main() { expect(data.keys, contains('nested')); expect(data.getString('name'), 'Firestore'); expect(data.getString('profile.url'), 'https://pic.com/123'); - var nested = data.getNestedData('nested'); + var nested = data.getNestedData('nested')!; expect(nested, const TypeMatcher()); expect(nested, hasLength(1)); expect(nested.getString('author'), 'Unknown'); @@ -63,7 +63,7 @@ void main() { })); var snapshot = await ref.get(); var data = snapshot.data; - var nested = data.getNestedData('nested'); + var nested = data.getNestedData('nested')!; expect(nested.getString('author'), 'Isaac Asimov'); }); @@ -98,12 +98,12 @@ void main() { expect(data.getBool('boolVal'), true); expect(data.getString('stringVal'), 'text'); expect(data.getGeoPoint('geoVal'), new GeoPoint(23.03, 19.84)); - expect(data.getBlob('blob').data, [1, 2, 3]); - var documentReference = data.getReference('refVal'); + expect(data.getBlob('blob')!.data, [1, 2, 3]); + var documentReference = data.getReference('refVal')!; expect(documentReference.path, 'users/23'); expect(data.getList('listVal'), [23, 84]); expect(data.getTimestamp('tsVal'), tsNow); - DocumentData nestedData = data.getNestedData('nestedData'); + DocumentData nestedData = data.getNestedData('nestedData')!; expect(nestedData.keys.length, 1); expect(nestedData.getString('nestedVal'), 'very nested'); // Check the field value (no getter here) @@ -153,17 +153,17 @@ void main() { expect(result.getInt('intVal'), 23); expect(result.getDouble('doubleVal'), 19.84); expect(result.getGeoPoint('geoVal'), new GeoPoint(23.03, 19.84)); - expect(result.getBlob('blobVal').data, [1, 2, 3]); - var docRef = result.getReference('refVal'); + expect(result.getBlob('blobVal')!.data, [1, 2, 3]); + var docRef = result.getReference('refVal')!; expect(docRef, const TypeMatcher()); expect(docRef.path, 'users/23'); expect(result.getList('listVal'), [23, 84]); expect(result.getTimestamp('tsVal'), Timestamp.fromDateTime(date)); - var nested = result.getNestedData('nestedVal'); + var nested = result.getNestedData('nestedVal')!; expect(nested.getString('nestedKey'), 'much nested'); expect( result.getTimestamp('serverTimestamp'), TypeMatcher()); - var complexVal = result.getNestedData('complexVal'); + var complexVal = result.getNestedData('complexVal')!; expect(complexVal.getList('sub'), [ { 'subList': [1] @@ -254,7 +254,7 @@ void main() { await ref.setData(data); var snapshot = await ref.get(); - var result = snapshot.data.getList('data'); + var result = snapshot.data.getList('data')!; expect(result, hasLength(4)); expect(result.elementAt(0), const TypeMatcher()); expect(result.elementAt(1), const TypeMatcher()); @@ -441,7 +441,7 @@ void main() { }); test('parent of root collection', () { - final parent = ref.parent; + final parent = ref.parent!; expect(parent, const TypeMatcher()); expect(parent.path, isEmpty); expect(parent.documentID, isNull); @@ -507,9 +507,9 @@ void main() { expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); expect(snapshot.documentChanges, hasLength(1)); - var doc = snapshot.documents.first; + var doc = snapshot.documents!.first; expect(doc.data.getString('name'), 'John Doe'); - var change = snapshot.documentChanges.first; + var change = snapshot.documentChanges!.first; expect(change.type, DocumentChangeType.added); }); @@ -528,7 +528,7 @@ void main() { expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); expect(snapshot.documentChanges, hasLength(1)); - var doc = snapshot.documents.first; + var doc = snapshot.documents!.first; expect(doc.data.getString('name'), 'doc1'); expect(doc.data.getReference('ref'), const TypeMatcher()); @@ -553,7 +553,7 @@ void main() { var snapshot = await query.get(); expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents.single; + var doc = snapshot.documents!.single; expect(doc.documentID, doc1.documentID); // Test with startAfter @@ -561,7 +561,7 @@ void main() { snapshot = await query.get(); expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); - doc = snapshot.documents.single; + doc = snapshot.documents!.single; expect(doc.documentID, doc2.documentID); }); @@ -581,7 +581,7 @@ void main() { var snapshot = await query.get(); expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents.single; + var doc = snapshot.documents!.single; expect(doc.documentID, doc1.documentID); // Test with startAfter @@ -589,7 +589,7 @@ void main() { snapshot = await query.get(); expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); - doc = snapshot.documents.single; + doc = snapshot.documents!.single; expect(doc.documentID, doc2.documentID); }); @@ -609,7 +609,7 @@ void main() { var snapshot = await query.get(); expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents.single; + var doc = snapshot.documents!.single; expect(doc.documentID, doc1.documentID); }); @@ -634,7 +634,7 @@ void main() { })); List _querySnapshotDocIds(QuerySnapshot querySnapshot) { - return querySnapshot.documents + return querySnapshot.documents! .map((snapshot) => snapshot.documentID) .toList(); } @@ -669,7 +669,7 @@ void main() { })); List _querySnapshotDocIds(QuerySnapshot querySnapshot) { - return querySnapshot.documents + return querySnapshot.documents! .map((snapshot) => snapshot.documentID) .toList(); } @@ -703,7 +703,7 @@ void main() { var snapshot = await query.get(); expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents.single; + var doc = snapshot.documents!.single; expect(doc.documentID, doc1.documentID); }); @@ -739,12 +739,12 @@ void main() { new DocumentData()..setInt('field1', 1)..setInt('field2', 2)); QuerySnapshot querySnapshot = await collRef.select(['field2']).get(); - var documentData = querySnapshot.documents.first.data; + var documentData = querySnapshot.documents!.first.data; expect(documentData.has('field2'), isTrue); expect(documentData.has('field1'), isFalse); querySnapshot = await collRef.select(['field2']).get(); - documentData = querySnapshot.documents.first.data; + documentData = querySnapshot.documents!.first.data; expect(documentData.has('field2'), isTrue); expect(documentData.has('field1'), isFalse); }); @@ -759,55 +759,55 @@ void main() { var docRefTwo = collRef.document('two'); await docRefTwo.setData(new DocumentData()..setInt('value', 2)); - List list; + List? list; // limit QuerySnapshot querySnapshot = await collRef.limit(1).get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); // offset querySnapshot = await collRef.orderBy('value').offset(1).get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); // order by querySnapshot = await collRef.orderBy('value').get(); list = querySnapshot.documents; - expect(list.length, 2); + expect(list!.length, 2); expect(list.first.reference.documentID, "one"); // desc querySnapshot = await collRef.orderBy('value', descending: true).get(); list = querySnapshot.documents; - expect(list.length, 2); + expect(list!.length, 2); expect(list.first.reference.documentID, "two"); // start at querySnapshot = await collRef.orderBy('value').startAt(values: [2]).get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); expect(list.first.reference.documentID, "two"); // start after querySnapshot = await collRef.orderBy('value').startAfter(values: [1]).get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); expect(list.first.reference.documentID, "two"); // end at querySnapshot = await collRef.orderBy('value').endAt(values: [1]).get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); expect(list.first.reference.documentID, "one"); // end before querySnapshot = await collRef.orderBy('value').endBefore(values: [2]).get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); expect(list.first.reference.documentID, "one"); // start after using snapshot @@ -816,13 +816,13 @@ void main() { .startAfter(snapshot: list.first) .get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); expect(list.first.reference.documentID, "two"); // where querySnapshot = await collRef.where('value', isGreaterThan: 1).get(); list = querySnapshot.documents; - expect(list.length, 1); + expect(list!.length, 1); expect(list.first.reference.documentID, "two"); }); @@ -912,7 +912,7 @@ void main() { collRef.orderBy('value').where('value', isGreaterThan: 1)); var list = query.documents; - var doc4 = (await tx.get(doc4Ref)).data.getInt('value'); + var doc4 = (await tx.get(doc4Ref)).data.getInt('value')!; tx.create(doc1Ref, new DocumentData()..setInt('value', 1 + doc4)); tx.update( doc2Ref, new UpdateData()..setInt('other.value', 22 + doc4)); @@ -921,7 +921,7 @@ void main() { merge: true); tx.delete(doc4Ref); - return list; + return list!; }); expect(list.length, 3); @@ -1010,7 +1010,7 @@ void main() { var transaction = app.firestore().runTransaction((Transaction tx) async { var doc1 = await tx.get(doc1Ref); - var val = doc1.data.getInt('value') + 1; + var val = doc1.data.getInt('value')! + 1; tx.set(doc1Ref, new DocumentData()..setInt('value', val)); return val; }); diff --git a/test/setup.dart b/test/setup.dart index 4de992c..8daff7c 100644 --- a/test/setup.dart +++ b/test/setup.dart @@ -9,7 +9,7 @@ import 'package:firebase_admin_interop/firebase_admin_interop.dart'; final Map env = dartify(process.env); -App initFirebaseApp() { +App? initFirebaseApp() { if (!env.containsKey('FIREBASE_CONFIG') || !env.containsKey('FIREBASE_SERVICE_ACCOUNT_JSON')) throw new StateError('Environment variables are not set.'); From d6f018bd66f4a0068a49be7995125a49086c8ed1 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 20 Sep 2021 19:15:22 +0200 Subject: [PATCH 04/24] fix: dart 2.14 lints --- .github/workflows/run_ci.yml | 27 +++ analysis_options.yaml | 3 + example/main.dart | 2 +- lib/src/admin.dart | 10 +- lib/src/app.dart | 10 +- lib/src/bindings.dart | 65 +++++--- lib/src/database.dart | 81 ++++----- lib/src/firestore.dart | 283 ++++++++++++++++---------------- lib/src/firestore_bindings.dart | 46 ++++-- lib/src/messaging.dart | 38 +++-- lib/src/storage_bindings.dart | 2 +- pubspec.yaml | 8 +- test/auth_test.dart | 3 +- test/database_test.dart | 23 +-- test/firestore_test.dart | 252 ++++++++++++++-------------- test/setup.dart | 24 +-- 16 files changed, 481 insertions(+), 396 deletions(-) create mode 100644 .github/workflows/run_ci.yml diff --git a/.github/workflows/run_ci.yml b/.github/workflows/run_ci.yml new file mode 100644 index 0000000..0b98e5b --- /dev/null +++ b/.github/workflows/run_ci.yml @@ -0,0 +1,27 @@ +name: Run CI +on: + push: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' # every sunday at midnight + +jobs: + test: + name: Test on ${{ matrix.os }} / ${{ matrix.dart }} + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: . + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + dart: [stable, beta, dev] + steps: + - uses: cedx/setup-dart@v2 + with: + release-channel: ${{ matrix.dart }} + - uses: actions/checkout@v2 + - run: dart --version + - run: dart pub get + - run: dart run tool/run_ci.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 0a6de08..61e2362 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,3 +1,6 @@ +# tekartik recommended lints (extension over google lints and pedantic) +include: package:tekartik_lints/recommended.yaml + analyzer: exclude: - example/** diff --git a/example/main.dart b/example/main.dart index 002001e..7b7e0b6 100644 --- a/example/main.dart +++ b/example/main.dart @@ -4,7 +4,7 @@ main() async { final serviceAccountKeyFilename = '/absolute/path/to/service-account.json'; final admin = FirebaseAdmin.instance; final cert = admin.certFromPath(serviceAccountKeyFilename); - final app = admin.initializeApp(new AppOptions( + final app = admin.initializeApp(AppOptions( credential: cert, databaseURL: "YOUR_DB_URL", )); diff --git a/lib/src/admin.dart b/lib/src/admin.dart index bec5e19..2a8f593 100644 --- a/lib/src/admin.dart +++ b/lib/src/admin.dart @@ -10,9 +10,9 @@ import 'bindings.dart' as js; /// with [initializeApp] method. class FirebaseAdmin { final js.FirebaseAdmin? _admin; - final Map _apps = new Map(); + final Map _apps = {}; - static FirebaseAdmin get instance => _instance ??= new FirebaseAdmin._(); + static FirebaseAdmin get instance => _instance ??= FirebaseAdmin._(); static FirebaseAdmin? _instance; FirebaseAdmin._() : _admin = js.admin; @@ -50,14 +50,14 @@ class FirebaseAdmin { // Special case for default app with Application Default Credentials. name = js.defaultAppName; if (!_apps.containsKey(name)) { - _apps[name] = new App(_admin!.initializeApp()); + _apps[name] = App(_admin!.initializeApp()); } return _apps[name]; } name ??= js.defaultAppName; if (!_apps.containsKey(name)) { - _apps[name] = new App(_admin!.initializeApp(options, name)); + _apps[name] = App(_admin!.initializeApp(options, name)); } return _apps[name]; } @@ -68,7 +68,7 @@ class FirebaseAdmin { required String? clientEmail, required String? privateKey, }) { - return _admin!.credential.cert(new js.ServiceAccountConfig( + return _admin!.credential.cert(js.ServiceAccountConfig( project_id: projectId, client_email: clientEmail, private_key: privateKey, diff --git a/lib/src/app.dart b/lib/src/app.dart index 9b2372e..0c88187 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -29,22 +29,20 @@ class App { js.AppOptions get options => nativeInstance.options; /// Gets the [Auth] service for this application. - Auth auth() => _auth ??= new Auth(nativeInstance.auth()); + Auth auth() => _auth ??= Auth(nativeInstance.auth()); Auth? _auth; /// Gets Realtime [Database] client for this application. Database database() => - _database ??= new Database(this.nativeInstance.database(), this); + _database ??= Database(nativeInstance.database(), this); Database? _database; /// Gets [Firestore] client for this application. - Firestore firestore() => - _firestore ??= new Firestore(nativeInstance.firestore()); + Firestore firestore() => _firestore ??= Firestore(nativeInstance.firestore()); Firestore? _firestore; /// Gets [Messaging] client for this application. - Messaging messaging() => - _messaging ??= new Messaging(nativeInstance.messaging()); + Messaging messaging() => _messaging ??= Messaging(nativeInstance.messaging()); Messaging? _messaging; /// Renders this app unusable and frees the resources of all associated diff --git a/lib/src/bindings.dart b/lib/src/bindings.dart index a8d4a7f..044c314 100644 --- a/lib/src/bindings.dart +++ b/lib/src/bindings.dart @@ -3,6 +3,7 @@ @JS() library firebase_admin; +import 'package:firebase_admin_interop/js.dart'; import 'package:js/js.dart'; import 'package:node_interop/node.dart'; @@ -15,7 +16,7 @@ export 'firestore_bindings.dart'; const defaultAppName = '[DEFAULT]'; /// Singleton instance of [FirebaseAdmin] module. -final FirebaseAdmin? admin = require('firebase-admin'); +final admin = require('firebase-admin') as FirebaseAdmin?; @JS() @anonymous @@ -24,6 +25,7 @@ abstract class FirebaseAdmin { external App initializeApp([options, String? name]); /// The current SDK version. + // ignore: non_constant_identifier_names external String get SDK_VERSION; /// A (read-only) array of all initialized apps. @@ -101,12 +103,20 @@ abstract class Credentials { @JS() @anonymous abstract class ServiceAccountConfig { + // ignore: non_constant_identifier_names external String get project_id; + // ignore: non_constant_identifier_names external String get client_email; + // ignore: non_constant_identifier_names external String get private_key; external factory ServiceAccountConfig( - {String? project_id, String? client_email, String? private_key}); + // ignore: non_constant_identifier_names + {String? project_id, + // ignore: non_constant_identifier_names + String? client_email, + // ignore: non_constant_identifier_names + String? private_key}); } /// Interface which provides Google OAuth2 access tokens used to authenticate @@ -127,9 +137,11 @@ abstract class Credential { @anonymous abstract class AccessToken { /// The actual Google OAuth2 access token. + // ignore: non_constant_identifier_names external String get access_token; /// The number of seconds from when the token was issued that it expires. + // ignore: non_constant_identifier_names external num get expires_in; } @@ -348,7 +360,7 @@ abstract class UserRecord { /// roles and propagated to an authenticated user's ID token. /// /// This is set via [Auth.setCustomUserClaims]. - external get customClaims; + external Object? get customClaims; /// Whether or not the user is disabled: true for disabled; false for enabled. external bool get disabled; @@ -472,6 +484,7 @@ abstract class DecodedIdToken { /// user initially logged in to this session. In a single session, the Firebase /// SDKs will refresh a user's ID tokens every hour. Each ID token will have a /// different [iat] value, but the same auth_time value. + // ignore: non_constant_identifier_names external num get auth_time; /// The ID token's expiration time, in seconds since the Unix epoch. @@ -525,11 +538,12 @@ abstract class DecodedIdToken { abstract class FirebaseSignInInfo { /// Provider-specific identity details corresponding to the provider used to /// sign in the user. - external get identities; + external Object? get identities; /// The ID of the provider used to sign in the user. One of "anonymous", /// "password", "facebook.com", "github.com", "google.com", "twitter.com", /// or "custom". + // ignore: non_constant_identifier_names external String get sign_in_provider; } @@ -538,10 +552,14 @@ abstract class FirebaseSignInInfo { @JS() @anonymous abstract class FirestoreService { + // ignore: non_constant_identifier_names external GeoPointUtil get GeoPoint; + // ignore: non_constant_identifier_names external FieldValues get FieldValue; - external dynamic get Timestamp; - external dynamic get FieldPath; + // ignore: non_constant_identifier_names + external Object get Timestamp; + // ignore: non_constant_identifier_names + external FieldPathPrototype get FieldPath; } // admin.messaging ================================================================ @@ -1527,7 +1545,8 @@ abstract class DatabaseService { // external Database call([App app]); /// Logs debugging information to the console. - external enableLogging([dynamic loggerOrBool, bool? persistent]); + external void enableLogging([dynamic loggerOrBool, bool? persistent]); + // ignore: non_constant_identifier_names external ServerValues get ServerValue; } @@ -1536,6 +1555,7 @@ abstract class DatabaseService { @JS() @anonymous abstract class ServerValues { + // ignore: non_constant_identifier_names external num get TIMESTAMP; } @@ -1611,7 +1631,7 @@ abstract class Reference extends Query { /// so the resulting list of items will be chronologically sorted. The keys /// are also designed to be unguessable (they contain 72 random bits of /// entropy). - external ThenableReference push([value, onComplete(JsError error)?]); + external ThenableReference push([value, Function(JsError error)? onComplete]); /// Removes the data at this Database location. /// @@ -1622,7 +1642,7 @@ abstract class Reference extends Query { /// Firebase servers will also be started, and the returned [Promise] will /// resolve when complete. If provided, the [onComplete] callback will be /// called asynchronously after synchronization has finished. - external Promise remove([onComplete(JsError error)?]); + external Promise remove([Function(JsError error)? onComplete]); /// Writes data to this Database location. /// @@ -1646,7 +1666,7 @@ abstract class Reference extends Query { /// /// A single [set] will generate a single "value" event at the location where /// the `set()` was performed. - external Promise set(value, [onComplete(JsError error)?]); + external Promise set(value, [Function(JsError error)? onComplete]); /// Sets a priority for the data at this Database location. /// @@ -1655,7 +1675,7 @@ abstract class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - external Promise setPriority(priority, [onComplete(JsError error)?]); + external Promise setPriority(priority, [Function(JsError error)? onComplete]); /// Writes data the Database location. Like [set] but also specifies the /// [priority] for that data. @@ -1666,7 +1686,7 @@ abstract class Reference extends Query { /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) external Promise setWithPriority(value, priority, - [onComplete(JsError error)?]); + [Function(JsError error)? onComplete]); /// Atomically modifies the data at this location. /// @@ -1692,8 +1712,10 @@ abstract class Reference extends Query { /// to perform a transaction. This is because the client-side nature of /// transactions requires the client to read the data in order to /// transactionally update it. - external Promise transaction(transactionUpdate(snapshot), - onComplete(error, bool committed, snapshot), bool applyLocally); + external Promise transaction( + Function(dynamic snapshot) transactionUpdate, + Function(dynamic error, bool committed, dynamic snapshot) onComplete, + bool applyLocally); /// Writes multiple values to the Database at once. /// @@ -1722,7 +1744,7 @@ abstract class Reference extends Query { /// [transaction] to modify the same data. /// /// Passing `null` to [update] will remove the data at this location. - external Promise update(values, [onComplete(JsError error)?]); + external Promise update(values, [Function(JsError error)? onComplete]); } @JS() @@ -1766,7 +1788,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise cancel([onComplete(JsError error)?]); + external Promise cancel([Function(JsError error)? onComplete]); /// Ensures the data at this location is deleted when the client is /// disconnected (due to closing the browser, navigating to a new page, or @@ -1775,7 +1797,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise remove([onComplete(JsError error)?]); + external Promise remove([Function(JsError error)? onComplete]); /// Ensures the data at this location is set to the specified [value] when the /// client is disconnected (due to closing the browser, navigating to a new @@ -1791,13 +1813,13 @@ abstract class OnDisconnect { /// /// See also: /// - [Enabling Offline Capabilities in JavaScript](https://firebase.google.com/docs/database/web/offline-capabilities) - external Promise set(value, [onComplete(JsError error)?]); + external Promise set(value, [Function(JsError error)? onComplete]); /// Ensures the data at this location is set to the specified [value] and /// [priority] when the client is disconnected (due to closing the browser, /// navigating to a new page, or network issues). external Promise setWithPriority(value, priority, - [onComplete(JsError error)?]); + [Function(JsError error)? onComplete]); /// Writes multiple [values] at this location when the client is disconnected /// (due to closing the browser, navigating to a new page, or network issues). @@ -1813,7 +1835,7 @@ abstract class OnDisconnect { /// /// See [Reference.update] for examples of using the connected version of /// update. - external Promise update(values, [onComplete(JsError error)?]); + external Promise update(values, [Function(JsError error)? onComplete]); } /// Sorts and filters the data at a [Database] location so only a subset of the @@ -1994,6 +2016,7 @@ abstract class Query { /// Append '.json' to the returned URL when typed into a browser to download /// JSON-formatted data. If the location is secured (that is, not publicly /// readable), you will get a permission-denied error. + @override external String toString(); } @@ -2063,7 +2086,7 @@ abstract class DataSnapshot { /// If no explicit `orderBy*()` method is used, results are returned ordered /// by key (unless priorities are used, in which case, results are returned by /// priority). - external bool forEach(bool action(DataSnapshot child)); + external bool forEach(bool Function(DataSnapshot child) action); /// Gets the priority value of the data in this DataSnapshot. /// diff --git a/lib/src/database.dart b/lib/src/database.dart index e7dd0ce..e8d6678 100644 --- a/lib/src/database.dart +++ b/lib/src/database.dart @@ -5,8 +5,8 @@ import 'dart:async'; import 'dart:js'; import 'package:meta/meta.dart'; -import 'package:node_interop/util.dart'; import 'package:node_interop/js.dart'; +import 'package:node_interop/util.dart'; import 'app.dart'; import 'bindings.dart' as js; @@ -32,12 +32,11 @@ class Database { /// Returns a [Reference] representing the location in the Database /// corresponding to the provided [path]. If no path is provided, the /// Reference will point to the root of the Database. - Reference ref([String? path]) => new Reference(nativeInstance.ref(path)); + Reference ref([String? path]) => Reference(nativeInstance.ref(path)); /// Returns a [Reference] representing the location in the Database /// corresponding to the provided Firebase URL. - Reference refFromUrl(String url) => - new Reference(nativeInstance.refFromURL(url)); + Reference refFromUrl(String url) => Reference(nativeInstance.refFromURL(url)); } /// List of event types supported in [Query.on] and [Query.off]. @@ -97,7 +96,7 @@ class QuerySubscription { /// /// See also [Query.off] for other ways of canceling subscriptions. void cancel() { - _nativeInstance.off(this.eventType, this._callback); + _nativeInstance.off(eventType, _callback); } } @@ -123,7 +122,7 @@ class Query { Query(this.nativeInstance); /// Returns a [Reference] to the [Query]'s location. - Reference get ref => _ref ??= new Reference(nativeInstance.ref); + Reference get ref => _ref ??= Reference(nativeInstance.ref); Reference? _ref; /// Creates a [Query] with the specified ending point. @@ -145,9 +144,9 @@ class Query { /// priority. Query endAt(value, [String? key]) { if (key == null) { - return new Query(nativeInstance.endAt(value)); + return Query(nativeInstance.endAt(value)); } - return new Query(nativeInstance.endAt(value, key)); + return Query(nativeInstance.endAt(value, key)); } /// Creates a [Query] that includes children that match the specified value. @@ -165,9 +164,9 @@ class Query { /// be used to filter result sets with many matches for the same value. Query equalTo(value, [String? key]) { if (key == null) { - return new Query(nativeInstance.equalTo(value)); + return Query(nativeInstance.equalTo(value)); } - return new Query(nativeInstance.equalTo(value, key)); + return Query(nativeInstance.equalTo(value, key)); } /// Returns `true` if this and [other] query are equal. @@ -191,8 +190,7 @@ class Query { /// the first 100 ordered messages. As items change, we will receive /// child_removed events for each item that drops out of the active list so /// that the total number stays at 100. - Query limitToFirst(int limit) => - new Query(nativeInstance.limitToFirst(limit)); + Query limitToFirst(int limit) => Query(nativeInstance.limitToFirst(limit)); /// Generates a new [Query] limited to the last specific number of children. /// @@ -204,13 +202,13 @@ class Query { /// the last 100 ordered messages. As items change, we will receive /// child_removed events for each item that drops out of the active list so /// that the total number stays at 100. - Query limitToLast(int limit) => new Query(nativeInstance.limitToLast(limit)); + Query limitToLast(int limit) => Query(nativeInstance.limitToLast(limit)); /// Listens for exactly one event of the specified [eventType], and then stops /// listening. Future> once(String eventType) { - return promiseToFuture(nativeInstance.once(eventType)) - .then((snapshot) => new DataSnapshot(snapshot)); + return promiseToFuture(nativeInstance.once(eventType)) + .then((snapshot) => DataSnapshot(snapshot)); } /// Cancels previously created subscription with [on]. @@ -245,7 +243,8 @@ class Query { QuerySubscription on( String eventType, Function(DataSnapshot snapshot) callback, [Function()? cancelCallback]) { - var fn = allowInterop((snapshot) => callback(new DataSnapshot(snapshot))); + var fn = allowInterop( + (snapshot) => callback(DataSnapshot(snapshot as js.DataSnapshot))); if (cancelCallback != null) { nativeInstance.on(eventType, fn, allowInterop(cancelCallback)); } else { @@ -258,8 +257,7 @@ class Query { /// /// Queries can only order by one key at a time. Calling [orderByChild] /// multiple times on the same query is an error. - Query orderByChild(String path) => - new Query(nativeInstance.orderByChild(path)); + Query orderByChild(String path) => Query(nativeInstance.orderByChild(path)); /// Generates a new [Query] object ordered by key. /// @@ -267,7 +265,7 @@ class Query { /// /// See also: /// - [Sort data](https://firebase.google.com/docs/database/web/lists-of-data#sort_data) - Query orderByKey() => new Query(nativeInstance.orderByKey()); + Query orderByKey() => Query(nativeInstance.orderByKey()); /// Generates a new [Query] object ordered by priority. /// @@ -276,7 +274,7 @@ class Query { /// /// See also: /// - [Sort data](https://firebase.google.com/docs/database/web/lists-of-data#sort_data) - Query orderByPriority() => new Query(nativeInstance.orderByPriority()); + Query orderByPriority() => Query(nativeInstance.orderByPriority()); /// Generates a new [Query] object ordered by value. /// @@ -285,7 +283,7 @@ class Query { /// /// See also: /// - [Sort data](https://firebase.google.com/docs/database/web/lists-of-data#sort_data) - Query orderByValue() => new Query(nativeInstance.orderByValue()); + Query orderByValue() => Query(nativeInstance.orderByValue()); /// Creates a [Query] with the specified starting point. /// @@ -301,9 +299,9 @@ class Query { /// - [Filtering data](https://firebase.google.com/docs/database/web/lists-of-data#filtering_data) Query startAt(value, [String? key]) { if (key == null) { - return new Query(nativeInstance.startAt(value)); + return Query(nativeInstance.startAt(value)); } - return new Query(nativeInstance.startAt(value, key)); + return Query(nativeInstance.startAt(value, key)); } // Note: intentionally not following JS convention and using Dart convention instead. @@ -341,25 +339,25 @@ class Reference extends Query { /// The parent location of this Reference. /// /// The parent of a root Reference is `null`. - Reference get parent => _parent ??= new Reference(nativeInstance.parent); + Reference get parent => _parent ??= Reference(nativeInstance.parent); Reference? _parent; /// The root [Reference] of the [Database]. - Reference get root => _root ??= new Reference(nativeInstance.root); + Reference get root => _root ??= Reference(nativeInstance.root); Reference? _root; /// Gets a [Reference] for the location at the specified relative [path]. /// /// The relative [path] can either be a simple child name (for example, "ada") /// or a deeper slash-separated path (for example, "ada/name/first"). - Reference child(String path) => new Reference(nativeInstance.child(path)); + Reference child(String path) => Reference(nativeInstance.child(path)); /// Returns an [OnDisconnect] object. /// /// For more information on how to use it see /// [Enabling Offline Capabilities in JavaScript](https://firebase.google.com/docs/database/web/offline-capabilities). dynamic onDisconnect() { - throw new UnimplementedError(); + throw UnimplementedError(); } /// Generates a new child location using a unique key and returns its @@ -379,13 +377,13 @@ class Reference extends Query { FutureReference push([T? value]) { if (value != null) { var futureRef = nativeInstance.push(jsify(value)); - return new FutureReference(futureRef, promiseToFuture(futureRef)); + return FutureReference(futureRef, promiseToFuture(futureRef)); } else { // JS side returns regular Reference if value is not provided, but // we still convert it to FutureReference to be consistent with declared // return type. var newRef = nativeInstance.push(); - return new FutureReference(newRef, new Future.value()); + return FutureReference(newRef, Future.value()); } } @@ -497,19 +495,19 @@ class Reference extends Query { return promiseToFuture(promise).then( (result) { final jsResult = result as js.TransactionResult; - return new DatabaseTransaction( - jsResult.committed, new DataSnapshot(jsResult.snapshot)); + return DatabaseTransaction( + jsResult.committed, DataSnapshot(jsResult.snapshot)); }, ); } - _onComplete(error, bool committed, snapshot) { + dynamic _onComplete(error, bool committed, snapshot) { // no-op, we use returned Promise instead. } Function _createTransactionHandler(DatabaseTransactionHandler handler) { return (currentData) { - final data = dartify(currentData); + final data = dartify(currentData); final result = handler(data); if (result.aborted) return undefined; return jsify(result.data!); @@ -556,12 +554,14 @@ typedef DatabaseTransactionHandler = TransactionResult Function( /// instance of this class according to logic in your transactions. class TransactionResult { TransactionResult._(this.aborted, this.data); + final bool aborted; final T data; - static TransactionResult abort = new TransactionResult._(true, null); + static TransactionResult abort = TransactionResult._(true, null); + static TransactionResult success(T data) => - new TransactionResult._(false, data); + TransactionResult._(false, data); } /// Firebase Realtime Database transaction result returned from @@ -585,6 +585,7 @@ class DatabaseTransaction { /// For more details see documentation for [Reference.push]. class FutureReference extends Reference { final Future done; + FutureReference(js.ThenableReference nativeInstance, this.done) : super(nativeInstance); } @@ -609,7 +610,7 @@ class DataSnapshot { String get key => nativeInstance.key; /// The [Reference] for the location that generated this [DataSnapshot]. - Reference get ref => new Reference(nativeInstance.ref); + Reference get ref => Reference(nativeInstance.ref); /// Gets [DataSnapshot] for the location at the specified relative [path]. /// @@ -618,7 +619,7 @@ class DataSnapshot { /// child location has no data, an empty DataSnapshot (that is, a /// DataSnapshot whose value is `null`) is returned. DataSnapshot child(String path) => - new DataSnapshot(nativeInstance.child(path)); + DataSnapshot(nativeInstance.child(path)); /// Returns `true` if this DataSnapshot contains any data. /// @@ -633,16 +634,18 @@ class DataSnapshot { /// If no explicit orderBy*() method is used, results are returned ordered by /// key (unless priorities are used, in which case, results are returned /// by priority). - bool forEach(bool action(DataSnapshot child)) { + bool forEach(bool Function(DataSnapshot child) action) { bool wrapper(js.DataSnapshot child) { - return action(new DataSnapshot(child)); + return action(DataSnapshot(child)); } return nativeInstance.forEach(allowInterop(wrapper)); } bool hasChild(String path) => nativeInstance.hasChild(path); + bool hasChildren() => nativeInstance.hasChildren(); + int numChildren() => nativeInstance.numChildren() as int; T? _value; diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 61eb3b2..2be873f 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -19,25 +19,27 @@ js.GeoPoint createGeoPoint(num latitude, num longitude) => _createJsGeoPoint(latitude, longitude); js.GeoPoint _createJsGeoPoint(num latitude, num longitude) { - final proto = new js.GeoPointProto(latitude: latitude, longitude: longitude); + final proto = js.GeoPointProto(latitude: latitude, longitude: longitude); return js.admin!.firestore.GeoPoint.fromProto(proto); } js.Timestamp? _createJsTimestamp(Timestamp ts) { - return callConstructor( - js.admin!.firestore.Timestamp, jsify([ts.seconds, ts.nanoseconds])); + return callConstructor(js.admin!.firestore.Timestamp, + jsify([ts.seconds, ts.nanoseconds]) as List?) as js.Timestamp?; } @Deprecated('This function will be hidden from public API in future versions.') js.FieldPath? createFieldPath(List fieldNames) { - return callConstructor(js.admin!.firestore.FieldPath, jsify(fieldNames)); + return callConstructor( + js.admin!.firestore.FieldPath, jsify(fieldNames) as List?) + as js.FieldPath; } /// Returns a special sentinel [FieldPath] to refer to the ID of a document. /// It can be used in queries to sort or filter by the document ID. @Deprecated('Use "Firestore.documentId" instead.') js.FieldPath documentId() { - final js.FieldPathPrototype proto = js.admin!.firestore.FieldPath; + final proto = js.admin!.firestore.FieldPath; return proto.documentId(); } @@ -51,7 +53,7 @@ class Firestore { /// Returns a special sentinel [FieldPath] to refer to the ID of a document. /// It can be used in queries to sort or filter by the document ID. static js.FieldPath documentId() { - final js.FieldPathPrototype proto = js.admin!.firestore.FieldPath; + final proto = js.admin!.firestore.FieldPath; return proto.documentId(); } @@ -72,7 +74,7 @@ class Firestore { /// Gets a [CollectionReference] for the specified Firestore path. CollectionReference collection(String path) { - return new CollectionReference(nativeInstance.collection(path), this); + return CollectionReference(nativeInstance.collection(path), this); } /// Creates and returns a new Query that includes all documents in the @@ -83,13 +85,12 @@ class Firestore { /// or subcollection with this ID as the last segment of its path will be /// included. Cannot contain a slash. DocumentQuery collectionGroup(String collectionId) { - return new DocumentQuery( - nativeInstance.collectionGroup(collectionId), this); + return DocumentQuery(nativeInstance.collectionGroup(collectionId), this); } /// Gets a [DocumentReference] for the specified Firestore path. DocumentReference document(String path) { - return new DocumentReference(nativeInstance.doc(path), this); + return DocumentReference(nativeInstance.doc(path), this); } /// Executes the given [updateFunction] and commits the changes applied within @@ -104,10 +105,11 @@ class Firestore { /// with an error. If [updateFunction] throws then returned Future completes /// with the same error. Future runTransaction( - Future updateFunction(Transaction transaction)) { - var jsUpdateFunction = (js.Transaction transaction) { - return futureToPromise(updateFunction(new Transaction(transaction))); - }; + Future Function(Transaction transaction) updateFunction) { + Promise jsUpdateFunction(js.Transaction transaction) { + return futureToPromise(updateFunction(Transaction(transaction))); + } + return promiseToFuture( nativeInstance.runTransaction(allowInterop(jsUpdateFunction))); } @@ -116,23 +118,24 @@ class Firestore { /// database. Future> listCollections() async => (await promiseToFuture(nativeInstance.listCollections())) - .map((nativeCollectionReference) => - CollectionReference(nativeCollectionReference, this)) + .map((nativeCollectionReference) => CollectionReference( + nativeCollectionReference as js.CollectionReference, this)) .toList(growable: false); /// Creates a write batch, used for performing multiple writes as a single /// atomic operation. - WriteBatch batch() => new WriteBatch(nativeInstance.batch()); + WriteBatch batch() => WriteBatch(nativeInstance.batch()); /// Retrieves multiple documents from Firestore. Future> getAll(List refs) async { final nativeRefs = refs .map((DocumentReference ref) => ref.nativeInstance) .toList(growable: false); - final promise = callMethod(nativeInstance, 'getAll', nativeRefs); + final promise = callMethod(nativeInstance, 'getAll', nativeRefs) as Promise; final result = await promiseToFuture(promise); return result - .map((nativeSnapshot) => DocumentSnapshot(nativeSnapshot, this)) + .map((nativeSnapshot) => + DocumentSnapshot(nativeSnapshot as js.DocumentSnapshot, this)) .toList(growable: false); } } @@ -155,7 +158,7 @@ class CollectionReference extends DocumentQuery { /// For root collections, null is returned. DocumentReference? get parent { return (nativeInstance!.parent != null) - ? new DocumentReference(nativeInstance!.parent!, firestore) + ? DocumentReference(nativeInstance!.parent!, firestore) : null; } @@ -168,7 +171,7 @@ class CollectionReference extends DocumentQuery { DocumentReference document([String? path]) { final docRef = (path == null) ? nativeInstance!.doc() : nativeInstance!.doc(path); - return new DocumentReference(docRef, firestore); + return DocumentReference(docRef, firestore); } /// Returns a `DocumentReference` with an auto-generated ID, after @@ -177,8 +180,9 @@ class CollectionReference extends DocumentQuery { /// The unique key generated is prefixed with a client-generated timestamp /// so that the resulting list will be chronologically-sorted. Future add(DocumentData data) { - return promiseToFuture(nativeInstance!.add(data.nativeInstance)) - .then((jsRef) => new DocumentReference(jsRef, firestore)); + return promiseToFuture( + nativeInstance!.add(data.nativeInstance)) + .then((jsRef) => DocumentReference(jsRef, firestore)); } /// The last path element of the referenced collection. @@ -209,7 +213,7 @@ class DocumentReference { String get documentID => nativeInstance.id; CollectionReference get parent { - return new CollectionReference(nativeInstance.parent, firestore); + return CollectionReference(nativeInstance.parent, firestore); } /// Writes to the document referred to by this [DocumentReference]. If the @@ -228,15 +232,15 @@ class DocumentReference { /// If no document exists yet, the update will fail. Future updateData(UpdateData data) { final docData = data.nativeInstance; - return promiseToFuture(nativeInstance.update(docData)); + return promiseToFuture(nativeInstance.update(docData as js.UpdateData)); } /// Reads the document referenced by this [DocumentReference]. /// /// If no document exists, the read will return null. Future get() { - return promiseToFuture(nativeInstance.get()) - .then((jsSnapshot) => new DocumentSnapshot(jsSnapshot, firestore)); + return promiseToFuture(nativeInstance.get()).then((jsSnapshot) => + DocumentSnapshot(jsSnapshot as js.DocumentSnapshot, firestore)); } /// Deletes the document referred to by this [DocumentReference]. @@ -245,7 +249,7 @@ class DocumentReference { /// Returns the reference of a collection contained inside of this /// document. CollectionReference collection(String path) => - new CollectionReference(nativeInstance.collection(path), firestore); + CollectionReference(nativeInstance.collection(path), firestore); /// Notifies of documents at this location. Stream get snapshots { @@ -255,16 +259,16 @@ class DocumentReference { late StreamController controller; // ignore: close_sinks void _onNextSnapshot(js.DocumentSnapshot jsSnapshot) { - controller.add(new DocumentSnapshot(jsSnapshot, firestore)); + controller.add(DocumentSnapshot(jsSnapshot, firestore)); } - controller = new StreamController.broadcast( + controller = StreamController.broadcast( onListen: () { cancelCallback = nativeInstance.onSnapshot(allowInterop(_onNextSnapshot)); }, onCancel: () { - cancelCallback(); + (cancelCallback as Function())(); }, ); return controller.stream; @@ -273,8 +277,8 @@ class DocumentReference { /// Fetches the subcollections that are direct children of this document. Future> listCollections() async => (await promiseToFuture(nativeInstance.listCollections())) - .map((nativeCollectionReference) => - CollectionReference(nativeCollectionReference, firestore)) + .map((nativeCollectionReference) => CollectionReference( + nativeCollectionReference as js.CollectionReference, firestore)) .toList(growable: false); } @@ -336,7 +340,7 @@ class DocumentChange { /// The document affected by this change. DocumentSnapshot get document => - _document ??= new DocumentSnapshot(nativeInstance.doc, firestore); + _document ??= DocumentSnapshot(nativeInstance.doc, firestore); DocumentSnapshot? _document; } @@ -349,11 +353,11 @@ class DocumentSnapshot { /// The reference that produced this snapshot DocumentReference get reference => - _reference ??= new DocumentReference(nativeInstance.ref, firestore); + _reference ??= DocumentReference(nativeInstance.ref, firestore); DocumentReference? _reference; /// Contains all the data of this snapshot - DocumentData get data => _data ??= new DocumentData(nativeInstance.data()); + DocumentData get data => _data ??= DocumentData(nativeInstance.data()); DocumentData? _data; /// Returns `true` if the document exists. @@ -367,7 +371,7 @@ class DocumentSnapshot { Timestamp? get createTime { final ts = nativeInstance.createTime; if (ts == null) return null; - return new Timestamp(ts.seconds, ts.nanoseconds); + return Timestamp(ts.seconds, ts.nanoseconds); } /// The time the document was last updated (at the time the snapshot was @@ -378,15 +382,15 @@ class DocumentSnapshot { Timestamp? get updateTime { final ts = nativeInstance.updateTime; if (ts == null) return null; - return new Timestamp(ts.seconds, ts.nanoseconds); + return Timestamp(ts.seconds, ts.nanoseconds); } } class _FirestoreData { _FirestoreData([Object? nativeInstance]) - : nativeInstance = nativeInstance ?? newObject(); + : nativeInstance = (nativeInstance ?? newObject()) as js.DocumentData; @protected - final dynamic nativeInstance; + final js.DocumentData nativeInstance; /// Length of this document. int get length => objectKeys(nativeInstance).length; @@ -422,10 +426,9 @@ class _FirestoreData { } else if (value is FieldValue) { setFieldValue(key, value); } else if (value is Map) { - setNestedData( - key, new DocumentData.fromMap(value.cast())); + setNestedData(key, DocumentData.fromMap(value.cast())); } else { - throw new ArgumentError.value( + throw ArgumentError.value( value, key, 'Unsupported value type for Firestore.'); } } @@ -461,17 +464,17 @@ class _FirestoreData { @Deprecated('Migrate to using Firestore Timestamps and "getTimestamp()".') DateTime? getDateTime(String key) { - final Date? value = getProperty(nativeInstance, key); + final value = getProperty(nativeInstance, key) as Date?; if (value == null) return null; assert(_isDate(value), 'Tried to get Date and got $value'); - return new DateTime.fromMillisecondsSinceEpoch(value.getTime()); + return DateTime.fromMillisecondsSinceEpoch(value.getTime()); } Timestamp? getTimestamp(String key) { - js.Timestamp? ts = getProperty(nativeInstance, key); + var ts = getProperty(nativeInstance, key) as js.Timestamp?; if (ts == null) return null; assert(_isTimestamp(ts), 'Tried to get Timestamp and got $ts.'); - return new Timestamp(ts.seconds, ts.nanoseconds); + return Timestamp(ts.seconds, ts.nanoseconds); } @Deprecated('Migrate to using Firestore Timestamps and "setTimestamp()".') @@ -486,22 +489,22 @@ class _FirestoreData { } GeoPoint? getGeoPoint(String key) { - js.GeoPoint? value = getProperty(nativeInstance, key); + var value = getProperty(nativeInstance, key) as js.GeoPoint?; if (value == null) return null; assert(_isGeoPoint(value), 'Invalid value provided to $runtimeType.getGeoPoint().'); - return new GeoPoint(value.latitude.toDouble(), value.longitude.toDouble()); + return GeoPoint(value.latitude.toDouble(), value.longitude.toDouble()); } Blob? getBlob(String key) { - var value = getProperty(nativeInstance, key); + var value = getProperty(nativeInstance, key) as Object?; if (value == null) return null; assert(_isBlob(value), 'Invalid value provided to $runtimeType.getBlob().'); - return new Blob(value); + return Blob(value as List); } void setGeoPoint(String key, GeoPoint value) { - final data =_createJsGeoPoint(value.latitude, value.longitude); + final data = _createJsGeoPoint(value.latitude, value.longitude); setProperty(nativeInstance, key, data); } @@ -511,7 +514,6 @@ class _FirestoreData { } void setFieldValue(String key, FieldValue value) { - setProperty(nativeInstance, key, value._jsify()); } @@ -530,7 +532,7 @@ class _FirestoreData { final data = getProperty(nativeInstance, key); if (data == null) return null; if (data is! List) { - throw new StateError('Expected list but got ${data.runtimeType}.'); + throw StateError('Expected list but got ${data.runtimeType}.'); } final result = []; for (var item in data) { @@ -548,13 +550,13 @@ class _FirestoreData { } DocumentReference? getReference(String key) { - js.DocumentReference? ref = getProperty(nativeInstance, key); + var ref = getProperty(nativeInstance, key) as js.DocumentReference?; if (ref == null) return null; assert(_isReference(ref), 'Invalid value provided to $runtimeType.getReference().'); - js.Firestore firestore = ref.firestore; - return new DocumentReference(ref, new Firestore(firestore)); + var firestore = ref.firestore; + return DocumentReference(ref, Firestore(firestore)); } void setReference(String key, DocumentReference value) { @@ -562,32 +564,32 @@ class _FirestoreData { setProperty(nativeInstance, key, data); } - bool _isTimestamp(value) => + bool _isTimestamp(Object value) => hasProperty(value, '_seconds') && hasProperty(value, '_nanoseconds'); // Workarounds for dart2js as `value is Type` doesn't work as expected. - bool _isDate(value) => + bool _isDate(Object value) => hasProperty(value, 'toDateString') && hasProperty(value, 'getTime') && getProperty(value, 'getTime') is Function; - bool _isGeoPoint(value) => + bool _isGeoPoint(Object value) => hasProperty(value, '_latitude') && hasProperty(value, '_longitude'); - bool _isBlob(value) { + bool _isBlob(Object value) { if (value is Uint8List) { return true; } else { - var proto = getProperty(value, '__proto__'); + var proto = getProperty(value, '__proto__') as Object?; if (proto != null) { - return getProperty(proto, "writeUInt8") is Function && - getProperty(proto, "readUInt8") is Function; + return getProperty(proto, 'writeUInt8') is Function && + getProperty(proto, 'readUInt8') is Function; } return false; } } - bool _isReference(value) => + bool _isReference(Object value) => hasProperty(value, 'firestore') && hasProperty(value, 'id') && hasProperty(value, 'onSnapshot') && @@ -608,17 +610,17 @@ class _FirestoreData { if (_isPrimitive(item)) { return item; } else if (item is GeoPoint) { - GeoPoint point = item; + var point = item; return _createJsGeoPoint(point.latitude, point.longitude); } else if (item is DocumentReference) { - DocumentReference ref = item; + var ref = item; return ref.nativeInstance; } else if (item is Blob) { - Blob blob = item; + var blob = item; return blob.data; } else if (item is DateTime) { - DateTime date = item; - return new Date(date.millisecondsSinceEpoch); + var date = item; + return Date(date.millisecondsSinceEpoch); } else if (item is Timestamp) { return _createJsTimestamp(item); } else if (item is FieldValue) { @@ -633,7 +635,7 @@ class _FirestoreData { } } - dynamic _dartify(item) { + dynamic _dartify(Object? item) { /// This is a best-effort implementation which attempts to convert /// built-in Firestore data types into Dart objects. /// @@ -660,20 +662,20 @@ class _FirestoreData { /// See: https://github.com/googleapis/nodejs-firestore/blob/35c1af0d0afc660b467d411f5de39792f8330be2/src/document.js#L129 if (_isPrimitive(item)) { return item; - } else if (_isGeoPoint(item)) { - js.GeoPoint point = item; + } else if (_isGeoPoint(item!)) { + var point = item as js.GeoPoint; return GeoPoint(point.latitude.toDouble(), point.longitude.toDouble()); } else if (_isReference(item)) { - js.DocumentReference ref = item; - js.Firestore firestore = ref.firestore; - return DocumentReference(ref, new Firestore(firestore)); + var ref = item as js.DocumentReference; + var firestore = ref.firestore; + return DocumentReference(ref, Firestore(firestore)); } else if (_isBlob(item)) { - return Blob(item); + return Blob(item as List); } else if (_isTimestamp(item)) { - js.Timestamp ts = item; + var ts = item as js.Timestamp; return Timestamp(ts.seconds, ts.nanoseconds); } else if (_isDate(item)) { - Date date = item; + var date = item as Date; return DateTime.fromMillisecondsSinceEpoch(date.getTime()); } else if (_isFieldValue(item)) { return FieldValue._fromJs(item); @@ -702,8 +704,8 @@ class _FirestoreData { return list.map(_dartify).toList(); } - Map _dartifyObject(object) { - return DocumentData(object).toMap(); + Map _dartifyObject(Object object) { + return DocumentData(object as js.DocumentData).toMap(); } @override @@ -724,15 +726,15 @@ class DocumentData extends _FirestoreData { DocumentData([js.DocumentData? nativeInstance]) : super(nativeInstance); factory DocumentData.fromMap(Map data) { - final doc = new DocumentData(); + final doc = DocumentData(); data.forEach(doc._setField); return doc; } DocumentData? getNestedData(String key) { - final data = getProperty(nativeInstance, key); + final data = getProperty(nativeInstance, key) as js.DocumentData?; if (data == null) return null; - return new DocumentData(data); + return DocumentData(data); } /// List of keys in this document data. @@ -740,7 +742,7 @@ class DocumentData extends _FirestoreData { /// Converts this document data into a [Map]. Map toMap() { - final Map map = {}; + final map = {}; for (var key in keys) { map[key] = _dartify(getProperty(nativeInstance, key)); } @@ -771,7 +773,7 @@ class UpdateData extends _FirestoreData { UpdateData([js.UpdateData? nativeInstance]) : super(nativeInstance); factory UpdateData.fromMap(Map data) { - final doc = new UpdateData(); + final doc = UpdateData(); data.forEach(doc._setField); return doc; } @@ -785,8 +787,8 @@ class Timestamp { Timestamp(this.seconds, this.nanoseconds); factory Timestamp.fromDateTime(DateTime dateTime) { - final int seconds = dateTime.millisecondsSinceEpoch ~/ 1000; - final int nanoseconds = (dateTime.microsecondsSinceEpoch % 1000000) * 1000; + final seconds = dateTime.millisecondsSinceEpoch ~/ 1000; + final nanoseconds = (dateTime.microsecondsSinceEpoch % 1000000) * 1000; return Timestamp(seconds, nanoseconds); } @@ -794,7 +796,7 @@ class Timestamp { bool operator ==(Object other) { if (identical(this, other)) return true; if (other is! Timestamp) return false; - Timestamp typedOther = other; + var typedOther = other; return seconds == typedOther.seconds && nanoseconds == typedOther.nanoseconds; } @@ -809,7 +811,7 @@ class Timestamp { } DateTime toDateTime() { - return new DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch); + return DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch); } } @@ -824,7 +826,7 @@ class GeoPoint { bool operator ==(other) { if (identical(this, other)) return true; if (other is! GeoPoint) return false; - GeoPoint point = other; + var point = other; return latitude == point.latitude && longitude == point.longitude; } @@ -842,7 +844,7 @@ class Blob { final Uint8List _data; /// Creates new blob from list of bytes in [data]. - Blob(List data) : _data = new Uint8List.fromList(data); + Blob(List data) : _data = Uint8List.fromList(data); /// Creates new blob from list of bytes in [Uint8List]. Blob.fromUint8List(this._data); @@ -869,8 +871,8 @@ class QuerySnapshot { /// Gets a list of all the documents included in this snapshot List? get documents { if (isEmpty) return const []; - _documents ??= new List.from(nativeInstance.docs) - .map((jsDoc) => new DocumentSnapshot(jsDoc, firestore)) + _documents ??= List.from(nativeInstance.docs) + .map((jsDoc) => DocumentSnapshot(jsDoc, firestore)) .toList(growable: false); return _documents; } @@ -884,8 +886,8 @@ class QuerySnapshot { if (nativeInstance.docChanges() == null) { _changes = const []; } else { - _changes = new List.from(nativeInstance.docChanges()!) - .map((jsChange) => new DocumentChange(jsChange, firestore)) + _changes = List.from(nativeInstance.docChanges()!) + .map((jsChange) => DocumentChange(jsChange, firestore)) .toList(growable: false); } } @@ -904,8 +906,8 @@ class DocumentQuery { final Firestore firestore; Future get() { - return promiseToFuture(nativeInstance!.get()) - .then((jsSnapshot) => new QuerySnapshot(jsSnapshot, firestore)); + return promiseToFuture(nativeInstance!.get()) + .then((jsSnapshot) => QuerySnapshot(jsSnapshot, firestore)); } /// Notifies of query results at this location. @@ -915,22 +917,22 @@ class DocumentQuery { late StreamController controller; // ignore: close_sinks void onSnapshot(js.QuerySnapshot snapshot) { - controller.add(new QuerySnapshot(snapshot, firestore)); + controller.add(QuerySnapshot(snapshot, firestore)); } - void onError(error) { + void onError(Object error) { controller.addError(error); } late Function unsubscribe; - controller = new StreamController.broadcast( + controller = StreamController.broadcast( onListen: () { unsubscribe = nativeInstance! .onSnapshot(allowInterop(onSnapshot), allowInterop(onError)); }, onCancel: () { - unsubscribe(); + (unsubscribe as Function())(); }, ); return controller.stream; @@ -951,7 +953,7 @@ class DocumentQuery { dynamic arrayContains, bool? isNull, }) { - js.DocumentQuery? query = nativeInstance; + var query = nativeInstance; void addCondition(String field, String opStr, dynamic value) { query = query!.where(field, opStr, _FirestoreData._jsify(value)); @@ -959,13 +961,16 @@ class DocumentQuery { if (isEqualTo != null) addCondition(field, '==', isEqualTo); if (isLessThan != null) addCondition(field, '<', isLessThan); - if (isLessThanOrEqualTo != null) + if (isLessThanOrEqualTo != null) { addCondition(field, '<=', isLessThanOrEqualTo); + } if (isGreaterThan != null) addCondition(field, '>', isGreaterThan); - if (isGreaterThanOrEqualTo != null) + if (isGreaterThanOrEqualTo != null) { addCondition(field, '>=', isGreaterThanOrEqualTo); - if (arrayContains != null) + } + if (arrayContains != null) { addCondition(field, 'array-contains', arrayContains); + } if (isNull != null) { assert( @@ -975,15 +980,14 @@ class DocumentQuery { addCondition(field, '==', null); } - return new DocumentQuery(query, firestore); + return DocumentQuery(query, firestore); } /// Creates and returns a new [DocumentQuery] that's additionally sorted by the specified /// [field]. - DocumentQuery orderBy(String field, {bool descending: false}) { - String direction = descending ? 'desc' : 'asc'; - return new DocumentQuery( - nativeInstance!.orderBy(field, direction), firestore); + DocumentQuery orderBy(String field, {bool descending = false}) { + var direction = descending ? 'desc' : 'asc'; + return DocumentQuery(nativeInstance!.orderBy(field, direction), firestore); } /// Takes a [snapshot] or a list of [values], creates and returns a new [DocumentQuery] @@ -995,8 +999,8 @@ class DocumentQuery { /// Cannot be used in combination with [startAt]. DocumentQuery startAfter( {DocumentSnapshot? snapshot, List? values}) { - return new DocumentQuery( - _wrapPaginatingFunctionCall("startAfter", snapshot, values), firestore); + return DocumentQuery( + _wrapPaginatingFunctionCall('startAfter', snapshot, values), firestore); } /// Takes a [snapshot] or a list of [values], creates and returns a new [DocumentQuery] @@ -1007,8 +1011,8 @@ class DocumentQuery { /// /// Cannot be used in combination with [startAfter]. DocumentQuery startAt({DocumentSnapshot? snapshot, List? values}) { - return new DocumentQuery( - _wrapPaginatingFunctionCall("startAt", snapshot, values), firestore); + return DocumentQuery( + _wrapPaginatingFunctionCall('startAt', snapshot, values), firestore); } /// Takes a [snapshot] or a list of [values], creates and returns a new [DocumentQuery] @@ -1019,8 +1023,8 @@ class DocumentQuery { /// /// Cannot be used in combination with [endBefore]. DocumentQuery endAt({DocumentSnapshot? snapshot, List? values}) { - return new DocumentQuery( - _wrapPaginatingFunctionCall("endAt", snapshot, values), firestore); + return DocumentQuery( + _wrapPaginatingFunctionCall('endAt', snapshot, values), firestore); } /// Takes a [snapshot] or a list of [values], creates and returns a new [DocumentQuery] @@ -1031,19 +1035,19 @@ class DocumentQuery { /// /// Cannot be used in combination with [endAt]. DocumentQuery endBefore({DocumentSnapshot? snapshot, List? values}) { - return new DocumentQuery( - _wrapPaginatingFunctionCall("endBefore", snapshot, values), firestore); + return DocumentQuery( + _wrapPaginatingFunctionCall('endBefore', snapshot, values), firestore); } /// Creates and returns a new Query that's additionally limited to only return up /// to the specified number of documents. DocumentQuery limit(int length) { - return new DocumentQuery(nativeInstance!.limit(length), firestore); + return DocumentQuery(nativeInstance!.limit(length), firestore); } /// Specifies the offset of the returned results. DocumentQuery offset(int offset) { - return new DocumentQuery(nativeInstance!.offset(offset), firestore); + return DocumentQuery(nativeInstance!.offset(offset), firestore); } /// Calls js paginating [method] with [DocumentSnapshot] or List of [values]. @@ -1052,16 +1056,16 @@ class DocumentQuery { js.DocumentQuery? _wrapPaginatingFunctionCall( String method, DocumentSnapshot? snapshot, List? values) { if (snapshot == null && values == null) { - throw new ArgumentError( - "Please provide either snapshot or values parameter."); + throw ArgumentError( + 'Please provide either snapshot or values parameter.'); } else if (snapshot != null && values != null) { - throw new ArgumentError( + throw ArgumentError( 'Cannot provide both snapshot and values parameters.'); } - List args = (snapshot != null) + var args = (snapshot != null) ? [snapshot.nativeInstance] : values!.map(_FirestoreData._jsify).toList(); - return callMethod(nativeInstance!, method, args); + return callMethod(nativeInstance!, method, args) as js.DocumentQuery; } /// Creates and returns a new Query instance that applies a field mask @@ -1070,8 +1074,9 @@ class DocumentQuery { /// list to only return the references of matching documents. DocumentQuery select(List fieldPaths) { // Dart doesn't support varargs - return new DocumentQuery( - callMethod(nativeInstance!, "select", fieldPaths), firestore); + return DocumentQuery( + callMethod(nativeInstance!, 'select', fieldPaths) as js.DocumentQuery, + firestore); } } @@ -1088,16 +1093,17 @@ class Transaction { /// Holds a pessimistic lock on the returned document. Future get(DocumentReference documentRef) { final nativeRef = documentRef.nativeInstance; - return promiseToFuture(nativeInstance.get(nativeRef)).then((jsSnapshot) => - new DocumentSnapshot(jsSnapshot, documentRef.firestore)); + return promiseToFuture(nativeInstance.get(nativeRef)) + .then((jsSnapshot) => + DocumentSnapshot(jsSnapshot, documentRef.firestore)); } /// Retrieves a query result. Holds a pessimistic lock on the returned /// documents. Future getQuery(DocumentQuery query) { final nativeQuery = query.nativeInstance; - return promiseToFuture(nativeInstance.get(nativeQuery)) - .then((jsSnapshot) => new QuerySnapshot(jsSnapshot, query.firestore)); + return promiseToFuture(nativeInstance.get(nativeQuery)) + .then((jsSnapshot) => QuerySnapshot(jsSnapshot, query.firestore)); } /// Create the document referred to by the provided [documentRef]. @@ -1113,7 +1119,7 @@ class Transaction { /// If the document does not exist yet, it will be created. If you pass /// [options], the provided data can be merged into the existing document. void set(DocumentReference documentRef, DocumentData data, - {bool merge: false}) { + {bool merge = false}) { final docData = data.nativeInstance; final nativeRef = documentRef.nativeInstance; nativeInstance.set(nativeRef, docData, _getNativeSetOptions(merge)); @@ -1187,7 +1193,8 @@ class WriteBatch { /// /// Nested fields can be updated by providing dot-separated field path strings. void updateData(DocumentReference documentRef, UpdateData data) => - nativeInstance.update(documentRef.nativeInstance, data.nativeInstance); + nativeInstance.update( + documentRef.nativeInstance, data.nativeInstance as js.UpdateData?); /// Deletes the document referred to by the provided [documentRef]. void delete(DocumentReference documentRef) => @@ -1203,7 +1210,7 @@ class WriteBatch { /// documents that match the specified restrictions. js.Precondition _getNativePrecondition(Timestamp lastUpdateTime) { final ts = _createJsTimestamp(lastUpdateTime); - return new js.Precondition(lastUpdateTime: ts); + return js.Precondition(lastUpdateTime: ts); } /// An options object that configures the behavior of [set] calls in @@ -1212,7 +1219,7 @@ js.Precondition _getNativePrecondition(Timestamp lastUpdateTime) { /// documents in their entirety by providing a [SetOptions] with [merge]: true. js.SetOptions _getNativeSetOptions(bool merge) { //assert(merge != null, 'SetOption merge can`t be null'); - return new js.SetOptions(merge: merge); + return js.SetOptions(merge: merge); } class _FieldValueDelete implements FieldValue { @@ -1245,7 +1252,7 @@ class _FieldValueArrayUnion extends _FieldValueArray { _FieldValueArrayUnion(List elements) : super(elements); @override - _jsify() { + dynamic _jsify() { return callMethod(js.admin!.firestore.FieldValue, 'arrayUnion', _FirestoreData._jsifyList(elements)); } @@ -1258,7 +1265,7 @@ class _FieldValueArrayRemove extends _FieldValueArray { _FieldValueArrayRemove(List elements) : super(elements); @override - _jsify() { + dynamic _jsify() { return callMethod(js.admin!.firestore.FieldValue, 'arrayRemove', _FirestoreData._jsifyList(elements)); } diff --git a/lib/src/firestore_bindings.dart b/lib/src/firestore_bindings.dart index 3f1b133..4fd5072 100644 --- a/lib/src/firestore_bindings.dart +++ b/lib/src/firestore_bindings.dart @@ -1,18 +1,21 @@ @JS() library firestore; -import "package:js/js.dart"; -import "package:node_interop/node.dart"; -import "package:node_interop/stream.dart"; +import 'package:js/js.dart'; +import 'package:node_interop/node.dart'; +import 'package:node_interop/stream.dart'; @JS() @anonymous abstract class FirestoreModule { /// Sets the log function for all active Firestore instances. - external void setLogFunction(void logger(String msg)); + external void setLogFunction(void Function(String msg) logger); + // ignore: non_constant_identifier_names external Function get Firestore; + // ignore: non_constant_identifier_names external GeoPointUtil get GeoPoint; + // ignore: non_constant_identifier_names external FieldValues get FieldValue; /// Reference to constructor function of [FieldPath]. @@ -21,8 +24,10 @@ abstract class FirestoreModule { /// - [FieldPathPrototype] which exposes static members of this class. /// - [documentId] which is a convenience function to create sentinel /// [FieldPath] to refer to the ID of a document. + // ignore: non_constant_identifier_names external dynamic get FieldPath; + // ignore: non_constant_identifier_names external TimestampProto get Timestamp; } @@ -120,7 +125,7 @@ abstract class Firestore { /// transaction failed, a rejected Future with the corresponding failure error /// will be returned. external Promise runTransaction( - Promise updateFunction(Transaction transaction)); + Promise Function(Transaction transaction) updateFunction); /// Creates a write batch, used for performing multiple writes as a single /// atomic operation. @@ -196,7 +201,7 @@ abstract class Transaction { /// documents. /*external Promise get(Query query);*/ external Promise /*Promise|Promise*/ get( - dynamic /*DocumentReference|Query*/ documentRef_query); + dynamic /*DocumentReference|Query*/ documentRefQuery); /// Create the document referred to by the provided `DocumentReference`. /// The operation will fail the transaction if a document exists at the @@ -229,8 +234,8 @@ abstract class Transaction { /// update. /*external Transaction update(DocumentReference documentRef, String|FieldPath field, dynamic value, [dynamic fieldsOrPrecondition1, dynamic fieldsOrPrecondition2, dynamic fieldsOrPrecondition3, dynamic fieldsOrPrecondition4, dynamic fieldsOrPrecondition5]);*/ external Transaction update( - DocumentReference documentRef, dynamic /*String|FieldPath*/ data_field, - [dynamic /*Precondition|dynamic*/ precondition_value, + DocumentReference documentRef, dynamic /*String|FieldPath*/ dataField, + [dynamic /*Precondition|dynamic*/ preconditionValue, List? fieldsOrPrecondition]); /// Deletes the document referred to by the provided `DocumentReference`. @@ -386,8 +391,8 @@ abstract class DocumentReference { /// is available. /// cancelled. No further callbacks will occur. /// the snapshot listener. - external Function onSnapshot(void onNext(DocumentSnapshot snapshot), - [void onError(Error error)?]); + external Function onSnapshot(void Function(DocumentSnapshot snapshot) onNext, + [void Function(Error error)? onError]); } /// A `DocumentSnapshot` contains data read from a document in your Firestore @@ -427,7 +432,7 @@ abstract class DocumentSnapshot { /// Retrieves all fields in the document as an Object. Returns 'undefined' if /// the document doesn't exist. - external dynamic /*DocumentData|dynamic*/ data(); + external DocumentData? data(); /// Retrieves the field specified by `fieldPath`. /// field exists in the document. @@ -446,16 +451,21 @@ abstract class DocumentSnapshot { @anonymous abstract class QueryDocumentSnapshot extends DocumentSnapshot { /// The time the document was created. + @override external Timestamp? get createTime; + @override external set createTime(Timestamp? v); /// The time the document was last updated (at the time the snapshot was /// generated). + @override external Timestamp? get updateTime; + @override external set updateTime(Timestamp? v); /// Retrieves all fields in the document as an Object. /// @override + @override external DocumentData data(); } @@ -531,7 +541,7 @@ abstract class DocumentQuery { dynamic fieldValues4, dynamic fieldValues5]);*/ external DocumentQuery startAt( - dynamic /*DocumentSnapshot|List*/ snapshot_fieldValues); + dynamic /*DocumentSnapshot|List*/ snapshotFieldValues); /// Creates and returns a new Query that starts after the provided document /// (exclusive). The starting position is relative to the order of the query. @@ -549,7 +559,7 @@ abstract class DocumentQuery { dynamic fieldValues4, dynamic fieldValues5]);*/ external DocumentQuery startAfter( - dynamic /*DocumentSnapshot|List*/ snapshot_fieldValues); + dynamic /*DocumentSnapshot|List*/ snapshotFieldValues); /// Creates and returns a new Query that ends before the provided document /// (exclusive). The end position is relative to the order of the query. The @@ -567,7 +577,7 @@ abstract class DocumentQuery { dynamic fieldValues4, dynamic fieldValues5]);*/ external DocumentQuery endBefore( - dynamic /*DocumentSnapshot|List*/ snapshot_fieldValues); + dynamic /*DocumentSnapshot|List*/ snapshotFieldValues); /// Creates and returns a new Query that ends at the provided document /// (inclusive). The end position is relative to the order of the query. The @@ -585,7 +595,7 @@ abstract class DocumentQuery { dynamic fieldValues4, dynamic fieldValues5]);*/ external DocumentQuery endAt( - dynamic /*DocumentSnapshot|List*/ snapshot_fieldValues); + dynamic /*DocumentSnapshot|List*/ snapshotFieldValues); /// Executes the query and returns the results as a `QuerySnapshot`. external Promise get(); @@ -597,8 +607,8 @@ abstract class DocumentQuery { /// is available. /// cancelled. No further callbacks will occur. /// the snapshot listener. - external Function onSnapshot(void onNext(QuerySnapshot snapshot), - [void onError(Error error)?]); + external Function onSnapshot(void Function(QuerySnapshot snapshot) onNext, + [void Function(Error error)? onError]); } /// A `QuerySnapshot` contains zero or more `QueryDocumentSnapshot` objects @@ -637,7 +647,7 @@ abstract class QuerySnapshot { /// Enumerates all of the documents in the QuerySnapshot. /// each document in the snapshot. - external void forEach(void callback(QueryDocumentSnapshot result), + external void forEach(void Function(QueryDocumentSnapshot result) callback, [dynamic thisArg]); } diff --git a/lib/src/messaging.dart b/lib/src/messaging.dart index 6ceaa61..ebf33bd 100644 --- a/lib/src/messaging.dart +++ b/lib/src/messaging.dart @@ -65,10 +65,11 @@ class Messaging { /// Returns Future fulfilled with a unique message ID string after the /// message has been successfully handed off to the FCM service for delivery Future send(FcmMessage message, [bool? dryRun]) { - if (dryRun != null) + if (dryRun != null) { return promiseToFuture(nativeInstance.send(message, dryRun)); - else + } else { return promiseToFuture(nativeInstance.send(message)); + } } /// Sends all the [messages] in the given array via Firebase Cloud Messaging. @@ -76,10 +77,11 @@ class Messaging { /// Returns Future fulfilled with an object representing the /// result of the send operation. Future sendAll(List messages, [bool? dryRun]) { - if (dryRun != null) + if (dryRun != null) { return promiseToFuture(nativeInstance.sendAll(messages, dryRun)); - else + } else { return promiseToFuture(nativeInstance.sendAll(messages)); + } } /// Sends the given multicast [message] to all the FCM registration tokens @@ -87,11 +89,13 @@ class Messaging { /// /// Returns Future fulfilled with an object representing the /// result of the send operation. - Future sendMulticast(MulticastMessage message, [bool? dryRun]) { - if (dryRun != null) + Future sendMulticast(MulticastMessage message, + [bool? dryRun]) { + if (dryRun != null) { return promiseToFuture(nativeInstance.sendMulticast(message, dryRun)); - else + } else { return promiseToFuture(nativeInstance.sendMulticast(message)); + } } /// Sends an FCM message to a [condition]. @@ -101,12 +105,13 @@ class Messaging { Future sendToCondition( String condition, MessagingPayload payload, [MessagingOptions? options]) { - if (options != null) + if (options != null) { return promiseToFuture( nativeInstance.sendToCondition(condition, payload, options)); - else + } else { return promiseToFuture( nativeInstance.sendToCondition(condition, payload)); + } } /// Sends an FCM message to a single device corresponding to the provided @@ -117,12 +122,13 @@ class Messaging { Future sendToDevice( String registrationToken, MessagingPayload payload, [MessagingOptions? options]) { - if (options != null) + if (options != null) { return promiseToFuture( nativeInstance.sendToDevice(registrationToken, payload, options)); - else + } else { return promiseToFuture( nativeInstance.sendToDevice(registrationToken, payload)); + } } /// Sends an FCM message to a device group corresponding to the provided @@ -133,12 +139,13 @@ class Messaging { Future sendToDeviceGroup( String notificationKey, MessagingPayload payload, [MessagingOptions? options]) { - if (options != null) + if (options != null) { return promiseToFuture( nativeInstance.sendToDeviceGroup(notificationKey, payload, options)); - else + } else { return promiseToFuture( nativeInstance.sendToDeviceGroup(notificationKey, payload)); + } } /// Sends an FCM message to a [topic]. @@ -148,11 +155,12 @@ class Messaging { Future sendToTopic( String topic, MessagingPayload payload, [MessagingOptions? options]) { - if (options != null) + if (options != null) { return promiseToFuture( nativeInstance.sendToTopic(topic, payload, options)); - else + } else { return promiseToFuture(nativeInstance.sendToTopic(topic, payload)); + } } /// Subscribes a device to an FCM [topic]. diff --git a/lib/src/storage_bindings.dart b/lib/src/storage_bindings.dart index b11db9c..3ce739a 100644 --- a/lib/src/storage_bindings.dart +++ b/lib/src/storage_bindings.dart @@ -1,7 +1,7 @@ @JS() library firebase_storage; -import "package:js/js.dart"; +import 'package:js/js.dart'; import 'package:node_interop/node.dart'; import 'bindings.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 6df6db7..85a986c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: firebase_admin_interop description: Firebase Admin SDK for Dart written as a wrapper around official Node.js SDK. version: 2.1.0 homepage: https://github.com/pulyaevskiy/firebase-admin-interop -author: Anatoly Pulyaevskiy +publish_to: none environment: sdk: '>=2.12.0 <3.0.0' @@ -15,6 +15,12 @@ dependencies: dev_dependencies: test: ^1.12.0 + tekartik_lints: + git: + url: git://github.com/tekartik/common.dart + ref: null_safety + path: packages/lints + version: '>=0.1.0' # build_runner: ^1.7.4 # build_node_compilers: ^0.2.4 # build_test: ^0.10.12+1 diff --git a/test/auth_test.dart b/test/auth_test.dart index 911859a..2835268 100644 --- a/test/auth_test.dart +++ b/test/auth_test.dart @@ -18,9 +18,8 @@ void main() { try { user = await app!.auth().getUser('testuser'); } catch (_) {} - ; if (user == null) { - await app!.auth().createUser(new CreateUserRequest(uid: 'testuser')); + await app!.auth().createUser(CreateUserRequest(uid: 'testuser')); } }); diff --git a/test/database_test.dart b/test/database_test.dart index bd6b7d1..5ad94a9 100644 --- a/test/database_test.dart +++ b/test/database_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { - App? app = initFirebaseApp(); + var app = initFirebaseApp(); group('Database', () { tearDownAll(() { @@ -31,8 +31,8 @@ void main() { test('querying works', () async { var ref = app.database().ref('/app/users').endAt('Firebase'); - var value = await ref.once('value'); - var records = new Map.from(value.val()); + var value = await ref.once('value'); + var records = Map.from(value.val()!); expect(records, hasLength(2)); }); @@ -52,7 +52,7 @@ void main() { final values = await result; expect(values, ['Firebase', 'Second', 'Last']); sub.cancel(); - controller.close(); + await controller.close(); }); }); @@ -114,11 +114,11 @@ void main() { test('update()', () async { await refUpdate.update({'num': 23, 'nested/thing': '1984'}); - var snapshot = await refUpdate.once('value'); - Map data = snapshot.val(); + var snapshot = await refUpdate.once('value'); + var data = snapshot.val()!; expect(data, hasLength(2)); expect(data['num'], 23); - expect(data['nested']['thing'], '1984'); + expect((data['nested'] as Map)['thing'], '1984'); }); test('transaction abort', () async { @@ -134,21 +134,22 @@ void main() { var tx = await refUpdate.transaction((dynamic currentData) { // Not sure I fully understand why Firebase sends initial `null` value // here, but this should not have anything to do with our Dart code. - if (currentData == null) + if (currentData == null) { return TransactionResult.success(currentData); - final data = new Map.from(currentData); + } + final data = Map.from(currentData as Map); data['tx'] = true; return TransactionResult.success(data); }); expect(tx.committed, isTrue); - Map value = tx.snapshot.val(); + var value = tx.snapshot.val() as Map; expect(value['tx'], isTrue); }); }); group('DataSnapshot', () { var ref = app!.database().ref('/app/users/3/notifications'); - late var childKey; + late String childKey; setUp(() async { await ref.remove(); diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 79c23ba..9dad83c 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { - App app = initFirebaseApp()!; + var app = initFirebaseApp()!; app.firestore().settings(FirestoreSettings(timestampsInSnapshots: true)); group('$Firestore', () { @@ -22,11 +22,11 @@ void main() { var ref = app.firestore().document('users/23'); setUp(() async { - final data = new DocumentData.fromMap({ + final data = DocumentData.fromMap({ 'name': 'Firestore', - 'profile.url': "https://pic.com/123", + 'profile.url': 'https://pic.com/123', }); - final nested = new DocumentData.fromMap({'author': 'Unknown'}); + final nested = DocumentData.fromMap({'author': 'Unknown'}); data.setNestedData('nested', nested); // This completely overwrites the whole document. await ref.setData(data); @@ -58,7 +58,7 @@ void main() { }); test('update value', () async { - await ref.updateData(new UpdateData.fromMap({ + await ref.updateData(UpdateData.fromMap({ 'nested.author': 'Isaac Asimov', })); var snapshot = await ref.get(); @@ -70,19 +70,19 @@ void main() { // test setter and getters and fromMap/toMap round trip for // all types test('get set', () { - var data = new DocumentData(); - DateTime now = new DateTime.now(); + var data = DocumentData(); + var now = DateTime.now(); final tsNow = Timestamp.fromDateTime(now); data.setInt('intVal', 1); data.setDouble('doubleVal', 1.5); data.setBool('boolVal', true); data.setString('stringVal', 'text'); - data.setGeoPoint('geoVal', new GeoPoint(23.03, 19.84)); - data.setBlob('blob', new Blob([1, 2, 3])); + data.setGeoPoint('geoVal', GeoPoint(23.03, 19.84)); + data.setBlob('blob', Blob([1, 2, 3])); data.setReference('refVal', app.firestore().document('users/23')); data.setList('listVal', [23, 84]); data.setTimestamp('tsVal', tsNow); - var nestedData = new DocumentData(); + var nestedData = DocumentData(); nestedData.setString('nestedVal', 'very nested'); data.setNestedData('nestedData', nestedData); data.setFieldValue('serverTimestampFieldValue', @@ -91,23 +91,23 @@ void main() { data.setList( 'fieldValueInList', [Firestore.fieldValues.serverTimestamp()]); - _check() { + void _check() { expect(data.keys.length, 13); expect(data.getInt('intVal'), 1); expect(data.getDouble('doubleVal'), 1.5); expect(data.getBool('boolVal'), true); expect(data.getString('stringVal'), 'text'); - expect(data.getGeoPoint('geoVal'), new GeoPoint(23.03, 19.84)); + expect(data.getGeoPoint('geoVal'), GeoPoint(23.03, 19.84)); expect(data.getBlob('blob')!.data, [1, 2, 3]); var documentReference = data.getReference('refVal')!; expect(documentReference.path, 'users/23'); expect(data.getList('listVal'), [23, 84]); expect(data.getTimestamp('tsVal'), tsNow); - DocumentData nestedData = data.getNestedData('nestedData')!; + var nestedData = data.getNestedData('nestedData')!; expect(nestedData.keys.length, 1); expect(nestedData.getString('nestedVal'), 'very nested'); // Check the field value (no getter here) - Map map = data.toMap(); + var map = data.toMap(); expect(map['serverTimestampFieldValue'], Firestore.fieldValues.serverTimestamp()); expect(map['deleteFieldValue'], Firestore.fieldValues.delete()); @@ -117,14 +117,14 @@ void main() { _check(); // from/to map - data = new DocumentData.fromMap(data.toMap()); + data = DocumentData.fromMap(data.toMap()); _check(); }); test('data types', () async { - var date = new DateTime.now(); + var date = DateTime.now(); var ref = app.firestore().document('tests/data-types'); - var data = new DocumentData.fromMap({ + var data = DocumentData.fromMap({ 'boolVal': true, 'stringVal': 'text', 'intVal': 23, @@ -152,7 +152,7 @@ void main() { expect(result.getString('stringVal'), 'text'); expect(result.getInt('intVal'), 23); expect(result.getDouble('doubleVal'), 19.84); - expect(result.getGeoPoint('geoVal'), new GeoPoint(23.03, 19.84)); + expect(result.getGeoPoint('geoVal'), GeoPoint(23.03, 19.84)); expect(result.getBlob('blobVal')!.data, [1, 2, 3]); var docRef = result.getReference('refVal')!; expect(docRef, const TypeMatcher()); @@ -172,17 +172,17 @@ void main() { }); test('$DocumentData.toMap', () async { - var date = new DateTime.now(); + var date = DateTime.now(); var ts = Timestamp.fromDateTime(date); var ref = app.firestore().document('tests/data-types-toMap'); - var data = new DocumentData.fromMap({ + var data = DocumentData.fromMap({ 'boolVal': true, 'stringVal': 'text', 'intVal': 23, 'doubleVal': 19.84, - 'geoVal': new GeoPoint(23.03, 19.84), + 'geoVal': GeoPoint(23.03, 19.84), 'refVal': app.firestore().document('users/23'), - 'blobVal': new Blob([4, 5, 6]), + 'blobVal': Blob([4, 5, 6]), 'listVal': [23, 84], 'tsVal': ts, 'mapVal': { @@ -192,15 +192,15 @@ void main() { ] }, }); - var nested = new DocumentData.fromMap({'nestedVal': 'very nested'}); + var nested = DocumentData.fromMap({'nestedVal': 'very nested'}); data.setNestedData('nestedData', nested); - var fakeGeoPoint = new DocumentData.fromMap( + var fakeGeoPoint = DocumentData.fromMap( {'latitude': 23.03, 'longitude': 84.19, 'toString': 'GeoPoint'}); data.setNestedData('fakeGeoPoint', fakeGeoPoint); - var fakeRef = new DocumentData.fromMap( + var fakeRef = DocumentData.fromMap( {'firestore': 'Nope', 'id': 'Nah', 'onSnapshot': 'Function'}); data.setNestedData('fakeRef', fakeRef); - var fakeDate = new DocumentData.fromMap( + var fakeDate = DocumentData.fromMap( {'toDateString': 'date', 'getTime': 'Function'}); data.setNestedData('fakeDate', fakeDate); await ref.setData(data); @@ -211,9 +211,9 @@ void main() { expect(result['stringVal'], 'text'); expect(result['intVal'], 23); expect(result['doubleVal'], 19.84); - expect(result['geoVal'], new GeoPoint(23.03, 19.84)); + expect(result['geoVal'], GeoPoint(23.03, 19.84)); expect((result['blobVal'] as Blob).data, [4, 5, 6]); - var docRef = result['refVal']; + var docRef = result['refVal'] as DocumentReference; expect(docRef, const TypeMatcher()); expect(docRef.path, 'users/23'); expect(result['listVal'], [23, 84]); @@ -235,8 +235,8 @@ void main() { test('unsupported data types', () async { var ref = app.firestore().document('tests/unsupported'); - await ref.setData(new DocumentData() - ..setGeoPoint('geoVal', new GeoPoint(23.03, 19.84))); + await ref.setData( + DocumentData()..setGeoPoint('geoVal', GeoPoint(23.03, 19.84))); var snapshot = await ref.get(); var data = snapshot.data; expect(() => data.getList('geoVal'), throwsStateError); @@ -244,7 +244,7 @@ void main() { test('lists with complex types', () async { var ref = app.firestore().document('tests/complex-lists'); - var data = new DocumentData(); + var data = DocumentData(); data.setList('data', [ Timestamp.fromDateTime(DateTime.now()), GeoPoint(1.0, 2.0), @@ -266,41 +266,39 @@ void main() { var ref = app.firestore().document('tests/delete_field'); // Make sure FieldValue class is exported by using it here - FieldValue fieldValueDelete = Firestore.fieldValues.delete(); + var fieldValueDelete = Firestore.fieldValues.delete(); // create document - var documentData = new DocumentData(); - documentData.setString("some_key", "some_value"); - documentData.setString("other_key", "other_value"); + var documentData = DocumentData(); + documentData.setString('some_key', 'some_value'); + documentData.setString('other_key', 'other_value'); await ref.setData(documentData); // read it documentData = (await ref.get()).data; - expect(documentData.getString("some_key"), "some_value"); + expect(documentData.getString('some_key'), 'some_value'); // delete field - var updateData = new UpdateData(); - updateData.setFieldValue("some_key", fieldValueDelete); + var updateData = UpdateData(); + updateData.setFieldValue('some_key', fieldValueDelete); await ref.updateData(updateData); // read again documentData = (await ref.get()).data; - expect(documentData.getString("some_key"), isNull); - expect(documentData.has("some_key"), isFalse); - expect(documentData.getString("other_key"), "other_value"); + expect(documentData.getString('some_key'), isNull); + expect(documentData.has('some_key'), isFalse); + expect(documentData.getString('other_key'), 'other_value'); }); test('array field value', () async { var ref = app.firestore().document('tests/array_field_value'); // Make sure FieldValue class is exported by using it here - FieldValue fieldValueArrayUnion = - Firestore.fieldValues.arrayUnion([1, 2]); - FieldValue fieldValueArrayUnion2 = - Firestore.fieldValues.arrayUnion([10, 11]); - FieldValue fieldValueArrayComplex = Firestore.fieldValues.arrayUnion([ + var fieldValueArrayUnion = Firestore.fieldValues.arrayUnion([1, 2]); + var fieldValueArrayUnion2 = Firestore.fieldValues.arrayUnion([10, 11]); + var fieldValueArrayComplex = Firestore.fieldValues.arrayUnion([ 100, - "text", + 'text', { 'sub': [1] }, @@ -308,18 +306,18 @@ void main() { ]); // create document - var documentData = new DocumentData(); - documentData.setFieldValue("array", fieldValueArrayUnion); - documentData.setFieldValue("array2", fieldValueArrayUnion2); - documentData.setFieldValue("complex", fieldValueArrayComplex); + var documentData = DocumentData(); + documentData.setFieldValue('array', fieldValueArrayUnion); + documentData.setFieldValue('array2', fieldValueArrayUnion2); + documentData.setFieldValue('complex', fieldValueArrayComplex); await ref.setData(documentData); // read it documentData = (await ref.get()).data; - expect(documentData.getList("array"), [1, 2]); - expect(documentData.getList("array2"), [10, 11]); - expect(documentData.getList("complex"), [ + expect(documentData.getList('array'), [1, 2]); + expect(documentData.getList('array2'), [10, 11]); + expect(documentData.getList('complex'), [ 100, 'text', { @@ -329,17 +327,17 @@ void main() { ]); // update and remove some data - var updateData = new UpdateData(); + var updateData = UpdateData(); updateData.setFieldValue( - "array", Firestore.fieldValues.arrayUnion([2, 3])); + 'array', Firestore.fieldValues.arrayUnion([2, 3])); updateData.setFieldValue( - "array2", Firestore.fieldValues.arrayRemove([11, 12])); + 'array2', Firestore.fieldValues.arrayRemove([11, 12])); // try to remove a complex object updateData.setFieldValue( - "complex", + 'complex', Firestore.fieldValues.arrayRemove([ 100, - "text", + 'text', { 'sub': [1] } @@ -348,24 +346,24 @@ void main() { // read again documentData = (await ref.get()).data; - expect(documentData.getList("array"), [1, 2, 3]); - expect(documentData.getList("array2"), [10]); - expect(documentData.getList("complex"), [GeoPoint(1.0, 2.0)]); + expect(documentData.getList('array'), [1, 2, 3]); + expect(documentData.getList('array2'), [10]); + expect(documentData.getList('complex'), [GeoPoint(1.0, 2.0)]); }); test('set options', () async { var ref = app.firestore().document('tests/set_options'); - var documentData = new DocumentData(); + var documentData = DocumentData(); documentData.setInt('value1', 1); documentData.setInt('value2', 2); await ref.setData(documentData); - documentData = new DocumentData(); + documentData = DocumentData(); documentData.setInt('value2', 3); // Set with merge, value1 should remain - await ref.setData(documentData, new SetOptions(merge: true)); + await ref.setData(documentData, SetOptions(merge: true)); var readData = (await ref.get()).data; expect(readData.toMap(), {'value1': 1, 'value2': 3}); @@ -459,9 +457,9 @@ void main() { }); test('add document to collection', () async { - final point = new GeoPoint(37.7991232, -122.4485953); - final data = new DocumentData.fromMap({'name': 'Added Doc'}); - data.setGeoPoint("location", point); + final point = GeoPoint(37.7991232, -122.4485953); + final data = DocumentData.fromMap({'name': 'Added Doc'}); + data.setGeoPoint('location', point); final doc = await ref.add(data); expect(doc.documentID, isNotNull); final snapshot = await doc.get(); @@ -497,7 +495,7 @@ void main() { // Some test assume that this collection is empty or already contains // one item. On the first ever call, create the item if (snapshot.isEmpty) { - await ref.add(new DocumentData()..setString('name', 'John Doe')); + await ref.add(DocumentData()..setString('name', 'John Doe')); } }); @@ -517,8 +515,8 @@ void main() { var collection = app.firestore().collection('tests/query/where-ref'); var doc1 = collection.document(); var doc2 = collection.document(); - await doc2.setData(new DocumentData.fromMap({'name': 'doc2'})); - var data1 = new DocumentData(); + await doc2.setData(DocumentData.fromMap({'name': 'doc2'})); + var data1 = DocumentData(); data1.setReference('ref', doc2); data1.setString('name', 'doc1'); await doc1.setData(data1); @@ -719,12 +717,12 @@ void main() { test('listen for query snapshot updates', () async { var ref = app.firestore().collection('tests/query/docs'); - Completer completer = new Completer(); + var completer = Completer(); var subscription = ref.snapshots.listen((event) { completer.complete(event); }); var snapshot = await completer.future; - subscription.cancel(); + await subscription.cancel(); expect(snapshot, isNotNull); expect(snapshot, isNotEmpty); expect(snapshot.documents, hasLength(1)); @@ -735,10 +733,11 @@ void main() { // set the content var docRef = collRef.document('one'); - await docRef.setData( - new DocumentData()..setInt('field1', 1)..setInt('field2', 2)); + await docRef.setData(DocumentData() + ..setInt('field1', 1) + ..setInt('field2', 2)); - QuerySnapshot querySnapshot = await collRef.select(['field2']).get(); + var querySnapshot = await collRef.select(['field2']).get(); var documentData = querySnapshot.documents!.first.data; expect(documentData.has('field2'), isTrue); expect(documentData.has('field1'), isFalse); @@ -755,14 +754,14 @@ void main() { // Create or update the content var docRefOne = collRef.document('one'); - await docRefOne.setData(new DocumentData()..setInt('value', 1)); + await docRefOne.setData(DocumentData()..setInt('value', 1)); var docRefTwo = collRef.document('two'); - await docRefTwo.setData(new DocumentData()..setInt('value', 2)); + await docRefTwo.setData(DocumentData()..setInt('value', 2)); List? list; // limit - QuerySnapshot querySnapshot = await collRef.limit(1).get(); + var querySnapshot = await collRef.limit(1).get(); list = querySnapshot.documents; expect(list!.length, 1); @@ -775,40 +774,40 @@ void main() { querySnapshot = await collRef.orderBy('value').get(); list = querySnapshot.documents; expect(list!.length, 2); - expect(list.first.reference.documentID, "one"); + expect(list.first.reference.documentID, 'one'); // desc querySnapshot = await collRef.orderBy('value', descending: true).get(); list = querySnapshot.documents; expect(list!.length, 2); - expect(list.first.reference.documentID, "two"); + expect(list.first.reference.documentID, 'two'); // start at querySnapshot = await collRef.orderBy('value').startAt(values: [2]).get(); list = querySnapshot.documents; expect(list!.length, 1); - expect(list.first.reference.documentID, "two"); + expect(list.first.reference.documentID, 'two'); // start after querySnapshot = await collRef.orderBy('value').startAfter(values: [1]).get(); list = querySnapshot.documents; expect(list!.length, 1); - expect(list.first.reference.documentID, "two"); + expect(list.first.reference.documentID, 'two'); // end at querySnapshot = await collRef.orderBy('value').endAt(values: [1]).get(); list = querySnapshot.documents; expect(list!.length, 1); - expect(list.first.reference.documentID, "one"); + expect(list.first.reference.documentID, 'one'); // end before querySnapshot = await collRef.orderBy('value').endBefore(values: [2]).get(); list = querySnapshot.documents; expect(list!.length, 1); - expect(list.first.reference.documentID, "one"); + expect(list.first.reference.documentID, 'one'); // start after using snapshot querySnapshot = await collRef @@ -817,13 +816,13 @@ void main() { .get(); list = querySnapshot.documents; expect(list!.length, 1); - expect(list.first.reference.documentID, "two"); + expect(list.first.reference.documentID, 'two'); // where querySnapshot = await collRef.where('value', isGreaterThan: 1).get(); list = querySnapshot.documents; expect(list!.length, 1); - expect(list.first.reference.documentID, "two"); + expect(list.first.reference.documentID, 'two'); }); test('snapshots changes', () async { @@ -836,13 +835,13 @@ void main() { // We'll them listen for changes while creating/modifying/deleting // the same item - final int stepCount = 4; + final stepCount = 4; // Create a completer for each step - var completers = new List>>.generate( - stepCount, (_) => new Completer>()); + var completers = List>>.generate( + stepCount, (_) => Completer>()); - int stepIndex = 0; + var stepIndex = 0; var subscription = collRef.snapshots.listen((QuerySnapshot querySnapshot) { // complete each step when receiving data @@ -851,14 +850,14 @@ void main() { } }); - int index = 0; + var index = 0; List documentChanges; // wait for receiving first data, ignore result await completers[index++].future; // create it - await docRef.setData(new DocumentData()); + await docRef.setData(DocumentData()); // wait for receiving change data documentChanges = await completers[index++].future; // expect creation @@ -866,7 +865,7 @@ void main() { expect(documentChanges.first.type, DocumentChangeType.added); // modify it - await docRef.setData(new DocumentData()..setInt('value', 1)); + await docRef.setData(DocumentData()..setInt('value', 1)); // wait for receiving change data documentChanges = await completers[index++].future; // expect a modified item @@ -899,25 +898,23 @@ void main() { var doc4Value = 4; await doc1Ref.delete(); - await doc2Ref.setData(new DocumentData()..setInt('value', 2)); - await doc3Ref.setData(new DocumentData()..setInt('value', 3)); - await doc4Ref.setData(new DocumentData()..setInt('value', doc4Value)); + await doc2Ref.setData(DocumentData()..setInt('value', 2)); + await doc3Ref.setData(DocumentData()..setInt('value', 3)); + await doc4Ref.setData(DocumentData()..setInt('value', doc4Value)); await Future.delayed( Duration(seconds: 1)); // to avoid too much contention errors - List list = - await app.firestore().runTransaction((Transaction tx) async { + var list = await app.firestore().runTransaction((Transaction tx) async { var query = await tx.getQuery( collRef.orderBy('value').where('value', isGreaterThan: 1)); var list = query.documents; var doc4 = (await tx.get(doc4Ref)).data.getInt('value')!; - tx.create(doc1Ref, new DocumentData()..setInt('value', 1 + doc4)); - tx.update( - doc2Ref, new UpdateData()..setInt('other.value', 22 + doc4)); - tx.set(doc3Ref, new DocumentData()..setInt('value', 3 + doc4)); - tx.set(doc3Ref, new DocumentData()..setInt('other.value', 33 + doc4), + tx.create(doc1Ref, DocumentData()..setInt('value', 1 + doc4)); + tx.update(doc2Ref, UpdateData()..setInt('other.value', 22 + doc4)); + tx.set(doc3Ref, DocumentData()..setInt('value', 3 + doc4)); + tx.set(doc3Ref, DocumentData()..setInt('other.value', 33 + doc4), merge: true); tx.delete(doc4Ref); @@ -925,9 +922,9 @@ void main() { }); expect(list.length, 3); - expect(list[0].documentID, "item2"); - expect(list[1].documentID, "item3"); - expect(list[2].documentID, "item4"); + expect(list[0].documentID, 'item2'); + expect(list[1].documentID, 'item3'); + expect(list[2].documentID, 'item4'); expect((await doc1Ref.get()).data.toMap(), {'value': 1 + doc4Value}); expect((await doc2Ref.get()).data.toMap(), { @@ -951,31 +948,32 @@ void main() { // this one will be deleted var doc2Ref = collRef.document('item2'); - await doc1Ref.setData(new DocumentData()..setInt('value', 1)); - await doc2Ref.setData(new DocumentData()..setInt('value', 2)); + await doc1Ref.setData(DocumentData()..setInt('value', 1)); + await doc2Ref.setData(DocumentData()..setInt('value', 2)); var doc1UpdateTime1 = (await doc1Ref.get()).updateTime; var doc2UpdateTime1 = (await doc2Ref.get()).updateTime; await Future.delayed( Duration(seconds: 1)); // to avoid too much contention errors - await doc1Ref.setData(new DocumentData()..setInt('value', 10)); - await doc2Ref.setData(new DocumentData()..setInt('value', 20)); + await doc1Ref.setData(DocumentData()..setInt('value', 10)); + await doc2Ref.setData(DocumentData()..setInt('value', 20)); var doc1UpdateTime2 = (await doc1Ref.get()).updateTime; var doc2UpdateTime2 = (await doc2Ref.get()).updateTime; await Future.delayed( Duration(seconds: 1)); // to avoid too much contention errors - Future result = app.firestore().runTransaction((Transaction tx) async { + var result = app.firestore().runTransaction((Transaction tx) async { var doc2 = (await tx.get(doc2Ref)).data; tx.update( - doc1Ref, new UpdateData()..setInt('value', doc2.getInt('value')), + doc1Ref, UpdateData()..setInt('value', doc2.getInt('value')), lastUpdateTime: doc1UpdateTime1); tx.delete(doc2Ref, lastUpdateTime: doc2UpdateTime1); }); - var error = await result.catchError((error) => error); + // ignore: invalid_return_type_for_catch_error + var error = await result.catchError((Object error) => error); expect(error.toString(), contains('does not match the required base version')); @@ -984,7 +982,7 @@ void main() { await app.firestore().runTransaction((Transaction tx) async { var doc2 = (await tx.get(doc2Ref)).data.getInt('value'); - tx.update(doc1Ref, new UpdateData()..setInt('value', doc2), + tx.update(doc1Ref, UpdateData()..setInt('value', doc2), lastUpdateTime: doc1UpdateTime2); tx.delete(doc2Ref, lastUpdateTime: doc2UpdateTime2); }); @@ -999,19 +997,19 @@ void main() { /// ABORTED: Too much contention on these documents. Please try again. var collRef = app.firestore().collection('tests/transaction/async'); var doc1Ref = collRef.document('counter'); - await doc1Ref.setData(new DocumentData()..setInt('value', 1)); + await doc1Ref.setData(DocumentData()..setInt('value', 1)); - List> futures = []; - List errors = []; - List complete = []; + var futures = >[]; + var errors = []; + var complete = []; var futuresCount = 5; - for (int i = 0; i < futuresCount; i++) { + for (var i = 0; i < futuresCount; i++) { var transaction = app.firestore().runTransaction((Transaction tx) async { var doc1 = await tx.get(doc1Ref); var val = doc1.data.getInt('value')! + 1; - tx.set(doc1Ref, new DocumentData()..setInt('value', val)); + tx.set(doc1Ref, DocumentData()..setInt('value', val)); return val; }); futures.add(transaction.then((int val) { @@ -1028,7 +1026,7 @@ void main() { var value = (await doc1Ref.get()).data.getInt('value'); expect(errors, isEmpty, reason: errors.toString()); expect(complete, hasLength(futuresCount), - reason: '${complete} of length ${complete.length}'); + reason: '$complete of length ${complete.length}'); expect(value, 6); }); }); @@ -1046,13 +1044,13 @@ void main() { var doc4Ref = collRef.document('item4'); await doc1Ref.delete(); - await doc2Ref.setData(new DocumentData()..setInt('value', 2)); - await doc4Ref.setData(new DocumentData()..setInt('value', 4)); + await doc2Ref.setData(DocumentData()..setInt('value', 2)); + await doc4Ref.setData(DocumentData()..setInt('value', 4)); var batch = app.firestore().batch(); - batch.setData(doc1Ref, new DocumentData()..setInt('value', 1)); - batch.updateData(doc2Ref, new UpdateData()..setInt('other.value', 22)); - batch.setData(doc3Ref, new DocumentData()..setInt('value', 3)); + batch.setData(doc1Ref, DocumentData()..setInt('value', 1)); + batch.updateData(doc2Ref, UpdateData()..setInt('other.value', 22)); + batch.setData(doc3Ref, DocumentData()..setInt('value', 3)); batch.delete(doc4Ref); await batch.commit(); diff --git a/test/setup.dart b/test/setup.dart index 8daff7c..513010f 100644 --- a/test/setup.dart +++ b/test/setup.dart @@ -3,25 +3,27 @@ import 'dart:convert'; +import 'package:firebase_admin_interop/firebase_admin_interop.dart'; import 'package:node_interop/node.dart'; import 'package:node_interop/util.dart'; -import 'package:firebase_admin_interop/firebase_admin_interop.dart'; final Map env = dartify(process.env); App? initFirebaseApp() { if (!env.containsKey('FIREBASE_CONFIG') || - !env.containsKey('FIREBASE_SERVICE_ACCOUNT_JSON')) - throw new StateError('Environment variables are not set.'); + !env.containsKey('FIREBASE_SERVICE_ACCOUNT_JSON')) { + throw StateError('Environment variables are not set.'); + } - Map certConfig = jsonDecode(env['FIREBASE_SERVICE_ACCOUNT_JSON']); + var certConfig = + jsonDecode(env['FIREBASE_SERVICE_ACCOUNT_JSON'] as String) as Map; final cert = FirebaseAdmin.instance.cert( - projectId: certConfig['project_id'], - clientEmail: certConfig['client_email'], - privateKey: certConfig['private_key'], + projectId: certConfig['project_id'] as String?, + clientEmail: certConfig['client_email'] as String?, + privateKey: certConfig['private_key'] as String?, ); - final Map config = jsonDecode(env['FIREBASE_CONFIG']); - final databaseUrl = config['databaseURL']; - return FirebaseAdmin.instance.initializeApp( - new AppOptions(credential: cert, databaseURL: databaseUrl)); + final config = jsonDecode(env['FIREBASE_CONFIG'] as String) as Map; + final databaseUrl = config['databaseURL'] as String?; + return FirebaseAdmin.instance + .initializeApp(AppOptions(credential: cert, databaseURL: databaseUrl)); } From 8f110900b08c3ae28c082f7fd0b3f9079eabf00b Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 20 Sep 2021 19:24:07 +0200 Subject: [PATCH 05/24] fix: dart 2.14 lints --- pubspec.yaml | 3 ++- tool/run_ci.dart | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tool/run_ci.dart diff --git a/pubspec.yaml b/pubspec.yaml index 85a986c..48a1b81 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: firebase_admin_interop description: Firebase Admin SDK for Dart written as a wrapper around official Node.js SDK. -version: 2.1.0 +version: 2.2.0 homepage: https://github.com/pulyaevskiy/firebase-admin-interop publish_to: none @@ -15,6 +15,7 @@ dependencies: dev_dependencies: test: ^1.12.0 + dev_test: tekartik_lints: git: url: git://github.com/tekartik/common.dart diff --git a/tool/run_ci.dart b/tool/run_ci.dart new file mode 100644 index 0000000..d9053ac --- /dev/null +++ b/tool/run_ci.dart @@ -0,0 +1,5 @@ +import 'package:dev_test/package.dart'; + +Future main() async { + await packageRunCi('.'); +} From 39314109d1d566241ef5686a57763539ab11bf48 Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 20 Sep 2021 21:43:36 +0200 Subject: [PATCH 06/24] fix: test --- test/firestore_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 9dad83c..9ade549 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -964,15 +964,16 @@ void main() { await Future.delayed( Duration(seconds: 1)); // to avoid too much contention errors - var result = app.firestore().runTransaction((Transaction tx) async { + var result = + app.firestore().runTransaction((Transaction tx) async { var doc2 = (await tx.get(doc2Ref)).data; tx.update( doc1Ref, UpdateData()..setInt('value', doc2.getInt('value')), lastUpdateTime: doc1UpdateTime1); tx.delete(doc2Ref, lastUpdateTime: doc2UpdateTime1); + return null; }); - // ignore: invalid_return_type_for_catch_error var error = await result.catchError((Object error) => error); expect(error.toString(), contains('does not match the required base version')); From d639a7e1e3c3c8662a5a8b980027c789b4e2ecfb Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 26 Sep 2021 10:09:16 +0200 Subject: [PATCH 07/24] fix: 2.15 lints --- lib/src/bindings.dart | 61 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/lib/src/bindings.dart b/lib/src/bindings.dart index 044c314..5853acf 100644 --- a/lib/src/bindings.dart +++ b/lib/src/bindings.dart @@ -7,8 +7,6 @@ import 'package:firebase_admin_interop/js.dart'; import 'package:js/js.dart'; import 'package:node_interop/node.dart'; -import 'firestore_bindings.dart' show Firestore, GeoPointUtil, FieldValues; - export 'firestore_bindings.dart'; // admin ========================================================================= @@ -71,6 +69,7 @@ abstract class FirebaseError implements JsError { @anonymous abstract class FirebaseArrayIndexError { external FirebaseError get error; + external num get index; } @@ -105,8 +104,10 @@ abstract class Credentials { abstract class ServiceAccountConfig { // ignore: non_constant_identifier_names external String get project_id; + // ignore: non_constant_identifier_names external String get client_email; + // ignore: non_constant_identifier_names external String get private_key; @@ -310,12 +311,19 @@ abstract class Auth { @anonymous abstract class CreateUserRequest { external bool get disabled; + external String get displayName; + external String get email; + external bool get emailVerified; + external String get password; + external String get phoneNumber; + external String get photoURL; + external String get uid; external factory CreateUserRequest({ @@ -334,11 +342,17 @@ abstract class CreateUserRequest { @anonymous abstract class UpdateUserRequest { external bool get disabled; + external String get displayName; + external String get email; + external bool get emailVerified; + external String get password; + external String get phoneNumber; + external String get photoURL; external factory UpdateUserRequest({ @@ -462,6 +476,7 @@ abstract class UserInfo { @anonymous abstract class ListUsersResult { external String get pageToken; + external List get users; } @@ -554,10 +569,13 @@ abstract class FirebaseSignInInfo { abstract class FirestoreService { // ignore: non_constant_identifier_names external GeoPointUtil get GeoPoint; + // ignore: non_constant_identifier_names external FieldValues get FieldValue; + // ignore: non_constant_identifier_names external Object get Timestamp; + // ignore: non_constant_identifier_names external FieldPathPrototype get FieldPath; } @@ -640,7 +658,9 @@ abstract class Messaging { @anonymous abstract class FcmMessage { external String get data; + external Notification get notification; + external String get token; external factory FcmMessage({ @@ -654,14 +674,20 @@ abstract class FcmMessage { @anonymous abstract class TopicMessage { external AndroidConfig get android; + external ApnsConfig get apns; + external dynamic get data; + external String get key; + external FcmOptions get fcmOptions; + external Notification get notification; /// Required external String get topic; + external WebpushConfig get webpush; external factory TopicMessage({ @@ -680,14 +706,20 @@ abstract class TopicMessage { @anonymous abstract class TokenMessage { external AndroidConfig get android; + external ApnsConfig get apns; + external dynamic get data; + external String get key; + external FcmOptions get fcmOptions; + external Notification get notification; /// Required external String get token; + external WebpushConfig get webpush; external factory TokenMessage({ @@ -706,14 +738,20 @@ abstract class TokenMessage { @anonymous abstract class ConditionMessage { external AndroidConfig get android; + external ApnsConfig get apns; /// Required external String get condition; + external dynamic get data; + external String get key; + external FcmOptions get fcmOptions; + external Notification get notification; + external WebpushConfig get webpush; external factory ConditionMessage({ @@ -732,14 +770,20 @@ abstract class ConditionMessage { @anonymous abstract class MulticastMessage { external AndroidConfig get android; + external ApnsConfig get apns; + external dynamic get data; + external String get key; + external FcmOptions get fcmOptions; + external Notification get notification; /// Required external List get tokens; + external WebpushConfig get webpush; external factory MulticastMessage({ @@ -932,6 +976,7 @@ abstract class DataMessagePayload { /// Keys can be any custom string, except for the following reserved strings: /// "from" and anything starting with "google." external String get key; + external dynamic get value; external factory DataMessagePayload({ @@ -1296,15 +1341,25 @@ abstract class Aps { @anonymous abstract class ApsAlert { external String get actionLocKey; + external String get body; + external String get launchImage; + external List get locArgs; + external String get locKey; + external String get subtitle; + external List get subtitleLocArgs; + external String get subtitleLocKey; + external String get title; + external List get titleLocArgs; + external String get titleLocKey; external factory ApsAlert({ @@ -1546,6 +1601,7 @@ abstract class DatabaseService { /// Logs debugging information to the console. external void enableLogging([dynamic loggerOrBool, bool? persistent]); + // ignore: non_constant_identifier_names external ServerValues get ServerValue; } @@ -1751,6 +1807,7 @@ abstract class Reference extends Query { @anonymous abstract class TransactionResult { bool get committed; + DataSnapshot get snapshot; } From 952fabf1417dba34576722ebead5f8f8cde0b1f7 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 2 Nov 2021 15:13:22 +0100 Subject: [PATCH 08/24] dart2_3 --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 48a1b81..295fafd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,8 +18,8 @@ dev_dependencies: dev_test: tekartik_lints: git: - url: git://github.com/tekartik/common.dart - ref: null_safety + url: https://github.com/tekartik/common.dart + ref: dart2_3 path: packages/lints version: '>=0.1.0' # build_runner: ^1.7.4 From 571ed190a1a67004bcdef174628dcacf63e06556 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 2 Nov 2021 15:16:04 +0100 Subject: [PATCH 09/24] dart2_3 --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 9b74a44..68e1c3a 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,13 @@ dependencies: firebase_admin_interop: [latest_version] ``` +Git: +```yaml + firebase_admin_interop: + git: + url: https://github.com/tekartikdev/firebase-admin-interop + ref: dart2_3 +``` Run `pub get`. 2. Create `package.json` file to install Node.js modules used by this library: From 5c00858991b6e458076194530c6be6948c4e4264 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 28 Nov 2021 08:35:12 +0100 Subject: [PATCH 10/24] ci --- .github/workflows/run_ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run_ci.yml b/.github/workflows/run_ci.yml index 0b98e5b..9e2025f 100644 --- a/.github/workflows/run_ci.yml +++ b/.github/workflows/run_ci.yml @@ -18,10 +18,10 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] dart: [stable, beta, dev] steps: - - uses: cedx/setup-dart@v2 - with: - release-channel: ${{ matrix.dart }} - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v1.3 + with: + sdk: ${{ matrix.dart }} - run: dart --version - run: dart pub get - - run: dart run tool/run_ci.dart + - run: dart run tool/run_ci.dart \ No newline at end of file From 89064dd25f26f94c16538edbd451ccc259c4beb1 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Mon, 20 Jun 2022 10:37:45 +0200 Subject: [PATCH 11/24] [firestore_node] feat: add support for whereIn --- lib/src/firestore.dart | 12 ++++++++++++ pubspec.yaml | 2 +- test/database_test.dart | 4 +++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 2be873f..bf9e4a7 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -952,6 +952,9 @@ class DocumentQuery { dynamic isGreaterThanOrEqualTo, dynamic arrayContains, bool? isNull, + List? whereIn, + List? notIn, + List? arrayContainsAny, }) { var query = nativeInstance; @@ -971,6 +974,15 @@ class DocumentQuery { if (arrayContains != null) { addCondition(field, 'array-contains', arrayContains); } + if (whereIn != null) { + addCondition(field, 'in', whereIn); + } + if (notIn != null) { + addCondition(field, 'not-in', whereIn); + } + if (arrayContainsAny != null) { + addCondition(field, 'array-contains-any', arrayContainsAny); + } if (isNull != null) { assert( diff --git a/pubspec.yaml b/pubspec.yaml index 295fafd..dce5c04 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: firebase_admin_interop description: Firebase Admin SDK for Dart written as a wrapper around official Node.js SDK. -version: 2.2.0 +version: 2.3.0 homepage: https://github.com/pulyaevskiy/firebase-admin-interop publish_to: none diff --git a/test/database_test.dart b/test/database_test.dart index 5ad94a9..6de4f10 100644 --- a/test/database_test.dart +++ b/test/database_test.dart @@ -1,9 +1,11 @@ +@TestOn('node') +library database_test; + // Copyright (c) 2017, Anatoly Pulyaevskiy. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. import 'dart:async'; -@TestOn('node') import 'package:firebase_admin_interop/firebase_admin_interop.dart'; import 'package:test/test.dart'; From 1fc8b176abb7c2379ae3f246c0192441202ce943 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Sun, 26 Jun 2022 23:35:00 +0200 Subject: [PATCH 12/24] fix: dart 2.18 lints --- lib/src/firestore.dart | 4 ++-- test/firestore_test.dart | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index bf9e4a7..e5ec8f0 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -258,14 +258,14 @@ class DocumentReference { // subscribers have cancelled; this analyzer warning is safe to ignore. late StreamController controller; // ignore: close_sinks - void _onNextSnapshot(js.DocumentSnapshot jsSnapshot) { + void onNextSnapshot(js.DocumentSnapshot jsSnapshot) { controller.add(DocumentSnapshot(jsSnapshot, firestore)); } controller = StreamController.broadcast( onListen: () { cancelCallback = - nativeInstance.onSnapshot(allowInterop(_onNextSnapshot)); + nativeInstance.onSnapshot(allowInterop(onNextSnapshot)); }, onCancel: () { (cancelCallback as Function())(); diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 9ade549..8442044 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -91,7 +91,7 @@ void main() { data.setList( 'fieldValueInList', [Firestore.fieldValues.serverTimestamp()]); - void _check() { + void doCheck() { expect(data.keys.length, 13); expect(data.getInt('intVal'), 1); expect(data.getDouble('doubleVal'), 1.5); @@ -115,10 +115,10 @@ void main() { [Firestore.fieldValues.serverTimestamp()]); } - _check(); + doCheck(); // from/to map data = DocumentData.fromMap(data.toMap()); - _check(); + doCheck(); }); test('data types', () async { @@ -631,7 +631,7 @@ void main() { 'sub': ['a', 'b'] })); - List _querySnapshotDocIds(QuerySnapshot querySnapshot) { + List querySnapshotDocIds(QuerySnapshot querySnapshot) { return querySnapshot.documents! .map((snapshot) => snapshot.documentID) .toList(); @@ -639,11 +639,11 @@ void main() { // complex object var querySnapshot = await collRef.where('sub', isEqualTo: ['a']).get(); - expect(_querySnapshotDocIds(querySnapshot), ['doc2']); + expect(querySnapshotDocIds(querySnapshot), ['doc2']); // ordered by sub (complex object) querySnapshot = await collRef.orderBy('sub').get(); - expect(_querySnapshotDocIds(querySnapshot), ['doc2', 'doc4', 'doc1']); + expect(querySnapshotDocIds(querySnapshot), ['doc2', 'doc4', 'doc1']); }); test('query filter with map object', () async { @@ -666,7 +666,7 @@ void main() { 'sub': {'other': 'a', 'value': 'c'} })); - List _querySnapshotDocIds(QuerySnapshot querySnapshot) { + List querySnapshotDocIds(QuerySnapshot querySnapshot) { return querySnapshot.documents! .map((snapshot) => snapshot.documentID) .toList(); @@ -675,11 +675,11 @@ void main() { // complex object var querySnapshot = await collRef.where('sub', isEqualTo: {'value': 'a'}).get(); - expect(_querySnapshotDocIds(querySnapshot), ['doc2']); + expect(querySnapshotDocIds(querySnapshot), ['doc2']); // ordered by sub (complex object) querySnapshot = await collRef.orderBy('sub').get(); - expect(_querySnapshotDocIds(querySnapshot), ['doc4', 'doc2', 'doc1']); + expect(querySnapshotDocIds(querySnapshot), ['doc4', 'doc2', 'doc1']); }); test('query filter with blob', () async { From 8bf8157ebe84a572c5b52acb7637d949d5ddcd4d Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Tue, 17 Jan 2023 01:40:49 +0100 Subject: [PATCH 13/24] fix: dart 2.18 lints --- analysis_options.yaml | 4 +-- lib/src/bindings.dart | 55 +++++++++++++++++++-------------- lib/src/database.dart | 22 ++++++------- lib/src/firestore.dart | 30 +++++++++--------- lib/src/firestore_bindings.dart | 2 +- lib/src/storage_bindings.dart | 9 +++--- pubspec.yaml | 4 +-- test/admin_test.dart | 2 +- test/database_test.dart | 24 +++++++------- test/firestore_test.dart | 8 ++--- 10 files changed, 84 insertions(+), 76 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 61e2362..e14c2db 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,5 +1,5 @@ # tekartik recommended lints (extension over google lints and pedantic) -include: package:tekartik_lints/recommended.yaml +include: package:tekartik_lints/strict.yaml analyzer: exclude: @@ -20,4 +20,4 @@ linter: - prefer_is_not_empty - test_types_in_equals - unrelated_type_equality_checks - - valid_regexps + - valid_regexps \ No newline at end of file diff --git a/lib/src/bindings.dart b/lib/src/bindings.dart index 5853acf..2409362 100644 --- a/lib/src/bindings.dart +++ b/lib/src/bindings.dart @@ -16,11 +16,13 @@ const defaultAppName = '[DEFAULT]'; /// Singleton instance of [FirebaseAdmin] module. final admin = require('firebase-admin') as FirebaseAdmin?; +typedef OnCompleteFunction = dynamic Function(JsError error)?; + @JS() @anonymous abstract class FirebaseAdmin { /// Creates and initializes a Firebase app instance. - external App initializeApp([options, String? name]); + external App initializeApp([Object? options, String? name]); /// The current SDK version. // ignore: non_constant_identifier_names @@ -90,13 +92,13 @@ abstract class Credentials { /// This credential can be used in the call to [initializeApp]. /// [credentials] must be a path to a service account key JSON file or an /// object representing a service account key. - external Credential cert(credentials); + external Credential cert(Object? credentials); /// Returns [Credential] created from the provided refresh token that grants /// admin access to Firebase services. /// /// This credential can be used in the call to [initializeApp]. - external Credential refreshToken(refreshTokenPathOrObject); + external Credential refreshToken(Object? refreshTokenPathOrObject); } @JS() @@ -225,7 +227,7 @@ abstract class Auth { /// /// Returns a promise fulfilled with a custom token string for the provided uid /// and payload. - external Promise createCustomToken(String uid, developerClaims); + external Promise createCustomToken(String uid, Object? developerClaims); /// Creates a new user. /// @@ -292,7 +294,7 @@ abstract class Auth { /// [customUserClaims] can be `null`. /// /// Returns a promise containing `void`. - external Promise setCustomUserClaims(String uid, customUserClaims); + external Promise setCustomUserClaims(String uid, Object? customUserClaims); /// Updates an existing user. /// @@ -1687,7 +1689,8 @@ abstract class Reference extends Query { /// so the resulting list of items will be chronologically sorted. The keys /// are also designed to be unguessable (they contain 72 random bits of /// entropy). - external ThenableReference push([value, Function(JsError error)? onComplete]); + external ThenableReference push( + [Object? value, OnCompleteFunction? onComplete]); /// Removes the data at this Database location. /// @@ -1698,7 +1701,7 @@ abstract class Reference extends Query { /// Firebase servers will also be started, and the returned [Promise] will /// resolve when complete. If provided, the [onComplete] callback will be /// called asynchronously after synchronization has finished. - external Promise remove([Function(JsError error)? onComplete]); + external Promise remove([OnCompleteFunction? onComplete]); /// Writes data to this Database location. /// @@ -1722,7 +1725,7 @@ abstract class Reference extends Query { /// /// A single [set] will generate a single "value" event at the location where /// the `set()` was performed. - external Promise set(value, [Function(JsError error)? onComplete]); + external Promise set(Object? value, [OnCompleteFunction? onComplete]); /// Sets a priority for the data at this Database location. /// @@ -1731,7 +1734,8 @@ abstract class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - external Promise setPriority(priority, [Function(JsError error)? onComplete]); + external Promise setPriority(Object? priority, + [OnCompleteFunction? onComplete]); /// Writes data the Database location. Like [set] but also specifies the /// [priority] for that data. @@ -1741,8 +1745,8 @@ abstract class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - external Promise setWithPriority(value, priority, - [Function(JsError error)? onComplete]); + external Promise setWithPriority(Object? value, Object? priority, + [OnCompleteFunction? onComplete]); /// Atomically modifies the data at this location. /// @@ -1769,8 +1773,9 @@ abstract class Reference extends Query { /// transactions requires the client to read the data in order to /// transactionally update it. external Promise transaction( - Function(dynamic snapshot) transactionUpdate, - Function(dynamic error, bool committed, dynamic snapshot) onComplete, + dynamic Function(dynamic snapshot) transactionUpdate, + dynamic Function(dynamic error, bool committed, dynamic snapshot) + onComplete, bool applyLocally); /// Writes multiple values to the Database at once. @@ -1800,7 +1805,7 @@ abstract class Reference extends Query { /// [transaction] to modify the same data. /// /// Passing `null` to [update] will remove the data at this location. - external Promise update(values, [Function(JsError error)? onComplete]); + external Promise update(Object? values, [OnCompleteFunction? onComplete]); } @JS() @@ -1845,7 +1850,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise cancel([Function(JsError error)? onComplete]); + external Promise cancel([OnCompleteFunction? onComplete]); /// Ensures the data at this location is deleted when the client is /// disconnected (due to closing the browser, navigating to a new page, or @@ -1854,7 +1859,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise remove([Function(JsError error)? onComplete]); + external Promise remove([OnCompleteFunction? onComplete]); /// Ensures the data at this location is set to the specified [value] when the /// client is disconnected (due to closing the browser, navigating to a new @@ -1870,13 +1875,13 @@ abstract class OnDisconnect { /// /// See also: /// - [Enabling Offline Capabilities in JavaScript](https://firebase.google.com/docs/database/web/offline-capabilities) - external Promise set(value, [Function(JsError error)? onComplete]); + external Promise set(Object? value, [OnCompleteFunction? onComplete]); /// Ensures the data at this location is set to the specified [value] and /// [priority] when the client is disconnected (due to closing the browser, /// navigating to a new page, or network issues). - external Promise setWithPriority(value, priority, - [Function(JsError error)? onComplete]); + external Promise setWithPriority(Object? value, Object? priority, + [OnCompleteFunction? onComplete]); /// Writes multiple [values] at this location when the client is disconnected /// (due to closing the browser, navigating to a new page, or network issues). @@ -1892,7 +1897,7 @@ abstract class OnDisconnect { /// /// See [Reference.update] for examples of using the connected version of /// update. - external Promise update(values, [Function(JsError error)? onComplete]); + external Promise update(Object? values, [OnCompleteFunction? onComplete]); } /// Sorts and filters the data at a [Database] location so only a subset of the @@ -1997,15 +2002,15 @@ abstract class Query { /// If a [callback] is not specified, all callbacks for the specified /// [eventType] will be removed. Similarly, if no [eventType] or [callback] is /// specified, all callbacks for the [Reference] will be removed. - external void off([String? eventType, callback, context]); + external void off([String? eventType, Object? callback, Object? context]); /// Listens for data changes at a particular location. /// /// This is the primary way to read data from a [Database]. Your callback will /// be triggered for the initial data and again whenever the data changes. /// Use [off] to stop receiving updates. - external void on(String eventType, callback, - [cancelCallbackOrContext, context]); + external void on(String eventType, Object? callback, + [Object? cancelCallbackOrContext, Object? context]); /// Listens for exactly one event of the specified [eventType], and then stops /// listening. @@ -2013,7 +2018,9 @@ abstract class Query { /// This is equivalent to calling [on], and then calling [off] inside the /// callback function. See [on] for details on the event types. external Promise once(String eventType, - [successCallback, failureCallbackOrContext, context]); + [Object? successCallback, + Object? failureCallbackOrContext, + Object? context]); /// Generates a new [Query] object ordered by the specified child key. /// diff --git a/lib/src/database.dart b/lib/src/database.dart index e8d6678..9879acb 100644 --- a/lib/src/database.dart +++ b/lib/src/database.dart @@ -142,7 +142,7 @@ class Query { /// Optional [key] is only allowed if ordering by priority and defines the /// child key to end at, among the children with the previously specified /// priority. - Query endAt(value, [String? key]) { + Query endAt(Object? value, [String? key]) { if (key == null) { return Query(nativeInstance.endAt(value)); } @@ -162,7 +162,7 @@ class Query { /// query. If it is specified, then children that have exactly the specified /// value must also have exactly the specified key as their key name. This can /// be used to filter result sets with many matches for the same value. - Query equalTo(value, [String? key]) { + Query equalTo(Object? value, [String? key]) { if (key == null) { return Query(nativeInstance.equalTo(value)); } @@ -241,10 +241,10 @@ class Query { /// /// Returns [QuerySubscription] which can be used to cancel the subscription. QuerySubscription on( - String eventType, Function(DataSnapshot snapshot) callback, - [Function()? cancelCallback]) { - var fn = allowInterop( - (snapshot) => callback(DataSnapshot(snapshot as js.DataSnapshot))); + String eventType, dynamic Function(DataSnapshot snapshot) callback, + [dynamic Function()? cancelCallback]) { + var fn = allowInterop((Object? snapshot) => + callback(DataSnapshot(snapshot as js.DataSnapshot))); if (cancelCallback != null) { nativeInstance.on(eventType, fn, allowInterop(cancelCallback)); } else { @@ -297,7 +297,7 @@ class Query { /// /// See also: /// - [Filtering data](https://firebase.google.com/docs/database/web/lists-of-data#filtering_data) - Query startAt(value, [String? key]) { + Query startAt(Object? value, [String? key]) { if (key == null) { return Query(nativeInstance.startAt(value)); } @@ -429,7 +429,7 @@ class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - Future setPriority(priority) => + Future setPriority(Object? priority) => promiseToFuture(nativeInstance.setPriority(priority)); /// Writes data the Database location. Like [setValue] but also specifies the @@ -440,7 +440,7 @@ class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - Future setWithPriority(T value, priority) { + Future setWithPriority(T value, Object? priority) { return promiseToFuture( nativeInstance.setWithPriority(jsify(value!), priority)); } @@ -492,7 +492,7 @@ class Reference extends Query { allowInterop(_onComplete), applyLocally, ); - return promiseToFuture(promise).then( + return promiseToFuture(promise).then( (result) { final jsResult = result as js.TransactionResult; return DatabaseTransaction( @@ -506,7 +506,7 @@ class Reference extends Query { } Function _createTransactionHandler(DatabaseTransactionHandler handler) { - return (currentData) { + return (Object? currentData) { final data = dartify(currentData); final result = handler(data); if (result.aborted) return undefined; diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index e5ec8f0..82957e1 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -239,7 +239,7 @@ class DocumentReference { /// /// If no document exists, the read will return null. Future get() { - return promiseToFuture(nativeInstance.get()).then((jsSnapshot) => + return promiseToFuture(nativeInstance.get()).then((jsSnapshot) => DocumentSnapshot(jsSnapshot as js.DocumentSnapshot, firestore)); } @@ -268,7 +268,7 @@ class DocumentReference { nativeInstance.onSnapshot(allowInterop(onNextSnapshot)); }, onCancel: () { - (cancelCallback as Function())(); + (cancelCallback as dynamic Function())(); }, ); return controller.stream; @@ -521,7 +521,7 @@ class _FirestoreData { setProperty(nativeInstance, key, value.nativeInstance); } - static bool _isPrimitive(value) => + static bool _isPrimitive(Object? value) => value == null || value is int || value is double || @@ -529,12 +529,12 @@ class _FirestoreData { value is bool; List? getList(String key) { - final data = getProperty(nativeInstance, key); + final data = getProperty(nativeInstance, key); if (data == null) return null; if (data is! List) { throw StateError('Expected list but got ${data.runtimeType}.'); } - final result = []; + final result = []; for (var item in data) { item = _dartify(item); result.add(item); @@ -571,7 +571,7 @@ class _FirestoreData { bool _isDate(Object value) => hasProperty(value, 'toDateString') && hasProperty(value, 'getTime') && - getProperty(value, 'getTime') is Function; + getProperty(value, 'getTime') is Function; bool _isGeoPoint(Object value) => hasProperty(value, '_latitude') && hasProperty(value, '_longitude'); @@ -582,8 +582,8 @@ class _FirestoreData { } else { var proto = getProperty(value, '__proto__') as Object?; if (proto != null) { - return getProperty(proto, 'writeUInt8') is Function && - getProperty(proto, 'readUInt8') is Function; + return getProperty(proto, 'writeUInt8') is Function && + getProperty(proto, 'readUInt8') is Function; } return false; } @@ -593,11 +593,11 @@ class _FirestoreData { hasProperty(value, 'firestore') && hasProperty(value, 'id') && hasProperty(value, 'onSnapshot') && - getProperty(value, 'onSnapshot') is Function; + getProperty(value, 'onSnapshot') is Function; // TODO: figure out how to handle array* field values. For now ignored as they // don't need js to dart conversion - bool _isFieldValue(value) { + bool _isFieldValue(Object? value) { if (value == js.admin!.firestore.FieldValue.delete() || value == js.admin!.firestore.FieldValue.serverTimestamp()) { return true; @@ -606,7 +606,7 @@ class _FirestoreData { } /// Supports nested List and maps. - static dynamic _jsify(item) { + static dynamic _jsify(Object? item) { if (_isPrimitive(item)) { return item; } else if (item is GeoPoint) { @@ -688,7 +688,7 @@ class _FirestoreData { } static List _jsifyList(List list) { - var data = []; + var data = []; for (dynamic item in list) { if (item is List) { // Otherwise this crashes in firestore @@ -932,7 +932,7 @@ class DocumentQuery { .onSnapshot(allowInterop(onSnapshot), allowInterop(onError)); }, onCancel: () { - (unsubscribe as Function())(); + (unsubscribe as dynamic Function())(); }, ); return controller.stream; @@ -1265,7 +1265,7 @@ class _FieldValueArrayUnion extends _FieldValueArray { @override dynamic _jsify() { - return callMethod(js.admin!.firestore.FieldValue, 'arrayUnion', + return callMethod(js.admin!.firestore.FieldValue, 'arrayUnion', _FirestoreData._jsifyList(elements)); } @@ -1278,7 +1278,7 @@ class _FieldValueArrayRemove extends _FieldValueArray { @override dynamic _jsify() { - return callMethod(js.admin!.firestore.FieldValue, 'arrayRemove', + return callMethod(js.admin!.firestore.FieldValue, 'arrayRemove', _FirestoreData._jsifyList(elements)); } diff --git a/lib/src/firestore_bindings.dart b/lib/src/firestore_bindings.dart index 4fd5072..1d5f722 100644 --- a/lib/src/firestore_bindings.dart +++ b/lib/src/firestore_bindings.dart @@ -52,7 +52,7 @@ abstract class Timestamp { @JS() @anonymous abstract class GeoPointUtil { - external GeoPoint fromProto(proto); + external GeoPoint fromProto(Object? proto); } @JS() diff --git a/lib/src/storage_bindings.dart b/lib/src/storage_bindings.dart index 3ce739a..4e7fbb3 100644 --- a/lib/src/storage_bindings.dart +++ b/lib/src/storage_bindings.dart @@ -35,18 +35,18 @@ abstract class Bucket { /// [0] [StorageFile] - The new file. /// [1] [Object] - The full API response. external Promise combine(List sources, dynamic destination, - [options, callback]); + [Object? options, Object? callback]); /// Create a bucket. /// /// Returns promise containing CreateBucketResponse. - external Promise create([CreateBucketRequest? metadata, callback]); + external Promise create([CreateBucketRequest? metadata, Object? callback]); /// Checks if the bucket exists. /// /// Returns promise containing list with following values: /// [0] [boolean] - Whether this bucket exists. - external Promise exists([BucketExistsOptions? options, callback]); + external Promise exists([BucketExistsOptions? options, Object? callback]); /// Creates a [StorageFile] object. /// @@ -66,7 +66,8 @@ abstract class Bucket { /// For faster crc32c computation, you must manually install `fast-crc32c`: /// /// npm install --save fast-crc32c - external Promise upload(String pathString, [options, callback]); + external Promise upload(String pathString, + [Object? options, Object? callback]); } @JS() diff --git a/pubspec.yaml b/pubspec.yaml index dce5c04..83f343d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/pulyaevskiy/firebase-admin-interop publish_to: none environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.18.0 <3.0.0' dependencies: js: ^0.6.1+1 @@ -24,4 +24,4 @@ dev_dependencies: version: '>=0.1.0' # build_runner: ^1.7.4 # build_node_compilers: ^0.2.4 - # build_test: ^0.10.12+1 + # build_test: ^0.10.12+1 \ No newline at end of file diff --git a/test/admin_test.dart b/test/admin_test.dart index a377b8f..5dbdc73 100644 --- a/test/admin_test.dart +++ b/test/admin_test.dart @@ -26,7 +26,7 @@ void main() { }); test('accessToken', () async { - var accessToken = await promiseToFuture( + var accessToken = await promiseToFuture( js.admin!.credential.applicationDefault().getAccessToken()) as js.AccessToken; expect(accessToken.access_token, isNotEmpty); diff --git a/test/database_test.dart b/test/database_test.dart index 6de4f10..257b241 100644 --- a/test/database_test.dart +++ b/test/database_test.dart @@ -47,7 +47,7 @@ void main() { // This sleep is needed for the initial value to trigger the first // event. - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(Duration(seconds: 1)); await ref.setValue('Second'); await ref.setValue('Last'); @@ -90,7 +90,7 @@ void main() { test('push()', () { var child = ref.child('notifications'); - var item = child.push(); + var item = child.push(); expect(item, const TypeMatcher()); expect(item.key, isNotEmpty); expect(item.key, isNot(child.key)); @@ -162,50 +162,50 @@ void main() { }); test('get key', () async { - var snapshot = await ref.once('value'); + var snapshot = await ref.once('value'); expect(snapshot.key, 'notifications'); }); test('exists()', () async { - var snapshot = await ref.once('value'); + var snapshot = await ref.once('value'); expect(snapshot.exists(), isTrue); }); test('child()', () async { - var snapshot = await ref.once('value'); + var snapshot = await ref.once('value'); var childSnapshot = snapshot.child(childKey); expect(childSnapshot.key, childKey); expect(childSnapshot.exists(), isTrue); }); test('child() not exists', () async { - var snapshot = await ref.once('value'); + var snapshot = await ref.once('value'); var childSnapshot = snapshot.child('no-such-child'); expect(childSnapshot.key, 'no-such-child'); expect(childSnapshot.exists(), isFalse); }); test('hasChild()', () async { - var snapshot = await ref.once('value'); + var snapshot = await ref.once('value'); expect(snapshot.hasChild('no-such-child'), isFalse); expect(snapshot.hasChild(childKey), isTrue); }); test('hasChildren()', () async { - var snapshot = await ref.once('value'); + var snapshot = await ref.once('value'); expect(snapshot.hasChildren(), isTrue); }); test('numChildren()', () async { - var snapshot = await ref.once('value'); + var snapshot = await ref.once('value'); expect(snapshot.numChildren(), 2); }); test('forEach', () async { - var snapshot = await ref.once('value'); - var values = []; + var snapshot = await ref.once('value'); + var values = []; snapshot.forEach((child) { - values.add(child.val()); + values.add(child.val()!); return false; }); expect(values, ['You got a message', 'Stuff to do']); diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 8442044..38ea097 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -902,7 +902,7 @@ void main() { await doc3Ref.setData(DocumentData()..setInt('value', 3)); await doc4Ref.setData(DocumentData()..setInt('value', doc4Value)); - await Future.delayed( + await Future.delayed( Duration(seconds: 1)); // to avoid too much contention errors var list = await app.firestore().runTransaction((Transaction tx) async { @@ -953,7 +953,7 @@ void main() { var doc1UpdateTime1 = (await doc1Ref.get()).updateTime; var doc2UpdateTime1 = (await doc2Ref.get()).updateTime; - await Future.delayed( + await Future.delayed( Duration(seconds: 1)); // to avoid too much contention errors await doc1Ref.setData(DocumentData()..setInt('value', 10)); @@ -961,7 +961,7 @@ void main() { var doc1UpdateTime2 = (await doc1Ref.get()).updateTime; var doc2UpdateTime2 = (await doc2Ref.get()).updateTime; - await Future.delayed( + await Future.delayed( Duration(seconds: 1)); // to avoid too much contention errors var result = @@ -1016,7 +1016,7 @@ void main() { futures.add(transaction.then((int val) { complete.add(val); return val; - }, onError: (e) { + }, onError: (Object e) { errors.add(e); })); } From c838c10ed440b83b8d30ba7dbfc4efb5a8c40fb1 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Sat, 11 Mar 2023 23:15:54 +0100 Subject: [PATCH 14/24] fix: dart 3.0 compiler error --- CHANGELOG.md | 2 +- lib/src/bindings.dart | 133 +++++++++++++++++--------------- lib/src/database.dart | 2 +- lib/src/firestore.dart | 19 ++--- lib/src/firestore_bindings.dart | 44 +++++------ lib/src/storage_bindings.dart | 12 +-- test/auth_test.dart | 4 +- test/setup.dart | 4 +- 8 files changed, 114 insertions(+), 106 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 078eb41..fa9fe71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -248,7 +248,7 @@ as soon as possible. Read "Firestore Timestamps migration" in README.md for more ## 0.1.0-beta.3 -- Updated JS bindings with type arguments for Promises. +- Updated JS bindings with type arguments for node.Promises. ## 0.1.0-beta.2 diff --git a/lib/src/bindings.dart b/lib/src/bindings.dart index 2409362..9bd2a90 100644 --- a/lib/src/bindings.dart +++ b/lib/src/bindings.dart @@ -5,7 +5,7 @@ library firebase_admin; import 'package:firebase_admin_interop/js.dart'; import 'package:js/js.dart'; -import 'package:node_interop/node.dart'; +import 'package:node_interop/node.dart' as node; export 'firestore_bindings.dart'; @@ -14,9 +14,9 @@ export 'firestore_bindings.dart'; const defaultAppName = '[DEFAULT]'; /// Singleton instance of [FirebaseAdmin] module. -final admin = require('firebase-admin') as FirebaseAdmin?; +final admin = node.require('firebase-admin') as FirebaseAdmin?; -typedef OnCompleteFunction = dynamic Function(JsError error)?; +typedef OnCompleteFunction = dynamic Function(node.JsError error)?; @JS() @anonymous @@ -57,7 +57,7 @@ abstract class FirebaseAdmin { @JS() @anonymous -abstract class FirebaseError implements JsError { +abstract class FirebaseError implements node.JsError { /// Error codes are strings using the following format: /// "service/string-code". Some examples include "auth/invalid-uid" and /// "messaging/invalid-recipient". @@ -130,8 +130,8 @@ abstract class Credential { /// Returns a Google OAuth2 [AccessToken] object used to authenticate with /// Firebase services. /// - /// Returns Promise. - external Promise getAccessToken(); + /// Returns node.Promise. + external node.Promise getAccessToken(); } /// Google OAuth2 access token object used to authenticate with Firebase @@ -172,7 +172,7 @@ abstract class App { /// Renders this app unusable and frees the resources of all associated /// services. - external Promise delete(); + external node.Promise delete(); /// Gets the [Firestore] client for this app. external Firestore firestore(); @@ -225,47 +225,47 @@ abstract class Auth { /// device to use to sign in with the client SDKs' signInWithCustomToken() /// methods. /// - /// Returns a promise fulfilled with a custom token string for the provided uid + /// Returns a node.Promise fulfilled with a custom token string for the provided uid /// and payload. - external Promise createCustomToken(String uid, Object? developerClaims); + external node.Promise createCustomToken(String uid, Object? developerClaims); /// Creates a new user. /// - /// Returns a promise fulfilled with [UserRecord] corresponding to the newly + /// Returns a node.Promise fulfilled with [UserRecord] corresponding to the newly /// created user. - external Promise createUser(CreateUserRequest properties); + external node.Promise createUser(CreateUserRequest properties); /// Deletes an existing user. /// - /// Returns a promise containing `void`. - external Promise deleteUser(String uid); + /// Returns a node.Promise containing `void`. + external node.Promise deleteUser(String uid); /// Gets the user data for the user corresponding to a given [uid]. /// - /// Returns a promise fulfilled with [UserRecord] corresponding to the provided + /// Returns a node.Promise fulfilled with [UserRecord] corresponding to the provided /// [uid]. - external Promise getUser(String uid); + external node.Promise getUser(String uid); /// Gets the user data for the user corresponding to a given [email]. /// - /// Returns a promise fulfilled with [UserRecord] corresponding to the provided + /// Returns a node.Promise fulfilled with [UserRecord] corresponding to the provided /// [email]. - external Promise getUserByEmail(String email); + external node.Promise getUserByEmail(String email); /// Gets the user data for the user corresponding to a given [phoneNumber]. /// - /// Returns a promise fulfilled with [UserRecord] corresponding to the provided + /// Returns a node.Promise fulfilled with [UserRecord] corresponding to the provided /// [phoneNumber]. - external Promise getUserByPhoneNumber(String phoneNumber); + external node.Promise getUserByPhoneNumber(String phoneNumber); /// Retrieves a list of users (single batch only) with a size of [maxResults] /// and starting from the offset as specified by [pageToken]. /// /// This is used to retrieve all the users of a specified project in batches. /// - /// Returns a promise that resolves with the current batch of downloaded users + /// Returns a node.Promise that resolves with the current batch of downloaded users /// and the next page token as an instance of [ListUsersResult]. - external Promise listUsers([num? maxResults, String? pageToken]); + external node.Promise listUsers([num? maxResults, String? pageToken]); /// Revokes all refresh tokens for an existing user. /// @@ -279,8 +279,8 @@ abstract class Auth { /// ID tokens are revoked, use [Auth.verifyIdToken] where `checkRevoked` is set /// to `true`. /// - /// Returns a promise containing `void`. - external Promise revokeRefreshTokens(String uid); + /// Returns a node.Promise containing `void`. + external node.Promise revokeRefreshTokens(String uid); /// Sets additional developer claims on an existing user identified by the /// provided uid, typically used to define user roles and levels of access. @@ -293,20 +293,21 @@ abstract class Auth { /// /// [customUserClaims] can be `null`. /// - /// Returns a promise containing `void`. - external Promise setCustomUserClaims(String uid, Object? customUserClaims); + /// Returns a node.Promise containing `void`. + external node.Promise setCustomUserClaims( + String uid, Object? customUserClaims); /// Updates an existing user. /// - /// Returns a promise containing updated [UserRecord]. - external Promise updateUser(String uid, UpdateUserRequest properties); + /// Returns a node.Promise containing updated [UserRecord]. + external node.Promise updateUser(String uid, UpdateUserRequest properties); /// Verifies a Firebase ID token (JWT). /// - /// If the token is valid, the returned promise is fulfilled with an instance of - /// [DecodedIdToken]; otherwise, the promise is rejected. An optional flag can + /// If the token is valid, the returned node.Promise is fulfilled with an instance of + /// [DecodedIdToken]; otherwise, the node.Promise is rejected. An optional flag can /// be passed to additionally check whether the ID token was revoked. - external Promise verifyIdToken(String idToken, [bool? checkRevoked]); + external node.Promise verifyIdToken(String idToken, [bool? checkRevoked]); } @JS() @@ -593,66 +594,68 @@ abstract class Messaging { /// Sends the given message via FCM. /// - /// Returns Promise fulfilled with a unique message ID string after the + /// Returns node.Promise fulfilled with a unique message ID string after the /// message has been successfully handed off to the FCM service for delivery - external Promise send(FcmMessage message, [bool? dryRun]); + external node.Promise send(FcmMessage message, [bool? dryRun]); /// Sends all the messages in the given array via Firebase Cloud Messaging. /// - /// Returns Promise fulfilled with an object representing the + /// Returns node.Promise fulfilled with an object representing the /// result of the send operation. - external Promise sendAll(List messages, [bool? dryRun]); + external node.Promise sendAll(List messages, [bool? dryRun]); /// Sends the given multicast message to all the FCM registration tokens /// specified in it. /// - /// Returns Promise fulfilled with an object representing the + /// Returns node.Promise fulfilled with an object representing the /// result of the send operation. - external Promise sendMulticast(MulticastMessage message, [bool? dryRun]); + external node.Promise sendMulticast(MulticastMessage message, [bool? dryRun]); /// Sends an FCM message to a condition. /// - /// Returns Promise fulfilled with the server's + /// Returns node.Promise fulfilled with the server's /// response after the message has been sent. - external Promise sendToCondition(String condition, MessagingPayload payload, + external node.Promise sendToCondition( + String condition, MessagingPayload payload, [MessagingOptions? options]); /// Sends an FCM message to a single device corresponding to the provided /// registration token. /// - /// Returns Promise fulfilled with the server's + /// Returns node.Promise fulfilled with the server's /// response after the message has been sent. - external Promise sendToDevice( + external node.Promise sendToDevice( String registrationToken, MessagingPayload payload, [MessagingOptions? options]); /// Sends an FCM message to a device group corresponding to the provided /// notification key. /// - /// Returns Promise fulfilled with the server's + /// Returns node.Promise fulfilled with the server's /// response after the message has been sent. - external Promise sendToDeviceGroup( + external node.Promise sendToDeviceGroup( String notificationKey, MessagingPayload payload, [MessagingOptions? options]); /// Sends an FCM message to a topic. /// - /// Returns Promise fulfilled with the server's + /// Returns node.Promise fulfilled with the server's /// response after the message has been sent. - external Promise sendToTopic(String topic, MessagingPayload payload, + external node.Promise sendToTopic(String topic, MessagingPayload payload, [MessagingOptions? options]); /// Subscribes a device to an FCM topic. /// - /// Returns Promise fulfilled with the + /// Returns node.Promise fulfilled with the /// server's response after the device has been subscribed to the topic. - external Promise subscribeToTopic(String registrationTokens, String topic); + external node.Promise subscribeToTopic( + String registrationTokens, String topic); /// Unsubscribes a device from an FCM topic. /// - /// Returns Promise fulfilled with the + /// Returns node.Promise fulfilled with the /// server's response after the device has been subscribed to the topic. - external Promise unsubscribeFromTopic( + external node.Promise unsubscribeFromTopic( String registrationTokens, String topic); } @@ -1698,10 +1701,10 @@ abstract class Reference extends Query { /// /// The effect of the remove will be visible immediately and the corresponding /// event 'value' will be triggered. Synchronization of the remove to the - /// Firebase servers will also be started, and the returned [Promise] will + /// Firebase servers will also be started, and the returned [node.Promise] will /// resolve when complete. If provided, the [onComplete] callback will be /// called asynchronously after synchronization has finished. - external Promise remove([OnCompleteFunction? onComplete]); + external node.Promise remove([OnCompleteFunction? onComplete]); /// Writes data to this Database location. /// @@ -1710,7 +1713,7 @@ abstract class Reference extends Query { /// The effect of the write will be visible immediately, and the corresponding /// events ("value", "child_added", etc.) will be triggered. Synchronization /// of the data to the Firebase servers will also be started, and the returned - /// [Promise] will resolve when complete. If provided, the [onComplete] + /// [node.Promise] will resolve when complete. If provided, the [onComplete] /// callback will be called asynchronously after synchronization has finished. /// /// Passing `null` for the new value is equivalent to calling [remove]; @@ -1725,7 +1728,7 @@ abstract class Reference extends Query { /// /// A single [set] will generate a single "value" event at the location where /// the `set()` was performed. - external Promise set(Object? value, [OnCompleteFunction? onComplete]); + external node.Promise set(Object? value, [OnCompleteFunction? onComplete]); /// Sets a priority for the data at this Database location. /// @@ -1734,7 +1737,7 @@ abstract class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - external Promise setPriority(Object? priority, + external node.Promise setPriority(Object? priority, [OnCompleteFunction? onComplete]); /// Writes data the Database location. Like [set] but also specifies the @@ -1745,7 +1748,7 @@ abstract class Reference extends Query { /// /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) - external Promise setWithPriority(Object? value, Object? priority, + external node.Promise setWithPriority(Object? value, Object? priority, [OnCompleteFunction? onComplete]); /// Atomically modifies the data at this location. @@ -1772,7 +1775,7 @@ abstract class Reference extends Query { /// to perform a transaction. This is because the client-side nature of /// transactions requires the client to read the data in order to /// transactionally update it. - external Promise transaction( + external node.Promise transaction( dynamic Function(dynamic snapshot) transactionUpdate, dynamic Function(dynamic error, bool committed, dynamic snapshot) onComplete, @@ -1792,7 +1795,7 @@ abstract class Reference extends Query { /// The effect of the write will be visible immediately, and the corresponding /// events ('value', 'child_added', etc.) will be triggered. Synchronization of /// the data to the Firebase servers will also be started, and the returned - /// `Promise` will resolve when complete. + /// `node.Promise` will resolve when complete. /// /// If provided, the [onComplete] callback will be called asynchronously after /// synchronization has finished. @@ -1805,7 +1808,8 @@ abstract class Reference extends Query { /// [transaction] to modify the same data. /// /// Passing `null` to [update] will remove the data at this location. - external Promise update(Object? values, [OnCompleteFunction? onComplete]); + external node.Promise update(Object? values, + [OnCompleteFunction? onComplete]); } @JS() @@ -1818,7 +1822,7 @@ abstract class TransactionResult { @JS() @anonymous -abstract class ThenableReference extends Reference implements Promise {} +abstract class ThenableReference extends Reference implements node.Promise {} /// Allows you to write or clear data when your client disconnects from the /// [Database] server. These updates occur whether your client disconnects @@ -1850,7 +1854,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise cancel([OnCompleteFunction? onComplete]); + external node.Promise cancel([OnCompleteFunction? onComplete]); /// Ensures the data at this location is deleted when the client is /// disconnected (due to closing the browser, navigating to a new page, or @@ -1859,7 +1863,7 @@ abstract class OnDisconnect { /// Optional [onComplete] function that will be called when synchronization to /// the server has completed. The callback will be passed a single parameter: /// `null` for success, or a [JsError] object indicating a failure. - external Promise remove([OnCompleteFunction? onComplete]); + external node.Promise remove([OnCompleteFunction? onComplete]); /// Ensures the data at this location is set to the specified [value] when the /// client is disconnected (due to closing the browser, navigating to a new @@ -1875,12 +1879,12 @@ abstract class OnDisconnect { /// /// See also: /// - [Enabling Offline Capabilities in JavaScript](https://firebase.google.com/docs/database/web/offline-capabilities) - external Promise set(Object? value, [OnCompleteFunction? onComplete]); + external node.Promise set(Object? value, [OnCompleteFunction? onComplete]); /// Ensures the data at this location is set to the specified [value] and /// [priority] when the client is disconnected (due to closing the browser, /// navigating to a new page, or network issues). - external Promise setWithPriority(Object? value, Object? priority, + external node.Promise setWithPriority(Object? value, Object? priority, [OnCompleteFunction? onComplete]); /// Writes multiple [values] at this location when the client is disconnected @@ -1897,7 +1901,8 @@ abstract class OnDisconnect { /// /// See [Reference.update] for examples of using the connected version of /// update. - external Promise update(Object? values, [OnCompleteFunction? onComplete]); + external node.Promise update(Object? values, + [OnCompleteFunction? onComplete]); } /// Sorts and filters the data at a [Database] location so only a subset of the @@ -2017,7 +2022,7 @@ abstract class Query { /// /// This is equivalent to calling [on], and then calling [off] inside the /// callback function. See [on] for details on the event types. - external Promise once(String eventType, + external node.Promise once(String eventType, [Object? successCallback, Object? failureCallbackOrContext, Object? context]); diff --git a/lib/src/database.dart b/lib/src/database.dart index 9879acb..b7f1fa5 100644 --- a/lib/src/database.dart +++ b/lib/src/database.dart @@ -502,7 +502,7 @@ class Reference extends Query { } dynamic _onComplete(error, bool committed, snapshot) { - // no-op, we use returned Promise instead. + // no-op, we use returned node.Promise instead. } Function _createTransactionHandler(DatabaseTransactionHandler handler) { diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 82957e1..69a9d20 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -7,8 +7,8 @@ import 'dart:typed_data'; import 'package:js/js.dart'; import 'package:meta/meta.dart'; -import 'package:node_interop/js.dart'; -import 'package:node_interop/node.dart'; +import 'package:node_interop/js.dart' as node; +import 'package:node_interop/node.dart' as node; import 'package:node_interop/util.dart'; import 'package:quiver/core.dart'; @@ -106,7 +106,7 @@ class Firestore { /// with the same error. Future runTransaction( Future Function(Transaction transaction) updateFunction) { - Promise jsUpdateFunction(js.Transaction transaction) { + node.Promise jsUpdateFunction(js.Transaction transaction) { return futureToPromise(updateFunction(Transaction(transaction))); } @@ -131,7 +131,8 @@ class Firestore { final nativeRefs = refs .map((DocumentReference ref) => ref.nativeInstance) .toList(growable: false); - final promise = callMethod(nativeInstance, 'getAll', nativeRefs) as Promise; + final promise = + callMethod(nativeInstance, 'getAll', nativeRefs) as node.Promise; final result = await promiseToFuture(promise); return result .map((nativeSnapshot) => @@ -464,7 +465,7 @@ class _FirestoreData { @Deprecated('Migrate to using Firestore Timestamps and "getTimestamp()".') DateTime? getDateTime(String key) { - final value = getProperty(nativeInstance, key) as Date?; + final value = getProperty(nativeInstance, key) as node.Date?; if (value == null) return null; assert(_isDate(value), 'Tried to get Date and got $value'); return DateTime.fromMillisecondsSinceEpoch(value.getTime()); @@ -479,7 +480,7 @@ class _FirestoreData { @Deprecated('Migrate to using Firestore Timestamps and "setTimestamp()".') void setDateTime(String key, DateTime value) { - final data = Date(value.millisecondsSinceEpoch); + final data = node.Date(value.millisecondsSinceEpoch); setProperty(nativeInstance, key, data); } @@ -620,7 +621,7 @@ class _FirestoreData { return blob.data; } else if (item is DateTime) { var date = item; - return Date(date.millisecondsSinceEpoch); + return node.Date(date.millisecondsSinceEpoch); } else if (item is Timestamp) { return _createJsTimestamp(item); } else if (item is FieldValue) { @@ -675,7 +676,7 @@ class _FirestoreData { var ts = item as js.Timestamp; return Timestamp(ts.seconds, ts.nanoseconds); } else if (_isDate(item)) { - var date = item as Date; + var date = item as node.Date; return DateTime.fromMillisecondsSinceEpoch(date.getTime()); } else if (_isFieldValue(item)) { return FieldValue._fromJs(item); @@ -738,7 +739,7 @@ class DocumentData extends _FirestoreData { } /// List of keys in this document data. - List get keys => objectKeys(nativeInstance); + List get keys => node.objectKeys(nativeInstance); /// Converts this document data into a [Map]. Map toMap() { diff --git a/lib/src/firestore_bindings.dart b/lib/src/firestore_bindings.dart index 1d5f722..932c700 100644 --- a/lib/src/firestore_bindings.dart +++ b/lib/src/firestore_bindings.dart @@ -2,7 +2,7 @@ library firestore; import 'package:js/js.dart'; -import 'package:node_interop/node.dart'; +import 'package:node_interop/node.dart' as node; import 'package:node_interop/stream.dart'; @JS() @@ -35,7 +35,7 @@ abstract class FirestoreModule { @anonymous abstract class TimestampProto { external Timestamp now(); - external Timestamp fromDate(Date date); + external Timestamp fromDate(node.Date date); external Timestamp fromMillis(int milliseconds); } @@ -45,7 +45,7 @@ abstract class Timestamp { external int get seconds; external int get nanoseconds; - external Date toDate(); + external node.Date toDate(); external int toMillis(); } @@ -100,7 +100,7 @@ abstract class Firestore { /// Retrieves multiple documents from Firestore. /// snapshots. - external Promise getAll( + external node.Promise getAll( [DocumentReference? documentRef1, DocumentReference? documentRef2, DocumentReference? documentRef3, @@ -109,7 +109,7 @@ abstract class Firestore { /// Fetches the root collections that are associated with this Firestore /// database. - external Promise listCollections(); + external node.Promise listCollections(); /// Executes the given updateFunction and commits the changes applied within /// the transaction. @@ -124,8 +124,8 @@ abstract class Firestore { /// returned by the updateFunction will be returned here. Else if the /// transaction failed, a rejected Future with the corresponding failure error /// will be returned. - external Promise runTransaction( - Promise Function(Transaction transaction) updateFunction); + external node.Promise runTransaction( + node.Promise Function(Transaction transaction) updateFunction); /// Creates a write batch, used for performing multiple writes as a single /// atomic operation. @@ -196,12 +196,12 @@ abstract class GeoPoint { abstract class Transaction { /// Reads the document referenced by the provided `DocumentReference.` /// Holds a pessimistic lock on the returned document. - /*external Promise get(DocumentReference documentRef);*/ + /*external node.Promise get(DocumentReference documentRef);*/ /// Retrieves a query result. Holds a pessimistic lock on the returned /// documents. - /*external Promise get(Query query);*/ - external Promise /*Promise|Promise*/ get( - dynamic /*DocumentReference|Query*/ documentRefQuery); + /*external node.Promise get(Query query);*/ + external node.Promise /*Promise|Promise*/ + get(dynamic /*DocumentReference|Query*/ documentRefQuery); /// Create the document referred to by the provided `DocumentReference`. /// The operation will fail the transaction if a document exists at the @@ -276,7 +276,7 @@ abstract class WriteBatch { external WriteBatch delete(DocumentReference documentRef); /// Commits all of the writes in this write batch as a single atomic unit. - external Promise commit(); + external node.Promise commit(); } /// An options object that configures conditional behavior of `update()` and @@ -349,23 +349,23 @@ abstract class DocumentReference { external CollectionReference collection(String collectionPath); /// Fetches the subcollections that are direct children of this document. - external Promise listCollections(); + external node.Promise listCollections(); /// Creates a document referred to by this `DocumentReference` with the /// provided object values. The write fails if the document already exists - external Promise create(DocumentData data); + external node.Promise create(DocumentData data); /// Writes to the document referred to by this `DocumentReference`. If the /// document does not yet exist, it will be created. If you pass /// `SetOptions`, the provided data can be merged into an existing document. - external Promise set(DocumentData? data, [SetOptions? options]); + external node.Promise set(DocumentData? data, [SetOptions? options]); /// Updates fields in the document referred to by this `DocumentReference`. /// The update will fail if applied to a document that does not exist. /// Nested fields can be updated by providing dot-separated field path /// strings. /// update the document. - external Promise update(UpdateData? data, [Precondition? precondition]); + external node.Promise update(UpdateData? data, [Precondition? precondition]); /// Updates fields in the document referred to by this `DocumentReference`. /// The update will fail if applied to a document that does not exist. @@ -375,17 +375,17 @@ abstract class DocumentReference { /// argument. /// values to update, optionally followed by a `Precondition` to enforce on /// this update. - /*external Promise update(String|FieldPath field, dynamic value, [dynamic moreFieldsOrPrecondition1, dynamic moreFieldsOrPrecondition2, dynamic moreFieldsOrPrecondition3, dynamic moreFieldsOrPrecondition4, dynamic moreFieldsOrPrecondition5]);*/ - // external Promise update(dynamic /*String|FieldPath*/ data_field, + /*external node.Promise update(String|FieldPath field, dynamic value, [dynamic moreFieldsOrPrecondition1, dynamic moreFieldsOrPrecondition2, dynamic moreFieldsOrPrecondition3, dynamic moreFieldsOrPrecondition4, dynamic moreFieldsOrPrecondition5]);*/ + // external node.Promise update(dynamic /*String|FieldPath*/ data_field, // [dynamic /*Precondition|dynamic*/ precondition_value, // List moreFieldsOrPrecondition]); /// Deletes the document referred to by this `DocumentReference`. - external Promise delete([Precondition? precondition]); + external node.Promise delete([Precondition? precondition]); /// Reads the document referred to by this `DocumentReference`. /// current document contents. - external Promise get(); + external node.Promise get(); /// Attaches a listener for DocumentSnapshot events. /// is available. @@ -598,7 +598,7 @@ abstract class DocumentQuery { dynamic /*DocumentSnapshot|List*/ snapshotFieldValues); /// Executes the query and returns the results as a `QuerySnapshot`. - external Promise get(); + external node.Promise get(); /// Executes the query and returns the results as Node Stream. external Readable stream(); @@ -713,7 +713,7 @@ abstract class CollectionReference extends DocumentQuery { /// Add a new document to this collection with the specified data, assigning /// it a document ID automatically. /// newly created document after it has been written to the backend. - external Promise add(DocumentData? data); + external node.Promise add(DocumentData? data); } /// Sentinel values that can be used when writing document fields with set() diff --git a/lib/src/storage_bindings.dart b/lib/src/storage_bindings.dart index 4e7fbb3..1f02093 100644 --- a/lib/src/storage_bindings.dart +++ b/lib/src/storage_bindings.dart @@ -2,7 +2,7 @@ library firebase_storage; import 'package:js/js.dart'; -import 'package:node_interop/node.dart'; +import 'package:node_interop/node.dart' as node; import 'bindings.dart'; @@ -34,19 +34,21 @@ abstract class Bucket { /// Returns promise containing list with following values: /// [0] [StorageFile] - The new file. /// [1] [Object] - The full API response. - external Promise combine(List sources, dynamic destination, + external node.Promise combine(List sources, dynamic destination, [Object? options, Object? callback]); /// Create a bucket. /// /// Returns promise containing CreateBucketResponse. - external Promise create([CreateBucketRequest? metadata, Object? callback]); + external node.Promise create( + [CreateBucketRequest? metadata, Object? callback]); /// Checks if the bucket exists. /// /// Returns promise containing list with following values: /// [0] [boolean] - Whether this bucket exists. - external Promise exists([BucketExistsOptions? options, Object? callback]); + external node.Promise exists( + [BucketExistsOptions? options, Object? callback]); /// Creates a [StorageFile] object. /// @@ -66,7 +68,7 @@ abstract class Bucket { /// For faster crc32c computation, you must manually install `fast-crc32c`: /// /// npm install --save fast-crc32c - external Promise upload(String pathString, + external node.Promise upload(String pathString, [Object? options, Object? callback]); } diff --git a/test/auth_test.dart b/test/auth_test.dart index 2835268..ba575df 100644 --- a/test/auth_test.dart +++ b/test/auth_test.dart @@ -3,7 +3,7 @@ @TestOn('node') import 'package:firebase_admin_interop/firebase_admin_interop.dart'; -import 'package:node_interop/node.dart'; +import 'package:node_interop/node.dart' as node; import 'package:test/test.dart'; import 'setup.dart'; @@ -40,7 +40,7 @@ void main() { test('getUser which does not exist', () async { var result = app!.auth().getUser('noSuchUser'); - expect(result, throwsA(const TypeMatcher())); + expect(result, throwsA(const TypeMatcher())); }); test('listUsers', () async { diff --git a/test/setup.dart b/test/setup.dart index 513010f..f6330b0 100644 --- a/test/setup.dart +++ b/test/setup.dart @@ -4,10 +4,10 @@ import 'dart:convert'; import 'package:firebase_admin_interop/firebase_admin_interop.dart'; -import 'package:node_interop/node.dart'; +import 'package:node_interop/node.dart' as node; import 'package:node_interop/util.dart'; -final Map env = dartify(process.env); +final Map env = dartify(node.process.env); App? initFirebaseApp() { if (!env.containsKey('FIREBASE_CONFIG') || From 47cabb05f4fb72a0159249dfe14888d77c98a7a5 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Sat, 11 Mar 2023 23:28:17 +0100 Subject: [PATCH 15/24] fix: dart 3.0 compiler error --- lib/src/app.dart | 4 +- lib/src/auth.dart | 37 ++++++----- lib/src/database.dart | 31 ++++----- lib/src/firestore.dart | 148 ++++++++++++++++++++++------------------- lib/src/messaging.dart | 39 +++++------ test/admin_test.dart | 4 +- test/setup.dart | 4 +- 7 files changed, 140 insertions(+), 127 deletions(-) diff --git a/lib/src/app.dart b/lib/src/app.dart index 0c88187..366089e 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -4,7 +4,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; -import 'package:node_interop/util.dart'; +import 'package:node_interop/util.dart' as node; import 'admin.dart'; import 'auth.dart'; @@ -47,5 +47,5 @@ class App { /// Renders this app unusable and frees the resources of all associated /// services. - Future delete() => promiseToFuture(nativeInstance.delete()); + Future delete() => node.promiseToFuture(nativeInstance.delete()); } diff --git a/lib/src/auth.dart b/lib/src/auth.dart index 0ecc734..f00364b 100644 --- a/lib/src/auth.dart +++ b/lib/src/auth.dart @@ -4,7 +4,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; -import 'package:node_interop/util.dart'; +import 'package:node_interop/util.dart' as node; import 'bindings.dart' as js show Auth; import 'bindings.dart' @@ -40,28 +40,28 @@ class Auth { /// and payload. Future createCustomToken(String uid, [Map? developerClaims]) => - promiseToFuture(nativeInstance.createCustomToken( - uid, jsify(developerClaims ?? {}))); + node.promiseToFuture(nativeInstance.createCustomToken( + uid, node.jsify(developerClaims ?? {}))); /// Creates a new user. Future createUser(CreateUserRequest properties) => - promiseToFuture(nativeInstance.createUser(properties)); + node.promiseToFuture(nativeInstance.createUser(properties)); /// Deletes an existing user. Future deleteUser(String uid) => - promiseToFuture(nativeInstance.deleteUser(uid)); + node.promiseToFuture(nativeInstance.deleteUser(uid)); /// Gets the user data for the user corresponding to a given [uid]. Future getUser(String uid) => - promiseToFuture(nativeInstance.getUser(uid)); + node.promiseToFuture(nativeInstance.getUser(uid)); /// Gets the user data for the user corresponding to a given [email]. Future getUserByEmail(String email) => - promiseToFuture(nativeInstance.getUserByEmail(email)); + node.promiseToFuture(nativeInstance.getUserByEmail(email)); /// Gets the user data for the user corresponding to a given [phoneNumber]. Future getUserByPhoneNumber(String phoneNumber) => - promiseToFuture(nativeInstance.getUserByPhoneNumber(phoneNumber)); + node.promiseToFuture(nativeInstance.getUserByPhoneNumber(phoneNumber)); /// Retrieves a list of users (single batch only) with a size of [maxResults] /// and starting from the offset as specified by [pageToken]. @@ -69,11 +69,12 @@ class Auth { /// This is used to retrieve all the users of a specified project in batches. Future listUsers([num? maxResults, String? pageToken]) { if (pageToken != null && maxResults != null) { - return promiseToFuture(nativeInstance.listUsers(maxResults, pageToken)); + return node + .promiseToFuture(nativeInstance.listUsers(maxResults, pageToken)); } else if (maxResults != null) { - return promiseToFuture(nativeInstance.listUsers(maxResults)); + return node.promiseToFuture(nativeInstance.listUsers(maxResults)); } else { - return promiseToFuture(nativeInstance.listUsers()); + return node.promiseToFuture(nativeInstance.listUsers()); } } @@ -89,7 +90,7 @@ class Auth { /// ID tokens are revoked, use [Auth.verifyIdToken] where `checkRevoked` is set to /// `true`. Future revokeRefreshTokens(String uid) => - promiseToFuture(nativeInstance.revokeRefreshTokens(uid)); + node.promiseToFuture(nativeInstance.revokeRefreshTokens(uid)); /// Sets additional developer claims on an existing user identified by the /// provided [uid], typically used to define user roles and levels of access. @@ -107,12 +108,12 @@ class Auth { /// related user attributes, use database or other separate storage systems. Future setCustomUserClaims( String uid, Map customUserClaims) => - promiseToFuture( - nativeInstance.setCustomUserClaims(uid, jsify(customUserClaims))); + node.promiseToFuture(nativeInstance.setCustomUserClaims( + uid, node.jsify(customUserClaims))); /// Updates an existing user. Future updateUser(String uid, UpdateUserRequest properties) => - promiseToFuture(nativeInstance.updateUser(uid, properties)); + node.promiseToFuture(nativeInstance.updateUser(uid, properties)); /// Verifies a Firebase ID token (JWT). /// @@ -122,10 +123,10 @@ class Auth { /// was revoked. Future verifyIdToken(String idToken, [bool? checkRevoked]) { if (checkRevoked != null) { - return promiseToFuture( - nativeInstance.verifyIdToken(idToken, checkRevoked)); + return node + .promiseToFuture(nativeInstance.verifyIdToken(idToken, checkRevoked)); } else { - return promiseToFuture(nativeInstance.verifyIdToken(idToken)); + return node.promiseToFuture(nativeInstance.verifyIdToken(idToken)); } } } diff --git a/lib/src/database.dart b/lib/src/database.dart index b7f1fa5..fd63e88 100644 --- a/lib/src/database.dart +++ b/lib/src/database.dart @@ -6,7 +6,7 @@ import 'dart:js'; import 'package:meta/meta.dart'; import 'package:node_interop/js.dart'; -import 'package:node_interop/util.dart'; +import 'package:node_interop/util.dart' as node; import 'app.dart'; import 'bindings.dart' as js; @@ -207,7 +207,8 @@ class Query { /// Listens for exactly one event of the specified [eventType], and then stops /// listening. Future> once(String eventType) { - return promiseToFuture(nativeInstance.once(eventType)) + return node + .promiseToFuture(nativeInstance.once(eventType)) .then((snapshot) => DataSnapshot(snapshot)); } @@ -376,8 +377,8 @@ class Reference extends Query { /// entropy). FutureReference push([T? value]) { if (value != null) { - var futureRef = nativeInstance.push(jsify(value)); - return FutureReference(futureRef, promiseToFuture(futureRef)); + var futureRef = nativeInstance.push(node.jsify(value)); + return FutureReference(futureRef, node.promiseToFuture(futureRef)); } else { // JS side returns regular Reference if value is not provided, but // we still convert it to FutureReference to be consistent with declared @@ -395,7 +396,7 @@ class Reference extends Query { /// event 'value' will be triggered. Synchronization of the remove to the /// Firebase servers will also be started, and the returned [Future] will /// resolve when complete. - Future remove() => promiseToFuture(nativeInstance.remove()); + Future remove() => node.promiseToFuture(nativeInstance.remove()); /// Writes data to this Database location. /// @@ -419,7 +420,7 @@ class Reference extends Query { /// A single [setValue] will generate a single "value" event at the location /// where the `setValue()` was performed. Future setValue(T value) { - return promiseToFuture(nativeInstance.set(jsify(value!))); + return node.promiseToFuture(nativeInstance.set(node.jsify(value!))); } /// Sets a priority for the data at this Database location. @@ -430,7 +431,7 @@ class Reference extends Query { /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) Future setPriority(Object? priority) => - promiseToFuture(nativeInstance.setPriority(priority)); + node.promiseToFuture(nativeInstance.setPriority(priority)); /// Writes data the Database location. Like [setValue] but also specifies the /// [priority] for that data. @@ -441,8 +442,8 @@ class Reference extends Query { /// See also: /// - [Sorting and filtering data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data) Future setWithPriority(T value, Object? priority) { - return promiseToFuture( - nativeInstance.setWithPriority(jsify(value!), priority)); + return node.promiseToFuture( + nativeInstance.setWithPriority(node.jsify(value!), priority)); } /// Atomically modifies the data at this location. @@ -492,7 +493,7 @@ class Reference extends Query { allowInterop(_onComplete), applyLocally, ); - return promiseToFuture(promise).then( + return node.promiseToFuture(promise).then( (result) { final jsResult = result as js.TransactionResult; return DatabaseTransaction( @@ -507,10 +508,10 @@ class Reference extends Query { Function _createTransactionHandler(DatabaseTransactionHandler handler) { return (Object? currentData) { - final data = dartify(currentData); + final data = node.dartify(currentData); final result = handler(data); if (result.aborted) return undefined; - return jsify(result.data!); + return node.jsify(result.data!); }; } @@ -539,7 +540,7 @@ class Reference extends Query { /// /// Passing `null` to [update] will remove the data at this location. Future update(Map values) { - return promiseToFuture(nativeInstance.update(jsify(values))); + return node.promiseToFuture(nativeInstance.update(node.jsify(values))); } } @@ -655,11 +656,11 @@ class DataSnapshot { if (_value != null) return _value; if (!exists()) return null; // Don't attempt to dartify empty snapshot. - _value = dartify(nativeInstance.val()); + _value = node.dartify(nativeInstance.val()); return _value; } // NOTE: intentionally not following JS library name – using Dart convention. /// Returns a JSON-serializable representation of this data snapshot. - Object toJson() => dartify(nativeInstance.toJSON()); + Object toJson() => node.dartify(nativeInstance.toJSON()); } diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 69a9d20..627cc1c 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -9,7 +9,7 @@ import 'package:js/js.dart'; import 'package:meta/meta.dart'; import 'package:node_interop/js.dart' as node; import 'package:node_interop/node.dart' as node; -import 'package:node_interop/util.dart'; +import 'package:node_interop/util.dart' as node; import 'package:quiver/core.dart'; import 'bindings.dart' as js; @@ -24,15 +24,15 @@ js.GeoPoint _createJsGeoPoint(num latitude, num longitude) { } js.Timestamp? _createJsTimestamp(Timestamp ts) { - return callConstructor(js.admin!.firestore.Timestamp, - jsify([ts.seconds, ts.nanoseconds]) as List?) as js.Timestamp?; + return node.callConstructor(js.admin!.firestore.Timestamp, + node.jsify([ts.seconds, ts.nanoseconds]) as List?) + as js.Timestamp?; } @Deprecated('This function will be hidden from public API in future versions.') js.FieldPath? createFieldPath(List fieldNames) { - return callConstructor( - js.admin!.firestore.FieldPath, jsify(fieldNames) as List?) - as js.FieldPath; + return node.callConstructor(js.admin!.firestore.FieldPath, + node.jsify(fieldNames) as List?) as js.FieldPath; } /// Returns a special sentinel [FieldPath] to refer to the ID of a document. @@ -107,17 +107,17 @@ class Firestore { Future runTransaction( Future Function(Transaction transaction) updateFunction) { node.Promise jsUpdateFunction(js.Transaction transaction) { - return futureToPromise(updateFunction(Transaction(transaction))); + return node.futureToPromise(updateFunction(Transaction(transaction))); } - return promiseToFuture( + return node.promiseToFuture( nativeInstance.runTransaction(allowInterop(jsUpdateFunction))); } /// Fetches the root collections that are associated with this Firestore /// database. Future> listCollections() async => - (await promiseToFuture(nativeInstance.listCollections())) + (await node.promiseToFuture(nativeInstance.listCollections())) .map((nativeCollectionReference) => CollectionReference( nativeCollectionReference as js.CollectionReference, this)) .toList(growable: false); @@ -132,8 +132,8 @@ class Firestore { .map((DocumentReference ref) => ref.nativeInstance) .toList(growable: false); final promise = - callMethod(nativeInstance, 'getAll', nativeRefs) as node.Promise; - final result = await promiseToFuture(promise); + node.callMethod(nativeInstance, 'getAll', nativeRefs) as node.Promise; + final result = await node.promiseToFuture(promise); return result .map((nativeSnapshot) => DocumentSnapshot(nativeSnapshot as js.DocumentSnapshot, this)) @@ -181,7 +181,8 @@ class CollectionReference extends DocumentQuery { /// The unique key generated is prefixed with a client-generated timestamp /// so that the resulting list will be chronologically-sorted. Future add(DocumentData data) { - return promiseToFuture( + return node + .promiseToFuture( nativeInstance!.add(data.nativeInstance)) .then((jsRef) => DocumentReference(jsRef, firestore)); } @@ -223,9 +224,9 @@ class DocumentReference { Future setData(DocumentData data, [js.SetOptions? options]) { final docData = data.nativeInstance; if (options != null) { - return promiseToFuture(nativeInstance.set(docData, options)); + return node.promiseToFuture(nativeInstance.set(docData, options)); } - return promiseToFuture(nativeInstance.set(docData)); + return node.promiseToFuture(nativeInstance.set(docData)); } /// Updates fields in the document referred to by this [DocumentReference]. @@ -233,19 +234,21 @@ class DocumentReference { /// If no document exists yet, the update will fail. Future updateData(UpdateData data) { final docData = data.nativeInstance; - return promiseToFuture(nativeInstance.update(docData as js.UpdateData)); + return node + .promiseToFuture(nativeInstance.update(docData as js.UpdateData)); } /// Reads the document referenced by this [DocumentReference]. /// /// If no document exists, the read will return null. Future get() { - return promiseToFuture(nativeInstance.get()).then((jsSnapshot) => - DocumentSnapshot(jsSnapshot as js.DocumentSnapshot, firestore)); + return node.promiseToFuture(nativeInstance.get()).then( + (jsSnapshot) => + DocumentSnapshot(jsSnapshot as js.DocumentSnapshot, firestore)); } /// Deletes the document referred to by this [DocumentReference]. - Future delete() => promiseToFuture(nativeInstance.delete()); + Future delete() => node.promiseToFuture(nativeInstance.delete()); /// Returns the reference of a collection contained inside of this /// document. @@ -277,7 +280,7 @@ class DocumentReference { /// Fetches the subcollections that are direct children of this document. Future> listCollections() async => - (await promiseToFuture(nativeInstance.listCollections())) + (await node.promiseToFuture(nativeInstance.listCollections())) .map((nativeCollectionReference) => CollectionReference( nativeCollectionReference as js.CollectionReference, firestore)) .toList(growable: false); @@ -389,12 +392,13 @@ class DocumentSnapshot { class _FirestoreData { _FirestoreData([Object? nativeInstance]) - : nativeInstance = (nativeInstance ?? newObject()) as js.DocumentData; + : nativeInstance = + (nativeInstance ?? node.newObject()) as js.DocumentData; @protected final js.DocumentData nativeInstance; /// Length of this document. - int get length => objectKeys(nativeInstance).length; + int get length => node.objectKeys(nativeInstance).length; bool get isEmpty => length == 0; @@ -402,7 +406,7 @@ class _FirestoreData { void _setField(String key, dynamic value) { if (value == null) { - setProperty(nativeInstance, key, null); + node.setProperty(nativeInstance, key, null); } else if (value is String) { setString(key, value); } else if (value is int) { @@ -435,44 +439,44 @@ class _FirestoreData { } String? getString(String key) => - (getProperty(nativeInstance, key) as String?); + (node.getProperty(nativeInstance, key) as String?); void setString(String key, String value) { - setProperty(nativeInstance, key, value); + node.setProperty(nativeInstance, key, value); } - int? getInt(String key) => (getProperty(nativeInstance, key) as int?); + int? getInt(String key) => (node.getProperty(nativeInstance, key) as int?); void setInt(String key, int? value) { - setProperty(nativeInstance, key, value); + node.setProperty(nativeInstance, key, value); } double? getDouble(String key) => - (getProperty(nativeInstance, key) as double?); + (node.getProperty(nativeInstance, key) as double?); void setDouble(String key, double value) { - setProperty(nativeInstance, key, value); + node.setProperty(nativeInstance, key, value); } - bool? getBool(String key) => (getProperty(nativeInstance, key) as bool?); + bool? getBool(String key) => (node.getProperty(nativeInstance, key) as bool?); void setBool(String key, bool value) { - setProperty(nativeInstance, key, value); + node.setProperty(nativeInstance, key, value); } /// Returns true if this data contains an entry with the given [key]. - bool has(String key) => hasProperty(nativeInstance, key); + bool has(String key) => node.hasProperty(nativeInstance, key); @Deprecated('Migrate to using Firestore Timestamps and "getTimestamp()".') DateTime? getDateTime(String key) { - final value = getProperty(nativeInstance, key) as node.Date?; + final value = node.getProperty(nativeInstance, key) as node.Date?; if (value == null) return null; assert(_isDate(value), 'Tried to get Date and got $value'); return DateTime.fromMillisecondsSinceEpoch(value.getTime()); } Timestamp? getTimestamp(String key) { - var ts = getProperty(nativeInstance, key) as js.Timestamp?; + var ts = node.getProperty(nativeInstance, key) as js.Timestamp?; if (ts == null) return null; assert(_isTimestamp(ts), 'Tried to get Timestamp and got $ts.'); return Timestamp(ts.seconds, ts.nanoseconds); @@ -481,16 +485,16 @@ class _FirestoreData { @Deprecated('Migrate to using Firestore Timestamps and "setTimestamp()".') void setDateTime(String key, DateTime value) { final data = node.Date(value.millisecondsSinceEpoch); - setProperty(nativeInstance, key, data); + node.setProperty(nativeInstance, key, data); } void setTimestamp(String key, Timestamp value) { final ts = _createJsTimestamp(value); - setProperty(nativeInstance, key, ts); + node.setProperty(nativeInstance, key, ts); } GeoPoint? getGeoPoint(String key) { - var value = getProperty(nativeInstance, key) as js.GeoPoint?; + var value = node.getProperty(nativeInstance, key) as js.GeoPoint?; if (value == null) return null; assert(_isGeoPoint(value), 'Invalid value provided to $runtimeType.getGeoPoint().'); @@ -498,7 +502,7 @@ class _FirestoreData { } Blob? getBlob(String key) { - var value = getProperty(nativeInstance, key) as Object?; + var value = node.getProperty(nativeInstance, key) as Object?; if (value == null) return null; assert(_isBlob(value), 'Invalid value provided to $runtimeType.getBlob().'); return Blob(value as List); @@ -506,20 +510,20 @@ class _FirestoreData { void setGeoPoint(String key, GeoPoint value) { final data = _createJsGeoPoint(value.latitude, value.longitude); - setProperty(nativeInstance, key, data); + node.setProperty(nativeInstance, key, data); } void setBlob(String key, Blob value) { final data = value.data; - setProperty(nativeInstance, key, data); + node.setProperty(nativeInstance, key, data); } void setFieldValue(String key, FieldValue value) { - setProperty(nativeInstance, key, value._jsify()); + node.setProperty(nativeInstance, key, value._jsify()); } void setNestedData(String key, DocumentData value) { - setProperty(nativeInstance, key, value.nativeInstance); + node.setProperty(nativeInstance, key, value.nativeInstance); } static bool _isPrimitive(Object? value) => @@ -530,7 +534,7 @@ class _FirestoreData { value is bool; List? getList(String key) { - final data = getProperty(nativeInstance, key); + final data = node.getProperty(nativeInstance, key); if (data == null) return null; if (data is! List) { throw StateError('Expected list but got ${data.runtimeType}.'); @@ -547,11 +551,11 @@ class _FirestoreData { // The contents remains is js final data = _jsifyList(value); - setProperty(nativeInstance, key, data); + node.setProperty(nativeInstance, key, data); } DocumentReference? getReference(String key) { - var ref = getProperty(nativeInstance, key) as js.DocumentReference?; + var ref = node.getProperty(nativeInstance, key) as js.DocumentReference?; if (ref == null) return null; assert(_isReference(ref), 'Invalid value provided to $runtimeType.getReference().'); @@ -562,39 +566,41 @@ class _FirestoreData { void setReference(String key, DocumentReference value) { final data = value.nativeInstance; - setProperty(nativeInstance, key, data); + node.setProperty(nativeInstance, key, data); } bool _isTimestamp(Object value) => - hasProperty(value, '_seconds') && hasProperty(value, '_nanoseconds'); + node.hasProperty(value, '_seconds') && + node.hasProperty(value, '_nanoseconds'); // Workarounds for dart2js as `value is Type` doesn't work as expected. bool _isDate(Object value) => - hasProperty(value, 'toDateString') && - hasProperty(value, 'getTime') && - getProperty(value, 'getTime') is Function; + node.hasProperty(value, 'toDateString') && + node.hasProperty(value, 'getTime') && + node.getProperty(value, 'getTime') is Function; bool _isGeoPoint(Object value) => - hasProperty(value, '_latitude') && hasProperty(value, '_longitude'); + node.hasProperty(value, '_latitude') && + node.hasProperty(value, '_longitude'); bool _isBlob(Object value) { if (value is Uint8List) { return true; } else { - var proto = getProperty(value, '__proto__') as Object?; + var proto = node.getProperty(value, '__proto__') as Object?; if (proto != null) { - return getProperty(proto, 'writeUInt8') is Function && - getProperty(proto, 'readUInt8') is Function; + return node.getProperty(proto, 'writeUInt8') is Function && + node.getProperty(proto, 'readUInt8') is Function; } return false; } } bool _isReference(Object value) => - hasProperty(value, 'firestore') && - hasProperty(value, 'id') && - hasProperty(value, 'onSnapshot') && - getProperty(value, 'onSnapshot') is Function; + node.hasProperty(value, 'firestore') && + node.hasProperty(value, 'id') && + node.hasProperty(value, 'onSnapshot') && + node.getProperty(value, 'onSnapshot') is Function; // TODO: figure out how to handle array* field values. For now ignored as they // don't need js to dart conversion @@ -733,7 +739,7 @@ class DocumentData extends _FirestoreData { } DocumentData? getNestedData(String key) { - final data = getProperty(nativeInstance, key) as js.DocumentData?; + final data = node.getProperty(nativeInstance, key) as js.DocumentData?; if (data == null) return null; return DocumentData(data); } @@ -745,7 +751,7 @@ class DocumentData extends _FirestoreData { Map toMap() { final map = {}; for (var key in keys) { - map[key] = _dartify(getProperty(nativeInstance, key)); + map[key] = _dartify(node.getProperty(nativeInstance, key)); } return map; } @@ -907,7 +913,8 @@ class DocumentQuery { final Firestore firestore; Future get() { - return promiseToFuture(nativeInstance!.get()) + return node + .promiseToFuture(nativeInstance!.get()) .then((jsSnapshot) => QuerySnapshot(jsSnapshot, firestore)); } @@ -1078,7 +1085,7 @@ class DocumentQuery { var args = (snapshot != null) ? [snapshot.nativeInstance] : values!.map(_FirestoreData._jsify).toList(); - return callMethod(nativeInstance!, method, args) as js.DocumentQuery; + return node.callMethod(nativeInstance!, method, args) as js.DocumentQuery; } /// Creates and returns a new Query instance that applies a field mask @@ -1088,7 +1095,8 @@ class DocumentQuery { DocumentQuery select(List fieldPaths) { // Dart doesn't support varargs return DocumentQuery( - callMethod(nativeInstance!, 'select', fieldPaths) as js.DocumentQuery, + node.callMethod(nativeInstance!, 'select', fieldPaths) + as js.DocumentQuery, firestore); } } @@ -1106,7 +1114,8 @@ class Transaction { /// Holds a pessimistic lock on the returned document. Future get(DocumentReference documentRef) { final nativeRef = documentRef.nativeInstance; - return promiseToFuture(nativeInstance.get(nativeRef)) + return node + .promiseToFuture(nativeInstance.get(nativeRef)) .then((jsSnapshot) => DocumentSnapshot(jsSnapshot, documentRef.firestore)); } @@ -1115,7 +1124,8 @@ class Transaction { /// documents. Future getQuery(DocumentQuery query) { final nativeQuery = query.nativeInstance; - return promiseToFuture(nativeInstance.get(nativeQuery)) + return node + .promiseToFuture(nativeInstance.get(nativeQuery)) .then((jsSnapshot) => QuerySnapshot(jsSnapshot, query.firestore)); } @@ -1214,7 +1224,7 @@ class WriteBatch { nativeInstance.delete(documentRef.nativeInstance); /// Commits all of the writes in this write batch as a single atomic unit. - Future commit() => promiseToFuture(nativeInstance.commit()); + Future commit() => node.promiseToFuture(nativeInstance.commit()); } /// An options object that configures conditional behavior of [update] and @@ -1266,8 +1276,8 @@ class _FieldValueArrayUnion extends _FieldValueArray { @override dynamic _jsify() { - return callMethod(js.admin!.firestore.FieldValue, 'arrayUnion', - _FirestoreData._jsifyList(elements)); + return node.callMethod(js.admin!.firestore.FieldValue, + 'arrayUnion', _FirestoreData._jsifyList(elements)); } @override @@ -1279,8 +1289,8 @@ class _FieldValueArrayRemove extends _FieldValueArray { @override dynamic _jsify() { - return callMethod(js.admin!.firestore.FieldValue, 'arrayRemove', - _FirestoreData._jsifyList(elements)); + return node.callMethod(js.admin!.firestore.FieldValue, + 'arrayRemove', _FirestoreData._jsifyList(elements)); } @override diff --git a/lib/src/messaging.dart b/lib/src/messaging.dart index ebf33bd..0c5548d 100644 --- a/lib/src/messaging.dart +++ b/lib/src/messaging.dart @@ -4,7 +4,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; -import 'package:node_interop/util.dart'; +import 'package:node_interop/util.dart' as node; import 'bindings.dart' as js show Messaging; import 'bindings.dart' @@ -66,9 +66,9 @@ class Messaging { /// message has been successfully handed off to the FCM service for delivery Future send(FcmMessage message, [bool? dryRun]) { if (dryRun != null) { - return promiseToFuture(nativeInstance.send(message, dryRun)); + return node.promiseToFuture(nativeInstance.send(message, dryRun)); } else { - return promiseToFuture(nativeInstance.send(message)); + return node.promiseToFuture(nativeInstance.send(message)); } } @@ -78,9 +78,9 @@ class Messaging { /// result of the send operation. Future sendAll(List messages, [bool? dryRun]) { if (dryRun != null) { - return promiseToFuture(nativeInstance.sendAll(messages, dryRun)); + return node.promiseToFuture(nativeInstance.sendAll(messages, dryRun)); } else { - return promiseToFuture(nativeInstance.sendAll(messages)); + return node.promiseToFuture(nativeInstance.sendAll(messages)); } } @@ -92,9 +92,10 @@ class Messaging { Future sendMulticast(MulticastMessage message, [bool? dryRun]) { if (dryRun != null) { - return promiseToFuture(nativeInstance.sendMulticast(message, dryRun)); + return node + .promiseToFuture(nativeInstance.sendMulticast(message, dryRun)); } else { - return promiseToFuture(nativeInstance.sendMulticast(message)); + return node.promiseToFuture(nativeInstance.sendMulticast(message)); } } @@ -106,11 +107,11 @@ class Messaging { String condition, MessagingPayload payload, [MessagingOptions? options]) { if (options != null) { - return promiseToFuture( + return node.promiseToFuture( nativeInstance.sendToCondition(condition, payload, options)); } else { - return promiseToFuture( - nativeInstance.sendToCondition(condition, payload)); + return node + .promiseToFuture(nativeInstance.sendToCondition(condition, payload)); } } @@ -123,10 +124,10 @@ class Messaging { String registrationToken, MessagingPayload payload, [MessagingOptions? options]) { if (options != null) { - return promiseToFuture( + return node.promiseToFuture( nativeInstance.sendToDevice(registrationToken, payload, options)); } else { - return promiseToFuture( + return node.promiseToFuture( nativeInstance.sendToDevice(registrationToken, payload)); } } @@ -140,10 +141,10 @@ class Messaging { String notificationKey, MessagingPayload payload, [MessagingOptions? options]) { if (options != null) { - return promiseToFuture( + return node.promiseToFuture( nativeInstance.sendToDeviceGroup(notificationKey, payload, options)); } else { - return promiseToFuture( + return node.promiseToFuture( nativeInstance.sendToDeviceGroup(notificationKey, payload)); } } @@ -156,10 +157,10 @@ class Messaging { String topic, MessagingPayload payload, [MessagingOptions? options]) { if (options != null) { - return promiseToFuture( - nativeInstance.sendToTopic(topic, payload, options)); + return node + .promiseToFuture(nativeInstance.sendToTopic(topic, payload, options)); } else { - return promiseToFuture(nativeInstance.sendToTopic(topic, payload)); + return node.promiseToFuture(nativeInstance.sendToTopic(topic, payload)); } } @@ -169,7 +170,7 @@ class Messaging { /// server's response after the device has been subscribed to the topic. Future subscribeToTopic( String registrationTokens, String topic) => - promiseToFuture( + node.promiseToFuture( nativeInstance.subscribeToTopic(registrationTokens, topic)); /// Unsubscribes a device from an FCM [topic]. @@ -178,6 +179,6 @@ class Messaging { /// server's response after the device has been subscribed to the topic. Future unsubscribeFromTopic( String registrationTokens, String topic) => - promiseToFuture( + node.promiseToFuture( nativeInstance.unsubscribeFromTopic(registrationTokens, topic)); } diff --git a/test/admin_test.dart b/test/admin_test.dart index 5dbdc73..ddfc180 100644 --- a/test/admin_test.dart +++ b/test/admin_test.dart @@ -4,7 +4,7 @@ @TestOn('node') import 'package:firebase_admin_interop/firebase_admin_interop.dart'; import 'package:firebase_admin_interop/js.dart' as js; -import 'package:node_interop/util.dart'; +import 'package:node_interop/util.dart' as node; import 'package:test/test.dart'; import 'setup.dart'; @@ -26,7 +26,7 @@ void main() { }); test('accessToken', () async { - var accessToken = await promiseToFuture( + var accessToken = await node.promiseToFuture( js.admin!.credential.applicationDefault().getAccessToken()) as js.AccessToken; expect(accessToken.access_token, isNotEmpty); diff --git a/test/setup.dart b/test/setup.dart index f6330b0..c1c49a9 100644 --- a/test/setup.dart +++ b/test/setup.dart @@ -5,9 +5,9 @@ import 'dart:convert'; import 'package:firebase_admin_interop/firebase_admin_interop.dart'; import 'package:node_interop/node.dart' as node; -import 'package:node_interop/util.dart'; +import 'package:node_interop/util.dart' as node; -final Map env = dartify(node.process.env); +final Map env = node.dartify(node.process.env); App? initFirebaseApp() { if (!env.containsKey('FIREBASE_CONFIG') || From e67ef0258473daf0a8897c8bba078c3827cfa998 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Mon, 20 Mar 2023 18:41:26 +0100 Subject: [PATCH 16/24] fix: dart 3.0 compiler error --- .gitignore | 1 + .travis.yml | 12 ------------ package.json | 4 ++-- pubspec.yaml | 12 ++++++------ test/setup.dart | 5 ++++- tool/ddc_test.sh | 5 ----- tool/travis.sh | 20 -------------------- 7 files changed, 13 insertions(+), 46 deletions(-) delete mode 100644 .travis.yml delete mode 100755 tool/ddc_test.sh delete mode 100755 tool/travis.sh diff --git a/.gitignore b/.gitignore index 1659be8..f8c2a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .packages .pub/ .dart_tool/ +.dart/ build/ # Remove the following pattern if you wish to check in your lock file pubspec.lock diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c56ffe9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: dart - -dart: -- stable - -before_install: -- nvm install 8.13.0 -- node --version - -script: -- ./tool/travis.sh -#- ./tool/ddc_test.sh diff --git a/package.json b/package.json index 45d69b5..5c2759f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "firebase-admin": "8.5.0", - "@google-cloud/firestore": "2.0.0" + "firebase-admin": "11.5.0", + "@google-cloud/firestore": "6.5.0" } } diff --git a/pubspec.yaml b/pubspec.yaml index 83f343d..e2ba108 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: firebase_admin_interop description: Firebase Admin SDK for Dart written as a wrapper around official Node.js SDK. -version: 2.3.0 +version: 2.3.1 homepage: https://github.com/pulyaevskiy/firebase-admin-interop publish_to: none @@ -8,13 +8,13 @@ environment: sdk: '>=2.18.0 <3.0.0' dependencies: - js: ^0.6.1+1 - node_interop: ^2.0.0 - meta: ^1.1.8 - quiver: ^3.0.0 + js: '>=0.6.1+1' + node_interop: '>=2.0.0' + meta: '>=1.1.8' + quiver: '>=3.0.0' dev_dependencies: - test: ^1.12.0 + test: '>=1.12.0' dev_test: tekartik_lints: git: diff --git a/test/setup.dart b/test/setup.dart index c1c49a9..75f7104 100644 --- a/test/setup.dart +++ b/test/setup.dart @@ -10,13 +10,16 @@ import 'package:node_interop/util.dart' as node; final Map env = node.dartify(node.process.env); App? initFirebaseApp() { + print(env); if (!env.containsKey('FIREBASE_CONFIG') || !env.containsKey('FIREBASE_SERVICE_ACCOUNT_JSON')) { - throw StateError('Environment variables are not set.'); + throw StateError( + 'Environment variables FIREBASE_SERVICE_ACCOUNT_JSON and FIREBASE_CONFIG are not set.'); } var certConfig = jsonDecode(env['FIREBASE_SERVICE_ACCOUNT_JSON'] as String) as Map; + final cert = FirebaseAdmin.instance.cert( projectId: certConfig['project_id'] as String?, clientEmail: certConfig['client_email'] as String?, diff --git a/tool/ddc_test.sh b/tool/ddc_test.sh deleted file mode 100755 index 6cab18b..0000000 --- a/tool/ddc_test.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -set -e - -pub run build_runner test --output=build/ diff --git a/tool/travis.sh b/tool/travis.sh deleted file mode 100755 index 27c4fd5..0000000 --- a/tool/travis.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -set -e - -echo '> pub get ================================================================' -pub get - -if [ -f "package.json" ]; then - echo '> npm install ============================================================' - npm install -fi - -echo "> pub run test -r expanded ===============================================" -pub run test -r expanded - -echo '> dartfmt -n --set-exit-if-changed . =====================================' -dartfmt -n --set-exit-if-changed lib/ - -echo "> dartanalyzer --fatal-infos --fatal-warnings . ==========================" -dartanalyzer --fatal-infos --fatal-warnings . From d10410f07f86cb644c2435da27777f0e0557c788 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Mon, 15 May 2023 23:38:45 +0200 Subject: [PATCH 17/24] feat: add orderByKey --- .github/dependabot.yml | 15 +++++++++++++++ .github/workflows/run_ci.yml | 4 ++-- lib/src/firestore.dart | 9 +++++++++ pubspec.yaml | 4 ++-- 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..cbb7686 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# Dependabot configuration file. +# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates +version: 2 + +enable-beta-ecosystems: true +updates: + - package-ecosystem: "pub" + directory: "." + schedule: + interval: "monthly" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/run_ci.yml b/.github/workflows/run_ci.yml index 9e2025f..8af5ef1 100644 --- a/.github/workflows/run_ci.yml +++ b/.github/workflows/run_ci.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - dart: [stable, beta, dev] + dart: [2.19.6, stable, beta, dev] steps: - uses: actions/checkout@v2 - uses: dart-lang/setup-dart@v1.3 @@ -24,4 +24,4 @@ jobs: sdk: ${{ matrix.dart }} - run: dart --version - run: dart pub get - - run: dart run tool/run_ci.dart \ No newline at end of file + - run: dart run tool/run_ci.dart diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 627cc1c..2e2d264 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -1010,6 +1010,15 @@ class DocumentQuery { return DocumentQuery(nativeInstance!.orderBy(field, direction), firestore); } + /// Creates and returns a new [DocumentQuery] sorted by id. (no other sort order allowed) + DocumentQuery orderByKey({bool descending = false}) { + var direction = descending ? 'desc' : 'asc'; + return DocumentQuery( + nativeInstance! + .orderBy(js.admin!.firestore.FieldPath.documentId(), direction), + firestore); + } + /// Takes a [snapshot] or a list of [values], creates and returns a new [DocumentQuery] /// that starts after the provided fields relative to the order of the query. /// diff --git a/pubspec.yaml b/pubspec.yaml index e2ba108..89dbfa5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/pulyaevskiy/firebase-admin-interop publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=2.18.0 <4.0.0' dependencies: js: '>=0.6.1+1' @@ -24,4 +24,4 @@ dev_dependencies: version: '>=0.1.0' # build_runner: ^1.7.4 # build_node_compilers: ^0.2.4 - # build_test: ^0.10.12+1 \ No newline at end of file + # build_test: ^0.10.12+1 From 103930e77050efeaa085be357047a9842722eea7 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Sun, 2 Jul 2023 14:46:54 +0200 Subject: [PATCH 18/24] fix dart 3.1 lints --- analysis_options.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index e14c2db..c53661c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -14,8 +14,7 @@ linter: - close_sinks - directives_ordering - hash_and_equals - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type - prefer_final_fields - prefer_is_not_empty - test_types_in_equals From 5965b93658936d105d3546b1e6bd3123544246ec Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Mon, 10 Jul 2023 11:27:05 +0200 Subject: [PATCH 19/24] dart3a branch --- .github/workflows/run_ci.yml | 2 +- README.md | 2 +- pubspec.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run_ci.yml b/.github/workflows/run_ci.yml index 8af5ef1..2fc46a3 100644 --- a/.github/workflows/run_ci.yml +++ b/.github/workflows/run_ci.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - dart: [2.19.6, stable, beta, dev] + dart: [3.0.5, stable, beta, dev] steps: - uses: actions/checkout@v2 - uses: dart-lang/setup-dart@v1.3 diff --git a/README.md b/README.md index 68e1c3a..be106d5 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Git: firebase_admin_interop: git: url: https://github.com/tekartikdev/firebase-admin-interop - ref: dart2_3 + ref: dart3a ``` Run `pub get`. diff --git a/pubspec.yaml b/pubspec.yaml index 89dbfa5..cb9641d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/pulyaevskiy/firebase-admin-interop publish_to: none environment: - sdk: '>=2.18.0 <4.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: js: '>=0.6.1+1' @@ -19,7 +19,7 @@ dev_dependencies: tekartik_lints: git: url: https://github.com/tekartik/common.dart - ref: dart2_3 + ref: dart3a path: packages/lints version: '>=0.1.0' # build_runner: ^1.7.4 From 5c473bf499731600097859190364d4542d0032c8 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Sun, 22 Oct 2023 23:04:35 +0200 Subject: [PATCH 20/24] ci --- .../workflows/run_ci_downgrade_analyze.yml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/run_ci_downgrade_analyze.yml diff --git a/.github/workflows/run_ci_downgrade_analyze.yml b/.github/workflows/run_ci_downgrade_analyze.yml new file mode 100644 index 0000000..0b2957e --- /dev/null +++ b/.github/workflows/run_ci_downgrade_analyze.yml @@ -0,0 +1,28 @@ +name: Run CI Downgrade analyze +on: + push: + pull_request: + workflow_dispatch: + schedule: + - cron: '0 0 * * 0' # every sunday at midnight + +jobs: + test: + name: Test on ${{ matrix.os }} / ${{ matrix.dart }} + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: . + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + dart: [stable] + steps: + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1.4 + with: + sdk: ${{ matrix.dart }} + - run: dart --version + - run: dart pub global activate dev_test + - run: dart pub global run dev_test:run_ci --pub-downgrade --analyze --no-override --recursive From 70589d286fb5e1f15c60f96cda2579f52d6a0584 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Mon, 30 Oct 2023 00:15:38 +0100 Subject: [PATCH 21/24] fix lints --- lib/src/database.dart | 5 ++--- lib/src/firestore.dart | 11 +++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/src/database.dart b/lib/src/database.dart index fd63e88..3750321 100644 --- a/lib/src/database.dart +++ b/lib/src/database.dart @@ -325,7 +325,7 @@ class Query { /// A Reference represents a specific location in your [Database] and can be /// used for reading or writing data to that Database location. class Reference extends Query { - Reference(js.Reference nativeInstance) : super(nativeInstance); + Reference(js.Reference super.nativeInstance); @override @protected @@ -587,8 +587,7 @@ class DatabaseTransaction { class FutureReference extends Reference { final Future done; - FutureReference(js.ThenableReference nativeInstance, this.done) - : super(nativeInstance); + FutureReference(js.ThenableReference super.nativeInstance, this.done); } /// A `DataSnapshot` contains data from a [Database] location. diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index 2e2d264..f4bac22 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -146,8 +146,7 @@ class Firestore { /// inherited from [DocumentQuery]). class CollectionReference extends DocumentQuery { CollectionReference( - js.CollectionReference nativeInstance, Firestore firestore) - : super(nativeInstance, firestore); + js.CollectionReference super.nativeInstance, super.firestore); @override @protected @@ -730,7 +729,7 @@ class _FirestoreData { /// - [UpdateData] which is used to update a part of a document and follows /// different pattern for handling nested fields. class DocumentData extends _FirestoreData { - DocumentData([js.DocumentData? nativeInstance]) : super(nativeInstance); + DocumentData([js.DocumentData? super.nativeInstance]); factory DocumentData.fromMap(Map data) { final doc = DocumentData(); @@ -777,7 +776,7 @@ class DocumentData extends _FirestoreData { /// UpdateData data = new UpdateData(); /// data.setString("profile.name", "John"); class UpdateData extends _FirestoreData { - UpdateData([js.UpdateData? nativeInstance]) : super(nativeInstance); + UpdateData([js.UpdateData? super.nativeInstance]); factory UpdateData.fromMap(Map data) { final doc = UpdateData(); @@ -1281,7 +1280,7 @@ abstract class _FieldValueArray implements FieldValue { } class _FieldValueArrayUnion extends _FieldValueArray { - _FieldValueArrayUnion(List elements) : super(elements); + _FieldValueArrayUnion(super.elements); @override dynamic _jsify() { @@ -1294,7 +1293,7 @@ class _FieldValueArrayUnion extends _FieldValueArray { } class _FieldValueArrayRemove extends _FieldValueArray { - _FieldValueArrayRemove(List elements) : super(elements); + _FieldValueArrayRemove(super.elements); @override dynamic _jsify() { From cc158295716f9052ef59bb9d10fab63a953f8e0f Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Wed, 31 Jan 2024 12:04:17 +0100 Subject: [PATCH 22/24] [firestore] supports aggregate queries --- .gitignore | 7 ++- lib/firebase_admin_interop.dart | 2 +- lib/src/bindings.dart | 3 ++ lib/src/dev_utils.dart | 12 +++++ lib/src/firestore.dart | 89 ++++++++++++++++++++++++++++++++- lib/src/firestore_bindings.dart | 71 ++++++++++++++++++++++++++ package.json | 4 +- pubspec.yaml | 2 +- test/firestore_test.dart | 10 ++++ 9 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 lib/src/dev_utils.dart diff --git a/.gitignore b/.gitignore index f8c2a7f..7025de2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,10 @@ tool/build_example.sh *.json !package.json -# Intellij/WebStorm +# Intellij/WebStorm/VSCode .idea/ +*.iml +.vscode/ + +# Local +/.local/ diff --git a/lib/firebase_admin_interop.dart b/lib/firebase_admin_interop.dart index 22f74c0..7aab984 100644 --- a/lib/firebase_admin_interop.dart +++ b/lib/firebase_admin_interop.dart @@ -48,5 +48,5 @@ export 'src/app.dart'; export 'src/auth.dart'; export 'src/bindings.dart' show AppOptions, SetOptions, FirestoreSettings; export 'src/database.dart'; -export 'src/firestore.dart'; +export 'src/firestore.dart' hide AggregateFieldWithAlias; export 'src/messaging.dart'; diff --git a/lib/src/bindings.dart b/lib/src/bindings.dart index 9bd2a90..fcc7459 100644 --- a/lib/src/bindings.dart +++ b/lib/src/bindings.dart @@ -581,6 +581,9 @@ abstract class FirestoreService { // ignore: non_constant_identifier_names external FieldPathPrototype get FieldPath; + + // ignore: non_constant_identifier_names + external AggregateFields get AggregateField; } // admin.messaging ================================================================ diff --git a/lib/src/dev_utils.dart b/lib/src/dev_utils.dart new file mode 100644 index 0000000..7117e71 --- /dev/null +++ b/lib/src/dev_utils.dart @@ -0,0 +1,12 @@ +bool _devPrintEnabled = true; + +@Deprecated('Dev only') +set devPrintEnabled(bool enabled) => _devPrintEnabled = enabled; + +/// Deprecated to prevent keeping the code used. +@Deprecated('Dev only') +void devPrint(Object? object) { + if (_devPrintEnabled) { + print(object); + } +} diff --git a/lib/src/firestore.dart b/lib/src/firestore.dart index f4bac22..914f40f 100644 --- a/lib/src/firestore.dart +++ b/lib/src/firestore.dart @@ -3,9 +3,9 @@ import 'dart:async'; import 'dart:js'; +import 'dart:js_interop_unsafe'; import 'dart:typed_data'; -import 'package:js/js.dart'; import 'package:meta/meta.dart'; import 'package:node_interop/js.dart' as node; import 'package:node_interop/node.dart' as node; @@ -13,6 +13,8 @@ import 'package:node_interop/util.dart' as node; import 'package:quiver/core.dart'; import 'bindings.dart' as js; +// ignore: unused_import +import 'dev_utils.dart'; @Deprecated('This function will be hidden from public API in future versions.') js.GeoPoint createGeoPoint(num latitude, num longitude) => @@ -1107,6 +1109,41 @@ class DocumentQuery { as js.DocumentQuery, firestore); } + + /// Calculates the number of documents in the result set of the given query without actually downloading the documents. + Future count() async { + var aggregateQuery = nativeInstance!.count(); + var result = await node + .promiseToFuture(aggregateQuery.get()); + return result.data()['count'] as int; + } + + js.AggregateField toNativeAggregateField(AggregateField field) { + /*devPrint('js.admin: ${js.admin}'); + devPrint('js.Timestamp: ${js.admin!.firestore.Timestamp}'); + devPrint('js.FieldValue: ${js.admin!.firestore.FieldValue}'); + devPrint('js.AggregateField: ${js.admin!.firestore.AggregateField}');*/ + + if (field is AggregateFieldSum) { + return js.admin!.firestore.AggregateField.sum((field).field!); + } else if (field is AggregateFieldAverage) { + return js.admin!.firestore.AggregateField.average((field).field!); + } else if (field is AggregateFieldCount) { + return js.admin!.firestore.AggregateField.count(); + } else { + throw ArgumentError('Unsupported aggregate field type $field.'); + } + } + + AggregateQuery aggregate(List fields) { + var param = js.AggregateSpec(); + for (var field in fields) { + var withAlias = field as AggregateFieldWithAlias; + var nativeField = toNativeAggregateField(field); + node.setProperty(param, withAlias.alias, nativeField); + } + return AggregateQuery(nativeInstance!.aggregate(param)); + } } /// A reference to a transaction. @@ -1357,3 +1394,53 @@ class FieldValues { final FieldValue _serverTimestamp = _FieldValueServerTimestamp(); final FieldValue _delete = _FieldValueDelete(); } + +class AggregateQuery { + final js.AggregateQuery nativeInstance; + + AggregateQuery(this.nativeInstance); + + Future get() { + return node + .promiseToFuture(nativeInstance.get()) + .then((jsSnapshot) => AggregateQuerySnapshot(jsSnapshot)); + } +} + +class AggregateQuerySnapshot { + final js.AggregateQuerySnapshot nativeInstance; + + AggregateQuerySnapshot(this.nativeInstance); + + int? get count => nativeInstance.data()['count'] as int?; + + num? getAlias(String alias) => nativeInstance.data()[alias] as num?; +} + +abstract class AggregateField {} + +abstract class AggregateFieldWithAlias implements AggregateField { + final String alias; + final String? field; + + AggregateFieldWithAlias(this.alias, this.field); +} + +class AggregateFieldCount extends AggregateFieldWithAlias { + AggregateFieldCount() : super('count', null); + + @override + String toString() => 'AggregateFieldCount()'; +} + +class AggregateFieldSum extends AggregateFieldWithAlias { + AggregateFieldSum(super.alias, super.field); + @override + String toString() => 'AggregateFieldSum($alias, $field)'; +} + +class AggregateFieldAverage extends AggregateFieldWithAlias { + AggregateFieldAverage(super.alias, super.field); + @override + String toString() => 'AggregateFieldAverage($alias, $field)'; +} diff --git a/lib/src/firestore_bindings.dart b/lib/src/firestore_bindings.dart index 932c700..326b879 100644 --- a/lib/src/firestore_bindings.dart +++ b/lib/src/firestore_bindings.dart @@ -1,6 +1,8 @@ @JS() library firestore; +import 'dart:js_interop'; + import 'package:js/js.dart'; import 'package:node_interop/node.dart' as node; import 'package:node_interop/stream.dart'; @@ -609,6 +611,13 @@ abstract class DocumentQuery { /// the snapshot listener. external Function onSnapshot(void Function(QuerySnapshot snapshot) onNext, [void Function(Error error)? onError]); + + /// Returns a query that counts the documents in the result set of this query. + external AggregateQuery count(); + + // https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/query + /// Returns a query that can perform the given aggregations. + external AggregateQuery aggregate(dynamic aggregateSpecs); } /// A `QuerySnapshot` contains zero or more `QueryDocumentSnapshot` objects @@ -776,3 +785,65 @@ abstract class FieldPath { String? fieldNames4, String? fieldNames5]); } + +@JS() +@anonymous +@staticInterop +abstract class AggregateFields {} + +extension AggregateFieldsExt on AggregateFields { + external AggregateField count(); + external AggregateField sum(String fieldPath); + external AggregateField average(String fieldPath); +} + +@JS() +@anonymous +@staticInterop +class AggregateSpec { + external factory AggregateSpec(); +} + +extension AggregateSpecExt on AggregateSpec { + external void count(); + external AggregateField sum(String fieldPath); + external AggregateField average(String fieldPath); +} + +@JS() +@anonymous +@staticInterop +abstract class AggregateField {} + +extension AggregateFieldExt on AggregateField { + external String get type; +} + +@JS() +@anonymous +@staticInterop +abstract class AggregateFieldCount extends AggregateField {} + +/// Aggregation query. +@JS() +@anonymous +@staticInterop +abstract class AggregateQuery {} + +/// Aggregation query. +extension AggregateQueryExt on AggregateQuery { + /// Executes the query and returns the results as a `AggregateQuerySnapshot`. + external node.Promise get(); +} + +/// The results of executing an aggregation query. +@JS() +@anonymous +@staticInterop +abstract class AggregateQuerySnapshot {} + +/// The results of executing an aggregation query. +extension AggregateQuerySnapshotExt on AggregateQuerySnapshot { + /// Executes the query and returns the results as a `AggregateQuerySnapshot`. + external JSObject data(); +} diff --git a/package.json b/package.json index 5c2759f..3deee7e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "firebase-admin": "11.5.0", - "@google-cloud/firestore": "6.5.0" + "firebase-admin": "12.0.0", + "@google-cloud/firestore": "7.2.0" } } diff --git a/pubspec.yaml b/pubspec.yaml index cb9641d..37c4f31 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: firebase_admin_interop description: Firebase Admin SDK for Dart written as a wrapper around official Node.js SDK. -version: 2.3.1 +version: 2.4.0 homepage: https://github.com/pulyaevskiy/firebase-admin-interop publish_to: none diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 38ea097..14dfe53 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -1076,5 +1076,15 @@ void main() { expect(snapshot.documents, hasLength(2)); }); }); + group('Aggregate', () { + test('AggregateField', () async { + var collRef = + app.firestore().collection('tests/aggregate/empty_collection'); + expect(await collRef.count(), 0); + var aggregateQuery = collRef.aggregate([AggregateFieldCount()]); + var snapshot = await aggregateQuery.get(); + expect(snapshot.count, 0); + }); + }); }); } From 5628fb81ac8c0b8922650d0900f9774f37b42a8e Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Sat, 8 Jun 2024 12:25:44 +0200 Subject: [PATCH 23/24] fix: lints --- .github/workflows/run_ci.yml | 17 +++++++++++++---- .github/workflows/run_ci_downgrade_analyze.yml | 5 +++-- test/admin_test.dart | 2 ++ test/auth_test.dart | 2 ++ test/firestore_test.dart | 2 ++ 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/run_ci.yml b/.github/workflows/run_ci.yml index 2fc46a3..89b74b8 100644 --- a/.github/workflows/run_ci.yml +++ b/.github/workflows/run_ci.yml @@ -15,11 +15,20 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - dart: [3.0.5, stable, beta, dev] + include: + - os: ubuntu-latest + dart: stable + - os: ubuntu-latest + dart: beta + - os: ubuntu-latest + dart: dev + - os: windows-latest + dart: stable + - os: macos-latest + dart: stable steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v1.3 + - uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1.4 with: sdk: ${{ matrix.dart }} - run: dart --version diff --git a/.github/workflows/run_ci_downgrade_analyze.yml b/.github/workflows/run_ci_downgrade_analyze.yml index 0b2957e..0f17a01 100644 --- a/.github/workflows/run_ci_downgrade_analyze.yml +++ b/.github/workflows/run_ci_downgrade_analyze.yml @@ -16,8 +16,9 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] - dart: [stable] + include: + - os: ubuntu-latest + dart: stable steps: - uses: actions/checkout@v4 - uses: dart-lang/setup-dart@v1.4 diff --git a/test/admin_test.dart b/test/admin_test.dart index ddfc180..719e46f 100644 --- a/test/admin_test.dart +++ b/test/admin_test.dart @@ -2,6 +2,8 @@ // is governed by a BSD-style license that can be found in the LICENSE file. @TestOn('node') +library; + import 'package:firebase_admin_interop/firebase_admin_interop.dart'; import 'package:firebase_admin_interop/js.dart' as js; import 'package:node_interop/util.dart' as node; diff --git a/test/auth_test.dart b/test/auth_test.dart index ba575df..6545672 100644 --- a/test/auth_test.dart +++ b/test/auth_test.dart @@ -2,6 +2,8 @@ // is governed by a BSD-style license that can be found in the LICENSE file. @TestOn('node') +library; + import 'package:firebase_admin_interop/firebase_admin_interop.dart'; import 'package:node_interop/node.dart' as node; import 'package:test/test.dart'; diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 14dfe53..470c356 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -2,6 +2,8 @@ // is governed by a BSD-style license that can be found in the LICENSE file. @TestOn('node') +library; + import 'dart:async'; import 'package:firebase_admin_interop/firebase_admin_interop.dart'; From 2d83948a2975b555b2422f0234d55b01e403dd13 Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Mon, 10 Jun 2024 10:30:10 +0200 Subject: [PATCH 24/24] fix: test when setup is not correct --- test/admin_test.dart | 10 +- test/auth_test.dart | 77 +- test/database_test.dart | 341 +++---- test/firestore_test.dart | 1978 +++++++++++++++++++------------------- test/setup.dart | 13 +- 5 files changed, 1217 insertions(+), 1202 deletions(-) diff --git a/test/admin_test.dart b/test/admin_test.dart index 719e46f..c7e7ba4 100644 --- a/test/admin_test.dart +++ b/test/admin_test.dart @@ -4,7 +4,6 @@ @TestOn('node') library; -import 'package:firebase_admin_interop/firebase_admin_interop.dart'; import 'package:firebase_admin_interop/js.dart' as js; import 'package:node_interop/util.dart' as node; import 'package:test/test.dart'; @@ -12,13 +11,8 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { + var app = initFirebaseAppOrNull(); group('FirebaseAdmin', () { - App? app; - - setUpAll(() { - app = initFirebaseApp(); - }); - tearDownAll(() { return app!.delete(); }); @@ -33,5 +27,5 @@ void main() { as js.AccessToken; expect(accessToken.access_token, isNotEmpty); }); - }); + }, skip: app == null ? 'Firebase app not initialized' : null); } diff --git a/test/auth_test.dart b/test/auth_test.dart index 6545672..500bf0a 100644 --- a/test/auth_test.dart +++ b/test/auth_test.dart @@ -11,43 +11,44 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { - group('Auth', () { - App? app; - - setUpAll(() async { - app = initFirebaseApp(); - UserRecord? user; - try { - user = await app!.auth().getUser('testuser'); - } catch (_) {} - if (user == null) { - await app!.auth().createUser(CreateUserRequest(uid: 'testuser')); - } + var app = initFirebaseAppOrNull(); + if (app != null) { + group('Auth', () { + setUpAll(() async { + app = initFirebaseApp(); + UserRecord? user; + try { + user = await app!.auth().getUser('testuser'); + } catch (_) {} + if (user == null) { + await app!.auth().createUser(CreateUserRequest(uid: 'testuser')); + } + }); + + tearDownAll(() { + return app!.delete(); + }); + + test('createCustomToken', () async { + var token = + await app!.auth().createCustomToken('testuser', {'role': 'admin'}); + expect(token, isNotEmpty); + }); + + test('getUser', () async { + var user = await app!.auth().getUser('testuser'); + expect(user.uid, 'testuser'); + }); + + test('getUser which does not exist', () async { + var result = app!.auth().getUser('noSuchUser'); + expect(result, throwsA(const TypeMatcher())); + }); + + test('listUsers', () async { + var result = await app!.auth().listUsers(); + expect(result.users, isNotEmpty); + }); }); - - tearDownAll(() { - return app!.delete(); - }); - - test('createCustomToken', () async { - var token = - await app!.auth().createCustomToken('testuser', {'role': 'admin'}); - expect(token, isNotEmpty); - }); - - test('getUser', () async { - var user = await app!.auth().getUser('testuser'); - expect(user.uid, 'testuser'); - }); - - test('getUser which does not exist', () async { - var result = app!.auth().getUser('noSuchUser'); - expect(result, throwsA(const TypeMatcher())); - }); - - test('listUsers', () async { - var result = await app!.auth().listUsers(); - expect(result.users, isNotEmpty); - }); - }); + } } diff --git a/test/database_test.dart b/test/database_test.dart index 257b241..69bc677 100644 --- a/test/database_test.dart +++ b/test/database_test.dart @@ -12,211 +12,212 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { - var app = initFirebaseApp(); - - group('Database', () { - tearDownAll(() { - return app!.delete(); - }); - - group('Query', () { - var ref = app!.database().ref('/app/users/23'); - - setUp(() async { - await ref.setValue('Firebase'); + var app = initFirebaseAppOrNull(); + if (app != null) { + group('Database', () { + tearDownAll(() { + return app.delete(); }); - test('read value once', () async { - var snapshot = await ref.once('value'); - expect(snapshot.val(), 'Firebase'); - }); + group('Query', () { + var ref = app.database().ref('/app/users/23'); - test('querying works', () async { - var ref = app.database().ref('/app/users').endAt('Firebase'); - var value = await ref.once('value'); - var records = Map.from(value.val()!); - expect(records, hasLength(2)); - }); + setUp(() async { + await ref.setValue('Firebase'); + }); - test('on and off', () async { - var controller = StreamController(); - final sub = ref.on(EventType.value, (DataSnapshot snapshot) { - controller.add(snapshot.val() as String?); + test('read value once', () async { + var snapshot = await ref.once('value'); + expect(snapshot.val(), 'Firebase'); }); - final result = controller.stream.take(3).toList(); - // This sleep is needed for the initial value to trigger the first - // event. - await Future.delayed(Duration(seconds: 1)); + test('querying works', () async { + var ref = app.database().ref('/app/users').endAt('Firebase'); + var value = await ref.once('value'); + var records = Map.from(value.val()!); + expect(records, hasLength(2)); + }); - await ref.setValue('Second'); - await ref.setValue('Last'); - final values = await result; - expect(values, ['Firebase', 'Second', 'Last']); - sub.cancel(); - await controller.close(); + test('on and off', () async { + var controller = StreamController(); + final sub = ref.on(EventType.value, (DataSnapshot snapshot) { + controller.add(snapshot.val() as String?); + }); + final result = controller.stream.take(3).toList(); + + // This sleep is needed for the initial value to trigger the first + // event. + await Future.delayed(Duration(seconds: 1)); + + await ref.setValue('Second'); + await ref.setValue('Last'); + final values = await result; + expect(values, ['Firebase', 'Second', 'Last']); + sub.cancel(); + await controller.close(); + }); }); - }); - group('Reference', () { - var ref = app!.database().ref('/app/users/23'); - var refUpdate = app.database().ref('/tests/refUpdate'); + group('Reference', () { + var ref = app.database().ref('/app/users/23'); + var refUpdate = app.database().ref('/tests/refUpdate'); - setUp(() async { - await refUpdate.remove(); - }); + setUp(() async { + await refUpdate.remove(); + }); - test('get key', () { - expect(ref.key, '23'); - }); + test('get key', () { + expect(ref.key, '23'); + }); - test('get parent', () { - expect(ref.parent, const TypeMatcher()); - expect(ref.parent.key, 'users'); - expect(ref.parent, same(ref.parent)); - }); + test('get parent', () { + expect(ref.parent, const TypeMatcher()); + expect(ref.parent.key, 'users'); + expect(ref.parent, same(ref.parent)); + }); - test('get root', () { - expect(ref.root, const TypeMatcher()); - expect(ref.root.key, isNull); - expect(ref.root, same(ref.root)); - }); + test('get root', () { + expect(ref.root, const TypeMatcher()); + expect(ref.root.key, isNull); + expect(ref.root, same(ref.root)); + }); - test('get child()', () { - var child = ref.child('settings'); - expect(child, const TypeMatcher()); - expect(child.key, 'settings'); - }); + test('get child()', () { + var child = ref.child('settings'); + expect(child, const TypeMatcher()); + expect(child.key, 'settings'); + }); - test('push()', () { - var child = ref.child('notifications'); - var item = child.push(); - expect(item, const TypeMatcher()); - expect(item.key, isNotEmpty); - expect(item.key, isNot(child.key)); - expect(item.done, completes); - }); + test('push()', () { + var child = ref.child('notifications'); + var item = child.push(); + expect(item, const TypeMatcher()); + expect(item.key, isNotEmpty); + expect(item.key, isNot(child.key)); + expect(item.done, completes); + }); - test('push() with value', () { - var child = ref.child('notifications'); - var item = child.push('You got a message.'); - expect(item, const TypeMatcher()); - expect(item.key, isNotEmpty); - expect(item.key, isNot(child.key)); - expect(item.done, completes); - }); + test('push() with value', () { + var child = ref.child('notifications'); + var item = child.push('You got a message.'); + expect(item, const TypeMatcher()); + expect(item.key, isNotEmpty); + expect(item.key, isNot(child.key)); + expect(item.done, completes); + }); - test('remove()', () { - expect(ref.remove(), completes); - }); + test('remove()', () { + expect(ref.remove(), completes); + }); - test('setValue()', () { - expect(ref.setValue('Firebase'), completes); - }); + test('setValue()', () { + expect(ref.setValue('Firebase'), completes); + }); - test('update()', () async { - await refUpdate.update({'num': 23, 'nested/thing': '1984'}); - var snapshot = await refUpdate.once('value'); - var data = snapshot.val()!; - expect(data, hasLength(2)); - expect(data['num'], 23); - expect((data['nested'] as Map)['thing'], '1984'); - }); + test('update()', () async { + await refUpdate.update({'num': 23, 'nested/thing': '1984'}); + var snapshot = await refUpdate.once('value'); + var data = snapshot.val()!; + expect(data, hasLength(2)); + expect(data['num'], 23); + expect((data['nested'] as Map)['thing'], '1984'); + }); - test('transaction abort', () async { - var result = await refUpdate.transaction((dynamic currentData) { - return TransactionResult.abort; + test('transaction abort', () async { + var result = await refUpdate.transaction((dynamic currentData) { + return TransactionResult.abort; + }); + expect(result.committed, isFalse); }); - expect(result.committed, isFalse); - }); - test('transaction commit', () async { - await refUpdate.update({'num': 23, 'nested/thing': '1984'}); - - var tx = await refUpdate.transaction((dynamic currentData) { - // Not sure I fully understand why Firebase sends initial `null` value - // here, but this should not have anything to do with our Dart code. - if (currentData == null) { - return TransactionResult.success(currentData); - } - final data = Map.from(currentData as Map); - data['tx'] = true; - return TransactionResult.success(data); - }); - expect(tx.committed, isTrue); - var value = tx.snapshot.val() as Map; - expect(value['tx'], isTrue); + test('transaction commit', () async { + await refUpdate.update({'num': 23, 'nested/thing': '1984'}); + + var tx = await refUpdate.transaction((dynamic currentData) { + // Not sure I fully understand why Firebase sends initial `null` value + // here, but this should not have anything to do with our Dart code. + if (currentData == null) { + return TransactionResult.success(currentData); + } + final data = Map.from(currentData as Map); + data['tx'] = true; + return TransactionResult.success(data); + }); + expect(tx.committed, isTrue); + var value = tx.snapshot.val() as Map; + expect(value['tx'], isTrue); + }); }); - }); - group('DataSnapshot', () { - var ref = app!.database().ref('/app/users/3/notifications'); - late String childKey; + group('DataSnapshot', () { + var ref = app.database().ref('/app/users/3/notifications'); + late String childKey; - setUp(() async { - await ref.remove(); - var childRef = ref.push('You got a message'); - childKey = childRef.key; - await childRef.done; - await ref.push('Stuff to do').done; - }); + setUp(() async { + await ref.remove(); + var childRef = ref.push('You got a message'); + childKey = childRef.key; + await childRef.done; + await ref.push('Stuff to do').done; + }); - test('get key', () async { - var snapshot = await ref.once('value'); - expect(snapshot.key, 'notifications'); - }); + test('get key', () async { + var snapshot = await ref.once('value'); + expect(snapshot.key, 'notifications'); + }); - test('exists()', () async { - var snapshot = await ref.once('value'); - expect(snapshot.exists(), isTrue); - }); + test('exists()', () async { + var snapshot = await ref.once('value'); + expect(snapshot.exists(), isTrue); + }); - test('child()', () async { - var snapshot = await ref.once('value'); - var childSnapshot = snapshot.child(childKey); - expect(childSnapshot.key, childKey); - expect(childSnapshot.exists(), isTrue); - }); + test('child()', () async { + var snapshot = await ref.once('value'); + var childSnapshot = snapshot.child(childKey); + expect(childSnapshot.key, childKey); + expect(childSnapshot.exists(), isTrue); + }); - test('child() not exists', () async { - var snapshot = await ref.once('value'); - var childSnapshot = snapshot.child('no-such-child'); - expect(childSnapshot.key, 'no-such-child'); - expect(childSnapshot.exists(), isFalse); - }); + test('child() not exists', () async { + var snapshot = await ref.once('value'); + var childSnapshot = snapshot.child('no-such-child'); + expect(childSnapshot.key, 'no-such-child'); + expect(childSnapshot.exists(), isFalse); + }); - test('hasChild()', () async { - var snapshot = await ref.once('value'); - expect(snapshot.hasChild('no-such-child'), isFalse); - expect(snapshot.hasChild(childKey), isTrue); - }); + test('hasChild()', () async { + var snapshot = await ref.once('value'); + expect(snapshot.hasChild('no-such-child'), isFalse); + expect(snapshot.hasChild(childKey), isTrue); + }); - test('hasChildren()', () async { - var snapshot = await ref.once('value'); - expect(snapshot.hasChildren(), isTrue); - }); + test('hasChildren()', () async { + var snapshot = await ref.once('value'); + expect(snapshot.hasChildren(), isTrue); + }); - test('numChildren()', () async { - var snapshot = await ref.once('value'); - expect(snapshot.numChildren(), 2); - }); + test('numChildren()', () async { + var snapshot = await ref.once('value'); + expect(snapshot.numChildren(), 2); + }); - test('forEach', () async { - var snapshot = await ref.once('value'); - var values = []; - snapshot.forEach((child) { - values.add(child.val()!); - return false; + test('forEach', () async { + var snapshot = await ref.once('value'); + var values = []; + snapshot.forEach((child) { + values.add(child.val()!); + return false; + }); + expect(values, ['You got a message', 'Stuff to do']); }); - expect(values, ['You got a message', 'Stuff to do']); - }); - test('val()', () async { - var snapshot = await ref.once('value'); - var val = snapshot.val()!; - expect(val, isMap); - expect(val.length, 2); + test('val()', () async { + var snapshot = await ref.once('value'); + var val = snapshot.val()!; + expect(val, isMap); + expect(val.length, 2); + }); }); }); - }); + } } diff --git a/test/firestore_test.dart b/test/firestore_test.dart index 470c356..6add5c8 100644 --- a/test/firestore_test.dart +++ b/test/firestore_test.dart @@ -12,1081 +12,1091 @@ import 'package:test/test.dart'; import 'setup.dart'; void main() { - var app = initFirebaseApp()!; - app.firestore().settings(FirestoreSettings(timestampsInSnapshots: true)); + var app = initFirebaseAppOrNull(); + if (app != null) { + app.firestore().settings(FirestoreSettings(timestampsInSnapshots: true)); - group('$Firestore', () { - tearDownAll(() { - return app.delete(); - }); + group('$Firestore', () { + tearDownAll(() { + return app.delete(); + }); - group('$DocumentReference', () { - var ref = app.firestore().document('users/23'); + group('$DocumentReference', () { + var ref = app.firestore().document('users/23'); - setUp(() async { - final data = DocumentData.fromMap({ - 'name': 'Firestore', - 'profile.url': 'https://pic.com/123', + setUp(() async { + final data = DocumentData.fromMap({ + 'name': 'Firestore', + 'profile.url': 'https://pic.com/123', + }); + final nested = DocumentData.fromMap({'author': 'Unknown'}); + data.setNestedData('nested', nested); + // This completely overwrites the whole document. + await ref.setData(data); }); - final nested = DocumentData.fromMap({'author': 'Unknown'}); - data.setNestedData('nested', nested); - // This completely overwrites the whole document. - await ref.setData(data); - }); - test('read-only fields', () { - expect(ref.path, 'users/23'); - expect(ref.documentID, '23'); - }); + test('read-only fields', () { + expect(ref.path, 'users/23'); + expect(ref.documentID, '23'); + }); - test('get value once', () async { - var snapshot = await ref.get(); - expect(snapshot.createTime, const TypeMatcher()); - expect(snapshot.updateTime, const TypeMatcher()); - - var data = snapshot.data; - expect(data, const TypeMatcher()); - expect(data, hasLength(3)); - expect(data.keys, hasLength(3)); - expect(data.keys, contains('name')); - expect(data.keys, contains('profile.url')); - expect(data.keys, contains('nested')); - expect(data.getString('name'), 'Firestore'); - expect(data.getString('profile.url'), 'https://pic.com/123'); - var nested = data.getNestedData('nested')!; - expect(nested, const TypeMatcher()); - expect(nested, hasLength(1)); - expect(nested.getString('author'), 'Unknown'); - }); + test('get value once', () async { + var snapshot = await ref.get(); + expect(snapshot.createTime, const TypeMatcher()); + expect(snapshot.updateTime, const TypeMatcher()); + + var data = snapshot.data; + expect(data, const TypeMatcher()); + expect(data, hasLength(3)); + expect(data.keys, hasLength(3)); + expect(data.keys, contains('name')); + expect(data.keys, contains('profile.url')); + expect(data.keys, contains('nested')); + expect(data.getString('name'), 'Firestore'); + expect(data.getString('profile.url'), 'https://pic.com/123'); + var nested = data.getNestedData('nested')!; + expect(nested, const TypeMatcher()); + expect(nested, hasLength(1)); + expect(nested.getString('author'), 'Unknown'); + }); - test('update value', () async { - await ref.updateData(UpdateData.fromMap({ - 'nested.author': 'Isaac Asimov', - })); - var snapshot = await ref.get(); - var data = snapshot.data; - var nested = data.getNestedData('nested')!; - expect(nested.getString('author'), 'Isaac Asimov'); - }); + test('update value', () async { + await ref.updateData(UpdateData.fromMap({ + 'nested.author': 'Isaac Asimov', + })); + var snapshot = await ref.get(); + var data = snapshot.data; + var nested = data.getNestedData('nested')!; + expect(nested.getString('author'), 'Isaac Asimov'); + }); - // test setter and getters and fromMap/toMap round trip for - // all types - test('get set', () { - var data = DocumentData(); - var now = DateTime.now(); - final tsNow = Timestamp.fromDateTime(now); - data.setInt('intVal', 1); - data.setDouble('doubleVal', 1.5); - data.setBool('boolVal', true); - data.setString('stringVal', 'text'); - data.setGeoPoint('geoVal', GeoPoint(23.03, 19.84)); - data.setBlob('blob', Blob([1, 2, 3])); - data.setReference('refVal', app.firestore().document('users/23')); - data.setList('listVal', [23, 84]); - data.setTimestamp('tsVal', tsNow); - var nestedData = DocumentData(); - nestedData.setString('nestedVal', 'very nested'); - data.setNestedData('nestedData', nestedData); - data.setFieldValue('serverTimestampFieldValue', - Firestore.fieldValues.serverTimestamp()); - data.setFieldValue('deleteFieldValue', Firestore.fieldValues.delete()); - data.setList( - 'fieldValueInList', [Firestore.fieldValues.serverTimestamp()]); - - void doCheck() { - expect(data.keys.length, 13); - expect(data.getInt('intVal'), 1); - expect(data.getDouble('doubleVal'), 1.5); - expect(data.getBool('boolVal'), true); - expect(data.getString('stringVal'), 'text'); - expect(data.getGeoPoint('geoVal'), GeoPoint(23.03, 19.84)); - expect(data.getBlob('blob')!.data, [1, 2, 3]); - var documentReference = data.getReference('refVal')!; - expect(documentReference.path, 'users/23'); - expect(data.getList('listVal'), [23, 84]); - expect(data.getTimestamp('tsVal'), tsNow); - var nestedData = data.getNestedData('nestedData')!; - expect(nestedData.keys.length, 1); - expect(nestedData.getString('nestedVal'), 'very nested'); - // Check the field value (no getter here) - var map = data.toMap(); - expect(map['serverTimestampFieldValue'], + // test setter and getters and fromMap/toMap round trip for + // all types + test('get set', () { + var data = DocumentData(); + var now = DateTime.now(); + final tsNow = Timestamp.fromDateTime(now); + data.setInt('intVal', 1); + data.setDouble('doubleVal', 1.5); + data.setBool('boolVal', true); + data.setString('stringVal', 'text'); + data.setGeoPoint('geoVal', GeoPoint(23.03, 19.84)); + data.setBlob('blob', Blob([1, 2, 3])); + data.setReference('refVal', app.firestore().document('users/23')); + data.setList('listVal', [23, 84]); + data.setTimestamp('tsVal', tsNow); + var nestedData = DocumentData(); + nestedData.setString('nestedVal', 'very nested'); + data.setNestedData('nestedData', nestedData); + data.setFieldValue('serverTimestampFieldValue', Firestore.fieldValues.serverTimestamp()); - expect(map['deleteFieldValue'], Firestore.fieldValues.delete()); - expect(map['fieldValueInList'], - [Firestore.fieldValues.serverTimestamp()]); - } - - doCheck(); - // from/to map - data = DocumentData.fromMap(data.toMap()); - doCheck(); - }); + data.setFieldValue( + 'deleteFieldValue', Firestore.fieldValues.delete()); + data.setList( + 'fieldValueInList', [Firestore.fieldValues.serverTimestamp()]); + + void doCheck() { + expect(data.keys.length, 13); + expect(data.getInt('intVal'), 1); + expect(data.getDouble('doubleVal'), 1.5); + expect(data.getBool('boolVal'), true); + expect(data.getString('stringVal'), 'text'); + expect(data.getGeoPoint('geoVal'), GeoPoint(23.03, 19.84)); + expect(data.getBlob('blob')!.data, [1, 2, 3]); + var documentReference = data.getReference('refVal')!; + expect(documentReference.path, 'users/23'); + expect(data.getList('listVal'), [23, 84]); + expect(data.getTimestamp('tsVal'), tsNow); + var nestedData = data.getNestedData('nestedData')!; + expect(nestedData.keys.length, 1); + expect(nestedData.getString('nestedVal'), 'very nested'); + // Check the field value (no getter here) + var map = data.toMap(); + expect(map['serverTimestampFieldValue'], + Firestore.fieldValues.serverTimestamp()); + expect(map['deleteFieldValue'], Firestore.fieldValues.delete()); + expect(map['fieldValueInList'], + [Firestore.fieldValues.serverTimestamp()]); + } - test('data types', () async { - var date = DateTime.now(); - var ref = app.firestore().document('tests/data-types'); - var data = DocumentData.fromMap({ - 'boolVal': true, - 'stringVal': 'text', - 'intVal': 23, - 'doubleVal': 19.84, - 'geoVal': GeoPoint(23.03, 19.84), - 'blobVal': Blob([1, 2, 3]), - 'refVal': app.firestore().document('users/23'), - 'listVal': [23, 84], - 'tsVal': Timestamp.fromDateTime(date), - 'nestedVal': {'nestedKey': 'much nested'}, - 'complexVal': { - 'sub': [ - { - 'subList': [1] - } - ] - }, - 'serverTimestamp': Firestore.fieldValues.serverTimestamp() + doCheck(); + // from/to map + data = DocumentData.fromMap(data.toMap()); + doCheck(); }); - await ref.setData(data); - - var snapshot = await ref.get(); - var result = snapshot.data; - expect(result.getBool('boolVal'), isTrue); - expect(result.getString('stringVal'), 'text'); - expect(result.getInt('intVal'), 23); - expect(result.getDouble('doubleVal'), 19.84); - expect(result.getGeoPoint('geoVal'), GeoPoint(23.03, 19.84)); - expect(result.getBlob('blobVal')!.data, [1, 2, 3]); - var docRef = result.getReference('refVal')!; - expect(docRef, const TypeMatcher()); - expect(docRef.path, 'users/23'); - expect(result.getList('listVal'), [23, 84]); - expect(result.getTimestamp('tsVal'), Timestamp.fromDateTime(date)); - var nested = result.getNestedData('nestedVal')!; - expect(nested.getString('nestedKey'), 'much nested'); - expect( - result.getTimestamp('serverTimestamp'), TypeMatcher()); - var complexVal = result.getNestedData('complexVal')!; - expect(complexVal.getList('sub'), [ - { - 'subList': [1] - } - ]); - }); - test('$DocumentData.toMap', () async { - var date = DateTime.now(); - var ts = Timestamp.fromDateTime(date); - var ref = app.firestore().document('tests/data-types-toMap'); - var data = DocumentData.fromMap({ - 'boolVal': true, - 'stringVal': 'text', - 'intVal': 23, - 'doubleVal': 19.84, - 'geoVal': GeoPoint(23.03, 19.84), - 'refVal': app.firestore().document('users/23'), - 'blobVal': Blob([4, 5, 6]), - 'listVal': [23, 84], - 'tsVal': ts, - 'mapVal': { + test('data types', () async { + var date = DateTime.now(); + var ref = app.firestore().document('tests/data-types'); + var data = DocumentData.fromMap({ + 'boolVal': true, + 'stringVal': 'text', + 'intVal': 23, + 'doubleVal': 19.84, + 'geoVal': GeoPoint(23.03, 19.84), + 'blobVal': Blob([1, 2, 3]), + 'refVal': app.firestore().document('users/23'), + 'listVal': [23, 84], + 'tsVal': Timestamp.fromDateTime(date), + 'nestedVal': {'nestedKey': 'much nested'}, + 'complexVal': { + 'sub': [ + { + 'subList': [1] + } + ] + }, + 'serverTimestamp': Firestore.fieldValues.serverTimestamp() + }); + await ref.setData(data); + + var snapshot = await ref.get(); + var result = snapshot.data; + expect(result.getBool('boolVal'), isTrue); + expect(result.getString('stringVal'), 'text'); + expect(result.getInt('intVal'), 23); + expect(result.getDouble('doubleVal'), 19.84); + expect(result.getGeoPoint('geoVal'), GeoPoint(23.03, 19.84)); + expect(result.getBlob('blobVal')!.data, [1, 2, 3]); + var docRef = result.getReference('refVal')!; + expect(docRef, const TypeMatcher()); + expect(docRef.path, 'users/23'); + expect(result.getList('listVal'), [23, 84]); + expect(result.getTimestamp('tsVal'), Timestamp.fromDateTime(date)); + var nested = result.getNestedData('nestedVal')!; + expect(nested.getString('nestedKey'), 'much nested'); + expect( + result.getTimestamp('serverTimestamp'), TypeMatcher()); + var complexVal = result.getNestedData('complexVal')!; + expect(complexVal.getList('sub'), [ + { + 'subList': [1] + } + ]); + }); + + test('$DocumentData.toMap', () async { + var date = DateTime.now(); + var ts = Timestamp.fromDateTime(date); + var ref = app.firestore().document('tests/data-types-toMap'); + var data = DocumentData.fromMap({ + 'boolVal': true, + 'stringVal': 'text', + 'intVal': 23, + 'doubleVal': 19.84, + 'geoVal': GeoPoint(23.03, 19.84), + 'refVal': app.firestore().document('users/23'), + 'blobVal': Blob([4, 5, 6]), + 'listVal': [23, 84], + 'tsVal': ts, + 'mapVal': { + 'nested': [ + 1, + {'sub': 3} + ] + }, + }); + var nested = DocumentData.fromMap({'nestedVal': 'very nested'}); + data.setNestedData('nestedData', nested); + var fakeGeoPoint = DocumentData.fromMap( + {'latitude': 23.03, 'longitude': 84.19, 'toString': 'GeoPoint'}); + data.setNestedData('fakeGeoPoint', fakeGeoPoint); + var fakeRef = DocumentData.fromMap( + {'firestore': 'Nope', 'id': 'Nah', 'onSnapshot': 'Function'}); + data.setNestedData('fakeRef', fakeRef); + var fakeDate = DocumentData.fromMap( + {'toDateString': 'date', 'getTime': 'Function'}); + data.setNestedData('fakeDate', fakeDate); + await ref.setData(data); + + var snapshot = await ref.get(); + var result = snapshot.data.toMap(); + expect(result['boolVal'], isTrue); + expect(result['stringVal'], 'text'); + expect(result['intVal'], 23); + expect(result['doubleVal'], 19.84); + expect(result['geoVal'], GeoPoint(23.03, 19.84)); + expect((result['blobVal'] as Blob).data, [4, 5, 6]); + var docRef = result['refVal'] as DocumentReference; + expect(docRef, const TypeMatcher()); + expect(docRef.path, 'users/23'); + expect(result['listVal'], [23, 84]); + expect(result['tsVal'], ts); + expect(result['mapVal'], { 'nested': [ 1, {'sub': 3} ] - }, + }); + expect(result['nestedData'], {'nestedVal': 'very nested'}); + expect(result['fakeGeoPoint'], + {'latitude': 23.03, 'longitude': 84.19, 'toString': 'GeoPoint'}); + expect(result['fakeRef'], + {'firestore': 'Nope', 'id': 'Nah', 'onSnapshot': 'Function'}); + expect(result['fakeDate'], + {'toDateString': 'date', 'getTime': 'Function'}); }); - var nested = DocumentData.fromMap({'nestedVal': 'very nested'}); - data.setNestedData('nestedData', nested); - var fakeGeoPoint = DocumentData.fromMap( - {'latitude': 23.03, 'longitude': 84.19, 'toString': 'GeoPoint'}); - data.setNestedData('fakeGeoPoint', fakeGeoPoint); - var fakeRef = DocumentData.fromMap( - {'firestore': 'Nope', 'id': 'Nah', 'onSnapshot': 'Function'}); - data.setNestedData('fakeRef', fakeRef); - var fakeDate = DocumentData.fromMap( - {'toDateString': 'date', 'getTime': 'Function'}); - data.setNestedData('fakeDate', fakeDate); - await ref.setData(data); - - var snapshot = await ref.get(); - var result = snapshot.data.toMap(); - expect(result['boolVal'], isTrue); - expect(result['stringVal'], 'text'); - expect(result['intVal'], 23); - expect(result['doubleVal'], 19.84); - expect(result['geoVal'], GeoPoint(23.03, 19.84)); - expect((result['blobVal'] as Blob).data, [4, 5, 6]); - var docRef = result['refVal'] as DocumentReference; - expect(docRef, const TypeMatcher()); - expect(docRef.path, 'users/23'); - expect(result['listVal'], [23, 84]); - expect(result['tsVal'], ts); - expect(result['mapVal'], { - 'nested': [ - 1, - {'sub': 3} - ] + + test('unsupported data types', () async { + var ref = app.firestore().document('tests/unsupported'); + await ref.setData( + DocumentData()..setGeoPoint('geoVal', GeoPoint(23.03, 19.84))); + var snapshot = await ref.get(); + var data = snapshot.data; + expect(() => data.getList('geoVal'), throwsStateError); }); - expect(result['nestedData'], {'nestedVal': 'very nested'}); - expect(result['fakeGeoPoint'], - {'latitude': 23.03, 'longitude': 84.19, 'toString': 'GeoPoint'}); - expect(result['fakeRef'], - {'firestore': 'Nope', 'id': 'Nah', 'onSnapshot': 'Function'}); - expect(result['fakeDate'], - {'toDateString': 'date', 'getTime': 'Function'}); - }); - test('unsupported data types', () async { - var ref = app.firestore().document('tests/unsupported'); - await ref.setData( - DocumentData()..setGeoPoint('geoVal', GeoPoint(23.03, 19.84))); - var snapshot = await ref.get(); - var data = snapshot.data; - expect(() => data.getList('geoVal'), throwsStateError); - }); + test('lists with complex types', () async { + var ref = app.firestore().document('tests/complex-lists'); + var data = DocumentData(); + data.setList('data', [ + Timestamp.fromDateTime(DateTime.now()), + GeoPoint(1.0, 2.0), + Blob([1, 2, 3]), + ref, + ]); + await ref.setData(data); + + var snapshot = await ref.get(); + var result = snapshot.data.getList('data')!; + expect(result, hasLength(4)); + expect(result.elementAt(0), const TypeMatcher()); + expect(result.elementAt(1), const TypeMatcher()); + expect(result.elementAt(2), const TypeMatcher()); + expect(result.elementAt(3), const TypeMatcher()); + }); - test('lists with complex types', () async { - var ref = app.firestore().document('tests/complex-lists'); - var data = DocumentData(); - data.setList('data', [ - Timestamp.fromDateTime(DateTime.now()), - GeoPoint(1.0, 2.0), - Blob([1, 2, 3]), - ref, - ]); - await ref.setData(data); - - var snapshot = await ref.get(); - var result = snapshot.data.getList('data')!; - expect(result, hasLength(4)); - expect(result.elementAt(0), const TypeMatcher()); - expect(result.elementAt(1), const TypeMatcher()); - expect(result.elementAt(2), const TypeMatcher()); - expect(result.elementAt(3), const TypeMatcher()); - }); + test('delete field', () async { + var ref = app.firestore().document('tests/delete_field'); - test('delete field', () async { - var ref = app.firestore().document('tests/delete_field'); + // Make sure FieldValue class is exported by using it here + var fieldValueDelete = Firestore.fieldValues.delete(); - // Make sure FieldValue class is exported by using it here - var fieldValueDelete = Firestore.fieldValues.delete(); + // create document + var documentData = DocumentData(); + documentData.setString('some_key', 'some_value'); + documentData.setString('other_key', 'other_value'); + await ref.setData(documentData); - // create document - var documentData = DocumentData(); - documentData.setString('some_key', 'some_value'); - documentData.setString('other_key', 'other_value'); - await ref.setData(documentData); + // read it + documentData = (await ref.get()).data; + expect(documentData.getString('some_key'), 'some_value'); - // read it - documentData = (await ref.get()).data; - expect(documentData.getString('some_key'), 'some_value'); + // delete field + var updateData = UpdateData(); + updateData.setFieldValue('some_key', fieldValueDelete); + await ref.updateData(updateData); - // delete field - var updateData = UpdateData(); - updateData.setFieldValue('some_key', fieldValueDelete); - await ref.updateData(updateData); + // read again + documentData = (await ref.get()).data; + expect(documentData.getString('some_key'), isNull); + expect(documentData.has('some_key'), isFalse); + expect(documentData.getString('other_key'), 'other_value'); + }); - // read again - documentData = (await ref.get()).data; - expect(documentData.getString('some_key'), isNull); - expect(documentData.has('some_key'), isFalse); - expect(documentData.getString('other_key'), 'other_value'); - }); + test('array field value', () async { + var ref = app.firestore().document('tests/array_field_value'); + + // Make sure FieldValue class is exported by using it here + var fieldValueArrayUnion = Firestore.fieldValues.arrayUnion([1, 2]); + var fieldValueArrayUnion2 = + Firestore.fieldValues.arrayUnion([10, 11]); + var fieldValueArrayComplex = Firestore.fieldValues.arrayUnion([ + 100, + 'text', + { + 'sub': [1] + }, + GeoPoint(1.0, 2.0) + ]); + + // create document + var documentData = DocumentData(); + documentData.setFieldValue('array', fieldValueArrayUnion); + documentData.setFieldValue('array2', fieldValueArrayUnion2); + documentData.setFieldValue('complex', fieldValueArrayComplex); + + await ref.setData(documentData); + + // read it + documentData = (await ref.get()).data; + expect(documentData.getList('array'), [1, 2]); + expect(documentData.getList('array2'), [10, 11]); + expect(documentData.getList('complex'), [ + 100, + 'text', + { + 'sub': [1] + }, + GeoPoint(1.0, 2.0) + ]); + + // update and remove some data + var updateData = UpdateData(); + updateData.setFieldValue( + 'array', Firestore.fieldValues.arrayUnion([2, 3])); + updateData.setFieldValue( + 'array2', Firestore.fieldValues.arrayRemove([11, 12])); + // try to remove a complex object + updateData.setFieldValue( + 'complex', + Firestore.fieldValues.arrayRemove([ + 100, + 'text', + { + 'sub': [1] + } + ])); + await ref.updateData(updateData); + + // read again + documentData = (await ref.get()).data; + expect(documentData.getList('array'), [1, 2, 3]); + expect(documentData.getList('array2'), [10]); + expect(documentData.getList('complex'), [GeoPoint(1.0, 2.0)]); + }); - test('array field value', () async { - var ref = app.firestore().document('tests/array_field_value'); - - // Make sure FieldValue class is exported by using it here - var fieldValueArrayUnion = Firestore.fieldValues.arrayUnion([1, 2]); - var fieldValueArrayUnion2 = Firestore.fieldValues.arrayUnion([10, 11]); - var fieldValueArrayComplex = Firestore.fieldValues.arrayUnion([ - 100, - 'text', - { - 'sub': [1] - }, - GeoPoint(1.0, 2.0) - ]); - - // create document - var documentData = DocumentData(); - documentData.setFieldValue('array', fieldValueArrayUnion); - documentData.setFieldValue('array2', fieldValueArrayUnion2); - documentData.setFieldValue('complex', fieldValueArrayComplex); - - await ref.setData(documentData); - - // read it - documentData = (await ref.get()).data; - expect(documentData.getList('array'), [1, 2]); - expect(documentData.getList('array2'), [10, 11]); - expect(documentData.getList('complex'), [ - 100, - 'text', - { - 'sub': [1] - }, - GeoPoint(1.0, 2.0) - ]); - - // update and remove some data - var updateData = UpdateData(); - updateData.setFieldValue( - 'array', Firestore.fieldValues.arrayUnion([2, 3])); - updateData.setFieldValue( - 'array2', Firestore.fieldValues.arrayRemove([11, 12])); - // try to remove a complex object - updateData.setFieldValue( - 'complex', - Firestore.fieldValues.arrayRemove([ - 100, - 'text', - { - 'sub': [1] - } - ])); - await ref.updateData(updateData); - - // read again - documentData = (await ref.get()).data; - expect(documentData.getList('array'), [1, 2, 3]); - expect(documentData.getList('array2'), [10]); - expect(documentData.getList('complex'), [GeoPoint(1.0, 2.0)]); - }); + test('set options', () async { + var ref = app.firestore().document('tests/set_options'); - test('set options', () async { - var ref = app.firestore().document('tests/set_options'); + var documentData = DocumentData(); + documentData.setInt('value1', 1); + documentData.setInt('value2', 2); + await ref.setData(documentData); - var documentData = DocumentData(); - documentData.setInt('value1', 1); - documentData.setInt('value2', 2); - await ref.setData(documentData); + documentData = DocumentData(); + documentData.setInt('value2', 3); - documentData = DocumentData(); - documentData.setInt('value2', 3); + // Set with merge, value1 should remain + await ref.setData(documentData, SetOptions(merge: true)); + var readData = (await ref.get()).data; + expect(readData.toMap(), {'value1': 1, 'value2': 3}); - // Set with merge, value1 should remain - await ref.setData(documentData, SetOptions(merge: true)); - var readData = (await ref.get()).data; - expect(readData.toMap(), {'value1': 1, 'value2': 3}); + // Set without merge, value1 should be gone + documentData.setInt('value2', 4); + await ref.setData(documentData); + readData = (await ref.get()).data; + expect(readData.toMap(), {'value2': 4}); + }); - // Set without merge, value1 should be gone - documentData.setInt('value2', 4); - await ref.setData(documentData); - readData = (await ref.get()).data; - expect(readData.toMap(), {'value2': 4}); - }); + test('update sub fields', () async { + var ref = app.firestore().document('tests/set_options'); + await ref + .setData(DocumentData.fromMap({'created': 1, 'modified': 2})); + await ref.updateData(UpdateData.fromMap({ + 'modified': 22, + 'added': 3, + // update allow specifying sub field using dot + 'sub.field': 4, + // but also supports regular map + 'other_sub': {'field': 5} + })); + expect((await ref.get()).data.toMap(), { + 'created': 1, + 'modified': 22, + 'added': 3, + 'sub': {'field': 4}, + 'other_sub': {'field': 5} + }); + }); - test('update sub fields', () async { - var ref = app.firestore().document('tests/set_options'); - await ref.setData(DocumentData.fromMap({'created': 1, 'modified': 2})); - await ref.updateData(UpdateData.fromMap({ - 'modified': 22, - 'added': 3, - // update allow specifying sub field using dot - 'sub.field': 4, - // but also supports regular map - 'other_sub': {'field': 5} - })); - expect((await ref.get()).data.toMap(), { - 'created': 1, - 'modified': 22, - 'added': 3, - 'sub': {'field': 4}, - 'other_sub': {'field': 5} + test('listCollections', () async { + var doc = app.firestore().document('tests/list_collections'); + // Create an element in a sub collection to make sure the collection + // exists + await doc + .collection('sub') + .document('item') + .setData(DocumentData.fromMap({})); + var collections = await doc.listCollections(); + expect(collections.any((CollectionReference col) => col.id == 'sub'), + isTrue); }); - }); - test('listCollections', () async { - var doc = app.firestore().document('tests/list_collections'); - // Create an element in a sub collection to make sure the collection - // exists - await doc - .collection('sub') - .document('item') - .setData(DocumentData.fromMap({})); - var collections = await doc.listCollections(); - expect(collections.any((CollectionReference col) => col.id == 'sub'), - isTrue); + test('getAll', () async { + // Create two records and try to read 3 (i.e. one missing) + var doc1 = app.firestore().document('tests/get_all_1'); + var doc2 = app.firestore().document('tests/get_all_2'); + var docDummy = app.firestore().document('tests/get_all_dummy'); + await doc1.setData(DocumentData.fromMap({'value': 1})); + await doc2.setData(DocumentData.fromMap({'value': 2})); + var snapshots = await app.firestore().getAll([doc1, doc2, docDummy]); + expect(snapshots, hasLength(3)); + expect(snapshots[0].reference.path, doc1.path); + expect(snapshots[0].exists, isTrue); + expect(snapshots[0].data.toMap(), {'value': 1}); + expect(snapshots[1].reference.path, doc2.path); + expect(snapshots[1].exists, isTrue); + expect(snapshots[1].data.toMap(), {'value': 2}); + expect(snapshots[2].reference.path, docDummy.path); + expect(snapshots[2].exists, isFalse); + }); }); - test('getAll', () async { - // Create two records and try to read 3 (i.e. one missing) - var doc1 = app.firestore().document('tests/get_all_1'); - var doc2 = app.firestore().document('tests/get_all_2'); - var docDummy = app.firestore().document('tests/get_all_dummy'); - await doc1.setData(DocumentData.fromMap({'value': 1})); - await doc2.setData(DocumentData.fromMap({'value': 2})); - var snapshots = await app.firestore().getAll([doc1, doc2, docDummy]); - expect(snapshots, hasLength(3)); - expect(snapshots[0].reference.path, doc1.path); - expect(snapshots[0].exists, isTrue); - expect(snapshots[0].data.toMap(), {'value': 1}); - expect(snapshots[1].reference.path, doc2.path); - expect(snapshots[1].exists, isTrue); - expect(snapshots[1].data.toMap(), {'value': 2}); - expect(snapshots[2].reference.path, docDummy.path); - expect(snapshots[2].exists, isFalse); - }); - }); + group('$CollectionReference', () { + var ref = app.firestore().collection('users'); - group('$CollectionReference', () { - var ref = app.firestore().collection('users'); + test('id and path', () { + expect(ref.id, 'users'); + expect(ref.path, 'users'); + var col = ref.document('sub').collection('item'); + expect(col.id, 'item'); + expect(col.path, 'users/sub/item'); + }); - test('id and path', () { - expect(ref.id, 'users'); - expect(ref.path, 'users'); - var col = ref.document('sub').collection('item'); - expect(col.id, 'item'); - expect(col.path, 'users/sub/item'); - }); + test('parent of root collection', () { + final parent = ref.parent!; + expect(parent, const TypeMatcher()); + expect(parent.path, isEmpty); + expect(parent.documentID, isNull); + }); - test('parent of root collection', () { - final parent = ref.parent!; - expect(parent, const TypeMatcher()); - expect(parent.path, isEmpty); - expect(parent.documentID, isNull); - }); + test('get new document', () { + final doc = ref.document(); + expect(doc.documentID, isNotEmpty); + }); - test('get new document', () { - final doc = ref.document(); - expect(doc.documentID, isNotEmpty); - }); + test('get document from collection', () { + final doc = ref.document('23'); + expect(doc.documentID, '23'); + expect(doc.path, 'users/23'); + }); - test('get document from collection', () { - final doc = ref.document('23'); - expect(doc.documentID, '23'); - expect(doc.path, 'users/23'); - }); + test('add document to collection', () async { + final point = GeoPoint(37.7991232, -122.4485953); + final data = DocumentData.fromMap({'name': 'Added Doc'}); + data.setGeoPoint('location', point); + final doc = await ref.add(data); + expect(doc.documentID, isNotNull); + final snapshot = await doc.get(); + var result = snapshot.data; + expect(result.getString('name'), 'Added Doc'); + expect(result.getGeoPoint('location'), point); + }); - test('add document to collection', () async { - final point = GeoPoint(37.7991232, -122.4485953); - final data = DocumentData.fromMap({'name': 'Added Doc'}); - data.setGeoPoint('location', point); - final doc = await ref.add(data); - expect(doc.documentID, isNotNull); - final snapshot = await doc.get(); - var result = snapshot.data; - expect(result.getString('name'), 'Added Doc'); - expect(result.getGeoPoint('location'), point); - }); + test('set new document in collection', () async { + final doc = ref.document('abc'); + expect(doc.documentID, 'abc'); + expect(doc.path, 'users/abc'); + }); - test('set new document in collection', () async { - final doc = ref.document('abc'); - expect(doc.documentID, 'abc'); - expect(doc.path, 'users/abc'); + test('listCollections', () async { + // create a document to make sure the collection is created + await ref.document('any').setData(DocumentData.fromMap({})); + var collections = await app.firestore().listCollections(); + // Find our collection + expect( + collections.any((CollectionReference col) => col.path == ref.path), + isTrue, + ); + }); }); - test('listCollections', () async { - // create a document to make sure the collection is created - await ref.document('any').setData(DocumentData.fromMap({})); - var collections = await app.firestore().listCollections(); - // Find our collection - expect( - collections.any((CollectionReference col) => col.path == ref.path), - isTrue, - ); - }); - }); + group('$DocumentQuery', () { + setUpAll(() async { + // setup tests/query/docs content as expected in the tests + var ref = app.firestore().collection('tests/query/docs'); + var snapshot = await ref.get(); - group('$DocumentQuery', () { - setUpAll(() async { - // setup tests/query/docs content as expected in the tests - var ref = app.firestore().collection('tests/query/docs'); - var snapshot = await ref.get(); - - // Some test assume that this collection is empty or already contains - // one item. On the first ever call, create the item - if (snapshot.isEmpty) { - await ref.add(DocumentData()..setString('name', 'John Doe')); - } - }); + // Some test assume that this collection is empty or already contains + // one item. On the first ever call, create the item + if (snapshot.isEmpty) { + await ref.add(DocumentData()..setString('name', 'John Doe')); + } + }); - test('get query snapshot', () async { - var ref = app.firestore().collection('tests/query/docs'); - var snapshot = await ref.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - expect(snapshot.documentChanges, hasLength(1)); - var doc = snapshot.documents!.first; - expect(doc.data.getString('name'), 'John Doe'); - var change = snapshot.documentChanges!.first; - expect(change.type, DocumentChangeType.added); - }); + test('get query snapshot', () async { + var ref = app.firestore().collection('tests/query/docs'); + var snapshot = await ref.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + expect(snapshot.documentChanges, hasLength(1)); + var doc = snapshot.documents!.first; + expect(doc.data.getString('name'), 'John Doe'); + var change = snapshot.documentChanges!.first; + expect(change.type, DocumentChangeType.added); + }); - test('query filter with document reference', () async { - var collection = app.firestore().collection('tests/query/where-ref'); - var doc1 = collection.document(); - var doc2 = collection.document(); - await doc2.setData(DocumentData.fromMap({'name': 'doc2'})); - var data1 = DocumentData(); - data1.setReference('ref', doc2); - data1.setString('name', 'doc1'); - await doc1.setData(data1); - - var query = collection.where('ref', isEqualTo: doc2); - var snapshot = await query.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - expect(snapshot.documentChanges, hasLength(1)); - var doc = snapshot.documents!.first; - expect(doc.data.getString('name'), 'doc1'); - expect(doc.data.getReference('ref'), - const TypeMatcher()); - }); + test('query filter with document reference', () async { + var collection = app.firestore().collection('tests/query/where-ref'); + var doc1 = collection.document(); + var doc2 = collection.document(); + await doc2.setData(DocumentData.fromMap({'name': 'doc2'})); + var data1 = DocumentData(); + data1.setReference('ref', doc2); + data1.setString('name', 'doc1'); + await doc1.setData(data1); + + var query = collection.where('ref', isEqualTo: doc2); + var snapshot = await query.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + expect(snapshot.documentChanges, hasLength(1)); + var doc = snapshot.documents!.first; + expect(doc.data.getString('name'), 'doc1'); + expect(doc.data.getReference('ref'), + const TypeMatcher()); + }); - test('query filter with timestamp', () async { - var collection = app.firestore().collection('tests/query/where-ts'); - var doc1 = collection.document('doc1'); - var doc2 = collection.document('doc2'); - final now = DateTime.now(); - await doc1.setData( - DocumentData.fromMap({'createdAt': Timestamp.fromDateTime(now)}), - ); - await doc2.setData( - DocumentData.fromMap({ - 'createdAt': Timestamp.fromDateTime(now.add(Duration(seconds: 10))) - }), - ); - - var query = collection.where('createdAt', - isEqualTo: Timestamp.fromDateTime(now)); - var snapshot = await query.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents!.single; - expect(doc.documentID, doc1.documentID); - - // Test with startAfter - query = collection.orderBy('createdAt').startAfter(values: [now]); - snapshot = await query.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - doc = snapshot.documents!.single; - expect(doc.documentID, doc2.documentID); - }); + test('query filter with timestamp', () async { + var collection = app.firestore().collection('tests/query/where-ts'); + var doc1 = collection.document('doc1'); + var doc2 = collection.document('doc2'); + final now = DateTime.now(); + await doc1.setData( + DocumentData.fromMap({'createdAt': Timestamp.fromDateTime(now)}), + ); + await doc2.setData( + DocumentData.fromMap({ + 'createdAt': + Timestamp.fromDateTime(now.add(Duration(seconds: 10))) + }), + ); + + var query = collection.where('createdAt', + isEqualTo: Timestamp.fromDateTime(now)); + var snapshot = await query.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + var doc = snapshot.documents!.single; + expect(doc.documentID, doc1.documentID); + + // Test with startAfter + query = collection.orderBy('createdAt').startAfter(values: [now]); + snapshot = await query.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + doc = snapshot.documents!.single; + expect(doc.documentID, doc2.documentID); + }); - test('query filter with date', () async { - var collection = app.firestore().collection('tests/query/where-date'); - var doc1 = collection.document('doc1'); - var doc2 = collection.document('doc2'); - final now = DateTime.now(); - await doc1.setData( - DocumentData.fromMap({'createdAt': now}), - ); - await doc2.setData( - DocumentData.fromMap({'createdAt': now.add(Duration(seconds: 10))}), - ); - - var query = collection.where('createdAt', isEqualTo: now); - var snapshot = await query.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents!.single; - expect(doc.documentID, doc1.documentID); - - // Test with startAfter - query = collection.orderBy('createdAt').startAfter(values: [now]); - snapshot = await query.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - doc = snapshot.documents!.single; - expect(doc.documentID, doc2.documentID); - }); + test('query filter with date', () async { + var collection = app.firestore().collection('tests/query/where-date'); + var doc1 = collection.document('doc1'); + var doc2 = collection.document('doc2'); + final now = DateTime.now(); + await doc1.setData( + DocumentData.fromMap({'createdAt': now}), + ); + await doc2.setData( + DocumentData.fromMap({'createdAt': now.add(Duration(seconds: 10))}), + ); + + var query = collection.where('createdAt', isEqualTo: now); + var snapshot = await query.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + var doc = snapshot.documents!.single; + expect(doc.documentID, doc1.documentID); + + // Test with startAfter + query = collection.orderBy('createdAt').startAfter(values: [now]); + snapshot = await query.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + doc = snapshot.documents!.single; + expect(doc.documentID, doc2.documentID); + }); - test('query filter with geo point', () async { - var collection = app.firestore().collection('tests/query/where-geo'); - var doc1 = collection.document('doc1'); - var doc2 = collection.document('doc2'); - await doc1.setData( - DocumentData.fromMap({'location': GeoPoint(12.34, 56.78)}), - ); - await doc2.setData( - DocumentData.fromMap({'location': GeoPoint(34.12, 78.56)}), - ); - - var query = - collection.where('location', isEqualTo: GeoPoint(12.34, 56.78)); - var snapshot = await query.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents!.single; - expect(doc.documentID, doc1.documentID); - }); + test('query filter with geo point', () async { + var collection = app.firestore().collection('tests/query/where-geo'); + var doc1 = collection.document('doc1'); + var doc2 = collection.document('doc2'); + await doc1.setData( + DocumentData.fromMap({'location': GeoPoint(12.34, 56.78)}), + ); + await doc2.setData( + DocumentData.fromMap({'location': GeoPoint(34.12, 78.56)}), + ); + + var query = + collection.where('location', isEqualTo: GeoPoint(12.34, 56.78)); + var snapshot = await query.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + var doc = snapshot.documents!.single; + expect(doc.documentID, doc1.documentID); + }); - test('query filter with list object', () async { - var collection = app.firestore().collection('tests/query/where-list'); - var collRef = - collection; // testsRef.doc('nested_order_test').collection('many'); - var docRefOne = collRef.document('doc1'); - - await docRefOne.setData(DocumentData.fromMap({ - 'sub': ['b'] - })); - var docRefTwo = collRef.document('doc2'); - await docRefTwo.setData(DocumentData.fromMap({ - 'sub': ['a'] - })); - var docRefThree = collRef.document('doc3'); - await docRefThree.setData(DocumentData.fromMap({'no_sub': false})); - var docRefFour = collRef.document('doc4'); - await docRefFour.setData(DocumentData.fromMap({ - 'sub': ['a', 'b'] - })); - - List querySnapshotDocIds(QuerySnapshot querySnapshot) { - return querySnapshot.documents! - .map((snapshot) => snapshot.documentID) - .toList(); - } - - // complex object - var querySnapshot = await collRef.where('sub', isEqualTo: ['a']).get(); - expect(querySnapshotDocIds(querySnapshot), ['doc2']); - - // ordered by sub (complex object) - querySnapshot = await collRef.orderBy('sub').get(); - expect(querySnapshotDocIds(querySnapshot), ['doc2', 'doc4', 'doc1']); - }); + test('query filter with list object', () async { + var collection = app.firestore().collection('tests/query/where-list'); + var collRef = + collection; // testsRef.doc('nested_order_test').collection('many'); + var docRefOne = collRef.document('doc1'); - test('query filter with map object', () async { - var collection = app.firestore().collection('tests/query/where-map'); - var collRef = - collection; // testsRef.doc('nested_order_test').collection('many'); - var docRefOne = collRef.document('doc1'); - - await docRefOne.setData(DocumentData.fromMap({ - 'sub': {'value': 'b'} - })); - var docRefTwo = collRef.document('doc2'); - await docRefTwo.setData(DocumentData.fromMap({ - 'sub': {'value': 'a'} - })); - var docRefThree = collRef.document('doc3'); - await docRefThree.setData(DocumentData.fromMap({'no_sub': false})); - var docRefFour = collRef.document('doc4'); - await docRefFour.setData(DocumentData.fromMap({ - 'sub': {'other': 'a', 'value': 'c'} - })); - - List querySnapshotDocIds(QuerySnapshot querySnapshot) { - return querySnapshot.documents! - .map((snapshot) => snapshot.documentID) - .toList(); - } - - // complex object - var querySnapshot = - await collRef.where('sub', isEqualTo: {'value': 'a'}).get(); - expect(querySnapshotDocIds(querySnapshot), ['doc2']); - - // ordered by sub (complex object) - querySnapshot = await collRef.orderBy('sub').get(); - expect(querySnapshotDocIds(querySnapshot), ['doc4', 'doc2', 'doc1']); - }); + await docRefOne.setData(DocumentData.fromMap({ + 'sub': ['b'] + })); + var docRefTwo = collRef.document('doc2'); + await docRefTwo.setData(DocumentData.fromMap({ + 'sub': ['a'] + })); + var docRefThree = collRef.document('doc3'); + await docRefThree.setData(DocumentData.fromMap({'no_sub': false})); + var docRefFour = collRef.document('doc4'); + await docRefFour.setData(DocumentData.fromMap({ + 'sub': ['a', 'b'] + })); - test('query filter with blob', () async { - var collection = app.firestore().collection('tests/query/where-blob'); - var doc1 = collection.document('doc1'); - var doc2 = collection.document('doc2'); - await doc1.setData( - DocumentData.fromMap({ - 'athing': Blob([1, 2, 3]) - }), - ); - await doc2.setData( - DocumentData.fromMap({ - 'athing': Blob([4, 5, 6]) - }), - ); - - var query = collection.where('athing', isEqualTo: Blob([1, 2, 3])); - var snapshot = await query.get(); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - var doc = snapshot.documents!.single; - expect(doc.documentID, doc1.documentID); - }); + List querySnapshotDocIds(QuerySnapshot querySnapshot) { + return querySnapshot.documents! + .map((snapshot) => snapshot.documentID) + .toList(); + } - test('get empty query snapshot', () async { - var ref = app.firestore().collection('tests/query/none'); - var snapshot = await ref.get(); - expect(snapshot, isEmpty); - expect(snapshot.documents, isNotNull); - expect(snapshot.documents, isEmpty); - expect(snapshot.documentChanges, isNotNull); - expect(snapshot.documentChanges, isEmpty); - }); + // complex object + var querySnapshot = + await collRef.where('sub', isEqualTo: ['a']).get(); + expect(querySnapshotDocIds(querySnapshot), ['doc2']); - test('listen for query snapshot updates', () async { - var ref = app.firestore().collection('tests/query/docs'); - var completer = Completer(); - var subscription = ref.snapshots.listen((event) { - completer.complete(event); + // ordered by sub (complex object) + querySnapshot = await collRef.orderBy('sub').get(); + expect(querySnapshotDocIds(querySnapshot), ['doc2', 'doc4', 'doc1']); }); - var snapshot = await completer.future; - await subscription.cancel(); - expect(snapshot, isNotNull); - expect(snapshot, isNotEmpty); - expect(snapshot.documents, hasLength(1)); - }); - test('select', () async { - var collRef = app.firestore().collection('tests/query/select'); + test('query filter with map object', () async { + var collection = app.firestore().collection('tests/query/where-map'); + var collRef = + collection; // testsRef.doc('nested_order_test').collection('many'); + var docRefOne = collRef.document('doc1'); - // set the content - var docRef = collRef.document('one'); - await docRef.setData(DocumentData() - ..setInt('field1', 1) - ..setInt('field2', 2)); + await docRefOne.setData(DocumentData.fromMap({ + 'sub': {'value': 'b'} + })); + var docRefTwo = collRef.document('doc2'); + await docRefTwo.setData(DocumentData.fromMap({ + 'sub': {'value': 'a'} + })); + var docRefThree = collRef.document('doc3'); + await docRefThree.setData(DocumentData.fromMap({'no_sub': false})); + var docRefFour = collRef.document('doc4'); + await docRefFour.setData(DocumentData.fromMap({ + 'sub': {'other': 'a', 'value': 'c'} + })); - var querySnapshot = await collRef.select(['field2']).get(); - var documentData = querySnapshot.documents!.first.data; - expect(documentData.has('field2'), isTrue); - expect(documentData.has('field1'), isFalse); + List querySnapshotDocIds(QuerySnapshot querySnapshot) { + return querySnapshot.documents! + .map((snapshot) => snapshot.documentID) + .toList(); + } - querySnapshot = await collRef.select(['field2']).get(); - documentData = querySnapshot.documents!.first.data; - expect(documentData.has('field2'), isTrue); - expect(documentData.has('field1'), isFalse); - }); + // complex object + var querySnapshot = + await collRef.where('sub', isEqualTo: {'value': 'a'}).get(); + expect(querySnapshotDocIds(querySnapshot), ['doc2']); - test('order and limits', () async { - var collRef = - app.firestore().collection('tests/query/order_and_limits'); - - // Create or update the content - var docRefOne = collRef.document('one'); - await docRefOne.setData(DocumentData()..setInt('value', 1)); - var docRefTwo = collRef.document('two'); - await docRefTwo.setData(DocumentData()..setInt('value', 2)); - - List? list; - - // limit - var querySnapshot = await collRef.limit(1).get(); - list = querySnapshot.documents; - expect(list!.length, 1); - - // offset - querySnapshot = await collRef.orderBy('value').offset(1).get(); - list = querySnapshot.documents; - expect(list!.length, 1); - - // order by - querySnapshot = await collRef.orderBy('value').get(); - list = querySnapshot.documents; - expect(list!.length, 2); - expect(list.first.reference.documentID, 'one'); - - // desc - querySnapshot = await collRef.orderBy('value', descending: true).get(); - list = querySnapshot.documents; - expect(list!.length, 2); - expect(list.first.reference.documentID, 'two'); - - // start at - querySnapshot = - await collRef.orderBy('value').startAt(values: [2]).get(); - list = querySnapshot.documents; - expect(list!.length, 1); - expect(list.first.reference.documentID, 'two'); - - // start after - querySnapshot = - await collRef.orderBy('value').startAfter(values: [1]).get(); - list = querySnapshot.documents; - expect(list!.length, 1); - expect(list.first.reference.documentID, 'two'); - - // end at - querySnapshot = await collRef.orderBy('value').endAt(values: [1]).get(); - list = querySnapshot.documents; - expect(list!.length, 1); - expect(list.first.reference.documentID, 'one'); - - // end before - querySnapshot = - await collRef.orderBy('value').endBefore(values: [2]).get(); - list = querySnapshot.documents; - expect(list!.length, 1); - expect(list.first.reference.documentID, 'one'); - - // start after using snapshot - querySnapshot = await collRef - .orderBy('value') - .startAfter(snapshot: list.first) - .get(); - list = querySnapshot.documents; - expect(list!.length, 1); - expect(list.first.reference.documentID, 'two'); - - // where - querySnapshot = await collRef.where('value', isGreaterThan: 1).get(); - list = querySnapshot.documents; - expect(list!.length, 1); - expect(list.first.reference.documentID, 'two'); - }); + // ordered by sub (complex object) + querySnapshot = await collRef.orderBy('sub').get(); + expect(querySnapshotDocIds(querySnapshot), ['doc4', 'doc2', 'doc1']); + }); - test('snapshots changes', () async { - var collRef = - app.firestore().collection('tests/query/snapshots_changes'); - var docRef = collRef.document('item'); + test('query filter with blob', () async { + var collection = app.firestore().collection('tests/query/where-blob'); + var doc1 = collection.document('doc1'); + var doc2 = collection.document('doc2'); + await doc1.setData( + DocumentData.fromMap({ + 'athing': Blob([1, 2, 3]) + }), + ); + await doc2.setData( + DocumentData.fromMap({ + 'athing': Blob([4, 5, 6]) + }), + ); + + var query = collection.where('athing', isEqualTo: Blob([1, 2, 3])); + var snapshot = await query.get(); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + var doc = snapshot.documents!.single; + expect(doc.documentID, doc1.documentID); + }); - // delete item - await docRef.delete(); + test('get empty query snapshot', () async { + var ref = app.firestore().collection('tests/query/none'); + var snapshot = await ref.get(); + expect(snapshot, isEmpty); + expect(snapshot.documents, isNotNull); + expect(snapshot.documents, isEmpty); + expect(snapshot.documentChanges, isNotNull); + expect(snapshot.documentChanges, isEmpty); + }); - // We'll them listen for changes while creating/modifying/deleting - // the same item - final stepCount = 4; + test('listen for query snapshot updates', () async { + var ref = app.firestore().collection('tests/query/docs'); + var completer = Completer(); + var subscription = ref.snapshots.listen((event) { + completer.complete(event); + }); + var snapshot = await completer.future; + await subscription.cancel(); + expect(snapshot, isNotNull); + expect(snapshot, isNotEmpty); + expect(snapshot.documents, hasLength(1)); + }); - // Create a completer for each step - var completers = List>>.generate( - stepCount, (_) => Completer>()); + test('select', () async { + var collRef = app.firestore().collection('tests/query/select'); - var stepIndex = 0; - var subscription = - collRef.snapshots.listen((QuerySnapshot querySnapshot) { - // complete each step when receiving data - if (stepIndex < stepCount) { - completers[stepIndex++].complete(querySnapshot.documentChanges); - } - }); + // set the content + var docRef = collRef.document('one'); + await docRef.setData(DocumentData() + ..setInt('field1', 1) + ..setInt('field2', 2)); - var index = 0; - List documentChanges; - - // wait for receiving first data, ignore result - await completers[index++].future; - - // create it - await docRef.setData(DocumentData()); - // wait for receiving change data - documentChanges = await completers[index++].future; - // expect creation - expect(documentChanges.length, 1); - expect(documentChanges.first.type, DocumentChangeType.added); - - // modify it - await docRef.setData(DocumentData()..setInt('value', 1)); - // wait for receiving change data - documentChanges = await completers[index++].future; - // expect a modified item - expect(documentChanges.length, 1); - expect(documentChanges.first.type, DocumentChangeType.modified); - - // delete it - await docRef.delete(); - // wait for receiving change data - documentChanges = await completers[index++].future; - // expect deletion - expect(documentChanges.length, 1); - expect(documentChanges.first.type, DocumentChangeType.removed); - - await subscription.cancel(); - }); - }); + var querySnapshot = await collRef.select(['field2']).get(); + var documentData = querySnapshot.documents!.first.data; + expect(documentData.has('field2'), isTrue); + expect(documentData.has('field1'), isFalse); - group('$Transaction', () { - test('runTransaction', () async { - var collRef = app.firestore().collection('tests/transaction/simple'); - // this one will be created - var doc1Ref = collRef.document('item1'); - // this one will be updated - var doc2Ref = collRef.document('item2'); - // this one will be set - var doc3Ref = collRef.document('item3'); - // this one will be deleted - var doc4Ref = collRef.document('item4'); - - var doc4Value = 4; - await doc1Ref.delete(); - await doc2Ref.setData(DocumentData()..setInt('value', 2)); - await doc3Ref.setData(DocumentData()..setInt('value', 3)); - await doc4Ref.setData(DocumentData()..setInt('value', doc4Value)); - - await Future.delayed( - Duration(seconds: 1)); // to avoid too much contention errors - - var list = await app.firestore().runTransaction((Transaction tx) async { - var query = await tx.getQuery( - collRef.orderBy('value').where('value', isGreaterThan: 1)); - var list = query.documents; - - var doc4 = (await tx.get(doc4Ref)).data.getInt('value')!; - tx.create(doc1Ref, DocumentData()..setInt('value', 1 + doc4)); - tx.update(doc2Ref, UpdateData()..setInt('other.value', 22 + doc4)); - tx.set(doc3Ref, DocumentData()..setInt('value', 3 + doc4)); - tx.set(doc3Ref, DocumentData()..setInt('other.value', 33 + doc4), - merge: true); - tx.delete(doc4Ref); - - return list!; + querySnapshot = await collRef.select(['field2']).get(); + documentData = querySnapshot.documents!.first.data; + expect(documentData.has('field2'), isTrue); + expect(documentData.has('field1'), isFalse); }); - expect(list.length, 3); - expect(list[0].documentID, 'item2'); - expect(list[1].documentID, 'item3'); - expect(list[2].documentID, 'item4'); - - expect((await doc1Ref.get()).data.toMap(), {'value': 1 + doc4Value}); - expect((await doc2Ref.get()).data.toMap(), { - 'value': 2, - 'other': { - 'value': 22 + doc4Value, - }, + test('order and limits', () async { + var collRef = + app.firestore().collection('tests/query/order_and_limits'); + + // Create or update the content + var docRefOne = collRef.document('one'); + await docRefOne.setData(DocumentData()..setInt('value', 1)); + var docRefTwo = collRef.document('two'); + await docRefTwo.setData(DocumentData()..setInt('value', 2)); + + List? list; + + // limit + var querySnapshot = await collRef.limit(1).get(); + list = querySnapshot.documents; + expect(list!.length, 1); + + // offset + querySnapshot = await collRef.orderBy('value').offset(1).get(); + list = querySnapshot.documents; + expect(list!.length, 1); + + // order by + querySnapshot = await collRef.orderBy('value').get(); + list = querySnapshot.documents; + expect(list!.length, 2); + expect(list.first.reference.documentID, 'one'); + + // desc + querySnapshot = + await collRef.orderBy('value', descending: true).get(); + list = querySnapshot.documents; + expect(list!.length, 2); + expect(list.first.reference.documentID, 'two'); + + // start at + querySnapshot = + await collRef.orderBy('value').startAt(values: [2]).get(); + list = querySnapshot.documents; + expect(list!.length, 1); + expect(list.first.reference.documentID, 'two'); + + // start after + querySnapshot = + await collRef.orderBy('value').startAfter(values: [1]).get(); + list = querySnapshot.documents; + expect(list!.length, 1); + expect(list.first.reference.documentID, 'two'); + + // end at + querySnapshot = + await collRef.orderBy('value').endAt(values: [1]).get(); + list = querySnapshot.documents; + expect(list!.length, 1); + expect(list.first.reference.documentID, 'one'); + + // end before + querySnapshot = + await collRef.orderBy('value').endBefore(values: [2]).get(); + list = querySnapshot.documents; + expect(list!.length, 1); + expect(list.first.reference.documentID, 'one'); + + // start after using snapshot + querySnapshot = await collRef + .orderBy('value') + .startAfter(snapshot: list.first) + .get(); + list = querySnapshot.documents; + expect(list!.length, 1); + expect(list.first.reference.documentID, 'two'); + + // where + querySnapshot = await collRef.where('value', isGreaterThan: 1).get(); + list = querySnapshot.documents; + expect(list!.length, 1); + expect(list.first.reference.documentID, 'two'); }); - expect((await doc3Ref.get()).data.toMap(), { - 'value': 3 + doc4Value, - 'other.value': 33 + doc4Value, + + test('snapshots changes', () async { + var collRef = + app.firestore().collection('tests/query/snapshots_changes'); + var docRef = collRef.document('item'); + + // delete item + await docRef.delete(); + + // We'll them listen for changes while creating/modifying/deleting + // the same item + final stepCount = 4; + + // Create a completer for each step + var completers = List>>.generate( + stepCount, (_) => Completer>()); + + var stepIndex = 0; + var subscription = + collRef.snapshots.listen((QuerySnapshot querySnapshot) { + // complete each step when receiving data + if (stepIndex < stepCount) { + completers[stepIndex++].complete(querySnapshot.documentChanges); + } + }); + + var index = 0; + List documentChanges; + + // wait for receiving first data, ignore result + await completers[index++].future; + + // create it + await docRef.setData(DocumentData()); + // wait for receiving change data + documentChanges = await completers[index++].future; + // expect creation + expect(documentChanges.length, 1); + expect(documentChanges.first.type, DocumentChangeType.added); + + // modify it + await docRef.setData(DocumentData()..setInt('value', 1)); + // wait for receiving change data + documentChanges = await completers[index++].future; + // expect a modified item + expect(documentChanges.length, 1); + expect(documentChanges.first.type, DocumentChangeType.modified); + + // delete it + await docRef.delete(); + // wait for receiving change data + documentChanges = await completers[index++].future; + // expect deletion + expect(documentChanges.length, 1); + expect(documentChanges.first.type, DocumentChangeType.removed); + + await subscription.cancel(); }); - expect((await doc4Ref.get()).exists, isFalse); }); - test('runTransaction, test Precondition lastUpdateTime', () async { - var collRef = - app.firestore().collection('tests/transaction/precondition'); - // this one will be updated - var doc1Ref = collRef.document('item1'); - // this one will be deleted - var doc2Ref = collRef.document('item2'); - - await doc1Ref.setData(DocumentData()..setInt('value', 1)); - await doc2Ref.setData(DocumentData()..setInt('value', 2)); - var doc1UpdateTime1 = (await doc1Ref.get()).updateTime; - var doc2UpdateTime1 = (await doc2Ref.get()).updateTime; - - await Future.delayed( - Duration(seconds: 1)); // to avoid too much contention errors - - await doc1Ref.setData(DocumentData()..setInt('value', 10)); - await doc2Ref.setData(DocumentData()..setInt('value', 20)); - var doc1UpdateTime2 = (await doc1Ref.get()).updateTime; - var doc2UpdateTime2 = (await doc2Ref.get()).updateTime; - - await Future.delayed( - Duration(seconds: 1)); // to avoid too much contention errors - - var result = - app.firestore().runTransaction((Transaction tx) async { - var doc2 = (await tx.get(doc2Ref)).data; - tx.update( - doc1Ref, UpdateData()..setInt('value', doc2.getInt('value')), - lastUpdateTime: doc1UpdateTime1); - tx.delete(doc2Ref, lastUpdateTime: doc2UpdateTime1); - return null; + group('$Transaction', () { + test('runTransaction', () async { + var collRef = app.firestore().collection('tests/transaction/simple'); + // this one will be created + var doc1Ref = collRef.document('item1'); + // this one will be updated + var doc2Ref = collRef.document('item2'); + // this one will be set + var doc3Ref = collRef.document('item3'); + // this one will be deleted + var doc4Ref = collRef.document('item4'); + + var doc4Value = 4; + await doc1Ref.delete(); + await doc2Ref.setData(DocumentData()..setInt('value', 2)); + await doc3Ref.setData(DocumentData()..setInt('value', 3)); + await doc4Ref.setData(DocumentData()..setInt('value', doc4Value)); + + await Future.delayed( + Duration(seconds: 1)); // to avoid too much contention errors + + var list = + await app.firestore().runTransaction((Transaction tx) async { + var query = await tx.getQuery( + collRef.orderBy('value').where('value', isGreaterThan: 1)); + var list = query.documents; + + var doc4 = (await tx.get(doc4Ref)).data.getInt('value')!; + tx.create(doc1Ref, DocumentData()..setInt('value', 1 + doc4)); + tx.update(doc2Ref, UpdateData()..setInt('other.value', 22 + doc4)); + tx.set(doc3Ref, DocumentData()..setInt('value', 3 + doc4)); + tx.set(doc3Ref, DocumentData()..setInt('other.value', 33 + doc4), + merge: true); + tx.delete(doc4Ref); + + return list!; + }); + + expect(list.length, 3); + expect(list[0].documentID, 'item2'); + expect(list[1].documentID, 'item3'); + expect(list[2].documentID, 'item4'); + + expect((await doc1Ref.get()).data.toMap(), {'value': 1 + doc4Value}); + expect((await doc2Ref.get()).data.toMap(), { + 'value': 2, + 'other': { + 'value': 22 + doc4Value, + }, + }); + expect((await doc3Ref.get()).data.toMap(), { + 'value': 3 + doc4Value, + 'other.value': 33 + doc4Value, + }); + expect((await doc4Ref.get()).exists, isFalse); }); - var error = await result.catchError((Object error) => error); - expect(error.toString(), - contains('does not match the required base version')); + test('runTransaction, test Precondition lastUpdateTime', () async { + var collRef = + app.firestore().collection('tests/transaction/precondition'); + // this one will be updated + var doc1Ref = collRef.document('item1'); + // this one will be deleted + var doc2Ref = collRef.document('item2'); + + await doc1Ref.setData(DocumentData()..setInt('value', 1)); + await doc2Ref.setData(DocumentData()..setInt('value', 2)); + var doc1UpdateTime1 = (await doc1Ref.get()).updateTime; + var doc2UpdateTime1 = (await doc2Ref.get()).updateTime; + + await Future.delayed( + Duration(seconds: 1)); // to avoid too much contention errors + + await doc1Ref.setData(DocumentData()..setInt('value', 10)); + await doc2Ref.setData(DocumentData()..setInt('value', 20)); + var doc1UpdateTime2 = (await doc1Ref.get()).updateTime; + var doc2UpdateTime2 = (await doc2Ref.get()).updateTime; + + await Future.delayed( + Duration(seconds: 1)); // to avoid too much contention errors + + var result = + app.firestore().runTransaction((Transaction tx) async { + var doc2 = (await tx.get(doc2Ref)).data; + tx.update( + doc1Ref, UpdateData()..setInt('value', doc2.getInt('value')), + lastUpdateTime: doc1UpdateTime1); + tx.delete(doc2Ref, lastUpdateTime: doc2UpdateTime1); + return null; + }); - expect((await doc1Ref.get()).data.toMap(), {'value': 10}); - expect((await doc2Ref.get()).data.toMap(), {'value': 20}); + var error = await result.catchError((Object error) => error); + expect(error.toString(), + contains('does not match the required base version')); - await app.firestore().runTransaction((Transaction tx) async { - var doc2 = (await tx.get(doc2Ref)).data.getInt('value'); - tx.update(doc1Ref, UpdateData()..setInt('value', doc2), - lastUpdateTime: doc1UpdateTime2); - tx.delete(doc2Ref, lastUpdateTime: doc2UpdateTime2); - }); - expect((await doc1Ref.get()).data.toMap(), {'value': 20}); - expect((await doc2Ref.get()).exists, isFalse); - }); + expect((await doc1Ref.get()).data.toMap(), {'value': 10}); + expect((await doc2Ref.get()).data.toMap(), {'value': 20}); - test('runTransaction, increment counter 5 times in async', () async { - /// This test originally had 10 updates which was causing following - /// error: - /// - /// ABORTED: Too much contention on these documents. Please try again. - var collRef = app.firestore().collection('tests/transaction/async'); - var doc1Ref = collRef.document('counter'); - await doc1Ref.setData(DocumentData()..setInt('value', 1)); - - var futures = >[]; - var errors = []; - var complete = []; - - var futuresCount = 5; - for (var i = 0; i < futuresCount; i++) { - var transaction = - app.firestore().runTransaction((Transaction tx) async { - var doc1 = await tx.get(doc1Ref); - var val = doc1.data.getInt('value')! + 1; - tx.set(doc1Ref, DocumentData()..setInt('value', val)); - return val; + await app.firestore().runTransaction((Transaction tx) async { + var doc2 = (await tx.get(doc2Ref)).data.getInt('value'); + tx.update(doc1Ref, UpdateData()..setInt('value', doc2), + lastUpdateTime: doc1UpdateTime2); + tx.delete(doc2Ref, lastUpdateTime: doc2UpdateTime2); }); - futures.add(transaction.then((int val) { - complete.add(val); - return val; - }, onError: (Object e) { - errors.add(e); - })); - } + expect((await doc1Ref.get()).data.toMap(), {'value': 20}); + expect((await doc2Ref.get()).exists, isFalse); + }); - await Future.wait(futures); - expect(errors.length + complete.length, futuresCount); + test('runTransaction, increment counter 5 times in async', () async { + /// This test originally had 10 updates which was causing following + /// error: + /// + /// ABORTED: Too much contention on these documents. Please try again. + var collRef = app.firestore().collection('tests/transaction/async'); + var doc1Ref = collRef.document('counter'); + await doc1Ref.setData(DocumentData()..setInt('value', 1)); + + var futures = >[]; + var errors = []; + var complete = []; + + var futuresCount = 5; + for (var i = 0; i < futuresCount; i++) { + var transaction = + app.firestore().runTransaction((Transaction tx) async { + var doc1 = await tx.get(doc1Ref); + var val = doc1.data.getInt('value')! + 1; + tx.set(doc1Ref, DocumentData()..setInt('value', val)); + return val; + }); + futures.add(transaction.then((int val) { + complete.add(val); + return val; + }, onError: (Object e) { + errors.add(e); + })); + } + + await Future.wait(futures); + expect(errors.length + complete.length, futuresCount); - var value = (await doc1Ref.get()).data.getInt('value'); - expect(errors, isEmpty, reason: errors.toString()); - expect(complete, hasLength(futuresCount), - reason: '$complete of length ${complete.length}'); - expect(value, 6); + var value = (await doc1Ref.get()).data.getInt('value'); + expect(errors, isEmpty, reason: errors.toString()); + expect(complete, hasLength(futuresCount), + reason: '$complete of length ${complete.length}'); + expect(value, 6); + }); }); - }); - group('$WriteBatch', () { - test('batch', () async { - var collRef = app.firestore().collection('tests/batch/simple'); - // this one will be created - var doc1Ref = collRef.document('item1'); - // this one will be updated - var doc2Ref = collRef.document('item2'); - // this one will be set - var doc3Ref = collRef.document('item3'); - // this one will be deleted - var doc4Ref = collRef.document('item4'); - - await doc1Ref.delete(); - await doc2Ref.setData(DocumentData()..setInt('value', 2)); - await doc4Ref.setData(DocumentData()..setInt('value', 4)); - - var batch = app.firestore().batch(); - batch.setData(doc1Ref, DocumentData()..setInt('value', 1)); - batch.updateData(doc2Ref, UpdateData()..setInt('other.value', 22)); - batch.setData(doc3Ref, DocumentData()..setInt('value', 3)); - batch.delete(doc4Ref); - await batch.commit(); - - expect((await doc1Ref.get()).data.toMap(), {'value': 1}); - expect((await doc2Ref.get()).data.toMap(), { - 'value': 2, - 'other': {'value': 22} + group('$WriteBatch', () { + test('batch', () async { + var collRef = app.firestore().collection('tests/batch/simple'); + // this one will be created + var doc1Ref = collRef.document('item1'); + // this one will be updated + var doc2Ref = collRef.document('item2'); + // this one will be set + var doc3Ref = collRef.document('item3'); + // this one will be deleted + var doc4Ref = collRef.document('item4'); + + await doc1Ref.delete(); + await doc2Ref.setData(DocumentData()..setInt('value', 2)); + await doc4Ref.setData(DocumentData()..setInt('value', 4)); + + var batch = app.firestore().batch(); + batch.setData(doc1Ref, DocumentData()..setInt('value', 1)); + batch.updateData(doc2Ref, UpdateData()..setInt('other.value', 22)); + batch.setData(doc3Ref, DocumentData()..setInt('value', 3)); + batch.delete(doc4Ref); + await batch.commit(); + + expect((await doc1Ref.get()).data.toMap(), {'value': 1}); + expect((await doc2Ref.get()).data.toMap(), { + 'value': 2, + 'other': {'value': 22} + }); + expect((await doc3Ref.get()).data.toMap(), {'value': 3}); + expect((await doc4Ref.get()).exists, isFalse); }); - expect((await doc3Ref.get()).data.toMap(), {'value': 3}); - expect((await doc4Ref.get()).exists, isFalse); }); - }); - group('Collection Groups', () { - test('fetch documents in a collection group', () async { - var doc1 = app.firestore().document('tests/one/group_a/doc1'); - var doc2 = app.firestore().document('tests/two/group_a/doc2'); - await doc1.setData(DocumentData.fromMap({'value': 1})); - await doc2.setData(DocumentData.fromMap({'value': 2})); + group('Collection Groups', () { + test('fetch documents in a collection group', () async { + var doc1 = app.firestore().document('tests/one/group_a/doc1'); + var doc2 = app.firestore().document('tests/two/group_a/doc2'); + await doc1.setData(DocumentData.fromMap({'value': 1})); + await doc2.setData(DocumentData.fromMap({'value': 2})); - var snapshot = await app.firestore().collectionGroup('group_a').get(); - expect(snapshot.documents, hasLength(2)); + var snapshot = await app.firestore().collectionGroup('group_a').get(); + expect(snapshot.documents, hasLength(2)); + }); }); - }); - group('Aggregate', () { - test('AggregateField', () async { - var collRef = - app.firestore().collection('tests/aggregate/empty_collection'); - expect(await collRef.count(), 0); - var aggregateQuery = collRef.aggregate([AggregateFieldCount()]); - var snapshot = await aggregateQuery.get(); - expect(snapshot.count, 0); + group('Aggregate', () { + test('AggregateField', () async { + var collRef = + app.firestore().collection('tests/aggregate/empty_collection'); + expect(await collRef.count(), 0); + var aggregateQuery = collRef.aggregate([AggregateFieldCount()]); + var snapshot = await aggregateQuery.get(); + expect(snapshot.count, 0); + }); }); }); - }); + } } diff --git a/test/setup.dart b/test/setup.dart index 75f7104..71b7749 100644 --- a/test/setup.dart +++ b/test/setup.dart @@ -9,7 +9,16 @@ import 'package:node_interop/util.dart' as node; final Map env = node.dartify(node.process.env); -App? initFirebaseApp() { +App? initFirebaseAppOrNull() { + try { + return initFirebaseApp(); + } catch (e) { + print('Failed to initialize Firebase app: $e'); + return null; + } +} + +App initFirebaseApp() { print(env); if (!env.containsKey('FIREBASE_CONFIG') || !env.containsKey('FIREBASE_SERVICE_ACCOUNT_JSON')) { @@ -28,5 +37,5 @@ App? initFirebaseApp() { final config = jsonDecode(env['FIREBASE_CONFIG'] as String) as Map; final databaseUrl = config['databaseURL'] as String?; return FirebaseAdmin.instance - .initializeApp(AppOptions(credential: cert, databaseURL: databaseUrl)); + .initializeApp(AppOptions(credential: cert, databaseURL: databaseUrl))!; }