From 3c23650d5eb2bae155b2019ea21dbd0198854f28 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Tue, 22 Oct 2019 16:12:49 +0000 Subject: [PATCH 01/25] initial --- packages/cloud_firestore/CHANGELOG.md | 4 +++ .../example/android/gradle.properties | 1 + .../example/test_driver/cloud_firestore.dart | 25 ++++++++++++++++++ .../cloud_firestore/lib/cloud_firestore.dart | 1 + .../cloud_firestore/lib/src/field_path.dart | 11 ++++++++ packages/cloud_firestore/pubspec.yaml | 2 +- .../test/cloud_firestore_test.dart | 26 +++++++++++++++++++ 7 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 packages/cloud_firestore/lib/src/field_path.dart diff --git a/packages/cloud_firestore/CHANGELOG.md b/packages/cloud_firestore/CHANGELOG.md index 14acb4c4ec2b..6be26bf18acf 100644 --- a/packages/cloud_firestore/CHANGELOG.md +++ b/packages/cloud_firestore/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.10 + +* Added `FieldPath` class and `FieldPath.documentId` to refer to the document id in queries. + ## 0.12.9+5 * Fixes a crash on Android when running a transaction without an internet connection. diff --git a/packages/cloud_firestore/example/android/gradle.properties b/packages/cloud_firestore/example/android/gradle.properties index 8bd86f680510..7be3d8b46841 100755 --- a/packages/cloud_firestore/example/android/gradle.properties +++ b/packages/cloud_firestore/example/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index 12460b7ad07f..bc37e8cb3290 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -294,5 +294,30 @@ void main() { await doc1.delete(); await doc2.delete(); }); + + test('FieldPath.documentId', () async { + // Populate the database with two test documents. + final CollectionReference messages = firestore.collection('messages'); + final DocumentReference doc = messages.document(); + // Use document ID as a unique identifier to ensure that we don't + // collide with other tests running against this database. + final String uniqueId = doc.documentID; + await doc.setData({ + 'message': 'testing field path', + 'created_at': FieldValue.serverTimestamp(), + }); + + final QuerySnapshot snapshot = await messages + .where(FieldPath.documentId, isEqualTo: uniqueId) + .getDocuments(); + + await doc.delete(); + + final List results = snapshot.documents; + final DocumentSnapshot result = results[0]; + + expect(results.length, 1); + expect(result.data['message'], 'testing field path'); + }); }); } diff --git a/packages/cloud_firestore/lib/cloud_firestore.dart b/packages/cloud_firestore/lib/cloud_firestore.dart index fba72b049cb6..aef386e77d26 100755 --- a/packages/cloud_firestore/lib/cloud_firestore.dart +++ b/packages/cloud_firestore/lib/cloud_firestore.dart @@ -22,6 +22,7 @@ part 'src/collection_reference.dart'; part 'src/document_change.dart'; part 'src/document_reference.dart'; part 'src/document_snapshot.dart'; +part 'src/field_path.dart'; part 'src/field_value.dart'; part 'src/firestore.dart'; part 'src/firestore_message_codec.dart'; diff --git a/packages/cloud_firestore/lib/src/field_path.dart b/packages/cloud_firestore/lib/src/field_path.dart new file mode 100644 index 000000000000..3177424a129d --- /dev/null +++ b/packages/cloud_firestore/lib/src/field_path.dart @@ -0,0 +1,11 @@ +// Copyright 2017, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +part of cloud_firestore; + +/// A [FieldPath] refers to a field in a document. +class FieldPath { + /// The path to the document id, which can be used in queries. + static String get documentId => '__name__'; +} diff --git a/packages/cloud_firestore/pubspec.yaml b/packages/cloud_firestore/pubspec.yaml index 4b1236a19312..b244f0e728b4 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/FirebaseExtended/flutterfire/tree/master/packages/cloud_firestore -version: 0.12.9+5 +version: 0.12.10 flutter: plugin: diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index 32729d6954d7..ff9f2afe8c91 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -968,6 +968,32 @@ void main() { ), ); }); + + test('FieldPath', () async { + await collectionReference + .where(FieldPath.documentId, isEqualTo: 'bar') + .getDocuments(); + expect( + log, + equals([ + isMethodCall( + 'Query#getDocuments', + arguments: { + 'app': app.name, + 'path': 'foo', + 'isCollectionGroup': false, + 'parameters': { + 'where': >[ + ['__name__', '==', 'bar'], + ], + 'orderBy': >[], + }, + 'source': 'default', + }, + ), + ]), + ); + }); }); group('FirestoreMessageCodec', () { From df77698c031bf3318430259055bb6f79efe035e1 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Tue, 22 Oct 2019 16:26:07 +0000 Subject: [PATCH 02/25] format --- .../test/cloud_firestore_test.dart | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index ff9f2afe8c91..9ba56f8bd638 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -970,30 +970,30 @@ void main() { }); test('FieldPath', () async { - await collectionReference - .where(FieldPath.documentId, isEqualTo: 'bar') - .getDocuments(); - expect( - log, - equals([ - isMethodCall( - 'Query#getDocuments', - arguments: { - 'app': app.name, - 'path': 'foo', - 'isCollectionGroup': false, - 'parameters': { - 'where': >[ - ['__name__', '==', 'bar'], - ], - 'orderBy': >[], - }, - 'source': 'default', + await collectionReference + .where(FieldPath.documentId, isEqualTo: 'bar') + .getDocuments(); + expect( + log, + equals([ + isMethodCall( + 'Query#getDocuments', + arguments: { + 'app': app.name, + 'path': 'foo', + 'isCollectionGroup': false, + 'parameters': { + 'where': >[ + ['__name__', '==', 'bar'], + ], + 'orderBy': >[], }, - ), - ]), - ); - }); + 'source': 'default', + }, + ), + ]), + ); + }); }); group('FirestoreMessageCodec', () { From c2d83867d9500a43d1796fe1148f2643ed247b75 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 07:59:53 +0000 Subject: [PATCH 03/25] Update gradle.properties --- packages/cloud_firestore/example/android/gradle.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cloud_firestore/example/android/gradle.properties b/packages/cloud_firestore/example/android/gradle.properties index 7be3d8b46841..8bd86f680510 100755 --- a/packages/cloud_firestore/example/android/gradle.properties +++ b/packages/cloud_firestore/example/android/gradle.properties @@ -1,2 +1 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true From 30e24d51d8f9628171095fd5456408529dc49836 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 10:34:02 +0000 Subject: [PATCH 04/25] codec implementation --- .../cloudfirestore/CloudFirestorePlugin.java | 3 +++ .../ios/Classes/CloudFirestorePlugin.m | 4 ++++ packages/cloud_firestore/lib/src/field_path.dart | 11 ++++++++++- .../lib/src/firestore_message_codec.dart | 12 ++++++++++++ packages/cloud_firestore/lib/src/query.dart | 13 ++++++++----- .../cloud_firestore/test/cloud_firestore_test.dart | 7 ++++++- 6 files changed, 43 insertions(+), 7 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 eddabe3aa400..fc1ad98d3750 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 @@ -859,6 +859,7 @@ final class FirestoreMessageCodec extends StandardMessageCodec { private static final byte TIMESTAMP = (byte) 136; private static final byte INCREMENT_DOUBLE = (byte) 137; private static final byte INCREMENT_INTEGER = (byte) 138; + private static final byte DOCUMENT_ID = (byte) 139; @Override protected void writeValue(ByteArrayOutputStream stream, Object value) { @@ -922,6 +923,8 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) { case INCREMENT_DOUBLE: final Number doubleIncrementValue = (Number) readValue(buffer); return FieldValue.increment(doubleIncrementValue.doubleValue()); + case DOCUMENT_ID: + return FieldPath.documentId(); default: return super.readValueOfType(type, buffer); } diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index ccb7dee868fc..a566ffcb515c 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -221,6 +221,7 @@ static FIRFirestoreSource getSource(NSDictionary *arguments) { const UInt8 TIMESTAMP = 136; const UInt8 INCREMENT_DOUBLE = 137; const UInt8 INCREMENT_INTEGER = 138; +const UInt8 DOCUMENT_ID = 138; @interface FirestoreWriter : FlutterStandardWriter - (void)writeValue:(id)value; @@ -323,6 +324,9 @@ - (id)readValueOfType:(UInt8)type { NSNumber *value = [self readValue]; return [FIRFieldValue fieldValueForIntegerIncrement:value.intValue]; } + case DOCUMENT_ID: { + return [FIRFieldPath documentID]; + } default: return [super readValueOfType:type]; } diff --git a/packages/cloud_firestore/lib/src/field_path.dart b/packages/cloud_firestore/lib/src/field_path.dart index 3177424a129d..e144cd0b0bea 100644 --- a/packages/cloud_firestore/lib/src/field_path.dart +++ b/packages/cloud_firestore/lib/src/field_path.dart @@ -4,8 +4,17 @@ part of cloud_firestore; +enum _FieldPathType { + documentId, +} + /// A [FieldPath] refers to a field in a document. class FieldPath { + const FieldPath._(this.type); + + @visibleForTesting + final _FieldPathType type; + /// The path to the document id, which can be used in queries. - static String get documentId => '__name__'; + static FieldPath get documentId => const FieldPath._(_FieldPathType.documentId); } diff --git a/packages/cloud_firestore/lib/src/firestore_message_codec.dart b/packages/cloud_firestore/lib/src/firestore_message_codec.dart index 14f6e020675c..1a555fce4bb9 100644 --- a/packages/cloud_firestore/lib/src/firestore_message_codec.dart +++ b/packages/cloud_firestore/lib/src/firestore_message_codec.dart @@ -19,6 +19,7 @@ class FirestoreMessageCodec extends StandardMessageCodec { static const int _kTimestamp = 136; static const int _kIncrementDouble = 137; static const int _kIncrementInteger = 138; + static const int _kDocumentId = 139; static const Map _kFieldValueCodes = { @@ -30,6 +31,11 @@ class FirestoreMessageCodec extends StandardMessageCodec { FieldValueType.incrementInteger: _kIncrementInteger, }; + static const Map<_FieldPathType, int> _kFieldPathCodes = + <_FieldPathType, int>{ + _FieldPathType.documentId: _kDocumentId, + }; + @override void writeValue(WriteBuffer buffer, dynamic value) { if (value is DateTime) { @@ -60,6 +66,10 @@ class FirestoreMessageCodec extends StandardMessageCodec { assert(code != null); buffer.putUint8(code); if (value.value != null) writeValue(buffer, value.value); + } else if (value is FieldPath) { + final int code = _kFieldPathCodes[value.type]; + assert(code != null); + buffer.putUint8(code); } else { super.writeValue(buffer, value); } @@ -104,6 +114,8 @@ class FirestoreMessageCodec extends StandardMessageCodec { case _kIncrementInteger: final int value = readValue(buffer); return FieldValue.increment(value); + case _kDocumentId: + return FieldPath.documentId; default: return super.readValueOfType(type, buffer); } diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index 114b48a6df57..6e1ec5152460 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -110,14 +110,16 @@ class Query { /// Creates and returns a new [Query] with additional filter on specified /// [field]. [field] refers to a field in a document. /// - /// The [field] may consist of a single field name (referring to a top level - /// field in the document), or a series of field names seperated by dots '.' + /// The [field] may be a [String] consisting of a single field name + /// (referring to a top level field in the document), + /// or a series of field names separated by dots '.' /// (referring to a nested field in the document). + /// Alternatively, the [field] can also be a [FieldPath]. /// /// Only documents satisfying provided condition are included in the result /// set. Query where( - String field, { + dynamic field, { dynamic isEqualTo, dynamic isLessThan, dynamic isLessThanOrEqualTo, @@ -130,7 +132,7 @@ class Query { final List> conditions = List>.from(_parameters['where']); - void addCondition(String field, String operator, dynamic value) { + void addCondition(dynamic field, String operator, dynamic value) { final List condition = [field, operator, value]; assert( conditions @@ -162,7 +164,8 @@ class Query { /// Creates and returns a new [Query] that's additionally sorted by the specified /// [field]. - Query orderBy(String field, {bool descending = false}) { + /// The field may be a [String] representing a single field name or a [FieldPath]. + Query orderBy(dynamic field, {bool descending = false}) { final List> orders = List>.from(_parameters['orderBy']); diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index 9ba56f8bd638..d43720231d4d 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -984,7 +984,7 @@ void main() { 'isCollectionGroup': false, 'parameters': { 'where': >[ - ['__name__', '==', 'bar'], + [FieldPath.documentId, '==', 'bar'], ], 'orderBy': >[], }, @@ -1030,6 +1030,10 @@ void main() { _checkEncodeDecode(codec, FieldValue.increment(1.0)); _checkEncodeDecode(codec, FieldValue.increment(1)); }); + + test('encode and decode FieldPath', () { + _checkEncodeDecode(codec, FieldPath.documentId); + }); }); group('Timestamp', () { @@ -1260,6 +1264,7 @@ bool _deepEquals(dynamic valueA, dynamic valueB) { if (valueA is FieldValue) { return valueB is FieldValue && _deepEqualsFieldValue(valueA, valueB); } + if (valueA is FieldPath) return valueB is FieldPath && valueA.type == valueB.type; return valueA == valueB; } From 52910ef1f0f2d6737eea3f780f4eebcc4fc90e6b Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:25:22 +0000 Subject: [PATCH 05/25] java implementation, tests --- .../cloudfirestore/CloudFirestorePlugin.java | 1059 +++++++++-------- .../example/test_driver/cloud_firestore.dart | 49 +- packages/cloud_firestore/lib/src/query.dart | 8 + 3 files changed, 606 insertions(+), 510 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 fc1ad98d3750..6beaef774fac 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 @@ -8,8 +8,10 @@ import android.os.AsyncTask; import android.util.Log; import android.util.SparseArray; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; @@ -38,13 +40,7 @@ import com.google.firebase.firestore.Source; 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; @@ -55,6 +51,14 @@ 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 { private static final String TAG = "CloudFirestorePlugin"; @@ -73,10 +77,10 @@ public class CloudFirestorePlugin implements MethodCallHandler { public static void registerWith(PluginRegistry.Registrar registrar) { final MethodChannel channel = - new MethodChannel( - registrar.messenger(), - "plugins.flutter.io/cloud_firestore", - new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); + new MethodChannel( + registrar.messenger(), + "plugins.flutter.io/cloud_firestore", + new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); channel.setMethodCallHandler(new CloudFirestorePlugin(channel, registrar.activity())); } @@ -123,22 +127,35 @@ private Source getSource(Map arguments) { } private Object[] getDocumentValues( - Map document, List> orderBy, Map arguments) { + Map document, List> orderBy, Map arguments) { 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); - if (orderByFieldName.contains(".")) { - String[] fieldNameParts = orderByFieldName.split("\\."); - Map current = (Map) documentData.get(fieldNameParts[0]); - for (int i = 1; i < fieldNameParts.length - 1; i++) { - current = (Map) current.get(fieldNameParts[i]); + final Object field = order.get(0); + + if (field instanceof FieldPath) { + final FieldPath fieldPath = (FieldPath) field; + if (fieldPath == FieldPath.documentId()) { + data.add(documentId); + } else { + // Unsupported type. + } + } else if (field instanceof String) { + String orderByFieldName = (String) field; + if (orderByFieldName.contains(".")) { + String[] fieldNameParts = orderByFieldName.split("\\."); + Map current = (Map) documentData.get(fieldNameParts[0]); + for (int i = 1; i < fieldNameParts.length - 1; i++) { + current = (Map) current.get(fieldNameParts[i]); + } + data.add(current.get(fieldNameParts[fieldNameParts.length - 1])); + } else { + data.add(documentData.get(orderByFieldName)); } - data.add(current.get(fieldNameParts[fieldNameParts.length - 1])); } else { - data.add(documentData.get(orderByFieldName)); + // Invalid type. } } } @@ -186,7 +203,7 @@ private Map parseQuerySnapshot(QuerySnapshot querySnapshot) { change.put("path", documentChange.getDocument().getReference().getPath()); Map metadata = new HashMap(); metadata.put( - "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); + "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); metadata.put("isFromCache", documentChange.getDocument().getMetadata().isFromCache()); change.put("metadata", metadata); documentChanges.add(change); @@ -213,21 +230,67 @@ private Query getQuery(Map arguments) { @SuppressWarnings("unchecked") List> whereConditions = (List>) parameters.get("where"); for (List condition : whereConditions) { - String fieldName = (String) condition.get(0); + String fieldName = null; + FieldPath fieldPath = null; + final Object field = condition.get(0); + if (field instanceof String) { + fieldName = (String) field; + } else if (field instanceof FieldPath) { + fieldPath = (FieldPath) field; + } else { + // Invalid type. + } + String operator = (String) condition.get(1); Object value = condition.get(2); if ("==".equals(operator)) { - query = query.whereEqualTo(fieldName, value); + if (fieldName != null) { + query = query.whereEqualTo(fieldName, value); + } else if (fieldPath != null) { + query = query.whereEqualTo(fieldPath, value); + } else { + // Invalid type. + } } else if ("<".equals(operator)) { - query = query.whereLessThan(fieldName, value); + if (fieldName != null) { + query = query.whereLessThan(fieldName, value); + } else if (fieldPath != null) { + query = query.whereLessThan(fieldPath, value); + } else { + // Invalid type. + } } else if ("<=".equals(operator)) { - query = query.whereLessThanOrEqualTo(fieldName, value); + if (fieldName != null) { + query = query.whereLessThanOrEqualTo(fieldName, value); + } else if (fieldPath != null) { + query = query.whereLessThanOrEqualTo(fieldPath, value); + } else { + // Invalid type. + } } else if (">".equals(operator)) { - query = query.whereGreaterThan(fieldName, value); + if (fieldName != null) { + query = query.whereGreaterThan(fieldName, value); + } else if (fieldPath != null) { + query = query.whereGreaterThan(fieldPath, value); + } else { + // Invalid type. + } } else if (">=".equals(operator)) { - query = query.whereGreaterThanOrEqualTo(fieldName, value); + if (fieldName != null) { + query = query.whereGreaterThanOrEqualTo(fieldName, value); + } else if (fieldPath != null) { + query = query.whereGreaterThanOrEqualTo(fieldPath, value); + } else { + // Invalid type. + } } else if ("array-contains".equals(operator)) { - query = query.whereArrayContains(fieldName, value); + if (fieldName != null) { + query = query.whereArrayContains(fieldName, value); + } else if (fieldPath != null) { + query = query.whereArrayContains(fieldPath, value); + } else { + // Invalid type. + } } else { // Invalid operator. } @@ -239,29 +302,46 @@ private Query getQuery(Map arguments) { List> orderBy = (List>) parameters.get("orderBy"); if (orderBy == null) return query; for (List order : orderBy) { - String orderByFieldName = (String) order.get(0); + String fieldName = null; + FieldPath fieldPath = null; + final Object field = order.get(0); + if (field instanceof String) { + fieldName = (String) field; + } else if (field instanceof FieldPath) { + fieldPath = (FieldPath) field; + } else { + // Invalid type. + } + boolean descending = (boolean) order.get(1); Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; - query = query.orderBy(orderByFieldName, direction); + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + + if (fieldName != null) { + query = query.orderBy(fieldName, direction); + } else if (fieldPath != null) { + query = query.orderBy(fieldPath, direction); + } else { + // Invalid type. + } } @SuppressWarnings("unchecked") Map startAtDocument = (Map) parameters.get("startAtDocument"); @SuppressWarnings("unchecked") Map startAfterDocument = - (Map) parameters.get("startAfterDocument"); + (Map) parameters.get("startAfterDocument"); @SuppressWarnings("unchecked") Map endAtDocument = (Map) parameters.get("endAtDocument"); @SuppressWarnings("unchecked") Map endBeforeDocument = - (Map) parameters.get("endBeforeDocument"); + (Map) parameters.get("endBeforeDocument"); if (startAtDocument != null - || startAfterDocument != null - || endAtDocument != null - || endBeforeDocument != null) { + || startAfterDocument != null + || endAtDocument != null + || endBeforeDocument != null) { boolean descending = (boolean) orderBy.get(orderBy.size() - 1).get(1); Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; query = query.orderBy(FieldPath.documentId(), direction); } if (startAtDocument != null) { @@ -346,492 +426,473 @@ public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) { private void addDefaultListeners(final String description, Task task, final Result result) { task.addOnSuccessListener( - new OnSuccessListener() { - @Override - public void onSuccess(Void ignored) { - result.success(null); - } - }); + 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); - } - }); + 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 TransactionResult apply(@NonNull Transaction transaction) { - // Store transaction. - int transactionId = (Integer) arguments.get("transactionId"); - transactions.append(transactionId, transaction); - completionTasks.append(transactionId, transactionTCS); - - // Start operations on Dart side. - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - 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) { - transactionTCS.trySetException( - new Exception("DoTransaction failed: " + errorMessage)); - } - - @Override - public void notImplemented() { - transactionTCS.trySetException( - new Exception("DoTransaction not implemented")); - } - }); + 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 TransactionResult apply(@NonNull Transaction transaction) { + // Store transaction. + int transactionId = (Integer) arguments.get("transactionId"); + transactions.append(transactionId, transaction); + completionTasks.append(transactionId, transactionTCS); + + // Start operations on Dart side. + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + 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) { + transactionTCS.trySetException( + new Exception("DoTransaction failed: " + errorMessage)); + } + + @Override + public void notImplemented() { + transactionTCS.trySetException( + new Exception("DoTransaction not implemented")); + } + }); + } + }); + + // Wait till transaction is complete. + try { + String timeoutKey = "transactionTimeout"; + long timeout = ((Number) arguments.get(timeoutKey)).longValue(); + final Map transactionResult = + Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); + + // Once transaction completes return the result to the Dart side. + return new TransactionResult(transactionResult); + } catch (Exception e) { + Log.e(TAG, e.getMessage(), e); + return new TransactionResult(e); } - }); - - // Wait till transaction is complete. - try { - String timeoutKey = "transactionTimeout"; - long timeout = ((Number) arguments.get(timeoutKey)).longValue(); - final Map transactionResult = - Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); - - // Once transaction completes return the result to the Dart side. - return new TransactionResult(transactionResult); - } catch (Exception e) { - Log.e(TAG, e.getMessage(), e); - return new TransactionResult(e); - } - } - }) - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(Task task) { - if (!task.isSuccessful()) { - result.error( - "Error performing transaction", task.getException().getMessage(), null); - return; - } - - TransactionResult transactionResult = task.getResult(); - if (transactionResult.exception == null) { - result.success(transactionResult.result); - } else { - result.error( - "Error performing transaction", - transactionResult.exception.getMessage(), - 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)); - final 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); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(snapshotMap); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - 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); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.error("Error performing Transaction#update", e.getMessage(), null); - } - }); - } - return null; - } - }.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"); - try { - transaction.set(getDocumentReference(arguments), data); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.error("Error performing Transaction#set", e.getMessage(), 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) { - try { - transaction.delete(getDocumentReference(arguments)); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.error("Error performing Transaction#delete", e.getMessage(), null); - } - }); + } + }) + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(Task task) { + if (!task.isSuccessful()) { + result.error( + "Error performing transaction", task.getException().getMessage(), null); + return; + } + + TransactionResult transactionResult = task.getResult(); + if (transactionResult.exception == null) { + result.success(transactionResult.result); + } else { + result.error( + "Error performing transaction", + transactionResult.exception.getMessage(), + 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)); + final Map snapshotMap = new HashMap<>(); + snapshotMap.put("path", documentSnapshot.getReference().getPath()); + if (documentSnapshot.exists()) { + snapshotMap.put("data", documentSnapshot.getData()); + } else { + snapshotMap.put("data", null); } - return null; + Map metadata = new HashMap(); + metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); + snapshotMap.put("metadata", metadata); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(snapshotMap); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#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")); + return null; } - 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); - MetadataChanges metadataChanges = - (Boolean) arguments.get("includeMetadataChanges") - ? MetadataChanges.INCLUDE - : MetadataChanges.EXCLUDE; - listenerRegistrations.put( - handle, getQuery(arguments).addSnapshotListener(metadataChanges, observer)); - result.success(handle); - break; - } - case "DocumentReference#addSnapshotListener": - { - Map arguments = call.arguments(); - int handle = nextListenerHandle++; - DocumentObserver observer = new DocumentObserver(handle); - documentObservers.put(handle, observer); - MetadataChanges metadataChanges = - (Boolean) arguments.get("includeMetadataChanges") - ? MetadataChanges.INCLUDE - : MetadataChanges.EXCLUDE; - listenerRegistrations.put( - handle, - getDocumentReference(arguments).addSnapshotListener(metadataChanges, observer)); - result.success(handle); - break; - } - case "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); - Source source = getSource(arguments); - Task task = query.get(source); - 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); + }.execute(); + break; + } + case "Transaction#update": { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { @SuppressWarnings("unchecked") - Map options = (Map) arguments.get("options"); + @Override + protected Void doInBackground(Void... voids) { + Map data = (Map) arguments.get("data"); + try { + transaction.update(getDocumentReference(arguments), data); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(null); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#update", e.getMessage(), null); + } + }); + } + return null; + } + }.execute(); + break; + } + case "Transaction#set": { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { @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); + @Override + protected Void doInBackground(Void... voids) { + Map data = (Map) arguments.get("data"); + try { + transaction.set(getDocumentReference(arguments), data); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(null); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#set", e.getMessage(), null); + } + }); + } + return null; } - addDefaultListeners("setData", task, result); - break; + }.execute(); + break; + } + case "Transaction#delete": { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + try { + transaction.delete(getDocumentReference(arguments)); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(null); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#delete", e.getMessage(), 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")); } - 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; + 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); + MetadataChanges metadataChanges = + (Boolean) arguments.get("includeMetadataChanges") + ? MetadataChanges.INCLUDE + : MetadataChanges.EXCLUDE; + listenerRegistrations.put( + handle, getQuery(arguments).addSnapshotListener(metadataChanges, observer)); + result.success(handle); + break; + } + case "DocumentReference#addSnapshotListener": { + Map arguments = call.arguments(); + int handle = nextListenerHandle++; + DocumentObserver observer = new DocumentObserver(handle); + documentObservers.put(handle, observer); + MetadataChanges metadataChanges = + (Boolean) arguments.get("includeMetadataChanges") + ? MetadataChanges.INCLUDE + : MetadataChanges.EXCLUDE; + listenerRegistrations.put( + handle, + getDocumentReference(arguments).addSnapshotListener(metadataChanges, observer)); + result.success(handle); + break; + } + case "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); + Source source = getSource(arguments); + Task task = query.get(source); + 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); } - case "DocumentReference#get": - { - Map arguments = call.arguments(); - DocumentReference documentReference = getDocumentReference(arguments); - Source source = getSource(arguments); - Task task = documentReference.get(source); - 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); + 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); + Source source = getSource(arguments); + Task task = documentReference.get(source); + 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); } - }); - 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")); - } + 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("sslEnabled") != null) { - builder.setSslEnabled((boolean) arguments.get("sslEnabled")); - } + if (arguments.get("persistenceEnabled") != null) { + builder.setPersistenceEnabled((boolean) arguments.get("persistenceEnabled")); + } - if (arguments.get("timestampsInSnapshotsEnabled") != null) { - builder.setTimestampsInSnapshotsEnabled( - (boolean) arguments.get("timestampsInSnapshotsEnabled")); - } + if (arguments.get("host") != null) { + builder.setHost((String) arguments.get("host")); + } - if (arguments.get("cacheSizeBytes") != null) { - builder.setCacheSizeBytes(((Integer) arguments.get("cacheSizeBytes")).longValue()); - } + if (arguments.get("sslEnabled") != null) { + builder.setSslEnabled((boolean) arguments.get("sslEnabled")); + } - FirebaseFirestoreSettings settings = builder.build(); - getFirestore(arguments).setFirestoreSettings(settings); - result.success(null); - break; + if (arguments.get("timestampsInSnapshotsEnabled") != null) { + builder.setTimestampsInSnapshotsEnabled( + (boolean) arguments.get("timestampsInSnapshotsEnabled")); } - default: - { - result.notImplemented(); - break; + + if (arguments.get("cacheSizeBytes") != null) { + builder.setCacheSizeBytes(((Integer) arguments.get("cacheSizeBytes")).longValue()); } + + FirebaseFirestoreSettings settings = builder.build(); + getFirestore(arguments).setFirestoreSettings(settings); + result.success(null); + break; + } + default: { + result.notImplemented(); + break; + } } } private static final class TransactionResult { - final @Nullable Map result; - final @Nullable Exception exception; + final @Nullable + Map result; + final @Nullable + Exception exception; TransactionResult(@NonNull Exception exception) { this.exception = exception; @@ -878,7 +939,7 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) { } else if (value instanceof DocumentReference) { stream.write(DOCUMENT_REFERENCE); writeBytes( - stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); + stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8)); } else if (value instanceof Blob) { stream.write(BLOB); @@ -902,7 +963,7 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) { final byte[] appNameBytes = readBytes(buffer); String appName = new String(appNameBytes, UTF8); final FirebaseFirestore firestore = - FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); + FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); final byte[] pathBytes = readBytes(buffer); final String path = new String(pathBytes, UTF8); return firestore.document(path); diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index bc37e8cb3290..e7f196cb4a5d 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -298,26 +298,53 @@ void main() { test('FieldPath.documentId', () async { // Populate the database with two test documents. final CollectionReference messages = firestore.collection('messages'); - final DocumentReference doc = messages.document(); + // Use document ID as a unique identifier to ensure that we don't // collide with other tests running against this database. - final String uniqueId = doc.documentID; - await doc.setData({ - 'message': 'testing field path', + final DocumentReference doc1 = messages.document(); + final DocumentReference doc2 = messages.document(); + final String id2 = doc2.documentID; + + await doc1.setData({ + 'message': 'testing field path [doc1]', + 'created_at': FieldValue.serverTimestamp(), + }); + await doc2.setData({ + 'message': 'testing field path [doc2]', 'created_at': FieldValue.serverTimestamp(), }); - final QuerySnapshot snapshot = await messages - .where(FieldPath.documentId, isEqualTo: uniqueId) + final DocumentSnapshot snapshot1 = await doc1.get(); + + // Need to test orderBy, where, and startAfterDocument in queries + // to test all implementations of FieldPath in the native code, + // e.g. in getQuery and getDocumentValues in the Java implementation. + final QuerySnapshot querySnapshot1 = await messages + .orderBy(FieldPath.documentId) + .where(FieldPath.documentId, isEqualTo: id2) + .startAfterDocument(snapshot1) .getDocuments(); + final QuerySnapshot querySnapshot2 = await messages + .orderBy(FieldPath.documentId) + .where(FieldPath.documentId, isEqualTo: id2) + .getDocuments(); + + await doc1.delete(); + await doc2.delete(); - await doc.delete(); + final List results1 = querySnapshot1.documents; + final DocumentSnapshot result1 = results1[0]; - final List results = snapshot.documents; - final DocumentSnapshot result = results[0]; + expect(results1.length, 1); + expect(result1.data['message'], 'testing field path [doc2]'); + expect(result1.documentID, id2); - expect(results.length, 1); - expect(result.data['message'], 'testing field path'); + final List results2 = querySnapshot2.documents; + final DocumentSnapshot result2 = results1[0]; + + expect(results2.length, 1); + expect(result2.data['message'], 'testing field path [doc2]'); + expect(result2.documentID, id2); }); }); } diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index 6e1ec5152460..2b7b2c91d6c0 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -165,6 +165,14 @@ class Query { /// Creates and returns a new [Query] that's additionally sorted by the specified /// [field]. /// The field may be a [String] representing a single field name or a [FieldPath]. + /// + /// After a [FieldPath.documentId] order by call, you cannot add any more [orderBy] + /// calls. This will result in the following [PlatformException]: + /// `INVALID_ARGUMENT: Order by clause cannot contain more fields after the key __name__` + /// Furthermore, you may not use [orderBy] on the [FieldPath.documentId] [field] when + /// using [startAfterDocument], [startAtDocument], [endAfterDocument], + /// or [endAtDocument] because the order by clause on the document id + /// is added by these methods implicitly. Query orderBy(dynamic field, {bool descending = false}) { final List> orders = List>.from(_parameters['orderBy']); From 97d41a9c3bef43ae01fd894da7d4fc82ca3e7310 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:32:25 +0000 Subject: [PATCH 06/25] revert java formatting --- .../cloudfirestore/CloudFirestorePlugin.java | 87 +++++++++++-------- .../example/test_driver/cloud_firestore.dart | 1 - 2 files changed, 51 insertions(+), 37 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 6beaef774fac..d439c8c7f3ca 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 @@ -8,10 +8,8 @@ import android.os.AsyncTask; import android.util.Log; import android.util.SparseArray; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; @@ -40,7 +38,13 @@ import com.google.firebase.firestore.Source; 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; @@ -51,14 +55,6 @@ 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 { private static final String TAG = "CloudFirestorePlugin"; @@ -444,7 +440,8 @@ public void onFailure(@NonNull Exception e) { @Override public void onMethodCall(MethodCall call, final Result result) { switch (call.method) { - case "Firestore#runTransaction": { + case "Firestore#runTransaction": + { final TaskCompletionSource> transactionTCS = new TaskCompletionSource<>(); final Task> transactionTCSTask = transactionTCS.getTask(); @@ -533,7 +530,8 @@ public void onComplete(Task task) { }); break; } - case "Transaction#get": { + case "Transaction#get": + { final Map arguments = call.arguments(); final Transaction transaction = getTransaction(arguments); new AsyncTask() { @@ -574,7 +572,8 @@ public void run() { }.execute(); break; } - case "Transaction#update": { + case "Transaction#update": + { final Map arguments = call.arguments(); final Transaction transaction = getTransaction(arguments); new AsyncTask() { @@ -605,7 +604,8 @@ public void run() { }.execute(); break; } - case "Transaction#set": { + case "Transaction#set": + { final Map arguments = call.arguments(); final Transaction transaction = getTransaction(arguments); new AsyncTask() { @@ -636,7 +636,8 @@ public void run() { }.execute(); break; } - case "Transaction#delete": { + case "Transaction#delete": + { final Map arguments = call.arguments(); final Transaction transaction = getTransaction(arguments); new AsyncTask() { @@ -665,7 +666,8 @@ public void run() { }.execute(); break; } - case "WriteBatch#create": { + case "WriteBatch#create": + { int handle = nextBatchHandle++; final Map arguments = call.arguments(); WriteBatch batch = getFirestore(arguments).batch(); @@ -673,7 +675,8 @@ public void run() { result.success(handle); break; } - case "WriteBatch#setData": { + case "WriteBatch#setData": + { Map arguments = call.arguments(); int handle = (Integer) arguments.get("handle"); DocumentReference reference = getDocumentReference(arguments); @@ -688,7 +691,8 @@ public void run() { result.success(null); break; } - case "WriteBatch#updateData": { + case "WriteBatch#updateData": + { Map arguments = call.arguments(); int handle = (Integer) arguments.get("handle"); DocumentReference reference = getDocumentReference(arguments); @@ -699,7 +703,8 @@ public void run() { result.success(null); break; } - case "WriteBatch#delete": { + case "WriteBatch#delete": + { Map arguments = call.arguments(); int handle = (Integer) arguments.get("handle"); DocumentReference reference = getDocumentReference(arguments); @@ -708,7 +713,8 @@ public void run() { result.success(null); break; } - case "WriteBatch#commit": { + case "WriteBatch#commit": + { Map arguments = call.arguments(); int handle = (Integer) arguments.get("handle"); WriteBatch batch = batches.get(handle); @@ -717,7 +723,8 @@ public void run() { addDefaultListeners("commit", task, result); break; } - case "Query#addSnapshotListener": { + case "Query#addSnapshotListener": + { Map arguments = call.arguments(); int handle = nextListenerHandle++; EventObserver observer = new EventObserver(handle); @@ -731,7 +738,8 @@ public void run() { result.success(handle); break; } - case "DocumentReference#addSnapshotListener": { + case "DocumentReference#addSnapshotListener": + { Map arguments = call.arguments(); int handle = nextListenerHandle++; DocumentObserver observer = new DocumentObserver(handle); @@ -746,7 +754,8 @@ public void run() { result.success(handle); break; } - case "removeListener": { + case "removeListener": + { Map arguments = call.arguments(); int handle = (Integer) arguments.get("handle"); listenerRegistrations.get(handle).remove(); @@ -755,7 +764,8 @@ public void run() { result.success(null); break; } - case "Query#getDocuments": { + case "Query#getDocuments": + { Map arguments = call.arguments(); Query query = getQuery(arguments); Source source = getSource(arguments); @@ -776,7 +786,8 @@ public void onFailure(@NonNull Exception e) { }); break; } - case "DocumentReference#setData": { + case "DocumentReference#setData": + { Map arguments = call.arguments(); DocumentReference documentReference = getDocumentReference(arguments); @SuppressWarnings("unchecked") @@ -792,7 +803,8 @@ public void onFailure(@NonNull Exception e) { addDefaultListeners("setData", task, result); break; } - case "DocumentReference#updateData": { + case "DocumentReference#updateData": + { Map arguments = call.arguments(); DocumentReference documentReference = getDocumentReference(arguments); @SuppressWarnings("unchecked") @@ -801,7 +813,8 @@ public void onFailure(@NonNull Exception e) { addDefaultListeners("updateData", task, result); break; } - case "DocumentReference#get": { + case "DocumentReference#get": + { Map arguments = call.arguments(); DocumentReference documentReference = getDocumentReference(arguments); Source source = getSource(arguments); @@ -834,14 +847,16 @@ public void onFailure(@NonNull Exception e) { }); break; } - case "DocumentReference#delete": { + case "DocumentReference#delete": + { Map arguments = call.arguments(); DocumentReference documentReference = getDocumentReference(arguments); Task task = documentReference.delete(); addDefaultListeners("delete", task, result); break; } - case "Firestore#enablePersistence": { + case "Firestore#enablePersistence": + { Map arguments = call.arguments(); boolean enable = (boolean) arguments.get("enable"); FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); @@ -851,7 +866,8 @@ public void onFailure(@NonNull Exception e) { result.success(null); break; } - case "Firestore#settings": { + case "Firestore#settings": + { final Map arguments = call.arguments(); final FirebaseFirestoreSettings.Builder builder = new FirebaseFirestoreSettings.Builder(); @@ -881,7 +897,8 @@ public void onFailure(@NonNull Exception e) { result.success(null); break; } - default: { + default: + { result.notImplemented(); break; } @@ -889,10 +906,8 @@ public void onFailure(@NonNull Exception e) { } private static final class TransactionResult { - final @Nullable - Map result; - final @Nullable - Exception exception; + final @Nullable Map result; + final @Nullable Exception exception; TransactionResult(@NonNull Exception exception) { this.exception = exception; diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index e7f196cb4a5d..1bc3dde373b9 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -320,7 +320,6 @@ void main() { // to test all implementations of FieldPath in the native code, // e.g. in getQuery and getDocumentValues in the Java implementation. final QuerySnapshot querySnapshot1 = await messages - .orderBy(FieldPath.documentId) .where(FieldPath.documentId, isEqualTo: id2) .startAfterDocument(snapshot1) .getDocuments(); From 20196b27c949be40dd21080780fc4af29d5ad525 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:40:27 +0000 Subject: [PATCH 07/25] revert java formatting --- .../cloudfirestore/CloudFirestorePlugin.java | 1016 ++++++++--------- 1 file changed, 470 insertions(+), 546 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 d439c8c7f3ca..fc1ad98d3750 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 @@ -73,10 +73,10 @@ public class CloudFirestorePlugin implements MethodCallHandler { public static void registerWith(PluginRegistry.Registrar registrar) { final MethodChannel channel = - new MethodChannel( - registrar.messenger(), - "plugins.flutter.io/cloud_firestore", - new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); + new MethodChannel( + registrar.messenger(), + "plugins.flutter.io/cloud_firestore", + new StandardMethodCodec(FirestoreMessageCodec.INSTANCE)); channel.setMethodCallHandler(new CloudFirestorePlugin(channel, registrar.activity())); } @@ -123,35 +123,22 @@ private Source getSource(Map arguments) { } private Object[] getDocumentValues( - Map document, List> orderBy, Map arguments) { + Map document, List> orderBy, Map arguments) { String documentId = (String) document.get("id"); Map documentData = (Map) document.get("data"); List data = new ArrayList<>(); if (orderBy != null) { for (List order : orderBy) { - final Object field = order.get(0); - - if (field instanceof FieldPath) { - final FieldPath fieldPath = (FieldPath) field; - if (fieldPath == FieldPath.documentId()) { - data.add(documentId); - } else { - // Unsupported type. - } - } else if (field instanceof String) { - String orderByFieldName = (String) field; - if (orderByFieldName.contains(".")) { - String[] fieldNameParts = orderByFieldName.split("\\."); - Map current = (Map) documentData.get(fieldNameParts[0]); - for (int i = 1; i < fieldNameParts.length - 1; i++) { - current = (Map) current.get(fieldNameParts[i]); - } - data.add(current.get(fieldNameParts[fieldNameParts.length - 1])); - } else { - data.add(documentData.get(orderByFieldName)); + String orderByFieldName = (String) order.get(0); + if (orderByFieldName.contains(".")) { + String[] fieldNameParts = orderByFieldName.split("\\."); + Map current = (Map) documentData.get(fieldNameParts[0]); + for (int i = 1; i < fieldNameParts.length - 1; i++) { + current = (Map) current.get(fieldNameParts[i]); } + data.add(current.get(fieldNameParts[fieldNameParts.length - 1])); } else { - // Invalid type. + data.add(documentData.get(orderByFieldName)); } } } @@ -199,7 +186,7 @@ private Map parseQuerySnapshot(QuerySnapshot querySnapshot) { change.put("path", documentChange.getDocument().getReference().getPath()); Map metadata = new HashMap(); metadata.put( - "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); + "hasPendingWrites", documentChange.getDocument().getMetadata().hasPendingWrites()); metadata.put("isFromCache", documentChange.getDocument().getMetadata().isFromCache()); change.put("metadata", metadata); documentChanges.add(change); @@ -226,67 +213,21 @@ private Query getQuery(Map arguments) { @SuppressWarnings("unchecked") List> whereConditions = (List>) parameters.get("where"); for (List condition : whereConditions) { - String fieldName = null; - FieldPath fieldPath = null; - final Object field = condition.get(0); - if (field instanceof String) { - fieldName = (String) field; - } else if (field instanceof FieldPath) { - fieldPath = (FieldPath) field; - } else { - // Invalid type. - } - + String fieldName = (String) condition.get(0); String operator = (String) condition.get(1); Object value = condition.get(2); if ("==".equals(operator)) { - if (fieldName != null) { - query = query.whereEqualTo(fieldName, value); - } else if (fieldPath != null) { - query = query.whereEqualTo(fieldPath, value); - } else { - // Invalid type. - } + query = query.whereEqualTo(fieldName, value); } else if ("<".equals(operator)) { - if (fieldName != null) { - query = query.whereLessThan(fieldName, value); - } else if (fieldPath != null) { - query = query.whereLessThan(fieldPath, value); - } else { - // Invalid type. - } + query = query.whereLessThan(fieldName, value); } else if ("<=".equals(operator)) { - if (fieldName != null) { - query = query.whereLessThanOrEqualTo(fieldName, value); - } else if (fieldPath != null) { - query = query.whereLessThanOrEqualTo(fieldPath, value); - } else { - // Invalid type. - } + query = query.whereLessThanOrEqualTo(fieldName, value); } else if (">".equals(operator)) { - if (fieldName != null) { - query = query.whereGreaterThan(fieldName, value); - } else if (fieldPath != null) { - query = query.whereGreaterThan(fieldPath, value); - } else { - // Invalid type. - } + query = query.whereGreaterThan(fieldName, value); } else if (">=".equals(operator)) { - if (fieldName != null) { - query = query.whereGreaterThanOrEqualTo(fieldName, value); - } else if (fieldPath != null) { - query = query.whereGreaterThanOrEqualTo(fieldPath, value); - } else { - // Invalid type. - } + query = query.whereGreaterThanOrEqualTo(fieldName, value); } else if ("array-contains".equals(operator)) { - if (fieldName != null) { - query = query.whereArrayContains(fieldName, value); - } else if (fieldPath != null) { - query = query.whereArrayContains(fieldPath, value); - } else { - // Invalid type. - } + query = query.whereArrayContains(fieldName, value); } else { // Invalid operator. } @@ -298,46 +239,29 @@ private Query getQuery(Map arguments) { List> orderBy = (List>) parameters.get("orderBy"); if (orderBy == null) return query; for (List order : orderBy) { - String fieldName = null; - FieldPath fieldPath = null; - final Object field = order.get(0); - if (field instanceof String) { - fieldName = (String) field; - } else if (field instanceof FieldPath) { - fieldPath = (FieldPath) field; - } else { - // Invalid type. - } - + String orderByFieldName = (String) order.get(0); boolean descending = (boolean) order.get(1); Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; - - if (fieldName != null) { - query = query.orderBy(fieldName, direction); - } else if (fieldPath != null) { - query = query.orderBy(fieldPath, direction); - } else { - // Invalid type. - } + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + query = query.orderBy(orderByFieldName, direction); } @SuppressWarnings("unchecked") Map startAtDocument = (Map) parameters.get("startAtDocument"); @SuppressWarnings("unchecked") Map startAfterDocument = - (Map) parameters.get("startAfterDocument"); + (Map) parameters.get("startAfterDocument"); @SuppressWarnings("unchecked") Map endAtDocument = (Map) parameters.get("endAtDocument"); @SuppressWarnings("unchecked") Map endBeforeDocument = - (Map) parameters.get("endBeforeDocument"); + (Map) parameters.get("endBeforeDocument"); if (startAtDocument != null - || startAfterDocument != null - || endAtDocument != null - || endBeforeDocument != null) { + || startAfterDocument != null + || endAtDocument != null + || endBeforeDocument != null) { boolean descending = (boolean) orderBy.get(orderBy.size() - 1).get(1); Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; query = query.orderBy(FieldPath.documentId(), direction); } if (startAtDocument != null) { @@ -422,486 +346,486 @@ public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) { private void addDefaultListeners(final String description, Task task, final Result result) { task.addOnSuccessListener( - new OnSuccessListener() { - @Override - public void onSuccess(Void ignored) { - result.success(null); - } - }); + 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); - } - }); + 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 TransactionResult apply(@NonNull Transaction transaction) { - // Store transaction. - int transactionId = (Integer) arguments.get("transactionId"); - transactions.append(transactionId, transaction); - completionTasks.append(transactionId, transactionTCS); - - // Start operations on Dart side. - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - 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) { - transactionTCS.trySetException( - new Exception("DoTransaction failed: " + errorMessage)); - } - - @Override - public void notImplemented() { - transactionTCS.trySetException( - new Exception("DoTransaction not implemented")); - } - }); - } - }); - - // Wait till transaction is complete. - try { - String timeoutKey = "transactionTimeout"; - long timeout = ((Number) arguments.get(timeoutKey)).longValue(); - final Map transactionResult = - Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); - - // Once transaction completes return the result to the Dart side. - return new TransactionResult(transactionResult); - } catch (Exception e) { - Log.e(TAG, e.getMessage(), e); - return new TransactionResult(e); - } - } - }) - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(Task task) { - if (!task.isSuccessful()) { - result.error( - "Error performing transaction", task.getException().getMessage(), null); - return; - } - - TransactionResult transactionResult = task.getResult(); - if (transactionResult.exception == null) { - result.success(transactionResult.result); - } else { - result.error( - "Error performing transaction", - transactionResult.exception.getMessage(), - null); + { + final TaskCompletionSource> transactionTCS = + new TaskCompletionSource<>(); + final Task> transactionTCSTask = transactionTCS.getTask(); + + final Map arguments = call.arguments(); + getFirestore(arguments) + .runTransaction( + new Transaction.Function() { + @Nullable + @Override + public TransactionResult apply(@NonNull Transaction transaction) { + // Store transaction. + int transactionId = (Integer) arguments.get("transactionId"); + transactions.append(transactionId, transaction); + completionTasks.append(transactionId, transactionTCS); + + // Start operations on Dart side. + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + 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) { + transactionTCS.trySetException( + new Exception("DoTransaction failed: " + errorMessage)); + } + + @Override + public void notImplemented() { + transactionTCS.trySetException( + new Exception("DoTransaction not implemented")); + } + }); } - } - }); - break; - } + }); + + // Wait till transaction is complete. + try { + String timeoutKey = "transactionTimeout"; + long timeout = ((Number) arguments.get(timeoutKey)).longValue(); + final Map transactionResult = + Tasks.await(transactionTCSTask, timeout, TimeUnit.MILLISECONDS); + + // Once transaction completes return the result to the Dart side. + return new TransactionResult(transactionResult); + } catch (Exception e) { + Log.e(TAG, e.getMessage(), e); + return new TransactionResult(e); + } + } + }) + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(Task task) { + if (!task.isSuccessful()) { + result.error( + "Error performing transaction", task.getException().getMessage(), null); + return; + } + + TransactionResult transactionResult = task.getResult(); + if (transactionResult.exception == null) { + result.success(transactionResult.result); + } else { + result.error( + "Error performing transaction", + transactionResult.exception.getMessage(), + 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)); - final Map snapshotMap = new HashMap<>(); - snapshotMap.put("path", documentSnapshot.getReference().getPath()); - if (documentSnapshot.exists()) { - snapshotMap.put("data", documentSnapshot.getData()); - } else { - snapshotMap.put("data", null); + { + 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)); + final 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); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(snapshotMap); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#get", e.getMessage(), null); + } + }); } - Map metadata = new HashMap(); - metadata.put("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); - metadata.put("isFromCache", documentSnapshot.getMetadata().isFromCache()); - snapshotMap.put("metadata", metadata); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(snapshotMap); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.error("Error performing Transaction#get", e.getMessage(), null); - } - }); + return null; } - return null; - } - }.execute(); - break; - } + }.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); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.error("Error performing Transaction#update", e.getMessage(), null); - } - }); + { + 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); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(null); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#update", e.getMessage(), null); + } + }); + } + return null; } - return null; - } - }.execute(); - 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"); - try { - transaction.set(getDocumentReference(arguments), data); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.error("Error performing Transaction#set", e.getMessage(), null); - } - }); + { + 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.set(getDocumentReference(arguments), data); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(null); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#set", e.getMessage(), null); + } + }); + } + return null; } - return null; - } - }.execute(); - break; - } + }.execute(); + break; + } case "Transaction#delete": - { - final Map arguments = call.arguments(); - final Transaction transaction = getTransaction(arguments); - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - try { - transaction.delete(getDocumentReference(arguments)); - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(null); - } - }); - } catch (final Exception e) { - activity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.error("Error performing Transaction#delete", e.getMessage(), null); - } - }); + { + final Map arguments = call.arguments(); + final Transaction transaction = getTransaction(arguments); + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + try { + transaction.delete(getDocumentReference(arguments)); + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(null); + } + }); + } catch (final Exception e) { + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.error("Error performing Transaction#delete", e.getMessage(), null); + } + }); + } + return null; } - return null; - } - }.execute(); - break; - } + }.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; - } + { + 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")); + { + 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; } - 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; - } + { + 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; - } + { + 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; - } + { + 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); - MetadataChanges metadataChanges = - (Boolean) arguments.get("includeMetadataChanges") - ? MetadataChanges.INCLUDE - : MetadataChanges.EXCLUDE; - listenerRegistrations.put( - handle, getQuery(arguments).addSnapshotListener(metadataChanges, observer)); - result.success(handle); - break; - } + { + Map arguments = call.arguments(); + int handle = nextListenerHandle++; + EventObserver observer = new EventObserver(handle); + observers.put(handle, observer); + MetadataChanges metadataChanges = + (Boolean) arguments.get("includeMetadataChanges") + ? MetadataChanges.INCLUDE + : MetadataChanges.EXCLUDE; + listenerRegistrations.put( + handle, getQuery(arguments).addSnapshotListener(metadataChanges, observer)); + result.success(handle); + break; + } case "DocumentReference#addSnapshotListener": - { - Map arguments = call.arguments(); - int handle = nextListenerHandle++; - DocumentObserver observer = new DocumentObserver(handle); - documentObservers.put(handle, observer); - MetadataChanges metadataChanges = - (Boolean) arguments.get("includeMetadataChanges") - ? MetadataChanges.INCLUDE - : MetadataChanges.EXCLUDE; - listenerRegistrations.put( - handle, - getDocumentReference(arguments).addSnapshotListener(metadataChanges, observer)); - result.success(handle); - break; - } + { + Map arguments = call.arguments(); + int handle = nextListenerHandle++; + DocumentObserver observer = new DocumentObserver(handle); + documentObservers.put(handle, observer); + MetadataChanges metadataChanges = + (Boolean) arguments.get("includeMetadataChanges") + ? MetadataChanges.INCLUDE + : MetadataChanges.EXCLUDE; + listenerRegistrations.put( + handle, + getDocumentReference(arguments).addSnapshotListener(metadataChanges, observer)); + result.success(handle); + break; + } case "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; - } + { + 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); - Source source = getSource(arguments); - Task task = query.get(source); - 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; - } + { + Map arguments = call.arguments(); + Query query = getQuery(arguments); + Source source = getSource(arguments); + Task task = query.get(source); + 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); + { + 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; } - 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; - } + { + 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); - Source source = getSource(arguments); - Task task = documentReference.get(source); - 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); + { + Map arguments = call.arguments(); + DocumentReference documentReference = getDocumentReference(arguments); + Source source = getSource(arguments); + Task task = documentReference.get(source); + 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); } - result.success(snapshotMap); - } - }) - .addOnFailureListener( - new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - result.error("Error performing get", e.getMessage(), null); - } - }); - break; - } + }) + .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; - } + { + 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; - } + { + 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(); + { + 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("persistenceEnabled") != null) { + builder.setPersistenceEnabled((boolean) arguments.get("persistenceEnabled")); + } - if (arguments.get("host") != null) { - builder.setHost((String) arguments.get("host")); - } + 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("sslEnabled") != null) { + builder.setSslEnabled((boolean) arguments.get("sslEnabled")); + } - if (arguments.get("timestampsInSnapshotsEnabled") != null) { - builder.setTimestampsInSnapshotsEnabled( - (boolean) arguments.get("timestampsInSnapshotsEnabled")); - } + if (arguments.get("timestampsInSnapshotsEnabled") != null) { + builder.setTimestampsInSnapshotsEnabled( + (boolean) arguments.get("timestampsInSnapshotsEnabled")); + } - if (arguments.get("cacheSizeBytes") != null) { - builder.setCacheSizeBytes(((Integer) arguments.get("cacheSizeBytes")).longValue()); - } + if (arguments.get("cacheSizeBytes") != null) { + builder.setCacheSizeBytes(((Integer) arguments.get("cacheSizeBytes")).longValue()); + } - FirebaseFirestoreSettings settings = builder.build(); - getFirestore(arguments).setFirestoreSettings(settings); - result.success(null); - break; - } + FirebaseFirestoreSettings settings = builder.build(); + getFirestore(arguments).setFirestoreSettings(settings); + result.success(null); + break; + } default: - { - result.notImplemented(); - break; - } + { + result.notImplemented(); + break; + } } } @@ -954,7 +878,7 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) { } else if (value instanceof DocumentReference) { stream.write(DOCUMENT_REFERENCE); writeBytes( - stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); + stream, ((DocumentReference) value).getFirestore().getApp().getName().getBytes(UTF8)); writeBytes(stream, ((DocumentReference) value).getPath().getBytes(UTF8)); } else if (value instanceof Blob) { stream.write(BLOB); @@ -978,7 +902,7 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) { final byte[] appNameBytes = readBytes(buffer); String appName = new String(appNameBytes, UTF8); final FirebaseFirestore firestore = - FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); + FirebaseFirestore.getInstance(FirebaseApp.getInstance(appName)); final byte[] pathBytes = readBytes(buffer); final String path = new String(pathBytes, UTF8); return firestore.document(path); From ce25aa01a151febc5b02771c2bc962ff9228171d Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:43:12 +0000 Subject: [PATCH 08/25] java implementation --- .../cloudfirestore/CloudFirestorePlugin.java | 112 +++++++++++++++--- 1 file changed, 94 insertions(+), 18 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 fc1ad98d3750..be56fa1bc1fe 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 @@ -129,16 +129,29 @@ private Object[] getDocumentValues( List data = new ArrayList<>(); if (orderBy != null) { for (List order : orderBy) { - String orderByFieldName = (String) order.get(0); - if (orderByFieldName.contains(".")) { - String[] fieldNameParts = orderByFieldName.split("\\."); - Map current = (Map) documentData.get(fieldNameParts[0]); - for (int i = 1; i < fieldNameParts.length - 1; i++) { - current = (Map) current.get(fieldNameParts[i]); + final Object field = order.get(0); + + if (field instanceof FieldPath) { + final FieldPath fieldPath = (FieldPath) field; + if (fieldPath == FieldPath.documentId()) { + data.add(documentId); + } else { + // Unsupported type. + } + } else if (field instanceof String) { + String orderByFieldName = (String) field; + if (orderByFieldName.contains(".")) { + String[] fieldNameParts = orderByFieldName.split("\\."); + Map current = (Map) documentData.get(fieldNameParts[0]); + for (int i = 1; i < fieldNameParts.length - 1; i++) { + current = (Map) current.get(fieldNameParts[i]); + } + data.add(current.get(fieldNameParts[fieldNameParts.length - 1])); + } else { + data.add(documentData.get(orderByFieldName)); } - data.add(current.get(fieldNameParts[fieldNameParts.length - 1])); } else { - data.add(documentData.get(orderByFieldName)); + // Invalid type. } } } @@ -213,21 +226,67 @@ private Query getQuery(Map arguments) { @SuppressWarnings("unchecked") List> whereConditions = (List>) parameters.get("where"); for (List condition : whereConditions) { - String fieldName = (String) condition.get(0); + String fieldName = null; + FieldPath fieldPath = null; + final Object field = condition.get(0); + if (field instanceof String) { + fieldName = (String) field; + } else if (field instanceof FieldPath) { + fieldPath = (FieldPath) field; + } else { + // Invalid type. + } + String operator = (String) condition.get(1); Object value = condition.get(2); if ("==".equals(operator)) { - query = query.whereEqualTo(fieldName, value); + if (fieldName != null) { + query = query.whereEqualTo(fieldName, value); + } else if (fieldPath != null) { + query = query.whereEqualTo(fieldPath, value); + } else { + // Invalid type. + } } else if ("<".equals(operator)) { - query = query.whereLessThan(fieldName, value); + if (fieldName != null) { + query = query.whereLessThan(fieldName, value); + } else if (fieldPath != null) { + query = query.whereLessThan(fieldPath, value); + } else { + // Invalid type. + } } else if ("<=".equals(operator)) { - query = query.whereLessThanOrEqualTo(fieldName, value); + if (fieldName != null) { + query = query.whereLessThanOrEqualTo(fieldName, value); + } else if (fieldPath != null) { + query = query.whereLessThanOrEqualTo(fieldPath, value); + } else { + // Invalid type. + } } else if (">".equals(operator)) { - query = query.whereGreaterThan(fieldName, value); + if (fieldName != null) { + query = query.whereGreaterThan(fieldName, value); + } else if (fieldPath != null) { + query = query.whereGreaterThan(fieldPath, value); + } else { + // Invalid type. + } } else if (">=".equals(operator)) { - query = query.whereGreaterThanOrEqualTo(fieldName, value); + if (fieldName != null) { + query = query.whereGreaterThanOrEqualTo(fieldName, value); + } else if (fieldPath != null) { + query = query.whereGreaterThanOrEqualTo(fieldPath, value); + } else { + // Invalid type. + } } else if ("array-contains".equals(operator)) { - query = query.whereArrayContains(fieldName, value); + if (fieldName != null) { + query = query.whereArrayContains(fieldName, value); + } else if (fieldPath != null) { + query = query.whereArrayContains(fieldPath, value); + } else { + // Invalid type. + } } else { // Invalid operator. } @@ -239,11 +298,28 @@ private Query getQuery(Map arguments) { List> orderBy = (List>) parameters.get("orderBy"); if (orderBy == null) return query; for (List order : orderBy) { - String orderByFieldName = (String) order.get(0); + String fieldName = null; + FieldPath fieldPath = null; + final Object field = order.get(0); + if (field instanceof String) { + fieldName = (String) field; + } else if (field instanceof FieldPath) { + fieldPath = (FieldPath) field; + } else { + // Invalid type. + } + boolean descending = (boolean) order.get(1); Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; - query = query.orderBy(orderByFieldName, direction); + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + + if (fieldName != null) { + query = query.orderBy(fieldName, direction); + } else if (fieldPath != null) { + query = query.orderBy(fieldPath, direction); + } else { + // Invalid type. + } } @SuppressWarnings("unchecked") Map startAtDocument = (Map) parameters.get("startAtDocument"); From 0ab69f30b15016cb28349178382871bbb953fa73 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:57:29 +0000 Subject: [PATCH 09/25] exception --- .../firebase/cloudfirestore/CloudFirestorePlugin.java | 8 +++++--- .../cloud_firestore/example/android/gradle.properties | 1 + .../example/test_driver/cloud_firestore.dart | 1 + 3 files changed, 7 insertions(+), 3 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 be56fa1bc1fe..f23161705a1e 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 @@ -132,9 +132,11 @@ private Object[] getDocumentValues( final Object field = order.get(0); if (field instanceof FieldPath) { - final FieldPath fieldPath = (FieldPath) field; - if (fieldPath == FieldPath.documentId()) { - data.add(documentId); + if (field == FieldPath.documentId()) { + throw new IllegalArgumentException("You cannot use {start/end}{At/After}Document " + + "when ordering by the document id. When using any of the mentioned methods, " + + "the library will order by the document id implicitly as " + + "it needs to add other fields to the order clause."); } else { // Unsupported type. } diff --git a/packages/cloud_firestore/example/android/gradle.properties b/packages/cloud_firestore/example/android/gradle.properties index 8bd86f680510..7be3d8b46841 100755 --- a/packages/cloud_firestore/example/android/gradle.properties +++ b/packages/cloud_firestore/example/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index 1bc3dde373b9..e7f196cb4a5d 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -320,6 +320,7 @@ void main() { // to test all implementations of FieldPath in the native code, // e.g. in getQuery and getDocumentValues in the Java implementation. final QuerySnapshot querySnapshot1 = await messages + .orderBy(FieldPath.documentId) .where(FieldPath.documentId, isEqualTo: id2) .startAfterDocument(snapshot1) .getDocuments(); From 0f1142dab4307c4d6a347f9030972a6e30a2f1ab Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 18:25:24 +0000 Subject: [PATCH 10/25] pre format --- .../cloudfirestore/CloudFirestorePlugin.java | 9 +++++---- .../example/test_driver/cloud_firestore.dart | 1 - packages/cloud_firestore/lib/src/query.dart | 19 +++++++++++++++++-- 3 files changed, 22 insertions(+), 7 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 f23161705a1e..5f8ba36226c3 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 @@ -127,16 +127,17 @@ private Object[] getDocumentValues( String documentId = (String) document.get("id"); Map documentData = (Map) document.get("data"); List data = new ArrayList<>(); + // TODO (logcat) if (orderBy != null) { for (List order : orderBy) { final Object field = order.get(0); if (field instanceof FieldPath) { if (field == FieldPath.documentId()) { - throw new IllegalArgumentException("You cannot use {start/end}{At/After}Document " + - "when ordering by the document id. When using any of the mentioned methods, " + - "the library will order by the document id implicitly as " + - "it needs to add other fields to the order clause."); + // This is also checked by an assertion on the Dart side. + throw new IllegalArgumentException("You cannot order by the document id when using" + + "{start/end}{At/After}Document a the library will order by the document id" + + "implicitly in order to to add other fields to the order clause."); } else { // Unsupported type. } diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index e7f196cb4a5d..1bc3dde373b9 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -320,7 +320,6 @@ void main() { // to test all implementations of FieldPath in the native code, // e.g. in getQuery and getDocumentValues in the Java implementation. final QuerySnapshot querySnapshot1 = await messages - .orderBy(FieldPath.documentId) .where(FieldPath.documentId, isEqualTo: id2) .startAfterDocument(snapshot1) .getDocuments(); diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index 2b7b2c91d6c0..f0bab0e0686f 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -128,6 +128,8 @@ class Query { dynamic arrayContains, bool isNull, }) { + assert(field is String || field is FieldPath, 'Supported [field] types are [String] and [FieldPath].'); + final ListEquality equality = const ListEquality(); final List> conditions = List>.from(_parameters['where']); @@ -167,19 +169,32 @@ class Query { /// The field may be a [String] representing a single field name or a [FieldPath]. /// /// After a [FieldPath.documentId] order by call, you cannot add any more [orderBy] - /// calls. This will result in the following [PlatformException]: - /// `INVALID_ARGUMENT: Order by clause cannot contain more fields after the key __name__` + /// calls. /// Furthermore, you may not use [orderBy] on the [FieldPath.documentId] [field] when /// using [startAfterDocument], [startAtDocument], [endAfterDocument], /// or [endAtDocument] because the order by clause on the document id /// is added by these methods implicitly. Query orderBy(dynamic field, {bool descending = false}) { + assert(field is String || field is FieldPath, 'Supported [field] types are [String] and [FieldPath].'); + final List> orders = List>.from(_parameters['orderBy']); final List order = [field, descending]; assert(orders.where((List item) => field == item[0]).isEmpty, 'OrderBy $field already exists in this query'); + + assert(() { + if (field == FieldPath.documentId) { + return !(_parameters.containsKey('startAfterDocument') || + _parameters.containsKey('startAtDocument') || + _parameters.containsKey('endAfterDocument') || + _parameters.containsKey('endAtDocument')); + } + return false; + }(), '{start/end}{At/After}Document order by document id themselves. ' + 'Hence, you may not use an order by [FieldPath.documentId] when using any of these methods.'); + orders.add(order); return _copyWithParameters({'orderBy': orders}); } From 88f60cc40dcc23e8dc442d2e447d48e1618a016e Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 19:17:29 +0000 Subject: [PATCH 11/25] assertions --- .../cloudfirestore/CloudFirestorePlugin.java | 2 +- .../cloud_firestore/lib/src/field_path.dart | 3 +- .../lib/src/firestore_message_codec.dart | 2 +- packages/cloud_firestore/lib/src/query.dart | 42 ++++++++++++--- .../test/cloud_firestore_test.dart | 51 ++++++++++++++++++- 5 files changed, 89 insertions(+), 11 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 5f8ba36226c3..a702ae8c2ef3 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 @@ -136,7 +136,7 @@ private Object[] getDocumentValues( if (field == FieldPath.documentId()) { // This is also checked by an assertion on the Dart side. throw new IllegalArgumentException("You cannot order by the document id when using" + - "{start/end}{At/After}Document a the library will order by the document id" + + "{start/end}{At/After/Before}Document a the library will order by the document id" + "implicitly in order to to add other fields to the order clause."); } else { // Unsupported type. diff --git a/packages/cloud_firestore/lib/src/field_path.dart b/packages/cloud_firestore/lib/src/field_path.dart index e144cd0b0bea..64cdde8fad31 100644 --- a/packages/cloud_firestore/lib/src/field_path.dart +++ b/packages/cloud_firestore/lib/src/field_path.dart @@ -16,5 +16,6 @@ class FieldPath { final _FieldPathType type; /// The path to the document id, which can be used in queries. - static FieldPath get documentId => const FieldPath._(_FieldPathType.documentId); + static FieldPath get documentId => + const FieldPath._(_FieldPathType.documentId); } diff --git a/packages/cloud_firestore/lib/src/firestore_message_codec.dart b/packages/cloud_firestore/lib/src/firestore_message_codec.dart index 1a555fce4bb9..9f658cd1c84b 100644 --- a/packages/cloud_firestore/lib/src/firestore_message_codec.dart +++ b/packages/cloud_firestore/lib/src/firestore_message_codec.dart @@ -34,7 +34,7 @@ class FirestoreMessageCodec extends StandardMessageCodec { static const Map<_FieldPathType, int> _kFieldPathCodes = <_FieldPathType, int>{ _FieldPathType.documentId: _kDocumentId, - }; + }; @override void writeValue(WriteBuffer buffer, dynamic value) { diff --git a/packages/cloud_firestore/lib/src/query.dart b/packages/cloud_firestore/lib/src/query.dart index f0bab0e0686f..9b273a9bb501 100644 --- a/packages/cloud_firestore/lib/src/query.dart +++ b/packages/cloud_firestore/lib/src/query.dart @@ -128,7 +128,8 @@ class Query { dynamic arrayContains, bool isNull, }) { - assert(field is String || field is FieldPath, 'Supported [field] types are [String] and [FieldPath].'); + assert(field is String || field is FieldPath, + 'Supported [field] types are [String] and [FieldPath].'); final ListEquality equality = const ListEquality(); final List> conditions = @@ -175,7 +176,9 @@ class Query { /// or [endAtDocument] because the order by clause on the document id /// is added by these methods implicitly. Query orderBy(dynamic field, {bool descending = false}) { - assert(field is String || field is FieldPath, 'Supported [field] types are [String] and [FieldPath].'); + assert(field != null && descending != null); + assert(field is String || field is FieldPath, + 'Supported [field] types are [String] and [FieldPath].'); final List> orders = List>.from(_parameters['orderBy']); @@ -188,12 +191,13 @@ class Query { if (field == FieldPath.documentId) { return !(_parameters.containsKey('startAfterDocument') || _parameters.containsKey('startAtDocument') || - _parameters.containsKey('endAfterDocument') || - _parameters.containsKey('endAtDocument')); + _parameters.containsKey('endAfterDocument') || + _parameters.containsKey('endAtDocument')); } - return false; - }(), '{start/end}{At/After}Document order by document id themselves. ' - 'Hence, you may not use an order by [FieldPath.documentId] when using any of these methods.'); + return true; + }(), + '{start/end}{At/After/Before}Document order by document id themselves. ' + 'Hence, you may not use an order by [FieldPath.documentId] when using any of these methods for a query.'); orders.add(order); return _copyWithParameters({'orderBy': orders}); @@ -218,6 +222,12 @@ class Query { assert(!_parameters.containsKey('startAt')); assert(!_parameters.containsKey('startAfterDocument')); assert(!_parameters.containsKey('startAtDocument')); + assert( + List>.from(_parameters['orderBy']) + .where((List item) => item[0] == FieldPath.documentId) + .isEmpty, + '[startAfterDocument] orders by document id itself. ' + 'Hence, you may not use an order by [FieldPath.documentId] when using [startAfterDocument].'); return _copyWithParameters({ 'startAfterDocument': { 'id': documentSnapshot.documentID, @@ -246,6 +256,12 @@ class Query { assert(!_parameters.containsKey('startAt')); assert(!_parameters.containsKey('startAfterDocument')); assert(!_parameters.containsKey('startAtDocument')); + assert( + List>.from(_parameters['orderBy']) + .where((List item) => item[0] == FieldPath.documentId) + .isEmpty, + '[startAtDocument] orders by document id itself. ' + 'Hence, you may not use an order by [FieldPath.documentId] when using [startAtDocument].'); return _copyWithParameters({ 'startAtDocument': { 'id': documentSnapshot.documentID, @@ -308,6 +324,12 @@ class Query { assert(!_parameters.containsKey('endAt')); assert(!_parameters.containsKey('endBeforeDocument')); assert(!_parameters.containsKey('endAtDocument')); + assert( + List>.from(_parameters['orderBy']) + .where((List item) => item[0] == FieldPath.documentId) + .isEmpty, + '[endAtDocument] orders by document id itself. ' + 'Hence, you may not use an order by [FieldPath.documentId] when using [endAtDocument].'); return _copyWithParameters({ 'endAtDocument': { 'id': documentSnapshot.documentID, @@ -353,6 +375,12 @@ class Query { assert(!_parameters.containsKey('endAt')); assert(!_parameters.containsKey('endBeforeDocument')); assert(!_parameters.containsKey('endAtDocument')); + assert( + List>.from(_parameters['orderBy']) + .where((List item) => item[0] == FieldPath.documentId) + .isEmpty, + '[endBeforeDocument] orders by document id itself. ' + 'Hence, you may not use an order by [FieldPath.documentId] when using [endBeforeDocument].'); return _copyWithParameters({ 'endBeforeDocument': { 'id': documentSnapshot.documentID, diff --git a/packages/cloud_firestore/test/cloud_firestore_test.dart b/packages/cloud_firestore/test/cloud_firestore_test.dart index d43720231d4d..967e27b991b1 100755 --- a/packages/cloud_firestore/test/cloud_firestore_test.dart +++ b/packages/cloud_firestore/test/cloud_firestore_test.dart @@ -994,6 +994,54 @@ void main() { ]), ); }); + test('orderBy assertions', () async { + // Can only order by the same field once. + expect(() { + firestore.collection('foo').orderBy('bar').orderBy('bar'); + }, throwsAssertionError); + // Cannot order by unsupported types. + expect(() { + firestore.collection('foo').orderBy(0); + }, throwsAssertionError); + // Parameters cannot be null. + expect(() { + firestore.collection('foo').orderBy(null); + }, throwsAssertionError); + expect(() { + firestore.collection('foo').orderBy('bar', descending: null); + }, throwsAssertionError); + + // Cannot order by document id when paginating with documents. + final DocumentReference documentReference = + firestore.document('foo/bar'); + final DocumentSnapshot snapshot = await documentReference.get(); + expect(() { + firestore + .collection('foo') + .startAfterDocument(snapshot) + .orderBy(FieldPath.documentId); + }, throwsAssertionError); + }); + test('document pagination FieldPath assertions', () async { + final DocumentReference documentReference = + firestore.document('foo/bar'); + final DocumentSnapshot snapshot = await documentReference.get(); + final Query query = + firestore.collection('foo').orderBy(FieldPath.documentId); + + expect(() { + query.startAfterDocument(snapshot); + }, throwsAssertionError); + expect(() { + query.startAtDocument(snapshot); + }, throwsAssertionError); + expect(() { + query.endAtDocument(snapshot); + }, throwsAssertionError); + expect(() { + query.endBeforeDocument(snapshot); + }, throwsAssertionError); + }); }); group('FirestoreMessageCodec', () { @@ -1264,7 +1312,8 @@ bool _deepEquals(dynamic valueA, dynamic valueB) { if (valueA is FieldValue) { return valueB is FieldValue && _deepEqualsFieldValue(valueA, valueB); } - if (valueA is FieldPath) return valueB is FieldPath && valueA.type == valueB.type; + if (valueA is FieldPath) + return valueB is FieldPath && valueA.type == valueB.type; return valueA == valueB; } From af85c955dc66c8a1f9249f3246be82a09c3ec0e2 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 19:26:31 +0000 Subject: [PATCH 12/25] Fix tests, add exceptions for debugging help --- .../plugins/firebase/cloudfirestore/CloudFirestorePlugin.java | 4 ++++ .../cloud_firestore/example/test_driver/cloud_firestore.dart | 1 + 2 files changed, 5 insertions(+) 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 a702ae8c2ef3..e5d3a63660fe 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 @@ -338,6 +338,10 @@ private Query getQuery(Map arguments) { || startAfterDocument != null || endAtDocument != null || endBeforeDocument != null) { + if (orderBy.size() == 0) { + throw new IllegalStateException("You need to order by at least one field when using " + + "{start/end}{At/After/Before}Document as you need some value to e.g. start after."); + } boolean descending = (boolean) orderBy.get(orderBy.size() - 1).get(1); Query.Direction direction = descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index 1bc3dde373b9..a8ff4d4702d2 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -320,6 +320,7 @@ void main() { // to test all implementations of FieldPath in the native code, // e.g. in getQuery and getDocumentValues in the Java implementation. final QuerySnapshot querySnapshot1 = await messages + .orderBy('message') .where(FieldPath.documentId, isEqualTo: id2) .startAfterDocument(snapshot1) .getDocuments(); From 0a61fcc2031a3928c28d2c24b86e50b068251653 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 19:36:31 +0000 Subject: [PATCH 13/25] fix tests --- .../example/test_driver/cloud_firestore.dart | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart index a8ff4d4702d2..21d51b538722 100644 --- a/packages/cloud_firestore/example/test_driver/cloud_firestore.dart +++ b/packages/cloud_firestore/example/test_driver/cloud_firestore.dart @@ -301,50 +301,32 @@ void main() { // Use document ID as a unique identifier to ensure that we don't // collide with other tests running against this database. - final DocumentReference doc1 = messages.document(); - final DocumentReference doc2 = messages.document(); - final String id2 = doc2.documentID; + final DocumentReference doc = messages.document(); + final String documentId = doc.documentID; - await doc1.setData({ - 'message': 'testing field path [doc1]', - 'created_at': FieldValue.serverTimestamp(), - }); - await doc2.setData({ - 'message': 'testing field path [doc2]', + await doc.setData({ + 'message': 'testing field path', 'created_at': FieldValue.serverTimestamp(), }); - final DocumentSnapshot snapshot1 = await doc1.get(); - - // Need to test orderBy, where, and startAfterDocument in queries - // to test all implementations of FieldPath in the native code, - // e.g. in getQuery and getDocumentValues in the Java implementation. - final QuerySnapshot querySnapshot1 = await messages - .orderBy('message') - .where(FieldPath.documentId, isEqualTo: id2) - .startAfterDocument(snapshot1) - .getDocuments(); - final QuerySnapshot querySnapshot2 = await messages + // This tests the native implementations of the where and + // orderBy methods handling FieldPath.documentId. + // There is also an error thrown when ordering by document id + // natively, however, that is also covered by assertion + // on the Dart side, which is tested with a unit test. + final QuerySnapshot querySnapshot = await messages .orderBy(FieldPath.documentId) - .where(FieldPath.documentId, isEqualTo: id2) + .where(FieldPath.documentId, isEqualTo: documentId) .getDocuments(); - await doc1.delete(); - await doc2.delete(); + await doc.delete(); - final List results1 = querySnapshot1.documents; - final DocumentSnapshot result1 = results1[0]; + final List results = querySnapshot.documents; + final DocumentSnapshot result = results[0]; - expect(results1.length, 1); - expect(result1.data['message'], 'testing field path [doc2]'); - expect(result1.documentID, id2); - - final List results2 = querySnapshot2.documents; - final DocumentSnapshot result2 = results1[0]; - - expect(results2.length, 1); - expect(result2.data['message'], 'testing field path [doc2]'); - expect(result2.documentID, id2); + expect(results.length, 1); + expect(result.data['message'], 'testing field path'); + expect(result.documentID, documentId); }); }); } From f28d4d67851641b2e1baddbb10369d0e429f8c27 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:01:23 +0000 Subject: [PATCH 14/25] iOS implementation --- .../cloudfirestore/CloudFirestorePlugin.java | 3 +- .../ios/Classes/CloudFirestorePlugin.m | 121 +++++++++++++++--- 2 files changed, 102 insertions(+), 22 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 e5d3a63660fe..a3f412ca0d06 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 @@ -127,7 +127,6 @@ private Object[] getDocumentValues( String documentId = (String) document.get("id"); Map documentData = (Map) document.get("data"); List data = new ArrayList<>(); - // TODO (logcat) if (orderBy != null) { for (List order : orderBy) { final Object field = order.get(0); @@ -141,7 +140,7 @@ private Object[] getDocumentValues( } else { // Unsupported type. } - } else if (field instanceof String) { + } else if (field instanceof String) { String orderByFieldName = (String) field; if (orderByFieldName.contains(".")) { String[] fieldNameParts = orderByFieldName.split("\\."); diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index a566ffcb515c..e714ddc8a3a5 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -30,18 +30,34 @@ NSDictionary *documentData = document[@"data"]; if (orderBy) { for (id item in orderBy) { - NSArray *orderByParameters = item; - NSString *fieldName = orderByParameters[0]; - if ([fieldName rangeOfString:@"."].location != NSNotFound) { - NSArray *fieldNameParts = [fieldName componentsSeparatedByString:@"."]; - NSDictionary *currentMap = [documentData objectForKey:[fieldNameParts objectAtIndex:0]]; - for (int i = 1; i < [fieldNameParts count] - 1; i++) { - currentMap = [currentMap objectForKey:[fieldNameParts objectAtIndex:i]]; + NSObject *field = orderByParameters[0]; + + if ([field isKindOfClass:[FIRFieldPath class]]) { + if ([field isEqual:FIRFieldPath.documentID]) { + // This is also checked by an assertion on the Dart side. + [NSException raise:@"Invalid use of FieldValue.documentId" + format:@"You cannot order by the document id when using" + "{start/end}{At/After/Before}Document a the library will order by the document" + "id implicitly in order to to add other fields to the order clause."]; + } else { + // Unsupported type. + } + } else if ([field isKindOfClass:[NSSTring class]]) { + NSArray *orderByParameters = item; + NSString *fieldName = orderByParameters[0]; + if ([fieldName rangeOfString:@"."].location != NSNotFound) { + NSArray *fieldNameParts = [fieldName componentsSeparatedByString:@"."]; + NSDictionary *currentMap = [documentData objectForKey:[fieldNameParts objectAtIndex:0]]; + for (int i = 1; i < [fieldNameParts count] - 1; i++) { + currentMap = [currentMap objectForKey:[fieldNameParts objectAtIndex:i]]; + } + [values addObject:[currentMap objectForKey:[fieldNameParts + objectAtIndex:[fieldNameParts count] - 1]]]; + } else { + [values addObject:[documentData objectForKey:fieldName]]; } - [values addObject:[currentMap objectForKey:[fieldNameParts - objectAtIndex:[fieldNameParts count] - 1]]]; } else { - [values addObject:[documentData objectForKey:fieldName]]; + // Invalid type. } } } @@ -68,21 +84,69 @@ NSArray *whereConditions = parameters[@"where"]; for (id item in whereConditions) { NSArray *condition = item; - NSString *fieldName = condition[0]; + + FIRFieldPath *fieldPath = nil; + NSString *fieldName = nil; + NSObject *field = condition[0]; + + if ([field isKindOfClass:[NSString class]]) { + fieldName = (NSString *)field; + } else if ([field isKindOfClass:[FIRFieldPath class]]) { + fieldPath = (FIRFieldPath *)field; + } else { + // Invalid type. + } + NSString *op = condition[1]; id value = condition[2]; if ([op isEqualToString:@"=="]) { - query = [query queryWhereField:fieldName isEqualTo:value]; + if (fieldName != nil) { + query = [query queryWhereField:fieldName isEqualTo:value]; + } else if (fieldPath != nil) { + query = [query queryWhereFieldPath:fieldPath isEqualTo:value]; + } else { + // Invalid type. + } } else if ([op isEqualToString:@"<"]) { - query = [query queryWhereField:fieldName isLessThan:value]; + if (fieldName != nil) { + query = [query queryWhereField:fieldName isLessThan:value]; + } else if (fieldPath != nil) { + query = [query queryWhereFieldPath:fieldPath isLessThan:value]; + } else { + // Invalid type. + } } else if ([op isEqualToString:@"<="]) { - query = [query queryWhereField:fieldName isLessThanOrEqualTo:value]; + if (fieldName != nil) { + query = [query queryWhereField:fieldName isLessThanOrEqualTo:value]; + } else if (fieldPath != nil) { + query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value]; + } else { + // Invalid type. + } } else if ([op isEqualToString:@">"]) { - query = [query queryWhereField:fieldName isGreaterThan:value]; + if (fieldName != nil) { + query = [query queryWhereField:fieldName isGreaterThan:value]; + } else if (fieldPath != nil) { + query = [query queryWhereFieldPath:fieldPath isGreaterThan:value]; + } else { + // Invalid type. + } } else if ([op isEqualToString:@">="]) { - query = [query queryWhereField:fieldName isGreaterThanOrEqualTo:value]; + if (fieldName != nil) { + query = [query queryWhereField:fieldName isGreaterThanOrEqualTo:value]; + } else if (fieldPath != nil) { + query = [query queryWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value]; + } else { + // Invalid type. + } } else if ([op isEqualToString:@"array-contains"]) { - query = [query queryWhereField:fieldName arrayContains:value]; + if (fieldName != nil) { + query = [query queryWhereField:fieldName arrayContains:value]; + } else if (fieldPath != nil) { + query = [query queryWhereFieldPath:fieldPath arrayContains:value]; + } else { + // Invalid type. + } } else { // Unsupported operator } @@ -95,9 +159,26 @@ NSArray *orderBy = parameters[@"orderBy"]; if (orderBy) { for (NSArray *orderByParameters in orderBy) { - NSString *fieldName = orderByParameters[0]; + FIRFieldPath *fieldPath = nil; + NSString *fieldName = nil; + NSObject *field = orderByParameters[0]; + if ([field isKindOfClass:[NSString class]]) { + fieldName = (NSString *)field; + } else if ([field isKindOfClass:[FIRFieldPath class]]) { + fieldPath = (FIRFieldPath *)field; + } else { + // Invalid type. + } + NSNumber *descending = orderByParameters[1]; - query = [query queryOrderedByField:fieldName descending:[descending boolValue]]; + + if (fieldName != nil) { + query = [query queryOrderedByField:fieldName descending:[descending boolValue]]; + } else if (fieldPath != nil) { + query = [query queryOrderedByFieldPath:fieldPath descending:[descending boolValue]]; + } else { + // Invalid type. + } } } id startAt = parameters[@"startAt"]; @@ -221,7 +302,7 @@ static FIRFirestoreSource getSource(NSDictionary *arguments) { const UInt8 TIMESTAMP = 136; const UInt8 INCREMENT_DOUBLE = 137; const UInt8 INCREMENT_INTEGER = 138; -const UInt8 DOCUMENT_ID = 138; +const UInt8 DOCUMENT_ID = 139; @interface FirestoreWriter : FlutterStandardWriter - (void)writeValue:(id)value; From ac9a0d2343dfb3a5ce299740cc2426a080cbbbfc Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:04:26 +0000 Subject: [PATCH 15/25] format --- .../firebase/cloudfirestore/CloudFirestorePlugin.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 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 a3f412ca0d06..3af6c7565ed0 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 @@ -313,7 +313,7 @@ private Query getQuery(Map arguments) { boolean descending = (boolean) order.get(1); Query.Direction direction = - descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; + descending ? Query.Direction.DESCENDING : Query.Direction.ASCENDING; if (fieldName != null) { query = query.orderBy(fieldName, direction); @@ -338,8 +338,10 @@ private Query getQuery(Map arguments) { || endAtDocument != null || endBeforeDocument != null) { if (orderBy.size() == 0) { - throw new IllegalStateException("You need to order by at least one field when using " + - "{start/end}{At/After/Before}Document as you need some value to e.g. start after."); + throw new IllegalArgumentException( + "You cannot order by the document id when using" + + "{start/end}{At/After/Before}Document a the library will order by the document id" + + "implicitly in order to to add other fields to the order clause."); } boolean descending = (boolean) orderBy.get(orderBy.size() - 1).get(1); Query.Direction direction = From 595b92a5992dc356acf378ce51e09332d9298737 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:14:05 +0000 Subject: [PATCH 16/25] exceptions --- packages/cloud_firestore/CHANGELOG.md | 1 + .../cloudfirestore/CloudFirestorePlugin.java | 15 +++++++-------- .../ios/Classes/CloudFirestorePlugin.m | 5 +++++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/cloud_firestore/CHANGELOG.md b/packages/cloud_firestore/CHANGELOG.md index ae6505d32eb6..d4fb026e92e0 100644 --- a/packages/cloud_firestore/CHANGELOG.md +++ b/packages/cloud_firestore/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.12.10 * Added `FieldPath` class and `FieldPath.documentId` to refer to the document id in queries. +* Added assertions and exceptions that help you building correct queries. ## 0.12.9+6 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 3af6c7565ed0..ff471626b7c5 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 @@ -134,9 +134,10 @@ private Object[] getDocumentValues( if (field instanceof FieldPath) { if (field == FieldPath.documentId()) { // This is also checked by an assertion on the Dart side. - throw new IllegalArgumentException("You cannot order by the document id when using" + - "{start/end}{At/After/Before}Document a the library will order by the document id" + - "implicitly in order to to add other fields to the order clause."); + throw new IllegalArgumentException( + "You cannot order by the document id when using" + + "{start/end}{At/After/Before}Document a the library will order by the document id" + + "implicitly in order to to add other fields to the order clause."); } else { // Unsupported type. } @@ -337,11 +338,9 @@ private Query getQuery(Map arguments) { || startAfterDocument != null || endAtDocument != null || endBeforeDocument != null) { - if (orderBy.size() == 0) { - throw new IllegalArgumentException( - "You cannot order by the document id when using" - + "{start/end}{At/After/Before}Document a the library will order by the document id" - + "implicitly in order to to add other fields to the order clause."); + if (orderBy.isEmpty()) { + throw new IllegalStateException("You need to order by at least one field when using " + + "{start/end}{At/After/Before}Document as you need some value to e.g. start after."); } boolean descending = (boolean) orderBy.get(orderBy.size() - 1).get(1); Query.Direction direction = diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index e714ddc8a3a5..6cbbeb03e2f2 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -191,6 +191,11 @@ id endAtDocument = parameters[@"endAtDocument"]; id endBeforeDocument = parameters[@"endBeforeDocument"]; if (startAtDocument || startAfterDocument || endAtDocument || endBeforeDocument) { + if ([orderBy count] == 0) { + [NSException raise:@"No order by clause specified" + format:@"You need to order by at least one field when using {start/end}{At/" + "After/Before}Document as you need some value to e.g. start after."]; + } NSArray *orderByParameters = [orderBy lastObject]; NSNumber *descending = orderByParameters[1]; query = [query queryOrderedByFieldPath:FIRFieldPath.documentID From 0ca445e9e83e0d7b62bdd10bbfe7abe42b9785e6 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:15:55 +0000 Subject: [PATCH 17/25] format --- .../ios/Classes/CloudFirestorePlugin.m | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index 6cbbeb03e2f2..c19faa06c7ff 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -35,10 +35,12 @@ if ([field isKindOfClass:[FIRFieldPath class]]) { if ([field isEqual:FIRFieldPath.documentID]) { // This is also checked by an assertion on the Dart side. - [NSException raise:@"Invalid use of FieldValue.documentId" - format:@"You cannot order by the document id when using" - "{start/end}{At/After/Before}Document a the library will order by the document" - "id implicitly in order to to add other fields to the order clause."]; + [NSException + raise:@"Invalid use of FieldValue.documentId" + format: + @"You cannot order by the document id when using" + "{start/end}{At/After/Before}Document a the library will order by the document" + "id implicitly in order to to add other fields to the order clause."]; } else { // Unsupported type. } @@ -51,8 +53,9 @@ for (int i = 1; i < [fieldNameParts count] - 1; i++) { currentMap = [currentMap objectForKey:[fieldNameParts objectAtIndex:i]]; } - [values addObject:[currentMap objectForKey:[fieldNameParts - objectAtIndex:[fieldNameParts count] - 1]]]; + [values + addObject:[currentMap objectForKey:[fieldNameParts + objectAtIndex:[fieldNameParts count] - 1]]]; } else { [values addObject:[documentData objectForKey:fieldName]]; } From 48dd4ad9f5e8dff5f9552d3b1f0ac3253b9d65a1 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:19:18 +0000 Subject: [PATCH 18/25] remove gradle properties --- .../android/gradle/wrapper/gradle-wrapper.properties | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 packages/cloud_firestore/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/cloud_firestore/android/gradle/wrapper/gradle-wrapper.properties b/packages/cloud_firestore/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 967359574342..000000000000 --- a/packages/cloud_firestore/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Tue Oct 22 15:42:15 UTC 2019 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip From 01818df66152e3ad680b255fba5e62d56bf24f85 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:20:22 +0000 Subject: [PATCH 19/25] remove example gradle properties --- .../example/android/gradle/wrapper/gradle-wrapper.properties | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 019065d1d650..000000000000 --- a/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip From 8fd37e76590492ed9a876113dc1b9a58af20808d Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:22:35 +0000 Subject: [PATCH 20/25] remove gradle collisions --- packages/cloud_firestore/example/android/gradle.properties | 1 - .../example/android/gradle/wrapper/gradle-wrapper.properties | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/cloud_firestore/example/android/gradle.properties b/packages/cloud_firestore/example/android/gradle.properties index 7be3d8b46841..8bd86f680510 100755 --- a/packages/cloud_firestore/example/android/gradle.properties +++ b/packages/cloud_firestore/example/android/gradle.properties @@ -1,2 +1 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true diff --git a/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..d6bf9085fb45 --- /dev/null +++ b/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip \ No newline at end of file From 2e3dbf5e45a44ed349dd672b47df82decbacca8f Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:23:30 +0000 Subject: [PATCH 21/25] i meant to say resolve --- .../example/android/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties index d6bf9085fb45..019065d1d650 100644 --- a/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/cloud_firestore/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip From 4d9ff62fcbb4de8786b2c98d86c2532b512cb65f Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:33:37 +0000 Subject: [PATCH 22/25] format --- .../firebase/cloudfirestore/CloudFirestorePlugin.java | 5 +++-- packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m | 4 ++-- 2 files changed, 5 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 ff471626b7c5..3ebc8339a62f 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 @@ -339,8 +339,9 @@ private Query getQuery(Map arguments) { || endAtDocument != null || endBeforeDocument != null) { if (orderBy.isEmpty()) { - throw new IllegalStateException("You need to order by at least one field when using " + - "{start/end}{At/After/Before}Document as you need some value to e.g. start after."); + throw new IllegalStateException( + "You need to order by at least one field when using " + + "{start/end}{At/After/Before}Document as you need some value to e.g. start after."); } boolean descending = (boolean) orderBy.get(orderBy.size() - 1).get(1); Query.Direction direction = diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index c19faa06c7ff..fe607410bb21 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -196,8 +196,8 @@ if (startAtDocument || startAfterDocument || endAtDocument || endBeforeDocument) { if ([orderBy count] == 0) { [NSException raise:@"No order by clause specified" - format:@"You need to order by at least one field when using {start/end}{At/" - "After/Before}Document as you need some value to e.g. start after."]; + format:@"You need to order by at least one field when using {start/end}{At/" + "After/Before}Document as you need some value to e.g. start after."]; } NSArray *orderByParameters = [orderBy lastObject]; NSNumber *descending = orderByParameters[1]; From 61719d570faf35985b23b087d8dbf03238983a5c Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 23 Oct 2019 20:59:38 +0000 Subject: [PATCH 23/25] fix iOS --- 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 fe607410bb21..b1588561a503 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -30,6 +30,7 @@ NSDictionary *documentData = document[@"data"]; if (orderBy) { for (id item in orderBy) { + NSArray *orderByParameters = item; NSObject *field = orderByParameters[0]; if ([field isKindOfClass:[FIRFieldPath class]]) { @@ -45,7 +46,6 @@ // Unsupported type. } } else if ([field isKindOfClass:[NSSTring class]]) { - NSArray *orderByParameters = item; NSString *fieldName = orderByParameters[0]; if ([fieldName rangeOfString:@"."].location != NSNotFound) { NSArray *fieldNameParts = [fieldName componentsSeparatedByString:@"."]; From 6073c9c16effab89a8866cc9ef9e144d2cb5a0ca Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Thu, 24 Oct 2019 10:14:56 +0000 Subject: [PATCH 24/25] Update CloudFirestorePlugin.m --- 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 b1588561a503..c16e5cc6c694 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -45,7 +45,7 @@ } else { // Unsupported type. } - } else if ([field isKindOfClass:[NSSTring class]]) { + } else if ([field isKindOfClass:[NSString class]]) { NSString *fieldName = orderByParameters[0]; if ([fieldName rangeOfString:@"."].location != NSNotFound) { NSArray *fieldNameParts = [fieldName componentsSeparatedByString:@"."]; From fec2965e1a1a62ff4826ef5dc229f04b009b47e8 Mon Sep 17 00:00:00 2001 From: creativecreatorormaybenot <19204050+creativecreatorormaybenot@users.noreply.github.com> Date: Wed, 30 Oct 2019 17:47:46 +0000 Subject: [PATCH 25/25] typo --- .../firebase/cloudfirestore/CloudFirestorePlugin.java | 4 ++-- packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m | 6 +++--- 2 files changed, 5 insertions(+), 5 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 3ebc8339a62f..c4e75669d517 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 @@ -136,8 +136,8 @@ private Object[] getDocumentValues( // This is also checked by an assertion on the Dart side. throw new IllegalArgumentException( "You cannot order by the document id when using" - + "{start/end}{At/After/Before}Document a the library will order by the document id" - + "implicitly in order to to add other fields to the order clause."); + + "{start/end}{At/After/Before}Document as the library will order by the document" + + " id implicitly in order to to add other fields to the order clause."); } else { // Unsupported type. } diff --git a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m index b1588561a503..b03abaac850b 100644 --- a/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m +++ b/packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m @@ -39,9 +39,9 @@ [NSException raise:@"Invalid use of FieldValue.documentId" format: - @"You cannot order by the document id when using" - "{start/end}{At/After/Before}Document a the library will order by the document" - "id implicitly in order to to add other fields to the order clause."]; + @"You cannot order by the document id when using " + "{start/end}{At/After/Before}Document as the library will order by the document" + " id implicitly in order to to add other fields to the order clause."]; } else { // Unsupported type. }