From 6f2589645e102087d2b82edfc2a469f913c099cb Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 8 May 2019 19:30:39 +0545 Subject: [PATCH 01/25] Migrated linkWithCredential function to user object --- .../firebase_auth/lib/src/firebase_auth.dart | 33 ------------------- .../firebase_auth/lib/src/firebase_user.dart | 33 +++++++++++++++++++ .../test/firebase_auth_test.dart | 27 ++++++++++----- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/packages/firebase_auth/lib/src/firebase_auth.dart b/packages/firebase_auth/lib/src/firebase_auth.dart index 4b2949f280d0..403eba41499a 100644 --- a/packages/firebase_auth/lib/src/firebase_auth.dart +++ b/packages/firebase_auth/lib/src/firebase_auth.dart @@ -417,39 +417,6 @@ class FirebaseAuth { return currentUser; } - /// Associates a user account from a third-party identity provider with this - /// user and returns additional identity provider data. - /// - /// This allows the user to sign in to this account in the future with - /// the given account. - /// - /// Errors: - /// • `ERROR_WEAK_PASSWORD` - If the password is not strong enough. - /// • `ERROR_INVALID_CREDENTIAL` - If the credential is malformed or has expired. - /// • `ERROR_CREDENTIAL_ALREADY_IN_USE` - If the account is already in use by a different account. - /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console) - /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve. - /// • `ERROR_PROVIDER_ALREADY_LINKED` - If the current user already has an account of this type linked. - /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that this type of account is not enabled. - /// • `ERROR_INVALID_ACTION_CODE` - If the action code in the link is malformed, expired, or has already been used. - /// This can only occur when using [EmailAuthProvider.getCredentialWithLink] to obtain the credential. - Future linkWithCredential(AuthCredential credential) async { - assert(credential != null); - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - final Map data = await channel.invokeMethod( - 'linkWithCredential', - { - 'app': app.name, - 'provider': credential._provider, - 'data': credential._data, - }, - ); - final FirebaseUser currentUser = FirebaseUser._(data, app); - return currentUser; - } - /// Sets the user-facing language code for auth operations that can be /// internationalized, such as [sendEmailVerification]. This language /// code should follow the conventions defined by the IETF in BCP47. diff --git a/packages/firebase_auth/lib/src/firebase_user.dart b/packages/firebase_auth/lib/src/firebase_user.dart index 15fa458f139f..4243ac30ed83 100644 --- a/packages/firebase_auth/lib/src/firebase_user.dart +++ b/packages/firebase_auth/lib/src/firebase_user.dart @@ -44,6 +44,39 @@ class FirebaseUser extends UserInfo { }); } + /// Associates a user account from a third-party identity provider with this + /// user and returns additional identity provider data. + /// + /// This allows the user to sign in to this account in the future with + /// the given account. + /// + /// Errors: + /// • `ERROR_WEAK_PASSWORD` - If the password is not strong enough. + /// • `ERROR_INVALID_CREDENTIAL` - If the credential is malformed or has expired. + /// • `ERROR_CREDENTIAL_ALREADY_IN_USE` - If the account is already in use by a different account. + /// • `ERROR_USER_DISABLED` - If the user has been disabled (for example, in the Firebase console) + /// • `ERROR_REQUIRES_RECENT_LOGIN` - If the user's last sign-in time does not meet the security threshold. Use reauthenticate methods to resolve. + /// • `ERROR_PROVIDER_ALREADY_LINKED` - If the current user already has an account of this type linked. + /// • `ERROR_OPERATION_NOT_ALLOWED` - Indicates that this type of account is not enabled. + /// • `ERROR_INVALID_ACTION_CODE` - If the action code in the link is malformed, expired, or has already been used. + /// This can only occur when using [EmailAuthProvider.getCredentialWithLink] to obtain the credential. + Future linkWithCredential(AuthCredential credential) async { + assert(credential != null); + // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. + // https://github.com/flutter/flutter/issues/26431 + // ignore: strong_mode_implicit_dynamic_method + final Map data = await FirebaseAuth.channel.invokeMethod( + 'linkWithCredential', + { + 'app': _app.name, + 'provider': credential._provider, + 'data': credential._data, + }, + ); + final FirebaseUser currentUser = FirebaseUser._(data, _app); + return currentUser; + } + /// Initiates email verification for the user. Future sendEmailVerification() async { // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. diff --git a/packages/firebase_auth/test/firebase_auth_test.dart b/packages/firebase_auth/test/firebase_auth_test.dart index 45b2a05749d9..dd70670c92ac 100755 --- a/packages/firebase_auth/test/firebase_auth_test.dart +++ b/packages/firebase_auth/test/firebase_auth_test.dart @@ -218,7 +218,8 @@ void main() { email: 'test@example.com', link: '', ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -294,7 +295,8 @@ void main() { authToken: kMockIdToken, authTokenSecret: kMockAccessToken, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -343,7 +345,8 @@ void main() { final AuthCredential credential = GithubAuthProvider.getCredential( token: kMockGithubToken, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -390,7 +393,8 @@ void main() { email: kMockEmail, password: kMockPassword, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -602,7 +606,8 @@ void main() { idToken: kMockIdToken, accessToken: kMockAccessToken, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -626,7 +631,8 @@ void main() { final AuthCredential credential = FacebookAuthProvider.getCredential( accessToken: kMockAccessToken, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -673,7 +679,8 @@ void main() { authToken: kMockAuthToken, authTokenSecret: kMockAuthTokenSecret, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -722,7 +729,8 @@ void main() { final AuthCredential credential = GithubAuthProvider.getCredential( token: kMockGithubToken, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, @@ -769,7 +777,8 @@ void main() { email: kMockEmail, password: kMockPassword, ); - final FirebaseUser user = await auth.linkWithCredential(credential); + FirebaseUser user = await auth.currentUser(); + user = await user.linkWithCredential(credential); verifyUser(user); expect( log, From 3f9839f661c6d9cf172f3e248be2eaff34a4ad58 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 8 May 2019 21:52:57 +0545 Subject: [PATCH 02/25] Fixed test --- .../test/firebase_auth_test.dart | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/firebase_auth/test/firebase_auth_test.dart b/packages/firebase_auth/test/firebase_auth_test.dart index dd70670c92ac..250569e45b71 100755 --- a/packages/firebase_auth/test/firebase_auth_test.dart +++ b/packages/firebase_auth/test/firebase_auth_test.dart @@ -224,6 +224,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -301,6 +307,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -351,6 +363,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -399,6 +417,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -612,6 +636,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -637,6 +667,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -685,6 +721,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -735,6 +777,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { @@ -783,6 +831,12 @@ void main() { expect( log, [ + isMethodCall( + 'currentUser', + arguments: { + 'app': auth.app.name, + }, + ), isMethodCall( 'linkWithCredential', arguments: { From 98917f5d8ab677d10006f0aae897d9864aec140e Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 8 May 2019 21:56:13 +0545 Subject: [PATCH 03/25] added change log and increased version of the plugin --- packages/firebase_auth/CHANGELOG.md | 6 ++++++ packages/firebase_auth/pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/firebase_auth/CHANGELOG.md b/packages/firebase_auth/CHANGELOG.md index a7ce8409a5d3..6da7412035d1 100644 --- a/packages/firebase_auth/CHANGELOG.md +++ b/packages/firebase_auth/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.11.0 + +* **Breaking change**: `linkWithCredential` is now a function of `FirebaseUser`instead of + `FirebaseAuth` or `linkWithCredential`. +* Test fix for newer `linkWithCredential` has been added. + ## 0.10.0+1 * Increase Firebase/Auth CocoaPod dependency to '~> 6.0'. diff --git a/packages/firebase_auth/pubspec.yaml b/packages/firebase_auth/pubspec.yaml index 1635ef047005..22e7d1b62bad 100755 --- a/packages/firebase_auth/pubspec.yaml +++ b/packages/firebase_auth/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for Firebase Auth, enabling Android and iOS like Google, Facebook and Twitter. author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_auth -version: "0.10.0+1" +version: "0.11.0" flutter: plugin: From 4a106f7a4c91b5627704e9e2717c0326a67f8b24 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 8 May 2019 21:59:00 +0545 Subject: [PATCH 04/25] Minor fixes --- packages/firebase_auth/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/firebase_auth/CHANGELOG.md b/packages/firebase_auth/CHANGELOG.md index 6da7412035d1..98b830419bbf 100644 --- a/packages/firebase_auth/CHANGELOG.md +++ b/packages/firebase_auth/CHANGELOG.md @@ -1,8 +1,8 @@ ## 0.11.0 * **Breaking change**: `linkWithCredential` is now a function of `FirebaseUser`instead of - `FirebaseAuth` or `linkWithCredential`. -* Test fix for newer `linkWithCredential` has been added. + `FirebaseAuth`. +* Added test for newer `linkWithCredential` function. ## 0.10.0+1 From a77e8f5d443e29a493cc4a6d9f2d440f83148271 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Tue, 21 May 2019 08:37:02 +0545 Subject: [PATCH 05/25] Added collection group query --- .../cloudfirestore/CloudFirestorePlugin.java | 1383 ++++++++--------- .../cloud_firestore/lib/src/firestore.dart | 10 + packages/cloud_firestore/lib/src/query.dart | 5 + 3 files changed, 705 insertions(+), 693 deletions(-) diff --git a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java index 9b7211326480..0cfe562bec41 100644 --- a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java +++ b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java @@ -6,8 +6,10 @@ import android.os.AsyncTask; import android.util.SparseArray; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.Task; @@ -33,13 +35,7 @@ import com.google.firebase.firestore.SetOptions; import com.google.firebase.firestore.Transaction; import com.google.firebase.firestore.WriteBatch; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugin.common.StandardMessageCodec; -import io.flutter.plugin.common.StandardMethodCodec; + import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -50,738 +46,739 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.StandardMessageCodec; +import io.flutter.plugin.common.StandardMethodCodec; + public class CloudFirestorePlugin implements MethodCallHandler { - public static final String TAG = "CloudFirestorePlugin"; - private final MethodChannel channel; - - // Handles are ints used as indexes into the sparse array of active observers - private int nextListenerHandle = 0; - private int nextBatchHandle = 0; - private final SparseArray observers = new SparseArray<>(); - private final SparseArray documentObservers = new SparseArray<>(); - private final SparseArray listenerRegistrations = new SparseArray<>(); - private final SparseArray batches = new SparseArray<>(); - private final SparseArray transactions = new SparseArray<>(); - private final SparseArray completionTasks = new SparseArray<>(); - - public static void registerWith(PluginRegistry.Registrar registrar) { - final MethodChannel channel = - new MethodChannel( - registrar.messenger(), - "plugins.flutter.io/cloud_firestore", - new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); - channel.setMethodCallHandler(new CloudFirestorePlugin(channel)); - } - - private CloudFirestorePlugin(MethodChannel channel) { - this.channel = channel; - } - - private FirebaseFirestore getFirestore(Map arguments) { - String appName = (String) arguments.get("app"); - return FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); - } - - private CollectionReference getCollectionReference(Map arguments) { - String path = (String) arguments.get("path"); - return getFirestore(arguments).collection(path); - } - - private DocumentReference getDocumentReference(Map arguments) { - String path = (String) arguments.get("path"); - return getFirestore(arguments).document(path); - } - - private Object[] getDocumentValues(Map document, List> orderBy) { - String documentId = (String) document.get("id"); - Map documentData = (Map) document.get("data"); - List data = new ArrayList<>(); - if (orderBy != null) { - for (List order : orderBy) { - String orderByFieldName = (String) order.get(0); - data.add(documentData.get(orderByFieldName)); - } + public static final String TAG = "CloudFirestorePlugin"; + private final MethodChannel channel; + + // Handles are ints used as indexes into the sparse array of active observers + private int nextListenerHandle = 0; + private int nextBatchHandle = 0; + private final SparseArray observers = new SparseArray<>(); + private final SparseArray documentObservers = new SparseArray<>(); + private final SparseArray listenerRegistrations = new SparseArray<>(); + private final SparseArray batches = new SparseArray<>(); + private final SparseArray transactions = new SparseArray<>(); + private final SparseArray completionTasks = new SparseArray<>(); + + public static void registerWith(PluginRegistry.Registrar registrar) { + final MethodChannel channel = + new MethodChannel( + registrar.messenger(), + "plugins.flutter.io/cloud_firestore", + new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); + channel.setMethodCallHandler(new CloudFirestorePlugin(channel)); } - data.add(documentId); - return data.toArray(); - } - - private Map parseQuerySnapshot(QuerySnapshot querySnapshot) { - if (querySnapshot == null) return new HashMap<>(); - Map data = new HashMap<>(); - List paths = new ArrayList<>(); - List> documents = new ArrayList<>(); - List> metadatas = new ArrayList<>(); - for (DocumentSnapshot document : querySnapshot.getDocuments()) { - paths.add(document.getReference().getPath()); - documents.add(document.getData()); - Map metadata = new HashMap(); - metadata.put("hasPendingWrites", document.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", document.getMetadata().isFromCache()); - metadatas.add(metadata); - } - data.put("paths", paths); - data.put("documents", documents); - data.put("metadatas", metadatas); - - List> documentChanges = new ArrayList<>(); - for (DocumentChange documentChange : querySnapshot.getDocumentChanges()) { - Map change = new HashMap<>(); - String type = null; - switch (documentChange.getType()) { - case ADDED: - type = "DocumentChangeType.added"; - break; - case MODIFIED: - type = "DocumentChangeType.modified"; - break; - case REMOVED: - type = "DocumentChangeType.removed"; - break; - } - change.put("type", type); - change.put("oldIndex", documentChange.getOldIndex()); - change.put("newIndex", documentChange.getNewIndex()); - change.put("document", documentChange.getDocument().getData()); - change.put("path", documentChange.getDocument().getReference().getPath()); - Map metadata = new HashMap(); - metadata.put( - "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentChange.getDocument().getMetadata().isFromCache()); - change.put("metadata", metadata); - documentChanges.add(change); - } - data.put("documentChanges", documentChanges); - - return data; - } - - private Transaction getTransaction(Map arguments) { - return transactions.get((Integer) arguments.get("transactionId")); - } - - private Query getQuery(Map arguments) { - Query query = getCollectionReference(arguments); - @SuppressWarnings("unchecked") - Map parameters = (Map) arguments.get("parameters"); - if (parameters == null) return query; - @SuppressWarnings("unchecked") - List> whereConditions = (List>) parameters.get("where"); - for (List condition : whereConditions) { - String fieldName = (String) condition.get(0); - String operator = (String) condition.get(1); - Object value = condition.get(2); - if ("==".equals(operator)) { - query = query.whereEqualTo(fieldName, value); - } else if ("<".equals(operator)) { - query = query.whereLessThan(fieldName, value); - } else if ("<=".equals(operator)) { - query = query.whereLessThanOrEqualTo(fieldName, value); - } else if (">".equals(operator)) { - query = query.whereGreaterThan(fieldName, value); - } else if (">=".equals(operator)) { - query = query.whereGreaterThanOrEqualTo(fieldName, value); - } else if ("array-contains".equals(operator)) { - query = query.whereArrayContains(fieldName, value); - } else { - // Invalid operator. - } - } - @SuppressWarnings("unchecked") - Number limit = (Number) parameters.get("limit"); - if (limit != null) query = query.limit(limit.longValue()); - @SuppressWarnings("unchecked") - List> orderBy = (List>) parameters.get("orderBy"); - if (orderBy == null) return query; - for (List order : orderBy) { - String orderByFieldName = (String) order.get(0); - Boolean descending = (Boolean) order.get(1); - Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; - query = query.orderBy(orderByFieldName, direction); - } - @SuppressWarnings("unchecked") - Map startAtDocument = (Map) parameters.get("startAtDocument"); - if (startAtDocument != null) { - query = - query - .orderBy(FieldPath.documentId()) - .startAt(getDocumentValues(startAtDocument, orderBy)); - } - @SuppressWarnings("unchecked") - Map startAfterDocument = - (Map) parameters.get("startAfterDocument"); - if (startAfterDocument != null) { - query = - query - .orderBy(FieldPath.documentId()) - .startAfter(getDocumentValues(startAfterDocument, orderBy)); - } - @SuppressWarnings("unchecked") - List startAt = (List) parameters.get("startAt"); - if (startAt != null) query = query.startAt(startAt.toArray()); - @SuppressWarnings("unchecked") - List startAfter = (List) parameters.get("startAfter"); - if (startAfter != null) query = query.startAfter(startAfter.toArray()); - @SuppressWarnings("unchecked") - Map endAtDocument = (Map) parameters.get("endAtDocument"); - if (endAtDocument != null) { - query = - query.orderBy(FieldPath.documentId()).endAt(getDocumentValues(endAtDocument, orderBy)); - } - @SuppressWarnings("unchecked") - Map endBeforeDocument = - (Map) parameters.get("endBeforeDocument"); - if (endBeforeDocument != null) { - query = - query - .orderBy(FieldPath.documentId()) - .endBefore(getDocumentValues(endBeforeDocument, orderBy)); - } - @SuppressWarnings("unchecked") - List endAt = (List) parameters.get("endAt"); - if (endAt != null) query = query.endAt(endAt.toArray()); - @SuppressWarnings("unchecked") - List endBefore = (List) parameters.get("endBefore"); - if (endBefore != null) query = query.endBefore(endBefore.toArray()); - return query; - } - - private class DocumentObserver implements EventListener { - private int handle; - - DocumentObserver(int handle) { - this.handle = handle; + + private CloudFirestorePlugin(MethodChannel channel) { + this.channel = channel; } - @Override - public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) { - if (e != null) { - // TODO: send error - System.out.println(e); - return; - } - Map arguments = new HashMap<>(); - Map metadata = new HashMap<>(); - arguments.put("handle", handle); - metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); - arguments.put("metadata", metadata); - if (documentSnapshot.exists()) { - arguments.put("data", documentSnapshot.getData()); - arguments.put("path", documentSnapshot.getReference().getPath()); - } else { - arguments.put("data", null); - arguments.put("path", documentSnapshot.getReference().getPath()); - } - channel.invokeMethod("DocumentSnapshot", arguments); + private FirebaseFirestore getFirestore(Map arguments) { + String appName = (String) arguments.get("app"); + return FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); } - } - private class EventObserver implements EventListener { - private int handle; - EventObserver(int handle) { - this.handle = handle; + private Query getReference(Map arguments) { + if ((boolean) arguments.get("collectionGroup")) + return getCollectionGroupReference(arguments); + else + return getCollectionReference(arguments); } - @Override - public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) { - if (e != null) { - // TODO: send error - System.out.println(e); - return; - } - - Map arguments = parseQuerySnapshot(querySnapshot); - arguments.put("handle", handle); + private Query getCollectionGroupReference(Map arguments) { + String path = (String) arguments.get("path"); + return getFirestore(arguments).collectionGroup(path); + } - channel.invokeMethod("QuerySnapshot", arguments); + private CollectionReference getCollectionReference(Map arguments) { + String path = (String) arguments.get("path"); + return getFirestore(arguments).collection(path); } - } - - private void addDefaultListeners(final String description, Task task, final Result result) { - task.addOnSuccessListener( - new OnSuccessListener() { - @Override - public void onSuccess(Void ignored) { - result.success(null); - } - }); - task.addOnFailureListener( - new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - result.error("Error performing " + description, e.getMessage(), null); - } - }); - } - - @Override - public void onMethodCall(MethodCall call, final Result result) { - switch (call.method) { - case "Firestore#runTransaction": - { - final TaskCompletionSource> transactionTCS = - new TaskCompletionSource<>(); - final Task> transactionTCSTask = transactionTCS.getTask(); - - final Map arguments = call.arguments(); - getFirestore(arguments) - .runTransaction( - new Transaction.Function() { - @Nullable - @Override - public Void apply(@NonNull Transaction transaction) - throws FirebaseFirestoreException { - // Store transaction. - int transactionId = (Integer) arguments.get("transactionId"); - transactions.append(transactionId, transaction); - completionTasks.append(transactionId, transactionTCS); - - // Start operations on Dart side. - channel.invokeMethod( - "DoTransaction", - arguments, - new Result() { - @SuppressWarnings("unchecked") - @Override - public void success(Object doTransactionResult) { - transactionTCS.trySetResult( - (Map) doTransactionResult); - } - @Override - public void error( - String errorCode, String errorMessage, Object errorDetails) { - // result.error(errorCode, errorMessage, errorDetails); - transactionTCS.trySetException( - new Exception("Do transaction failed.")); - } - @Override - public void notImplemented() { - // result.error("DoTransaction not implemented", null, null); - transactionTCS.setException( - new Exception("DoTransaction not implemented")); - } - }); - - // Wait till transaction is complete. - try { - String timeoutKey = "transactionTimeout"; - long timeout = ((Number) arguments.get(timeoutKey)).longValue(); - Map transactionResult = - Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); - - // Once transaction completes return the result to the Dart side. - result.success(transactionResult); - } catch (Exception e) { - result.error("Error performing transaction", e.getMessage(), null); - } - return null; - } - }); - break; - } - case "Transaction#get": - { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - try { - DocumentSnapshot documentSnapshot = - transaction.get(getDocumentReference(arguments)); - Map snapshotMap = new HashMap<>(); - snapshotMap.put("path", documentSnapshot.getReference().getPath()); - if (documentSnapshot.exists()) { - snapshotMap.put("data", documentSnapshot.getData()); - } else { - snapshotMap.put("data", null); - } - Map metadata = new HashMap(); - metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); - snapshotMap.put("metadata", metadata); - result.success(snapshotMap); - } catch (FirebaseFirestoreException e) { - result.error("Error performing Transaction#get", e.getMessage(), null); - } - return null; + private DocumentReference getDocumentReference(Map arguments) { + String path = (String) arguments.get("path"); + return getFirestore(arguments).document(path); + } + + private Object[] getDocumentValues(Map document, List> orderBy) { + String documentId = (String) document.get("id"); + Map documentData = (Map) document.get("data"); + List data = new ArrayList<>(); + if (orderBy != null) { + for (List order : orderBy) { + String orderByFieldName = (String) order.get(0); + data.add(documentData.get(orderByFieldName)); } - }.execute(); - break; } - case "Transaction#update": - { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @SuppressWarnings("unchecked") - @Override - protected Void doInBackground(Void... voids) { - Map data = (Map) arguments.get("data"); - try { - transaction.update(getDocumentReference(arguments), data); - result.success(null); - } catch (IllegalStateException e) { - result.error("Error performing Transaction#update", e.getMessage(), null); - } - return null; - } - }.execute(); - break; + data.add(documentId); + return data.toArray(); + } + + private Map parseQuerySnapshot(QuerySnapshot querySnapshot) { + if (querySnapshot == null) return new HashMap<>(); + Map data = new HashMap<>(); + List paths = new ArrayList<>(); + List> documents = new ArrayList<>(); + List> metadatas = new ArrayList<>(); + for (DocumentSnapshot document : querySnapshot.getDocuments()) { + paths.add(document.getReference().getPath()); + documents.add(document.getData()); + Map metadata = new HashMap(); + metadata.put("hasPendingWrites", document.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", document.getMetadata().isFromCache()); + metadatas.add(metadata); } - case "Transaction#set": - { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @SuppressWarnings("unchecked") - @Override - protected Void doInBackground(Void... voids) { - Map data = (Map) arguments.get("data"); - transaction.set(getDocumentReference(arguments), data); - result.success(null); - return null; + data.put("paths", paths); + data.put("documents", documents); + data.put("metadatas", metadatas); + + List> documentChanges = new ArrayList<>(); + for (DocumentChange documentChange : querySnapshot.getDocumentChanges()) { + Map change = new HashMap<>(); + String type = null; + switch (documentChange.getType()) { + case ADDED: + type = "DocumentChangeType.added"; + break; + case MODIFIED: + type = "DocumentChangeType.modified"; + break; + case REMOVED: + type = "DocumentChangeType.removed"; + break; } - }.execute(); - break; + change.put("type", type); + change.put("oldIndex", documentChange.getOldIndex()); + change.put("newIndex", documentChange.getNewIndex()); + change.put("document", documentChange.getDocument().getData()); + change.put("path", documentChange.getDocument().getReference().getPath()); + Map metadata = new HashMap(); + metadata.put( + "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentChange.getDocument().getMetadata().isFromCache()); + change.put("metadata", metadata); + documentChanges.add(change); } - case "Transaction#delete": - { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - transaction.delete(getDocumentReference(arguments)); - result.success(null); - return null; + data.put("documentChanges", documentChanges); + + return data; + } + + private Transaction getTransaction(Map arguments) { + return transactions.get((Integer) arguments.get("transactionId")); + } + + private Query getQuery(Map arguments) { + Query query = getReference(arguments); + @SuppressWarnings("unchecked") + Map parameters = (Map) arguments.get("parameters"); + if (parameters == null) return query; + @SuppressWarnings("unchecked") + List> whereConditions = (List>) parameters.get("where"); + for (List condition : whereConditions) { + String fieldName = (String) condition.get(0); + String operator = (String) condition.get(1); + Object value = condition.get(2); + if ("==".equals(operator)) { + query = query.whereEqualTo(fieldName, value); + } else if ("<".equals(operator)) { + query = query.whereLessThan(fieldName, value); + } else if ("<=".equals(operator)) { + query = query.whereLessThanOrEqualTo(fieldName, value); + } else if (">".equals(operator)) { + query = query.whereGreaterThan(fieldName, value); + } else if (">=".equals(operator)) { + query = query.whereGreaterThanOrEqualTo(fieldName, value); + } else if ("array-contains".equals(operator)) { + query = query.whereArrayContains(fieldName, value); + } else { + // Invalid operator. } - }.execute(); - break; } - case "WriteBatch#create": - { - int handle = nextBatchHandle++; - final Map arguments = call.arguments(); - WriteBatch batch = getFirestore(arguments).batch(); - batches.put(handle, batch); - result.success(handle); - break; + @SuppressWarnings("unchecked") + Number limit = (Number) parameters.get("limit"); + if (limit != null) query = query.limit(limit.longValue()); + @SuppressWarnings("unchecked") + List> orderBy = (List>) parameters.get("orderBy"); + if (orderBy == null) return query; + for (List order : orderBy) { + String orderByFieldName = (String) order.get(0); + Boolean descending = (Boolean) order.get(1); + Query.Direction direction = + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + query = query.orderBy(orderByFieldName, direction); } - case "WriteBatch#setData": - { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - DocumentReference reference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map options = (Map) arguments.get("options"); - WriteBatch batch = batches.get(handle); - if (options != null && (Boolean) options.get("merge")) { - batch.set(reference, arguments.get("data"), SetOptions.merge()); - } else { - batch.set(reference, arguments.get("data")); - } - result.success(null); - break; + @SuppressWarnings("unchecked") + Map startAtDocument = (Map) parameters.get("startAtDocument"); + if (startAtDocument != null) { + query = + query + .orderBy(FieldPath.documentId()) + .startAt(getDocumentValues(startAtDocument, orderBy)); } - case "WriteBatch#updateData": - { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - DocumentReference reference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map data = (Map) arguments.get("data"); - WriteBatch batch = batches.get(handle); - batch.update(reference, data); - result.success(null); - break; + @SuppressWarnings("unchecked") + Map startAfterDocument = + (Map) parameters.get("startAfterDocument"); + if (startAfterDocument != null) { + query = + query + .orderBy(FieldPath.documentId()) + .startAfter(getDocumentValues(startAfterDocument, orderBy)); } - case "WriteBatch#delete": - { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - DocumentReference reference = getDocumentReference(arguments); - WriteBatch batch = batches.get(handle); - batch.delete(reference); - result.success(null); - break; + @SuppressWarnings("unchecked") + List startAt = (List) parameters.get("startAt"); + if (startAt != null) query = query.startAt(startAt.toArray()); + @SuppressWarnings("unchecked") + List startAfter = (List) parameters.get("startAfter"); + if (startAfter != null) query = query.startAfter(startAfter.toArray()); + @SuppressWarnings("unchecked") + Map endAtDocument = (Map) parameters.get("endAtDocument"); + if (endAtDocument != null) { + query = + query.orderBy(FieldPath.documentId()).endAt(getDocumentValues(endAtDocument, orderBy)); } - case "WriteBatch#commit": - { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - WriteBatch batch = batches.get(handle); - Task task = batch.commit(); - batches.delete(handle); - addDefaultListeners("commit", task, result); - break; + @SuppressWarnings("unchecked") + Map endBeforeDocument = + (Map) parameters.get("endBeforeDocument"); + if (endBeforeDocument != null) { + query = + query + .orderBy(FieldPath.documentId()) + .endBefore(getDocumentValues(endBeforeDocument, orderBy)); } - case "Query#addSnapshotListener": - { - Map arguments = call.arguments(); - int handle = nextListenerHandle++; - EventObserver observer = new EventObserver(handle); - observers.put(handle, observer); - listenerRegistrations.put(handle, getQuery(arguments).addSnapshotListener(observer)); - result.success(handle); - break; + @SuppressWarnings("unchecked") + List endAt = (List) parameters.get("endAt"); + if (endAt != null) query = query.endAt(endAt.toArray()); + @SuppressWarnings("unchecked") + List endBefore = (List) parameters.get("endBefore"); + if (endBefore != null) query = query.endBefore(endBefore.toArray()); + return query; + } + + private class DocumentObserver implements EventListener { + private int handle; + + DocumentObserver(int handle) { + this.handle = handle; } - case "Query#addDocumentListener": - { - Map arguments = call.arguments(); - int handle = nextListenerHandle++; - DocumentObserver observer = new DocumentObserver(handle); - documentObservers.put(handle, observer); - listenerRegistrations.put( - handle, getDocumentReference(arguments).addSnapshotListener(observer)); - result.success(handle); - break; + + @Override + public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) { + if (e != null) { + // TODO: send error + System.out.println(e); + return; + } + Map arguments = new HashMap<>(); + Map metadata = new HashMap<>(); + arguments.put("handle", handle); + metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); + arguments.put("metadata", metadata); + if (documentSnapshot.exists()) { + arguments.put("data", documentSnapshot.getData()); + arguments.put("path", documentSnapshot.getReference().getPath()); + } else { + arguments.put("data", null); + arguments.put("path", documentSnapshot.getReference().getPath()); + } + channel.invokeMethod("DocumentSnapshot", arguments); } - case "Query#removeListener": - { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - listenerRegistrations.get(handle).remove(); - listenerRegistrations.remove(handle); - observers.remove(handle); - result.success(null); - break; + } + + private class EventObserver implements EventListener { + private int handle; + + EventObserver(int handle) { + this.handle = handle; } - case "Query#getDocuments": - { - Map arguments = call.arguments(); - Query query = getQuery(arguments); - Task task = query.get(); - task.addOnSuccessListener( - new OnSuccessListener() { + + @Override + public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) { + if (e != null) { + // TODO: send error + System.out.println(e); + return; + } + + Map arguments = parseQuerySnapshot(querySnapshot); + arguments.put("handle", handle); + + channel.invokeMethod("QuerySnapshot", arguments); + } + } + + private void addDefaultListeners(final String description, Task task, final Result result) { + task.addOnSuccessListener( + new OnSuccessListener() { @Override - public void onSuccess(QuerySnapshot querySnapshot) { - result.success(parseQuerySnapshot(querySnapshot)); + public void onSuccess(Void ignored) { + result.success(null); } - }) - .addOnFailureListener( - new OnFailureListener() { + }); + task.addOnFailureListener( + new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { - result.error("Error performing getDocuments", e.getMessage(), null); + result.error("Error performing " + description, e.getMessage(), null); } - }); - break; - } - case "DocumentReference#setData": - { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map options = (Map) arguments.get("options"); - @SuppressWarnings("unchecked") - Map data = (Map) arguments.get("data"); - Task task; - if (options != null && (Boolean) options.get("merge")) { - task = documentReference.set(data, SetOptions.merge()); - } else { - task = documentReference.set(data); - } - addDefaultListeners("setData", task, result); - break; - } - case "DocumentReference#updateData": - { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map data = (Map) arguments.get("data"); - Task task = documentReference.update(data); - addDefaultListeners("updateData", task, result); - break; - } - case "DocumentReference#get": - { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - Task task = documentReference.get(); - task.addOnSuccessListener( - new OnSuccessListener() { + }); + } + + @Override + public void onMethodCall(MethodCall call, final Result result) { + switch (call.method) { + case "Firestore#runTransaction": { + final TaskCompletionSource> transactionTCS = + new TaskCompletionSource<>(); + final Task> transactionTCSTask = transactionTCS.getTask(); + + final Map arguments = call.arguments(); + getFirestore(arguments) + .runTransaction( + new Transaction.Function() { + @Nullable + @Override + public Void apply(@NonNull Transaction transaction) + throws FirebaseFirestoreException { + // Store transaction. + int transactionId = (Integer) arguments.get("transactionId"); + transactions.append(transactionId, transaction); + completionTasks.append(transactionId, transactionTCS); + + // Start operations on Dart side. + channel.invokeMethod( + "DoTransaction", + arguments, + new Result() { + @SuppressWarnings("unchecked") + @Override + public void success(Object doTransactionResult) { + transactionTCS.trySetResult( + (Map) doTransactionResult); + } + + @Override + public void error( + String errorCode, String errorMessage, Object errorDetails) { + // result.error(errorCode, errorMessage, errorDetails); + transactionTCS.trySetException( + new Exception("Do transaction failed.")); + } + + @Override + public void notImplemented() { + // result.error("DoTransaction not implemented", null, null); + transactionTCS.setException( + new Exception("DoTransaction not implemented")); + } + }); + + // Wait till transaction is complete. + try { + String timeoutKey = "transactionTimeout"; + long timeout = ((Number) arguments.get(timeoutKey)).longValue(); + Map transactionResult = + Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); + + // Once transaction completes return the result to the Dart side. + result.success(transactionResult); + } catch (Exception e) { + result.error("Error performing transaction", e.getMessage(), null); + } + return null; + } + }); + break; + } + case "Transaction#get": { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { @Override - public void onSuccess(DocumentSnapshot documentSnapshot) { - Map snapshotMap = new HashMap<>(); - Map metadata = new HashMap<>(); - metadata.put( - "hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); - snapshotMap.put("metadata", metadata); - snapshotMap.put("path", documentSnapshot.getReference().getPath()); - if (documentSnapshot.exists()) { - snapshotMap.put("data", documentSnapshot.getData()); - } else { - snapshotMap.put("data", null); - } - result.success(snapshotMap); + protected Void doInBackground(Void... voids) { + try { + DocumentSnapshot documentSnapshot = + transaction.get(getDocumentReference(arguments)); + Map snapshotMap = new HashMap<>(); + snapshotMap.put("path", documentSnapshot.getReference().getPath()); + if (documentSnapshot.exists()) { + snapshotMap.put("data", documentSnapshot.getData()); + } else { + snapshotMap.put("data", null); + } + Map metadata = new HashMap(); + metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); + snapshotMap.put("metadata", metadata); + result.success(snapshotMap); + } catch (FirebaseFirestoreException e) { + result.error("Error performing Transaction#get", e.getMessage(), null); + } + return null; } - }) - .addOnFailureListener( - new OnFailureListener() { + }.execute(); + break; + } + case "Transaction#update": { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @SuppressWarnings("unchecked") @Override - public void onFailure(@NonNull Exception e) { - result.error("Error performing get", e.getMessage(), null); + protected Void doInBackground(Void... voids) { + Map data = (Map) arguments.get("data"); + try { + transaction.update(getDocumentReference(arguments), data); + result.success(null); + } catch (IllegalStateException e) { + result.error("Error performing Transaction#update", e.getMessage(), null); + } + return null; } - }); - break; - } - case "DocumentReference#delete": - { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - Task task = documentReference.delete(); - addDefaultListeners("delete", task, result); - break; - } - case "Firestore#enablePersistence": - { - Map arguments = call.arguments(); - Boolean enable = (Boolean) arguments.get("enable"); - FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); - builder.setPersistenceEnabled(enable); - FirebaseFirestoreSettings settings = builder.build(); - getFirestore(arguments).setFirestoreSettings(settings); - result.success(null); - break; - } - case "Firestore#settings": - { - final Map arguments = call.arguments(); - final FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); - - if (arguments.get("persistenceEnabled") != null) { - builder.setPersistenceEnabled((Boolean) arguments.get("persistenceEnabled")); - } - - if (arguments.get("host") != null) { - builder.setHost((String) arguments.get("host")); - } - - if (arguments.get("sslEnabled") != null) { - builder.setSslEnabled((Boolean) arguments.get("sslEnabled")); - } - - if (arguments.get("timestampsInSnapshotsEnabled") != null) { - builder.setTimestampsInSnapshotsEnabled( - (Boolean) arguments.get("timestampsInSnapshotsEnabled")); - } - - FirebaseFirestoreSettings settings = builder.build(); - getFirestore(arguments).setFirestoreSettings(settings); - result.success(null); - break; - } - default: - { - result.notImplemented(); - break; + }.execute(); + break; + } + case "Transaction#set": { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @SuppressWarnings("unchecked") + @Override + protected Void doInBackground(Void... voids) { + Map data = (Map) arguments.get("data"); + transaction.set(getDocumentReference(arguments), data); + result.success(null); + return null; + } + }.execute(); + break; + } + case "Transaction#delete": { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + transaction.delete(getDocumentReference(arguments)); + result.success(null); + return null; + } + }.execute(); + break; + } + case "WriteBatch#create": { + int handle = nextBatchHandle++; + final Map arguments = call.arguments(); + WriteBatch batch = getFirestore(arguments).batch(); + batches.put(handle, batch); + result.success(handle); + break; + } + case "WriteBatch#setData": { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + DocumentReference reference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map options = (Map) arguments.get("options"); + WriteBatch batch = batches.get(handle); + if (options != null && (Boolean) options.get("merge")) { + batch.set(reference, arguments.get("data"), SetOptions.merge()); + } else { + batch.set(reference, arguments.get("data")); + } + result.success(null); + break; + } + case "WriteBatch#updateData": { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + DocumentReference reference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map data = (Map) arguments.get("data"); + WriteBatch batch = batches.get(handle); + batch.update(reference, data); + result.success(null); + break; + } + case "WriteBatch#delete": { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + DocumentReference reference = getDocumentReference(arguments); + WriteBatch batch = batches.get(handle); + batch.delete(reference); + result.success(null); + break; + } + case "WriteBatch#commit": { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + WriteBatch batch = batches.get(handle); + Task task = batch.commit(); + batches.delete(handle); + addDefaultListeners("commit", task, result); + break; + } + case "Query#addSnapshotListener": { + Map arguments = call.arguments(); + int handle = nextListenerHandle++; + EventObserver observer = new EventObserver(handle); + observers.put(handle, observer); + listenerRegistrations.put(handle, getQuery(arguments).addSnapshotListener(observer)); + result.success(handle); + break; + } + case "Query#addDocumentListener": { + Map arguments = call.arguments(); + int handle = nextListenerHandle++; + DocumentObserver observer = new DocumentObserver(handle); + documentObservers.put(handle, observer); + listenerRegistrations.put( + handle, getDocumentReference(arguments).addSnapshotListener(observer)); + result.success(handle); + break; + } + case "Query#removeListener": { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + listenerRegistrations.get(handle).remove(); + listenerRegistrations.remove(handle); + observers.remove(handle); + result.success(null); + break; + } + case "Query#getDocuments": { + Map arguments = call.arguments(); + Query query = getQuery(arguments); + Task task = query.get(); + task.addOnSuccessListener( + new OnSuccessListener() { + @Override + public void onSuccess(QuerySnapshot querySnapshot) { + result.success(parseQuerySnapshot(querySnapshot)); + } + }) + .addOnFailureListener( + new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + result.error("Error performing getDocuments", e.getMessage(), null); + } + }); + break; + } + case "DocumentReference#setData": { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map options = (Map) arguments.get("options"); + @SuppressWarnings("unchecked") + Map data = (Map) arguments.get("data"); + Task task; + if (options != null && (Boolean) options.get("merge")) { + task = documentReference.set(data, SetOptions.merge()); + } else { + task = documentReference.set(data); + } + addDefaultListeners("setData", task, result); + break; + } + case "DocumentReference#updateData": { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map data = (Map) arguments.get("data"); + Task task = documentReference.update(data); + addDefaultListeners("updateData", task, result); + break; + } + case "DocumentReference#get": { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + Task task = documentReference.get(); + task.addOnSuccessListener( + new OnSuccessListener() { + @Override + public void onSuccess(DocumentSnapshot documentSnapshot) { + Map snapshotMap = new HashMap<>(); + Map metadata = new HashMap<>(); + metadata.put( + "hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); + snapshotMap.put("metadata", metadata); + snapshotMap.put("path", documentSnapshot.getReference().getPath()); + if (documentSnapshot.exists()) { + snapshotMap.put("data", documentSnapshot.getData()); + } else { + snapshotMap.put("data", null); + } + result.success(snapshotMap); + } + }) + .addOnFailureListener( + new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + result.error("Error performing get", e.getMessage(), null); + } + }); + break; + } + case "DocumentReference#delete": { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + Task task = documentReference.delete(); + addDefaultListeners("delete", task, result); + break; + } + case "Firestore#enablePersistence": { + Map arguments = call.arguments(); + Boolean enable = (Boolean) arguments.get("enable"); + FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); + builder.setPersistenceEnabled(enable); + FirebaseFirestoreSettings settings = builder.build(); + getFirestore(arguments).setFirestoreSettings(settings); + result.success(null); + break; + } + case "Firestore#settings": { + final Map arguments = call.arguments(); + final FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); + + if (arguments.get("persistenceEnabled") != null) { + builder.setPersistenceEnabled((Boolean) arguments.get("persistenceEnabled")); + } + + if (arguments.get("host") != null) { + builder.setHost((String) arguments.get("host")); + } + + if (arguments.get("sslEnabled") != null) { + builder.setSslEnabled((Boolean) arguments.get("sslEnabled")); + } + + if (arguments.get("timestampsInSnapshotsEnabled") != null) { + builder.setTimestampsInSnapshotsEnabled( + (Boolean) arguments.get("timestampsInSnapshotsEnabled")); + } + + FirebaseFirestoreSettings settings = builder.build(); + getFirestore(arguments).setFirestoreSettings(settings); + result.success(null); + break; + } + default: { + result.notImplemented(); + break; + } } } - } } final class FirestoreMessageCodec extends StandardMessageCodec { - public static final FirestoreMessageCodec INSTANCE = new FirestoreMessageCodec(); - private static final Charset UTF8 = Charset.forName("UTF8"); - private static final byte DATE_TIME = (byte) 128; - private static final byte GEO_POINT = (byte) 129; - private static final byte DOCUMENT_REFERENCE = (byte) 130; - private static final byte BLOB = (byte) 131; - private static final byte ARRAY_UNION = (byte) 132; - private static final byte ARRAY_REMOVE = (byte) 133; - private static final byte DELETE = (byte) 134; - private static final byte SERVER_TIMESTAMP = (byte) 135; - private static final byte TIMESTAMP = (byte) 136; - private static final byte INCREMENT_DOUBLE = (byte) 137; - private static final byte INCREMENT_INTEGER = (byte) 138; - - @Override - protected void writeValue(ByteArrayOutputStream stream, Object value) { - if (value instanceof Date) { - stream.write(DATE_TIME); - writeLong(stream, ((Date) value).getTime()); - } else if (value instanceof Timestamp) { - stream.write(TIMESTAMP); - writeLong(stream, ((Timestamp) value).getSeconds()); - writeInt(stream, ((Timestamp) value).getNanoseconds()); - } else if (value instanceof GeoPoint) { - stream.write(GEO_POINT); - writeAlignment(stream, 8); - writeDouble(stream, ((GeoPoint) value).getLatitude()); - writeDouble(stream, ((GeoPoint) value).getLongitude()); - } else if (value instanceof DocumentReference) { - stream.write(DOCUMENT_REFERENCE); - writeBytes( - stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); - writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8)); - } else if (value instanceof Blob) { - stream.write(BLOB); - writeBytes(stream, ((Blob) value).toBytes()); - } else { - super.writeValue(stream, value); - } - } - - @Override - protected Object readValueOfType(byte type, ByteBuffer buffer) { - switch (type) { - case DATE_TIME: - return new Date(buffer.getLong()); - case TIMESTAMP: - return new Timestamp(buffer.getLong(), buffer.getInt()); - case GEO_POINT: - readAlignment(buffer, 8); - return new GeoPoint(buffer.getDouble(), buffer.getDouble()); - case DOCUMENT_REFERENCE: - final byte[] appNameBytes = readBytes(buffer); - String appName = new String(appNameBytes, UTF8); - final FirebaseFirestore firestore = - FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); - final byte[] pathBytes = readBytes(buffer); - final String path = new String(pathBytes, UTF8); - return firestore.document(path); - case BLOB: - final byte[] bytes = readBytes(buffer); - return Blob.fromBytes(bytes); - case ARRAY_UNION: - return FieldValue.arrayUnion(toArray(readValue(buffer))); - case ARRAY_REMOVE: - return FieldValue.arrayRemove(toArray(readValue(buffer))); - case DELETE: - return FieldValue.delete(); - case SERVER_TIMESTAMP: - return FieldValue.serverTimestamp(); - case INCREMENT_INTEGER: - final Number integerIncrementValue = (Number) readValue(buffer); - return FieldValue.increment(integerIncrementValue.intValue()); - case INCREMENT_DOUBLE: - final Number doubleIncrementValue = (Number) readValue(buffer); - return FieldValue.increment(doubleIncrementValue.doubleValue()); - default: - return super.readValueOfType(type, buffer); - } - } + public static final FirestoreMessageCodec INSTANCE = new FirestoreMessageCodec(); + private static final Charset UTF8 = Charset.forName("UTF8"); + private static final byte DATE_TIME = (byte) 128; + private static final byte GEO_POINT = (byte) 129; + private static final byte DOCUMENT_REFERENCE = (byte) 130; + private static final byte BLOB = (byte) 131; + private static final byte ARRAY_UNION = (byte) 132; + private static final byte ARRAY_REMOVE = (byte) 133; + private static final byte DELETE = (byte) 134; + private static final byte SERVER_TIMESTAMP = (byte) 135; + private static final byte TIMESTAMP = (byte) 136; + private static final byte INCREMENT_DOUBLE = (byte) 137; + private static final byte INCREMENT_INTEGER = (byte) 138; - private Object[] toArray(Object source) { - if (source instanceof List) { - return ((List) source).toArray(); + @Override + protected void writeValue(ByteArrayOutputStream stream, Object value) { + if (value instanceof Date) { + stream.write(DATE_TIME); + writeLong(stream, ((Date) value).getTime()); + } else if (value instanceof Timestamp) { + stream.write(TIMESTAMP); + writeLong(stream, ((Timestamp) value).getSeconds()); + writeInt(stream, ((Timestamp) value).getNanoseconds()); + } else if (value instanceof GeoPoint) { + stream.write(GEO_POINT); + writeAlignment(stream, 8); + writeDouble(stream, ((GeoPoint) value).getLatitude()); + writeDouble(stream, ((GeoPoint) value).getLongitude()); + } else if (value instanceof DocumentReference) { + stream.write(DOCUMENT_REFERENCE); + writeBytes( + stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); + writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8)); + } else if (value instanceof Blob) { + stream.write(BLOB); + writeBytes(stream, ((Blob) value).toBytes()); + } else { + super.writeValue(stream, value); + } } - if (source == null) { - return new Object[0]; + @Override + protected Object readValueOfType(byte type, ByteBuffer buffer) { + switch (type) { + case DATE_TIME: + return new Date(buffer.getLong()); + case TIMESTAMP: + return new Timestamp(buffer.getLong(), buffer.getInt()); + case GEO_POINT: + readAlignment(buffer, 8); + return new GeoPoint(buffer.getDouble(), buffer.getDouble()); + case DOCUMENT_REFERENCE: + final byte[] appNameBytes = readBytes(buffer); + String appName = new String(appNameBytes, UTF8); + final FirebaseFirestore firestore = + FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); + final byte[] pathBytes = readBytes(buffer); + final String path = new String(pathBytes, UTF8); + return firestore.document(path); + case BLOB: + final byte[] bytes = readBytes(buffer); + return Blob.fromBytes(bytes); + case ARRAY_UNION: + return FieldValue.arrayUnion(toArray(readValue(buffer))); + case ARRAY_REMOVE: + return FieldValue.arrayRemove(toArray(readValue(buffer))); + case DELETE: + return FieldValue.delete(); + case SERVER_TIMESTAMP: + return FieldValue.serverTimestamp(); + case INCREMENT_INTEGER: + final Number integerIncrementValue = (Number) readValue(buffer); + return FieldValue.increment(integerIncrementValue.intValue()); + case INCREMENT_DOUBLE: + final Number doubleIncrementValue = (Number) readValue(buffer); + return FieldValue.increment(doubleIncrementValue.doubleValue()); + default: + return super.readValueOfType(type, buffer); + } } - String sourceType = source.getClass().getCanonicalName(); - String message = "java.util.List was expected, unable to convert '%s' to an object array"; - throw new IllegalArgumentException(String.format(message, sourceType)); - } + private Object[] toArray(Object source) { + if (source instanceof List) { + return ((List) source).toArray(); + } + + if (source == null) { + return new Object[0]; + } + + String sourceType = source.getClass().getCanonicalName(); + String message = "java.util.List was expected, unable to convert '%s' to an object array"; + throw new IllegalArgumentException(String.format(message, sourceType)); + } } diff --git a/packages/cloud_firestore/lib/src/firestore.dart b/packages/cloud_firestore/lib/src/firestore.dart index 94e62553c2a9..d204ba389f3d 100644 --- a/packages/cloud_firestore/lib/src/firestore.dart +++ b/packages/cloud_firestore/lib/src/firestore.dart @@ -71,6 +71,16 @@ class Firestore { return CollectionReference._(this, path.split('/')); } + /// Gets a [Query] for the specified Collection group. + Query collectionGroup(String path) { + assert(path != null); + return Query._( + firestore: this, + collectionGroup: true, + pathComponents: path.split('/'), + ); + } + /// Gets a [DocumentReference] for the specified Firestore path. DocumentReference document(String path) { assert(path != null); diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index 1fa1ab2d1434..258e2d6e6b76 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -9,8 +9,10 @@ class Query { Query._( {@required this.firestore, @required List pathComponents, + bool collectionGroup = false, Map parameters}) : _pathComponents = pathComponents, + _collectionGroup = collectionGroup, _parameters = parameters ?? Map.unmodifiable({ 'where': List>.unmodifiable(>[]), @@ -24,6 +26,7 @@ class Query { final List _pathComponents; final Map _parameters; + final bool _collectionGroup; String get _path => _pathComponents.join('/'); @@ -58,6 +61,7 @@ class Query { { 'app': firestore.app.name, 'path': _path, + 'collectionGroup': _collectionGroup, 'parameters': _parameters, }, ).then((dynamic result) => result); @@ -86,6 +90,7 @@ class Query { { 'app': firestore.app.name, 'path': _path, + 'collectionGroup': _collectionGroup, 'parameters': _parameters, }, ); From d509a554224370cec33f772435d10396bd2237ee Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Tue, 21 May 2019 08:39:15 +0545 Subject: [PATCH 06/25] Added an assertion --- packages/cloud_firestore/example/lib/main.dart | 8 ++++---- packages/cloud_firestore/lib/src/firestore.dart | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/cloud_firestore/example/lib/main.dart b/packages/cloud_firestore/example/lib/main.dart index 27138f2fbd9e..d11785ca6c9a 100755 --- a/packages/cloud_firestore/example/lib/main.dart +++ b/packages/cloud_firestore/example/lib/main.dart @@ -12,10 +12,10 @@ Future main() async { final FirebaseApp app = await FirebaseApp.configure( name: 'test', options: const FirebaseOptions( - googleAppID: '1:79601577497:ios:5f2bcc6ba8cecddd', + googleAppID: '1:287014366430:android:236f9daea101f77e', gcmSenderID: '79601577497', - apiKey: 'AIzaSyArgmRGfB5kiQT6CunAOmKRVKEsxKmy6YI-G72PVU', - projectID: 'flutter-firestore', + apiKey: 'AIzaSyDzR5QjMvUqgLMDqJCjgKFAwlU-dFxDhbk', + projectID: 'akhil-app-f3152', ), ); final Firestore firestore = Firestore(app: app); @@ -33,7 +33,7 @@ class MessageList extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: firestore.collection('messages').snapshots(), + stream: firestore.collectionGroup('restros').snapshots(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (!snapshot.hasData) return const Text('Loading...'); final int messageCount = snapshot.data.documents.length; diff --git a/packages/cloud_firestore/lib/src/firestore.dart b/packages/cloud_firestore/lib/src/firestore.dart index d204ba389f3d..7f35cd699c5f 100644 --- a/packages/cloud_firestore/lib/src/firestore.dart +++ b/packages/cloud_firestore/lib/src/firestore.dart @@ -74,6 +74,7 @@ class Firestore { /// Gets a [Query] for the specified Collection group. Query collectionGroup(String path) { assert(path != null); + assert(path.contains("/"), "Collection IDs must not contain '/'."); return Query._( firestore: this, collectionGroup: true, From e811d079152cde813b74dfc59bd2a2559e860925 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Tue, 21 May 2019 08:41:03 +0545 Subject: [PATCH 07/25] Formatted files --- .../cloudfirestore/CloudFirestorePlugin.java | 1393 +++++++++-------- 1 file changed, 703 insertions(+), 690 deletions(-) diff --git a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java index 0cfe562bec41..7418ece02e73 100644 --- a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java +++ b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java @@ -6,10 +6,8 @@ import android.os.AsyncTask; import android.util.SparseArray; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.Task; @@ -35,7 +33,13 @@ import com.google.firebase.firestore.SetOptions; import com.google.firebase.firestore.Transaction; import com.google.firebase.firestore.WriteBatch; - +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.StandardMessageCodec; +import io.flutter.plugin.common.StandardMethodCodec; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -46,739 +50,748 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugin.common.StandardMessageCodec; -import io.flutter.plugin.common.StandardMethodCodec; - public class CloudFirestorePlugin implements MethodCallHandler { - public static final String TAG = "CloudFirestorePlugin"; - private final MethodChannel channel; - - // Handles are ints used as indexes into the sparse array of active observers - private int nextListenerHandle = 0; - private int nextBatchHandle = 0; - private final SparseArray observers = new SparseArray<>(); - private final SparseArray documentObservers = new SparseArray<>(); - private final SparseArray listenerRegistrations = new SparseArray<>(); - private final SparseArray batches = new SparseArray<>(); - private final SparseArray transactions = new SparseArray<>(); - private final SparseArray completionTasks = new SparseArray<>(); - - public static void registerWith(PluginRegistry.Registrar registrar) { - final MethodChannel channel = - new MethodChannel( - registrar.messenger(), - "plugins.flutter.io/cloud_firestore", - new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); - channel.setMethodCallHandler(new CloudFirestorePlugin(channel)); + public static final String TAG = "CloudFirestorePlugin"; + private final MethodChannel channel; + + // Handles are ints used as indexes into the sparse array of active observers + private int nextListenerHandle = 0; + private int nextBatchHandle = 0; + private final SparseArray observers = new SparseArray<>(); + private final SparseArray documentObservers = new SparseArray<>(); + private final SparseArray listenerRegistrations = new SparseArray<>(); + private final SparseArray batches = new SparseArray<>(); + private final SparseArray transactions = new SparseArray<>(); + private final SparseArray completionTasks = new SparseArray<>(); + + public static void registerWith(PluginRegistry.Registrar registrar) { + final MethodChannel channel = + new MethodChannel( + registrar.messenger(), + "plugins.flutter.io/cloud_firestore", + new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); + channel.setMethodCallHandler(new CloudFirestorePlugin(channel)); + } + + private CloudFirestorePlugin(MethodChannel channel) { + this.channel = channel; + } + + private FirebaseFirestore getFirestore(Map arguments) { + String appName = (String) arguments.get("app"); + return FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); + } + + private Query getReference(Map arguments) { + if ((boolean) arguments.get("collectionGroup")) return getCollectionGroupReference(arguments); + else return getCollectionReference(arguments); + } + + private Query getCollectionGroupReference(Map arguments) { + String path = (String) arguments.get("path"); + return getFirestore(arguments).collectionGroup(path); + } + + private CollectionReference getCollectionReference(Map arguments) { + String path = (String) arguments.get("path"); + return getFirestore(arguments).collection(path); + } + + private DocumentReference getDocumentReference(Map arguments) { + String path = (String) arguments.get("path"); + return getFirestore(arguments).document(path); + } + + private Object[] getDocumentValues(Map document, List> orderBy) { + String documentId = (String) document.get("id"); + Map documentData = (Map) document.get("data"); + List data = new ArrayList<>(); + if (orderBy != null) { + for (List order : orderBy) { + String orderByFieldName = (String) order.get(0); + data.add(documentData.get(orderByFieldName)); + } } - - private CloudFirestorePlugin(MethodChannel channel) { - this.channel = channel; + data.add(documentId); + return data.toArray(); + } + + private Map parseQuerySnapshot(QuerySnapshot querySnapshot) { + if (querySnapshot == null) return new HashMap<>(); + Map data = new HashMap<>(); + List paths = new ArrayList<>(); + List> documents = new ArrayList<>(); + List> metadatas = new ArrayList<>(); + for (DocumentSnapshot document : querySnapshot.getDocuments()) { + paths.add(document.getReference().getPath()); + documents.add(document.getData()); + Map metadata = new HashMap(); + metadata.put("hasPendingWrites", document.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", document.getMetadata().isFromCache()); + metadatas.add(metadata); } - - private FirebaseFirestore getFirestore(Map arguments) { - String appName = (String) arguments.get("app"); - return FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); + data.put("paths", paths); + data.put("documents", documents); + data.put("metadatas", metadatas); + + List> documentChanges = new ArrayList<>(); + for (DocumentChange documentChange : querySnapshot.getDocumentChanges()) { + Map change = new HashMap<>(); + String type = null; + switch (documentChange.getType()) { + case ADDED: + type = "DocumentChangeType.added"; + break; + case MODIFIED: + type = "DocumentChangeType.modified"; + break; + case REMOVED: + type = "DocumentChangeType.removed"; + break; + } + change.put("type", type); + change.put("oldIndex", documentChange.getOldIndex()); + change.put("newIndex", documentChange.getNewIndex()); + change.put("document", documentChange.getDocument().getData()); + change.put("path", documentChange.getDocument().getReference().getPath()); + Map metadata = new HashMap(); + metadata.put( + "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentChange.getDocument().getMetadata().isFromCache()); + change.put("metadata", metadata); + documentChanges.add(change); } - - - private Query getReference(Map arguments) { - if ((boolean) arguments.get("collectionGroup")) - return getCollectionGroupReference(arguments); - else - return getCollectionReference(arguments); + data.put("documentChanges", documentChanges); + + return data; + } + + private Transaction getTransaction(Map arguments) { + return transactions.get((Integer) arguments.get("transactionId")); + } + + private Query getQuery(Map arguments) { + Query query = getReference(arguments); + @SuppressWarnings("unchecked") + Map parameters = (Map) arguments.get("parameters"); + if (parameters == null) return query; + @SuppressWarnings("unchecked") + List> whereConditions = (List>) parameters.get("where"); + for (List condition : whereConditions) { + String fieldName = (String) condition.get(0); + String operator = (String) condition.get(1); + Object value = condition.get(2); + if ("==".equals(operator)) { + query = query.whereEqualTo(fieldName, value); + } else if ("<".equals(operator)) { + query = query.whereLessThan(fieldName, value); + } else if ("<=".equals(operator)) { + query = query.whereLessThanOrEqualTo(fieldName, value); + } else if (">".equals(operator)) { + query = query.whereGreaterThan(fieldName, value); + } else if (">=".equals(operator)) { + query = query.whereGreaterThanOrEqualTo(fieldName, value); + } else if ("array-contains".equals(operator)) { + query = query.whereArrayContains(fieldName, value); + } else { + // Invalid operator. + } } - - private Query getCollectionGroupReference(Map arguments) { - String path = (String) arguments.get("path"); - return getFirestore(arguments).collectionGroup(path); + @SuppressWarnings("unchecked") + Number limit = (Number) parameters.get("limit"); + if (limit != null) query = query.limit(limit.longValue()); + @SuppressWarnings("unchecked") + List> orderBy = (List>) parameters.get("orderBy"); + if (orderBy == null) return query; + for (List order : orderBy) { + String orderByFieldName = (String) order.get(0); + Boolean descending = (Boolean) order.get(1); + Query.Direction direction = + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + query = query.orderBy(orderByFieldName, direction); + } + @SuppressWarnings("unchecked") + Map startAtDocument = (Map) parameters.get("startAtDocument"); + if (startAtDocument != null) { + query = + query + .orderBy(FieldPath.documentId()) + .startAt(getDocumentValues(startAtDocument, orderBy)); + } + @SuppressWarnings("unchecked") + Map startAfterDocument = + (Map) parameters.get("startAfterDocument"); + if (startAfterDocument != null) { + query = + query + .orderBy(FieldPath.documentId()) + .startAfter(getDocumentValues(startAfterDocument, orderBy)); + } + @SuppressWarnings("unchecked") + List startAt = (List) parameters.get("startAt"); + if (startAt != null) query = query.startAt(startAt.toArray()); + @SuppressWarnings("unchecked") + List startAfter = (List) parameters.get("startAfter"); + if (startAfter != null) query = query.startAfter(startAfter.toArray()); + @SuppressWarnings("unchecked") + Map endAtDocument = (Map) parameters.get("endAtDocument"); + if (endAtDocument != null) { + query = + query.orderBy(FieldPath.documentId()).endAt(getDocumentValues(endAtDocument, orderBy)); + } + @SuppressWarnings("unchecked") + Map endBeforeDocument = + (Map) parameters.get("endBeforeDocument"); + if (endBeforeDocument != null) { + query = + query + .orderBy(FieldPath.documentId()) + .endBefore(getDocumentValues(endBeforeDocument, orderBy)); + } + @SuppressWarnings("unchecked") + List endAt = (List) parameters.get("endAt"); + if (endAt != null) query = query.endAt(endAt.toArray()); + @SuppressWarnings("unchecked") + List endBefore = (List) parameters.get("endBefore"); + if (endBefore != null) query = query.endBefore(endBefore.toArray()); + return query; + } + + private class DocumentObserver implements EventListener { + private int handle; + + DocumentObserver(int handle) { + this.handle = handle; } - private CollectionReference getCollectionReference(Map arguments) { - String path = (String) arguments.get("path"); - return getFirestore(arguments).collection(path); + @Override + public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) { + if (e != null) { + // TODO: send error + System.out.println(e); + return; + } + Map arguments = new HashMap<>(); + Map metadata = new HashMap<>(); + arguments.put("handle", handle); + metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); + arguments.put("metadata", metadata); + if (documentSnapshot.exists()) { + arguments.put("data", documentSnapshot.getData()); + arguments.put("path", documentSnapshot.getReference().getPath()); + } else { + arguments.put("data", null); + arguments.put("path", documentSnapshot.getReference().getPath()); + } + channel.invokeMethod("DocumentSnapshot", arguments); } + } + private class EventObserver implements EventListener { + private int handle; - private DocumentReference getDocumentReference(Map arguments) { - String path = (String) arguments.get("path"); - return getFirestore(arguments).document(path); + EventObserver(int handle) { + this.handle = handle; } - private Object[] getDocumentValues(Map document, List> orderBy) { - String documentId = (String) document.get("id"); - Map documentData = (Map) document.get("data"); - List data = new ArrayList<>(); - if (orderBy != null) { - for (List order : orderBy) { - String orderByFieldName = (String) order.get(0); - data.add(documentData.get(orderByFieldName)); - } - } - data.add(documentId); - return data.toArray(); - } + @Override + public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) { + if (e != null) { + // TODO: send error + System.out.println(e); + return; + } - private Map parseQuerySnapshot(QuerySnapshot querySnapshot) { - if (querySnapshot == null) return new HashMap<>(); - Map data = new HashMap<>(); - List paths = new ArrayList<>(); - List> documents = new ArrayList<>(); - List> metadatas = new ArrayList<>(); - for (DocumentSnapshot document : querySnapshot.getDocuments()) { - paths.add(document.getReference().getPath()); - documents.add(document.getData()); - Map metadata = new HashMap(); - metadata.put("hasPendingWrites", document.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", document.getMetadata().isFromCache()); - metadatas.add(metadata); - } - data.put("paths", paths); - data.put("documents", documents); - data.put("metadatas", metadatas); - - List> documentChanges = new ArrayList<>(); - for (DocumentChange documentChange : querySnapshot.getDocumentChanges()) { - Map change = new HashMap<>(); - String type = null; - switch (documentChange.getType()) { - case ADDED: - type = "DocumentChangeType.added"; - break; - case MODIFIED: - type = "DocumentChangeType.modified"; - break; - case REMOVED: - type = "DocumentChangeType.removed"; - break; - } - change.put("type", type); - change.put("oldIndex", documentChange.getOldIndex()); - change.put("newIndex", documentChange.getNewIndex()); - change.put("document", documentChange.getDocument().getData()); - change.put("path", documentChange.getDocument().getReference().getPath()); - Map metadata = new HashMap(); - metadata.put( - "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentChange.getDocument().getMetadata().isFromCache()); - change.put("metadata", metadata); - documentChanges.add(change); - } - data.put("documentChanges", documentChanges); + Map arguments = parseQuerySnapshot(querySnapshot); + arguments.put("handle", handle); - return data; + channel.invokeMethod("QuerySnapshot", arguments); } + } + + private void addDefaultListeners(final String description, Task task, final Result result) { + task.addOnSuccessListener( + new OnSuccessListener() { + @Override + public void onSuccess(Void ignored) { + result.success(null); + } + }); + task.addOnFailureListener( + new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + result.error("Error performing " + description, e.getMessage(), null); + } + }); + } + + @Override + public void onMethodCall(MethodCall call, final Result result) { + switch (call.method) { + case "Firestore#runTransaction": + { + final TaskCompletionSource> transactionTCS = + new TaskCompletionSource<>(); + final Task> transactionTCSTask = transactionTCS.getTask(); + + final Map arguments = call.arguments(); + getFirestore(arguments) + .runTransaction( + new Transaction.Function() { + @Nullable + @Override + public Void apply(@NonNull Transaction transaction) + throws FirebaseFirestoreException { + // Store transaction. + int transactionId = (Integer) arguments.get("transactionId"); + transactions.append(transactionId, transaction); + completionTasks.append(transactionId, transactionTCS); + + // Start operations on Dart side. + channel.invokeMethod( + "DoTransaction", + arguments, + new Result() { + @SuppressWarnings("unchecked") + @Override + public void success(Object doTransactionResult) { + transactionTCS.trySetResult( + (Map) doTransactionResult); + } - private Transaction getTransaction(Map arguments) { - return transactions.get((Integer) arguments.get("transactionId")); - } + @Override + public void error( + String errorCode, String errorMessage, Object errorDetails) { + // result.error(errorCode, errorMessage, errorDetails); + transactionTCS.trySetException( + new Exception("Do transaction failed.")); + } - private Query getQuery(Map arguments) { - Query query = getReference(arguments); - @SuppressWarnings("unchecked") - Map parameters = (Map) arguments.get("parameters"); - if (parameters == null) return query; - @SuppressWarnings("unchecked") - List> whereConditions = (List>) parameters.get("where"); - for (List condition : whereConditions) { - String fieldName = (String) condition.get(0); - String operator = (String) condition.get(1); - Object value = condition.get(2); - if ("==".equals(operator)) { - query = query.whereEqualTo(fieldName, value); - } else if ("<".equals(operator)) { - query = query.whereLessThan(fieldName, value); - } else if ("<=".equals(operator)) { - query = query.whereLessThanOrEqualTo(fieldName, value); - } else if (">".equals(operator)) { - query = query.whereGreaterThan(fieldName, value); - } else if (">=".equals(operator)) { - query = query.whereGreaterThanOrEqualTo(fieldName, value); - } else if ("array-contains".equals(operator)) { - query = query.whereArrayContains(fieldName, value); - } else { - // Invalid operator. + @Override + public void notImplemented() { + // result.error("DoTransaction not implemented", null, null); + transactionTCS.setException( + new Exception("DoTransaction not implemented")); + } + }); + + // Wait till transaction is complete. + try { + String timeoutKey = "transactionTimeout"; + long timeout = ((Number) arguments.get(timeoutKey)).longValue(); + Map transactionResult = + Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); + + // Once transaction completes return the result to the Dart side. + result.success(transactionResult); + } catch (Exception e) { + result.error("Error performing transaction", e.getMessage(), null); + } + return null; + } + }); + break; + } + case "Transaction#get": + { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + try { + DocumentSnapshot documentSnapshot = + transaction.get(getDocumentReference(arguments)); + Map snapshotMap = new HashMap<>(); + snapshotMap.put("path", documentSnapshot.getReference().getPath()); + if (documentSnapshot.exists()) { + snapshotMap.put("data", documentSnapshot.getData()); + } else { + snapshotMap.put("data", null); + } + Map metadata = new HashMap(); + metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); + snapshotMap.put("metadata", metadata); + result.success(snapshotMap); + } catch (FirebaseFirestoreException e) { + result.error("Error performing Transaction#get", e.getMessage(), null); + } + return null; } + }.execute(); + break; } - @SuppressWarnings("unchecked") - Number limit = (Number) parameters.get("limit"); - if (limit != null) query = query.limit(limit.longValue()); - @SuppressWarnings("unchecked") - List> orderBy = (List>) parameters.get("orderBy"); - if (orderBy == null) return query; - for (List order : orderBy) { - String orderByFieldName = (String) order.get(0); - Boolean descending = (Boolean) order.get(1); - Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; - query = query.orderBy(orderByFieldName, direction); + case "Transaction#update": + { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @SuppressWarnings("unchecked") + @Override + protected Void doInBackground(Void... voids) { + Map data = (Map) arguments.get("data"); + try { + transaction.update(getDocumentReference(arguments), data); + result.success(null); + } catch (IllegalStateException e) { + result.error("Error performing Transaction#update", e.getMessage(), null); + } + return null; + } + }.execute(); + break; } - @SuppressWarnings("unchecked") - Map startAtDocument = (Map) parameters.get("startAtDocument"); - if (startAtDocument != null) { - query = - query - .orderBy(FieldPath.documentId()) - .startAt(getDocumentValues(startAtDocument, orderBy)); + case "Transaction#set": + { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @SuppressWarnings("unchecked") + @Override + protected Void doInBackground(Void... voids) { + Map data = (Map) arguments.get("data"); + transaction.set(getDocumentReference(arguments), data); + result.success(null); + return null; + } + }.execute(); + break; } - @SuppressWarnings("unchecked") - Map startAfterDocument = - (Map) parameters.get("startAfterDocument"); - if (startAfterDocument != null) { - query = - query - .orderBy(FieldPath.documentId()) - .startAfter(getDocumentValues(startAfterDocument, orderBy)); + case "Transaction#delete": + { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + transaction.delete(getDocumentReference(arguments)); + result.success(null); + return null; + } + }.execute(); + break; } - @SuppressWarnings("unchecked") - List startAt = (List) parameters.get("startAt"); - if (startAt != null) query = query.startAt(startAt.toArray()); - @SuppressWarnings("unchecked") - List startAfter = (List) parameters.get("startAfter"); - if (startAfter != null) query = query.startAfter(startAfter.toArray()); - @SuppressWarnings("unchecked") - Map endAtDocument = (Map) parameters.get("endAtDocument"); - if (endAtDocument != null) { - query = - query.orderBy(FieldPath.documentId()).endAt(getDocumentValues(endAtDocument, orderBy)); + case "WriteBatch#create": + { + int handle = nextBatchHandle++; + final Map arguments = call.arguments(); + WriteBatch batch = getFirestore(arguments).batch(); + batches.put(handle, batch); + result.success(handle); + break; } - @SuppressWarnings("unchecked") - Map endBeforeDocument = - (Map) parameters.get("endBeforeDocument"); - if (endBeforeDocument != null) { - query = - query - .orderBy(FieldPath.documentId()) - .endBefore(getDocumentValues(endBeforeDocument, orderBy)); + case "WriteBatch#setData": + { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + DocumentReference reference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map options = (Map) arguments.get("options"); + WriteBatch batch = batches.get(handle); + if (options != null && (Boolean) options.get("merge")) { + batch.set(reference, arguments.get("data"), SetOptions.merge()); + } else { + batch.set(reference, arguments.get("data")); + } + result.success(null); + break; } - @SuppressWarnings("unchecked") - List endAt = (List) parameters.get("endAt"); - if (endAt != null) query = query.endAt(endAt.toArray()); - @SuppressWarnings("unchecked") - List endBefore = (List) parameters.get("endBefore"); - if (endBefore != null) query = query.endBefore(endBefore.toArray()); - return query; - } - - private class DocumentObserver implements EventListener { - private int handle; - - DocumentObserver(int handle) { - this.handle = handle; + case "WriteBatch#updateData": + { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + DocumentReference reference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map data = (Map) arguments.get("data"); + WriteBatch batch = batches.get(handle); + batch.update(reference, data); + result.success(null); + break; } - - @Override - public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) { - if (e != null) { - // TODO: send error - System.out.println(e); - return; - } - Map arguments = new HashMap<>(); - Map metadata = new HashMap<>(); - arguments.put("handle", handle); - metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); - arguments.put("metadata", metadata); - if (documentSnapshot.exists()) { - arguments.put("data", documentSnapshot.getData()); - arguments.put("path", documentSnapshot.getReference().getPath()); - } else { - arguments.put("data", null); - arguments.put("path", documentSnapshot.getReference().getPath()); - } - channel.invokeMethod("DocumentSnapshot", arguments); + case "WriteBatch#delete": + { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + DocumentReference reference = getDocumentReference(arguments); + WriteBatch batch = batches.get(handle); + batch.delete(reference); + result.success(null); + break; } - } - - private class EventObserver implements EventListener { - private int handle; - - EventObserver(int handle) { - this.handle = handle; + case "WriteBatch#commit": + { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + WriteBatch batch = batches.get(handle); + Task task = batch.commit(); + batches.delete(handle); + addDefaultListeners("commit", task, result); + break; } - - @Override - public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) { - if (e != null) { - // TODO: send error - System.out.println(e); - return; - } - - Map arguments = parseQuerySnapshot(querySnapshot); - arguments.put("handle", handle); - - channel.invokeMethod("QuerySnapshot", arguments); + case "Query#addSnapshotListener": + { + Map arguments = call.arguments(); + int handle = nextListenerHandle++; + EventObserver observer = new EventObserver(handle); + observers.put(handle, observer); + listenerRegistrations.put(handle, getQuery(arguments).addSnapshotListener(observer)); + result.success(handle); + break; } - } - - private void addDefaultListeners(final String description, Task task, final Result result) { - task.addOnSuccessListener( - new OnSuccessListener() { + case "Query#addDocumentListener": + { + Map arguments = call.arguments(); + int handle = nextListenerHandle++; + DocumentObserver observer = new DocumentObserver(handle); + documentObservers.put(handle, observer); + listenerRegistrations.put( + handle, getDocumentReference(arguments).addSnapshotListener(observer)); + result.success(handle); + break; + } + case "Query#removeListener": + { + Map arguments = call.arguments(); + int handle = (Integer) arguments.get("handle"); + listenerRegistrations.get(handle).remove(); + listenerRegistrations.remove(handle); + observers.remove(handle); + result.success(null); + break; + } + case "Query#getDocuments": + { + Map arguments = call.arguments(); + Query query = getQuery(arguments); + Task task = query.get(); + task.addOnSuccessListener( + new OnSuccessListener() { @Override - public void onSuccess(Void ignored) { - result.success(null); + public void onSuccess(QuerySnapshot querySnapshot) { + result.success(parseQuerySnapshot(querySnapshot)); } - }); - task.addOnFailureListener( - new OnFailureListener() { + }) + .addOnFailureListener( + new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { - result.error("Error performing " + description, e.getMessage(), null); - } - }); - } - - @Override - public void onMethodCall(MethodCall call, final Result result) { - switch (call.method) { - case "Firestore#runTransaction": { - final TaskCompletionSource> transactionTCS = - new TaskCompletionSource<>(); - final Task> transactionTCSTask = transactionTCS.getTask(); - - final Map arguments = call.arguments(); - getFirestore(arguments) - .runTransaction( - new Transaction.Function() { - @Nullable - @Override - public Void apply(@NonNull Transaction transaction) - throws FirebaseFirestoreException { - // Store transaction. - int transactionId = (Integer) arguments.get("transactionId"); - transactions.append(transactionId, transaction); - completionTasks.append(transactionId, transactionTCS); - - // Start operations on Dart side. - channel.invokeMethod( - "DoTransaction", - arguments, - new Result() { - @SuppressWarnings("unchecked") - @Override - public void success(Object doTransactionResult) { - transactionTCS.trySetResult( - (Map) doTransactionResult); - } - - @Override - public void error( - String errorCode, String errorMessage, Object errorDetails) { - // result.error(errorCode, errorMessage, errorDetails); - transactionTCS.trySetException( - new Exception("Do transaction failed.")); - } - - @Override - public void notImplemented() { - // result.error("DoTransaction not implemented", null, null); - transactionTCS.setException( - new Exception("DoTransaction not implemented")); - } - }); - - // Wait till transaction is complete. - try { - String timeoutKey = "transactionTimeout"; - long timeout = ((Number) arguments.get(timeoutKey)).longValue(); - Map transactionResult = - Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); - - // Once transaction completes return the result to the Dart side. - result.success(transactionResult); - } catch (Exception e) { - result.error("Error performing transaction", e.getMessage(), null); - } - return null; - } - }); - break; - } - case "Transaction#get": { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - try { - DocumentSnapshot documentSnapshot = - transaction.get(getDocumentReference(arguments)); - Map snapshotMap = new HashMap<>(); - snapshotMap.put("path", documentSnapshot.getReference().getPath()); - if (documentSnapshot.exists()) { - snapshotMap.put("data", documentSnapshot.getData()); - } else { - snapshotMap.put("data", null); - } - Map metadata = new HashMap(); - metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); - snapshotMap.put("metadata", metadata); - result.success(snapshotMap); - } catch (FirebaseFirestoreException e) { - result.error("Error performing Transaction#get", e.getMessage(), null); - } - return null; - } - }.execute(); - break; - } - case "Transaction#update": { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @SuppressWarnings("unchecked") - @Override - protected Void doInBackground(Void... voids) { - Map data = (Map) arguments.get("data"); - try { - transaction.update(getDocumentReference(arguments), data); - result.success(null); - } catch (IllegalStateException e) { - result.error("Error performing Transaction#update", e.getMessage(), null); - } - return null; + result.error("Error performing getDocuments", e.getMessage(), null); } - }.execute(); - break; - } - case "Transaction#set": { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @SuppressWarnings("unchecked") + }); + break; + } + case "DocumentReference#setData": + { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map options = (Map) arguments.get("options"); + @SuppressWarnings("unchecked") + Map data = (Map) arguments.get("data"); + Task task; + if (options != null && (Boolean) options.get("merge")) { + task = documentReference.set(data, SetOptions.merge()); + } else { + task = documentReference.set(data); + } + addDefaultListeners("setData", task, result); + break; + } + case "DocumentReference#updateData": + { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + @SuppressWarnings("unchecked") + Map data = (Map) arguments.get("data"); + Task task = documentReference.update(data); + addDefaultListeners("updateData", task, result); + break; + } + case "DocumentReference#get": + { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + Task task = documentReference.get(); + task.addOnSuccessListener( + new OnSuccessListener() { @Override - protected Void doInBackground(Void... voids) { - Map data = (Map) arguments.get("data"); - transaction.set(getDocumentReference(arguments), data); - result.success(null); - return null; + public void onSuccess(DocumentSnapshot documentSnapshot) { + Map snapshotMap = new HashMap<>(); + Map metadata = new HashMap<>(); + metadata.put( + "hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); + snapshotMap.put("metadata", metadata); + snapshotMap.put("path", documentSnapshot.getReference().getPath()); + if (documentSnapshot.exists()) { + snapshotMap.put("data", documentSnapshot.getData()); + } else { + snapshotMap.put("data", null); + } + result.success(snapshotMap); } - }.execute(); - break; - } - case "Transaction#delete": { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { + }) + .addOnFailureListener( + new OnFailureListener() { @Override - protected Void doInBackground(Void... voids) { - transaction.delete(getDocumentReference(arguments)); - result.success(null); - return null; + public void onFailure(@NonNull Exception e) { + result.error("Error performing get", e.getMessage(), null); } - }.execute(); - break; - } - case "WriteBatch#create": { - int handle = nextBatchHandle++; - final Map arguments = call.arguments(); - WriteBatch batch = getFirestore(arguments).batch(); - batches.put(handle, batch); - result.success(handle); - break; - } - case "WriteBatch#setData": { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - DocumentReference reference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map options = (Map) arguments.get("options"); - WriteBatch batch = batches.get(handle); - if (options != null && (Boolean) options.get("merge")) { - batch.set(reference, arguments.get("data"), SetOptions.merge()); - } else { - batch.set(reference, arguments.get("data")); - } - result.success(null); - break; - } - case "WriteBatch#updateData": { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - DocumentReference reference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map data = (Map) arguments.get("data"); - WriteBatch batch = batches.get(handle); - batch.update(reference, data); - result.success(null); - break; - } - case "WriteBatch#delete": { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - DocumentReference reference = getDocumentReference(arguments); - WriteBatch batch = batches.get(handle); - batch.delete(reference); - result.success(null); - break; - } - case "WriteBatch#commit": { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - WriteBatch batch = batches.get(handle); - Task task = batch.commit(); - batches.delete(handle); - addDefaultListeners("commit", task, result); - break; - } - case "Query#addSnapshotListener": { - Map arguments = call.arguments(); - int handle = nextListenerHandle++; - EventObserver observer = new EventObserver(handle); - observers.put(handle, observer); - listenerRegistrations.put(handle, getQuery(arguments).addSnapshotListener(observer)); - result.success(handle); - break; - } - case "Query#addDocumentListener": { - Map arguments = call.arguments(); - int handle = nextListenerHandle++; - DocumentObserver observer = new DocumentObserver(handle); - documentObservers.put(handle, observer); - listenerRegistrations.put( - handle, getDocumentReference(arguments).addSnapshotListener(observer)); - result.success(handle); - break; - } - case "Query#removeListener": { - Map arguments = call.arguments(); - int handle = (Integer) arguments.get("handle"); - listenerRegistrations.get(handle).remove(); - listenerRegistrations.remove(handle); - observers.remove(handle); - result.success(null); - break; - } - case "Query#getDocuments": { - Map arguments = call.arguments(); - Query query = getQuery(arguments); - Task task = query.get(); - task.addOnSuccessListener( - new OnSuccessListener() { - @Override - public void onSuccess(QuerySnapshot querySnapshot) { - result.success(parseQuerySnapshot(querySnapshot)); - } - }) - .addOnFailureListener( - new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - result.error("Error performing getDocuments", e.getMessage(), null); - } - }); - break; - } - case "DocumentReference#setData": { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map options = (Map) arguments.get("options"); - @SuppressWarnings("unchecked") - Map data = (Map) arguments.get("data"); - Task task; - if (options != null && (Boolean) options.get("merge")) { - task = documentReference.set(data, SetOptions.merge()); - } else { - task = documentReference.set(data); - } - addDefaultListeners("setData", task, result); - break; - } - case "DocumentReference#updateData": { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - @SuppressWarnings("unchecked") - Map data = (Map) arguments.get("data"); - Task task = documentReference.update(data); - addDefaultListeners("updateData", task, result); - break; - } - case "DocumentReference#get": { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - Task task = documentReference.get(); - task.addOnSuccessListener( - new OnSuccessListener() { - @Override - public void onSuccess(DocumentSnapshot documentSnapshot) { - Map snapshotMap = new HashMap<>(); - Map metadata = new HashMap<>(); - metadata.put( - "hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); - snapshotMap.put("metadata", metadata); - snapshotMap.put("path", documentSnapshot.getReference().getPath()); - if (documentSnapshot.exists()) { - snapshotMap.put("data", documentSnapshot.getData()); - } else { - snapshotMap.put("data", null); - } - result.success(snapshotMap); - } - }) - .addOnFailureListener( - new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - result.error("Error performing get", e.getMessage(), null); - } - }); - break; - } - case "DocumentReference#delete": { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - Task task = documentReference.delete(); - addDefaultListeners("delete", task, result); - break; - } - case "Firestore#enablePersistence": { - Map arguments = call.arguments(); - Boolean enable = (Boolean) arguments.get("enable"); - FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); - builder.setPersistenceEnabled(enable); - FirebaseFirestoreSettings settings = builder.build(); - getFirestore(arguments).setFirestoreSettings(settings); - result.success(null); - break; - } - case "Firestore#settings": { - final Map arguments = call.arguments(); - final FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); - - if (arguments.get("persistenceEnabled") != null) { - builder.setPersistenceEnabled((Boolean) arguments.get("persistenceEnabled")); - } - - if (arguments.get("host") != null) { - builder.setHost((String) arguments.get("host")); - } - - if (arguments.get("sslEnabled") != null) { - builder.setSslEnabled((Boolean) arguments.get("sslEnabled")); - } - - if (arguments.get("timestampsInSnapshotsEnabled") != null) { - builder.setTimestampsInSnapshotsEnabled( - (Boolean) arguments.get("timestampsInSnapshotsEnabled")); - } - - FirebaseFirestoreSettings settings = builder.build(); - getFirestore(arguments).setFirestoreSettings(settings); - result.success(null); - break; - } - default: { - result.notImplemented(); - break; - } + }); + break; + } + case "DocumentReference#delete": + { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + Task task = documentReference.delete(); + addDefaultListeners("delete", task, result); + break; + } + case "Firestore#enablePersistence": + { + Map arguments = call.arguments(); + Boolean enable = (Boolean) arguments.get("enable"); + FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); + builder.setPersistenceEnabled(enable); + FirebaseFirestoreSettings settings = builder.build(); + getFirestore(arguments).setFirestoreSettings(settings); + result.success(null); + break; + } + case "Firestore#settings": + { + final Map arguments = call.arguments(); + final FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); + + if (arguments.get("persistenceEnabled") != null) { + builder.setPersistenceEnabled((Boolean) arguments.get("persistenceEnabled")); + } + + if (arguments.get("host") != null) { + builder.setHost((String) arguments.get("host")); + } + + if (arguments.get("sslEnabled") != null) { + builder.setSslEnabled((Boolean) arguments.get("sslEnabled")); + } + + if (arguments.get("timestampsInSnapshotsEnabled") != null) { + builder.setTimestampsInSnapshotsEnabled( + (Boolean) arguments.get("timestampsInSnapshotsEnabled")); + } + + FirebaseFirestoreSettings settings = builder.build(); + getFirestore(arguments).setFirestoreSettings(settings); + result.success(null); + break; + } + default: + { + result.notImplemented(); + break; } } + } } final class FirestoreMessageCodec extends StandardMessageCodec { - public static final FirestoreMessageCodec INSTANCE = new FirestoreMessageCodec(); - private static final Charset UTF8 = Charset.forName("UTF8"); - private static final byte DATE_TIME = (byte) 128; - private static final byte GEO_POINT = (byte) 129; - private static final byte DOCUMENT_REFERENCE = (byte) 130; - private static final byte BLOB = (byte) 131; - private static final byte ARRAY_UNION = (byte) 132; - private static final byte ARRAY_REMOVE = (byte) 133; - private static final byte DELETE = (byte) 134; - private static final byte SERVER_TIMESTAMP = (byte) 135; - private static final byte TIMESTAMP = (byte) 136; - private static final byte INCREMENT_DOUBLE = (byte) 137; - private static final byte INCREMENT_INTEGER = (byte) 138; - - @Override - protected void writeValue(ByteArrayOutputStream stream, Object value) { - if (value instanceof Date) { - stream.write(DATE_TIME); - writeLong(stream, ((Date) value).getTime()); - } else if (value instanceof Timestamp) { - stream.write(TIMESTAMP); - writeLong(stream, ((Timestamp) value).getSeconds()); - writeInt(stream, ((Timestamp) value).getNanoseconds()); - } else if (value instanceof GeoPoint) { - stream.write(GEO_POINT); - writeAlignment(stream, 8); - writeDouble(stream, ((GeoPoint) value).getLatitude()); - writeDouble(stream, ((GeoPoint) value).getLongitude()); - } else if (value instanceof DocumentReference) { - stream.write(DOCUMENT_REFERENCE); - writeBytes( - stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); - writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8)); - } else if (value instanceof Blob) { - stream.write(BLOB); - writeBytes(stream, ((Blob) value).toBytes()); - } else { - super.writeValue(stream, value); - } + public static final FirestoreMessageCodec INSTANCE = new FirestoreMessageCodec(); + private static final Charset UTF8 = Charset.forName("UTF8"); + private static final byte DATE_TIME = (byte) 128; + private static final byte GEO_POINT = (byte) 129; + private static final byte DOCUMENT_REFERENCE = (byte) 130; + private static final byte BLOB = (byte) 131; + private static final byte ARRAY_UNION = (byte) 132; + private static final byte ARRAY_REMOVE = (byte) 133; + private static final byte DELETE = (byte) 134; + private static final byte SERVER_TIMESTAMP = (byte) 135; + private static final byte TIMESTAMP = (byte) 136; + private static final byte INCREMENT_DOUBLE = (byte) 137; + private static final byte INCREMENT_INTEGER = (byte) 138; + + @Override + protected void writeValue(ByteArrayOutputStream stream, Object value) { + if (value instanceof Date) { + stream.write(DATE_TIME); + writeLong(stream, ((Date) value).getTime()); + } else if (value instanceof Timestamp) { + stream.write(TIMESTAMP); + writeLong(stream, ((Timestamp) value).getSeconds()); + writeInt(stream, ((Timestamp) value).getNanoseconds()); + } else if (value instanceof GeoPoint) { + stream.write(GEO_POINT); + writeAlignment(stream, 8); + writeDouble(stream, ((GeoPoint) value).getLatitude()); + writeDouble(stream, ((GeoPoint) value).getLongitude()); + } else if (value instanceof DocumentReference) { + stream.write(DOCUMENT_REFERENCE); + writeBytes( + stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); + writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8)); + } else if (value instanceof Blob) { + stream.write(BLOB); + writeBytes(stream, ((Blob) value).toBytes()); + } else { + super.writeValue(stream, value); } - - @Override - protected Object readValueOfType(byte type, ByteBuffer buffer) { - switch (type) { - case DATE_TIME: - return new Date(buffer.getLong()); - case TIMESTAMP: - return new Timestamp(buffer.getLong(), buffer.getInt()); - case GEO_POINT: - readAlignment(buffer, 8); - return new GeoPoint(buffer.getDouble(), buffer.getDouble()); - case DOCUMENT_REFERENCE: - final byte[] appNameBytes = readBytes(buffer); - String appName = new String(appNameBytes, UTF8); - final FirebaseFirestore firestore = - FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); - final byte[] pathBytes = readBytes(buffer); - final String path = new String(pathBytes, UTF8); - return firestore.document(path); - case BLOB: - final byte[] bytes = readBytes(buffer); - return Blob.fromBytes(bytes); - case ARRAY_UNION: - return FieldValue.arrayUnion(toArray(readValue(buffer))); - case ARRAY_REMOVE: - return FieldValue.arrayRemove(toArray(readValue(buffer))); - case DELETE: - return FieldValue.delete(); - case SERVER_TIMESTAMP: - return FieldValue.serverTimestamp(); - case INCREMENT_INTEGER: - final Number integerIncrementValue = (Number) readValue(buffer); - return FieldValue.increment(integerIncrementValue.intValue()); - case INCREMENT_DOUBLE: - final Number doubleIncrementValue = (Number) readValue(buffer); - return FieldValue.increment(doubleIncrementValue.doubleValue()); - default: - return super.readValueOfType(type, buffer); - } + } + + @Override + protected Object readValueOfType(byte type, ByteBuffer buffer) { + switch (type) { + case DATE_TIME: + return new Date(buffer.getLong()); + case TIMESTAMP: + return new Timestamp(buffer.getLong(), buffer.getInt()); + case GEO_POINT: + readAlignment(buffer, 8); + return new GeoPoint(buffer.getDouble(), buffer.getDouble()); + case DOCUMENT_REFERENCE: + final byte[] appNameBytes = readBytes(buffer); + String appName = new String(appNameBytes, UTF8); + final FirebaseFirestore firestore = + FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); + final byte[] pathBytes = readBytes(buffer); + final String path = new String(pathBytes, UTF8); + return firestore.document(path); + case BLOB: + final byte[] bytes = readBytes(buffer); + return Blob.fromBytes(bytes); + case ARRAY_UNION: + return FieldValue.arrayUnion(toArray(readValue(buffer))); + case ARRAY_REMOVE: + return FieldValue.arrayRemove(toArray(readValue(buffer))); + case DELETE: + return FieldValue.delete(); + case SERVER_TIMESTAMP: + return FieldValue.serverTimestamp(); + case INCREMENT_INTEGER: + final Number integerIncrementValue = (Number) readValue(buffer); + return FieldValue.increment(integerIncrementValue.intValue()); + case INCREMENT_DOUBLE: + final Number doubleIncrementValue = (Number) readValue(buffer); + return FieldValue.increment(doubleIncrementValue.doubleValue()); + default: + return super.readValueOfType(type, buffer); } + } - private Object[] toArray(Object source) { - if (source instanceof List) { - return ((List) source).toArray(); - } - - if (source == null) { - return new Object[0]; - } + private Object[] toArray(Object source) { + if (source instanceof List) { + return ((List) source).toArray(); + } - String sourceType = source.getClass().getCanonicalName(); - String message = "java.util.List was expected, unable to convert '%s' to an object array"; - throw new IllegalArgumentException(String.format(message, sourceType)); + if (source == null) { + return new Object[0]; } + + String sourceType = source.getClass().getCanonicalName(); + String message = "java.util.List was expected, unable to convert '%s' to an object array"; + throw new IllegalArgumentException(String.format(message, sourceType)); + } } From 77ceefe39dbe12078f05baa4fd11c097cc3f24aa Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Tue, 21 May 2019 08:43:23 +0545 Subject: [PATCH 08/25] Removed keys --- packages/cloud_firestore/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cloud_firestore/example/lib/main.dart b/packages/cloud_firestore/example/lib/main.dart index d11785ca6c9a..a57554e0c42e 100755 --- a/packages/cloud_firestore/example/lib/main.dart +++ b/packages/cloud_firestore/example/lib/main.dart @@ -33,7 +33,7 @@ class MessageList extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: firestore.collectionGroup('restros').snapshots(), + stream: firestore.collection('messages').snapshots(), builder: (BuildContext context, AsyncSnapshot snapshot) { if (!snapshot.hasData) return const Text('Loading...'); final int messageCount = snapshot.data.documents.length; From 5655c8855b4b4c2e04e4c3879eb54cd830e78539 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Tue, 21 May 2019 08:52:35 +0545 Subject: [PATCH 09/25] Minor changes --- packages/cloud_firestore/example/lib/main.dart | 6 +++--- packages/cloud_firestore/lib/src/firestore.dart | 2 +- packages/cloud_firestore/lib/src/query.dart | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/cloud_firestore/example/lib/main.dart b/packages/cloud_firestore/example/lib/main.dart index a57554e0c42e..27138f2fbd9e 100755 --- a/packages/cloud_firestore/example/lib/main.dart +++ b/packages/cloud_firestore/example/lib/main.dart @@ -12,10 +12,10 @@ Future main() async { final FirebaseApp app = await FirebaseApp.configure( name: 'test', options: const FirebaseOptions( - googleAppID: '1:287014366430:android:236f9daea101f77e', + googleAppID: '1:79601577497:ios:5f2bcc6ba8cecddd', gcmSenderID: '79601577497', - apiKey: 'AIzaSyDzR5QjMvUqgLMDqJCjgKFAwlU-dFxDhbk', - projectID: 'akhil-app-f3152', + apiKey: 'AIzaSyArgmRGfB5kiQT6CunAOmKRVKEsxKmy6YI-G72PVU', + projectID: 'flutter-firestore', ), ); final Firestore firestore = Firestore(app: app); diff --git a/packages/cloud_firestore/lib/src/firestore.dart b/packages/cloud_firestore/lib/src/firestore.dart index 7f35cd699c5f..038735279d53 100644 --- a/packages/cloud_firestore/lib/src/firestore.dart +++ b/packages/cloud_firestore/lib/src/firestore.dart @@ -77,7 +77,7 @@ class Firestore { assert(path.contains("/"), "Collection IDs must not contain '/'."); return Query._( firestore: this, - collectionGroup: true, + isCollectionGroup: true, pathComponents: path.split('/'), ); } diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index 258e2d6e6b76..51f0fface0e5 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -9,10 +9,10 @@ class Query { Query._( {@required this.firestore, @required List pathComponents, - bool collectionGroup = false, + bool isCollectionGroup = false, Map parameters}) : _pathComponents = pathComponents, - _collectionGroup = collectionGroup, + _isCollectionGroup = isCollectionGroup, _parameters = parameters ?? Map.unmodifiable({ 'where': List>.unmodifiable(>[]), @@ -26,7 +26,7 @@ class Query { final List _pathComponents; final Map _parameters; - final bool _collectionGroup; + final bool _isCollectionGroup; String get _path => _pathComponents.join('/'); @@ -61,7 +61,7 @@ class Query { { 'app': firestore.app.name, 'path': _path, - 'collectionGroup': _collectionGroup, + 'collectionGroup': _isCollectionGroup, 'parameters': _parameters, }, ).then((dynamic result) => result); @@ -90,7 +90,7 @@ class Query { { 'app': firestore.app.name, 'path': _path, - 'collectionGroup': _collectionGroup, + 'collectionGroup': _isCollectionGroup, 'parameters': _parameters, }, ); From 677c3f77de0858ddbf304858edd5b146d8f31210 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 21:34:13 +0545 Subject: [PATCH 10/25] Completed on IOS too --- .../cloud_firestore/ios/Classes/CloudFirestorePlugin.m | 8 +++++++- packages/cloud_firestore/lib/src/firestore.dart | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index 2ac51343725b..c59f3e62bc2c 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -39,7 +39,13 @@ } static FIRQuery *getQuery(NSDictionary *arguments) { - FIRQuery *query = [getFirestore(arguments) collectionWithPath:arguments[@"path"]]; + BOOL isCollectionGroup = arguments[@"collectionGroup"]; + FIRQuery *query; + if(isCollectionGroup){ + query = [getFirestore(arguments) collectionGroupWithID:arguments[@"path"]]; + } else { + query = [getFirestore(arguments) collectionWithPath:arguments[@"path"]]; + } NSDictionary *parameters = arguments[@"parameters"]; NSArray *whereConditions = parameters[@"where"]; for (id item in whereConditions) { diff --git a/packages/cloud_firestore/lib/src/firestore.dart b/packages/cloud_firestore/lib/src/firestore.dart index 038735279d53..b4e34dccb2fa 100644 --- a/packages/cloud_firestore/lib/src/firestore.dart +++ b/packages/cloud_firestore/lib/src/firestore.dart @@ -74,7 +74,7 @@ class Firestore { /// Gets a [Query] for the specified Collection group. Query collectionGroup(String path) { assert(path != null); - assert(path.contains("/"), "Collection IDs must not contain '/'."); + assert(!path.contains("/"), "Collection IDs must not contain '/'."); return Query._( firestore: this, isCollectionGroup: true, From 2649b9fdeef44d0ac7b0e0d8d4dbe70e238af93d Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 21:39:22 +0545 Subject: [PATCH 11/25] Making google formatter happy --- packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index c59f3e62bc2c..fed15f1b0ae3 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -41,7 +41,7 @@ static FIRQuery *getQuery(NSDictionary *arguments) { BOOL isCollectionGroup = arguments[@"collectionGroup"]; FIRQuery *query; - if(isCollectionGroup){ + if (isCollectionGroup) { query = [getFirestore(arguments) collectionGroupWithID:arguments[@"path"]]; } else { query = [getFirestore(arguments) collectionWithPath:arguments[@"path"]]; From 3cbc15e2d6ffeac72e35d7884cf00bb834f784cb Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 21:56:37 +0545 Subject: [PATCH 12/25] Added changelog and bumped version --- packages/cloud_firestore/CHANGELOG.md | 4 ++++ packages/cloud_firestore/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/cloud_firestore/CHANGELOG.md b/packages/cloud_firestore/CHANGELOG.md index afd7c7de6521..c30ea7ea43a9 100644 --- a/packages/cloud_firestore/CHANGELOG.md +++ b/packages/cloud_firestore/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.11.0+3 + +* Added support for collectionGroup query. + ## 0.11.0+2 * Remove iOS dependency on Firebase/Database and Firebase/Auth CocoaPods. diff --git a/packages/cloud_firestore/pubspec.yaml b/packages/cloud_firestore/pubspec.yaml index cb60be0d83ca..372e51aacfbc 100755 --- a/packages/cloud_firestore/pubspec.yaml +++ b/packages/cloud_firestore/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Cloud Firestore, a cloud-hosted, noSQL database live synchronization and offline support on Android and iOS. author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/cloud_firestore -version: 0.11.0+2 +version: 0.11.0+3 flutter: plugin: From 29fad5e7f1beedbb84ab74511d01f57f625ff128 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 22:01:03 +0545 Subject: [PATCH 13/25] Changed BOOL casting to NSNumber casting --- packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index fed15f1b0ae3..8f3bb46f03b0 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -39,7 +39,8 @@ } static FIRQuery *getQuery(NSDictionary *arguments) { - BOOL isCollectionGroup = arguments[@"collectionGroup"]; + NSNumber *data = arguments[@"collectionGroup"]; + BOOL isCollectionGroup = data.boolValue; FIRQuery *query; if (isCollectionGroup) { query = [getFirestore(arguments) collectionGroupWithID:arguments[@"path"]]; From 93f4ac73d64d51414e0804badd94382566be5564 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 22:01:56 +0545 Subject: [PATCH 14/25] decapitalized collection word --- packages/cloud_firestore/lib/src/firestore.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cloud_firestore/lib/src/firestore.dart b/packages/cloud_firestore/lib/src/firestore.dart index b4e34dccb2fa..3c6d7030280b 100644 --- a/packages/cloud_firestore/lib/src/firestore.dart +++ b/packages/cloud_firestore/lib/src/firestore.dart @@ -71,7 +71,7 @@ class Firestore { return CollectionReference._(this, path.split('/')); } - /// Gets a [Query] for the specified Collection group. + /// Gets a [Query] for the specified collection group. Query collectionGroup(String path) { assert(path != null); assert(!path.contains("/"), "Collection IDs must not contain '/'."); From 06c18e41c79120bd6beabae307b74057e1075735 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 22:05:22 +0545 Subject: [PATCH 15/25] Changrd bool variable name --- .../plugins/firebase/cloudfirestore/CloudFirestorePlugin.java | 2 +- packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m | 2 +- packages/cloud_firestore/lib/src/query.dart | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java index 7418ece02e73..b4820b4867ac 100644 --- a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java +++ b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java @@ -84,7 +84,7 @@ private FirebaseFirestore getFirestore(Map arguments) { } private Query getReference(Map arguments) { - if ((boolean) arguments.get("collectionGroup")) return getCollectionGroupReference(arguments); + if ((boolean) arguments.get("isCollectionGroup")) return getCollectionGroupReference(arguments); else return getCollectionReference(arguments); } diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index 8f3bb46f03b0..178e0cbd341d 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -39,7 +39,7 @@ } static FIRQuery *getQuery(NSDictionary *arguments) { - NSNumber *data = arguments[@"collectionGroup"]; + NSNumber *data = arguments[@"isCollectionGroup"]; BOOL isCollectionGroup = data.boolValue; FIRQuery *query; if (isCollectionGroup) { diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index 51f0fface0e5..1b189df535c5 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -61,7 +61,7 @@ class Query { { 'app': firestore.app.name, 'path': _path, - 'collectionGroup': _isCollectionGroup, + 'isCollectionGroup': _isCollectionGroup, 'parameters': _parameters, }, ).then((dynamic result) => result); @@ -90,7 +90,7 @@ class Query { { 'app': firestore.app.name, 'path': _path, - 'collectionGroup': _isCollectionGroup, + 'isCollectionGroup': _isCollectionGroup, 'parameters': _parameters, }, ); From 8d691e23eefda11cb2c4aa16d3326473aa544653 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 22:06:59 +0545 Subject: [PATCH 16/25] Added newer field value to the test --- packages/cloud_firestore/test/cloud_firestore_test.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index 4dc145313c1d..b5e9af8875fe 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -307,6 +307,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[], 'orderBy': >[], @@ -335,6 +336,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[ ['createdAt', '<', 100], @@ -366,6 +368,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[ ['profile', '==', null], @@ -397,6 +400,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[], 'orderBy': >[ @@ -601,6 +605,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[], 'orderBy': >[], @@ -612,6 +617,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[], 'orderBy': >[], @@ -627,6 +633,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[], 'orderBy': >[], @@ -642,6 +649,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[], 'orderBy': >[], @@ -657,6 +665,7 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', + 'isCollectionGroup': false, 'parameters': { 'where': >[], 'orderBy': >[], From 54c4b0fc5f598414d6d47fa95bbde0135154290f Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 22:28:23 +0545 Subject: [PATCH 17/25] Added some test for collection group query --- .../test/cloud_firestore_test.dart | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index b5e9af8875fe..418ada190c9a 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -17,6 +17,7 @@ void main() { Firestore firestore; final List log = []; CollectionReference collectionReference; + Query collectionGroupQuery; Transaction transaction; const Map kMockDocumentSnapshotData = { '1': 2 @@ -41,6 +42,7 @@ void main() { ); firestore = Firestore(app: app); collectionReference = firestore.collection('foo'); + collectionGroupQuery = firestore.collectionGroup('bar'); transaction = Transaction(0, firestore); Firestore.channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); @@ -682,6 +684,134 @@ void main() { }); }); + group('Query', () { + test('getDocuments', () async { + QuerySnapshot snapshot = await collectionGroupQuery.getDocuments(); + DocumentSnapshot document = snapshot.documents.first; + expect(document.documentID, equals('0')); + expect(document.reference.path, equals('bar/0')); + expect(document.data, equals(kMockDocumentSnapshotData)); + + // startAtDocument + snapshot = + await collectionGroupQuery.startAtDocument(document).getDocuments(); + document = snapshot.documents.first; + expect(document.documentID, equals('0')); + expect(document.reference.path, equals('bar/0')); + expect(document.data, equals(kMockDocumentSnapshotData)); + + // startAfterDocument + snapshot = await collectionGroupQuery + .startAfterDocument(document) + .getDocuments(); + document = snapshot.documents.first; + expect(document.documentID, equals('0')); + expect(document.reference.path, equals('bar/0')); + expect(document.data, equals(kMockDocumentSnapshotData)); + + // endAtDocument + snapshot = + await collectionGroupQuery.endAtDocument(document).getDocuments(); + document = snapshot.documents.first; + expect(document.documentID, equals('0')); + expect(document.reference.path, equals('bar/0')); + expect(document.data, equals(kMockDocumentSnapshotData)); + + // endBeforeDocument + snapshot = await collectionGroupQuery + .endBeforeDocument(document) + .getDocuments(); + document = snapshot.documents.first; + expect(document.documentID, equals('0')); + expect(document.reference.path, equals('bar/0')); + expect(document.data, equals(kMockDocumentSnapshotData)); + + expect( + log, + equals( + [ + isMethodCall( + 'Query#getDocuments', + arguments: { + 'app': app.name, + 'path': 'bar', + 'isCollectionGroup': true, + 'parameters': { + 'where': >[], + 'orderBy': >[], + }, + }, + ), + isMethodCall( + 'Query#getDocuments', + arguments: { + 'app': app.name, + 'path': 'bar', + 'isCollectionGroup': true, + 'parameters': { + 'where': >[], + 'orderBy': >[], + 'startAtDocument': { + 'id': '0', + 'data': kMockDocumentSnapshotData, + }, + }, + }, + ), + isMethodCall( + 'Query#getDocuments', + arguments: { + 'app': app.name, + 'path': 'bar', + 'isCollectionGroup': true, + 'parameters': { + 'where': >[], + 'orderBy': >[], + 'startAfterDocument': { + 'id': '0', + 'data': kMockDocumentSnapshotData, + }, + }, + }, + ), + isMethodCall( + 'Query#getDocuments', + arguments: { + 'app': app.name, + 'path': 'bar', + 'isCollectionGroup': true, + 'parameters': { + 'where': >[], + 'orderBy': >[], + 'endAtDocument': { + 'id': '0', + 'data': kMockDocumentSnapshotData, + }, + }, + }, + ), + isMethodCall( + 'Query#getDocuments', + arguments: { + 'app': app.name, + 'path': 'bar', + 'isCollectionGroup': true, + 'parameters': { + 'where': >[], + 'orderBy': >[], + 'endBeforeDocument': { + 'id': '0', + 'data': kMockDocumentSnapshotData, + }, + }, + }, + ), + ], + ), + ); + }); + }); + group('FirestoreMessageCodec', () { const MessageCodec codec = FirestoreMessageCodec(); final DateTime testTime = DateTime(2015, 10, 30, 11, 16); From 10f11b0ba00d8390a4e40e0e1d07a4ab481367dc Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 22:29:46 +0545 Subject: [PATCH 18/25] Changed name od the tests --- packages/cloud_firestore/test/cloud_firestore_test.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index 418ada190c9a..51d69252341f 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -557,7 +557,7 @@ void main() { }); group('Query', () { - test('getDocuments', () async { + test('getDocumentsFromCollection', () async { QuerySnapshot snapshot = await collectionReference.getDocuments(); DocumentSnapshot document = snapshot.documents.first; expect(document.documentID, equals('0')); @@ -682,10 +682,7 @@ void main() { ), ); }); - }); - - group('Query', () { - test('getDocuments', () async { + test('getDocumentsFromCollectionGroup', () async { QuerySnapshot snapshot = await collectionGroupQuery.getDocuments(); DocumentSnapshot document = snapshot.documents.first; expect(document.documentID, equals('0')); From 4da92b2c9d56a38fdff2ae561b87f64f8ac5b270 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Wed, 22 May 2019 22:37:07 +0545 Subject: [PATCH 19/25] Formatted test file --- packages/cloud_firestore/test/cloud_firestore_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index 51d69252341f..6689599a89c6 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -285,6 +285,7 @@ void main() { expect(a.hashCode == b.hashCode, isFalse); }); }); + group('CollectionsReference', () { test('id', () async { expect(collectionReference.id, equals('foo')); From 5630fe40a40b20520ff461bb804d89c14600b297 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Thu, 23 May 2019 09:14:47 +0545 Subject: [PATCH 20/25] Fixed test fail case --- packages/cloud_firestore/lib/src/query.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index 1b189df535c5..f7e317ae2dca 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -33,6 +33,7 @@ class Query { Query _copyWithParameters(Map parameters) { return Query._( firestore: firestore, + isCollectionGroup: _isCollectionGroup, pathComponents: _pathComponents, parameters: Map.unmodifiable( Map.from(_parameters)..addAll(parameters), From 6721d26ad1e51052a7faa27e3138d63636868a45 Mon Sep 17 00:00:00 2001 From: Aawaz Gyawali Date: Thu, 23 May 2019 11:00:22 +0545 Subject: [PATCH 21/25] Added som test(UNtested) --- .../example/test_driver/cloud_firestore.dart | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index 71ec266baf97..9db678f14301 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -25,7 +25,7 @@ void main() { firestore = Firestore(app: app); }); - test('getDocuments', () async { + test('getDocumentsFromCollection', () async { final Query query = firestore .collection('messages') .where('message', isEqualTo: 'Hello world!') @@ -40,6 +40,21 @@ void main() { expect(snapshot.data['message'], 'Hello world!'); }); + test('getDocumentsFromCollectionGroup', () async { + final Query query = firestore + .collectionGroup('reviews') + .where('message', isEqualTo: 'Hello world!') + .limit(1); + final QuerySnapshot querySnapshot = await query.getDocuments(); + expect(querySnapshot.documents.first['message'], 'Hello world!'); + final DocumentReference firstDoc = + querySnapshot.documents.first.reference; + final DocumentSnapshot documentSnapshot = await firstDoc.get(); + expect(documentSnapshot.data['message'], 'Hello world!'); + final DocumentSnapshot snapshot = await firstDoc.snapshots().first; + expect(snapshot.data['message'], 'Hello world!'); + }); + test('increment', () async { final DocumentReference ref = firestore.collection('messages').document(); await ref.setData({ From 930a47fc459ec8d68f68a4782696dff7f37a0cbe Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Tue, 4 Jun 2019 12:42:23 -0700 Subject: [PATCH 22/25] Fix test --- .../test/cloud_firestore_test.dart | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index 1c9e0d92a28c..50daf11b3859 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -26,7 +26,6 @@ void main() { "hasPendingWrites": false, "isFromCache": false, }; - setUp(() async { mockHandleId = 0; // Required for FirebaseApp.configure @@ -640,11 +639,8 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', -<<<<<<< HEAD 'isCollectionGroup': false, -======= 'source': 'server', ->>>>>>> origin/master 'parameters': { 'where': >[], 'orderBy': >[], @@ -656,11 +652,8 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', -<<<<<<< HEAD 'isCollectionGroup': false, -======= 'source': 'default', ->>>>>>> origin/master 'parameters': { 'where': >[], 'orderBy': >[], @@ -676,11 +669,8 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', -<<<<<<< HEAD 'isCollectionGroup': false, -======= 'source': 'default', ->>>>>>> origin/master 'parameters': { 'where': >[], 'orderBy': >[], @@ -696,11 +686,8 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', -<<<<<<< HEAD 'isCollectionGroup': false, -======= 'source': 'default', ->>>>>>> origin/master 'parameters': { 'where': >[], 'orderBy': >[], @@ -716,8 +703,8 @@ void main() { arguments: { 'app': app.name, 'path': 'foo', -<<<<<<< HEAD 'isCollectionGroup': false, + 'source': 'default', 'parameters': { 'where': >[], 'orderBy': >[], @@ -787,6 +774,7 @@ void main() { 'where': >[], 'orderBy': >[], }, + 'source': 'default', }, ), isMethodCall( @@ -803,6 +791,7 @@ void main() { 'data': kMockDocumentSnapshotData, }, }, + 'source': 'default', }, ), isMethodCall( @@ -819,6 +808,7 @@ void main() { 'data': kMockDocumentSnapshotData, }, }, + 'source': 'default', }, ), isMethodCall( @@ -835,6 +825,7 @@ void main() { 'data': kMockDocumentSnapshotData, }, }, + 'source': 'default', }, ), isMethodCall( @@ -843,9 +834,7 @@ void main() { 'app': app.name, 'path': 'bar', 'isCollectionGroup': true, -======= 'source': 'default', ->>>>>>> origin/master 'parameters': { 'where': >[], 'orderBy': >[], @@ -854,6 +843,7 @@ void main() { 'data': kMockDocumentSnapshotData, }, }, + 'source': 'default', }, ), ], From ab271f8aefade5bc3c97618d0fcdb5a3826ea84b Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Tue, 4 Jun 2019 13:21:47 -0700 Subject: [PATCH 23/25] Fix tests --- .../firebase/cloudfirestore/FlutterFirebaseAppRegistrar.java | 2 +- packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m | 2 +- packages/cloud_firestore/test/cloud_firestore_test.dart | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/FlutterFirebaseAppRegistrar.java b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/FlutterFirebaseAppRegistrar.java index efdf7eefcc17..83b35dcab473 100644 --- a/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/FlutterFirebaseAppRegistrar.java +++ b/packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/FlutterFirebaseAppRegistrar.java @@ -8,7 +8,7 @@ public class FlutterFirebaseAppRegistrar implements ComponentRegistrar { private static final String LIBRARY_NAME = "flutter-fire-fst"; - private static final String LIBRARY_VERSION = "0.12.2"; + private static final String LIBRARY_VERSION = "0.12.3"; @Override public List> getComponents() { diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index b8c8ff0831aa..2ce65c02ebff 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -7,7 +7,7 @@ #import #define LIBRARY_NAME @"flutter-firebase_cloud_firestore" -#define LIBRARY_VERSION @"0.12.2" +#define LIBRARY_VERSION @"0.12.3" static FlutterError *getFlutterError(NSError *error) { if (error == nil) return nil; diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index 50daf11b3859..bee9a4e01d41 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -590,7 +590,8 @@ void main() { group('Query', () { test('getDocumentsFromCollection', () async { - QuerySnapshot snapshot = await collectionReference.getDocuments(source: Source.server); + QuerySnapshot snapshot = + await collectionReference.getDocuments(source: Source.server); DocumentSnapshot document = snapshot.documents.first; expect(document.documentID, equals('0')); expect(document.reference.path, equals('foo/0')); From 8d87d51aaa4d1a845116e7f7b1a27283654f4fd9 Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Tue, 4 Jun 2019 14:23:21 -0700 Subject: [PATCH 24/25] Fix merge damage --- packages/cloud_firestore/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cloud_firestore/CHANGELOG.md b/packages/cloud_firestore/CHANGELOG.md index 45e01ae1b6b8..1b31f6262404 100644 --- a/packages/cloud_firestore/CHANGELOG.md +++ b/packages/cloud_firestore/CHANGELOG.md @@ -10,7 +10,6 @@ **Note** this is an Android only change, the iOS implementation was not impacted. * Updated the Firebase reporting string to `flutter-fire-fst` to be consistent with other reporting libraries. ->>>>>>> origin/master ## 0.12.1 From 0c964da43630dac0ca1ebfe1e00fae38faf37aa9 Mon Sep 17 00:00:00 2001 From: Collin Jackson Date: Tue, 4 Jun 2019 20:40:50 -0700 Subject: [PATCH 25/25] Ensure that CocoaPods on Cirrus is always the latest version. --- .cirrus.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.cirrus.yml b/.cirrus.yml index d323feeba2c2..0897b3dc4911 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -69,6 +69,7 @@ task: SIMCTL_CHILD_MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] setup_script: - brew update + - brew upgrade cocoapods - brew install libimobiledevice - brew install ideviceinstaller - brew install ios-deploy