Skip to content

Commit 4557064

Browse files
russellwheatleySalakar
authored andcommitted
feat(firestore): add support for multiple database instances (#11310)
1 parent d624a0c commit 4557064

30 files changed

+3330
-113
lines changed

melos.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ scripts:
160160
test:e2e:cloud_firestore:
161161
run: |
162162
cd packages/cloud_firestore/cloud_firestore/example
163-
flutter test integration_test/cloud_firestore_e2e_test.dart
163+
flutter test integration_test/e2e_test.dart
164164
description: |
165165
Run all e2e tests for cloud_firestore.
166166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2023 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.firebase.firestore;
6+
7+
import com.google.firebase.firestore.FirebaseFirestore;
8+
9+
public class FlutterFirebaseFirestoreExtension {
10+
private final FirebaseFirestore instance;
11+
private final String databaseURL;
12+
13+
public FlutterFirebaseFirestoreExtension(FirebaseFirestore instance, String databaseURL) {
14+
this.instance = instance;
15+
this.databaseURL = databaseURL;
16+
}
17+
18+
public FirebaseFirestore getInstance() {
19+
return instance;
20+
}
21+
22+
public String getDatabaseURL() {
23+
return databaseURL;
24+
}
25+
}

packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,18 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) {
7272
writeDouble(stream, ((GeoPoint) value).getLongitude());
7373
} else if (value instanceof DocumentReference) {
7474
stream.write(DATA_TYPE_DOCUMENT_REFERENCE);
75-
writeValue(stream, ((DocumentReference) value).getFirestore().getApp().getName());
75+
FirebaseFirestore firestore = ((DocumentReference) value).getFirestore();
76+
String appName = firestore.getApp().getName();
77+
writeValue(stream, appName);
7678
writeValue(stream, ((DocumentReference) value).getPath());
79+
String databaseURL;
80+
// There is no way of getting database URL from Firebase android SDK API so we cache it ourselves
81+
synchronized (FlutterFirebaseFirestorePlugin.firestoreInstanceCache) {
82+
databaseURL =
83+
FlutterFirebaseFirestorePlugin.getCachedFirebaseFirestoreInstanceForKey(firestore)
84+
.getDatabaseURL();
85+
}
86+
writeValue(stream, databaseURL);
7787
} else if (value instanceof DocumentSnapshot) {
7888
writeDocumentSnapshot(stream, (DocumentSnapshot) value);
7989
} else if (value instanceof QuerySnapshot) {
@@ -277,20 +287,22 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) {
277287

278288
private FirebaseFirestore readFirestoreInstance(ByteBuffer buffer) {
279289
String appName = (String) readValue(buffer);
290+
String databaseURL = (String) readValue(buffer);
280291
FirebaseFirestoreSettings settings = (FirebaseFirestoreSettings) readValue(buffer);
281-
282292
synchronized (FlutterFirebaseFirestorePlugin.firestoreInstanceCache) {
283-
if (FlutterFirebaseFirestorePlugin.getCachedFirebaseFirestoreInstanceForKey(appName)
293+
if (FlutterFirebaseFirestorePlugin.getFirestoreInstanceByNameAndDatabaseUrl(
294+
appName, databaseURL)
284295
!= null) {
285-
return FlutterFirebaseFirestorePlugin.getCachedFirebaseFirestoreInstanceForKey(appName);
296+
return FlutterFirebaseFirestorePlugin.getFirestoreInstanceByNameAndDatabaseUrl(
297+
appName, databaseURL);
286298
}
287299

288300
FirebaseApp app = FirebaseApp.getInstance(appName);
289-
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
290-
301+
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app, databaseURL);
291302
firestore.setFirestoreSettings(settings);
292303

293-
FlutterFirebaseFirestorePlugin.setCachedFirebaseFirestoreInstanceForKey(firestore, appName);
304+
FlutterFirebaseFirestorePlugin.setCachedFirebaseFirestoreInstanceForKey(
305+
firestore, databaseURL);
294306
return firestore;
295307
}
296308
}

packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454

5555
public class FlutterFirebaseFirestorePlugin
5656
implements FlutterFirebasePlugin, MethodCallHandler, FlutterPlugin, ActivityAware {
57-
protected static final HashMap<String, FirebaseFirestore> firestoreInstanceCache =
58-
new HashMap<>();
57+
protected static final HashMap<FirebaseFirestore, FlutterFirebaseFirestoreExtension>
58+
firestoreInstanceCache = new HashMap<>();
5959

6060
public static final String DEFAULT_ERROR_CODE = "firebase_firestore";
6161

@@ -79,27 +79,41 @@ public class FlutterFirebaseFirestorePlugin
7979
public static final Map<Integer, DocumentSnapshot.ServerTimestampBehavior>
8080
serverTimestampBehaviorHashMap = new HashMap<>();
8181

82-
protected static FirebaseFirestore getCachedFirebaseFirestoreInstanceForKey(String key) {
82+
protected static FlutterFirebaseFirestoreExtension getCachedFirebaseFirestoreInstanceForKey(
83+
FirebaseFirestore firestore) {
8384
synchronized (firestoreInstanceCache) {
84-
return firestoreInstanceCache.get(key);
85+
return firestoreInstanceCache.get(firestore);
8586
}
8687
}
8788

8889
protected static void setCachedFirebaseFirestoreInstanceForKey(
89-
FirebaseFirestore firestore, String key) {
90+
FirebaseFirestore firestore, String databaseURL) {
9091
synchronized (firestoreInstanceCache) {
91-
FirebaseFirestore existingInstance = firestoreInstanceCache.get(key);
92+
FlutterFirebaseFirestoreExtension existingInstance = firestoreInstanceCache.get(firestore);
9293
if (existingInstance == null) {
93-
firestoreInstanceCache.put(key, firestore);
94+
firestoreInstanceCache.put(
95+
firestore, new FlutterFirebaseFirestoreExtension(firestore, databaseURL));
9496
}
9597
}
9698
}
9799

98-
private static void destroyCachedFirebaseFirestoreInstanceForKey(String key) {
100+
protected static FirebaseFirestore getFirestoreInstanceByNameAndDatabaseUrl(
101+
String appName, String databaseURL) {
102+
for (Map.Entry<FirebaseFirestore, FlutterFirebaseFirestoreExtension> entry :
103+
firestoreInstanceCache.entrySet()) {
104+
if (entry.getValue().getInstance().getApp().getName().equals(appName)
105+
&& entry.getValue().getDatabaseURL().equals(databaseURL)) {
106+
return entry.getKey();
107+
}
108+
}
109+
return null;
110+
}
111+
112+
private static void destroyCachedFirebaseFirestoreInstanceForKey(FirebaseFirestore firestore) {
99113
synchronized (firestoreInstanceCache) {
100-
FirebaseFirestore existingInstance = firestoreInstanceCache.get(key);
114+
FlutterFirebaseFirestoreExtension existingInstance = firestoreInstanceCache.get(firestore);
101115
if (existingInstance != null) {
102-
firestoreInstanceCache.remove(key);
116+
firestoreInstanceCache.remove(firestore);
103117
}
104118
}
105119
}
@@ -511,7 +525,7 @@ private Task<Void> terminate(Map<String, Object> arguments) {
511525
FirebaseFirestore firestore =
512526
(FirebaseFirestore) Objects.requireNonNull(arguments.get("firestore"));
513527
Tasks.await(firestore.terminate());
514-
destroyCachedFirebaseFirestoreInstanceForKey(firestore.getApp().getName());
528+
destroyCachedFirebaseFirestoreInstanceForKey(firestore);
515529

516530
taskCompletionSource.setResult(null);
517531
} catch (Exception e) {
@@ -742,12 +756,12 @@ public Task<Void> didReinitializeFirebaseCore() {
742756
() -> {
743757
try {
744758
// Context is ignored by API so we don't send it over even though annotated non-null.
745-
746-
for (FirebaseApp app : FirebaseApp.getApps(null)) {
747-
FirebaseFirestore firestore = FirebaseFirestore.getInstance(app);
759+
for (Map.Entry<FirebaseFirestore, FlutterFirebaseFirestoreExtension> entry :
760+
firestoreInstanceCache.entrySet()) {
761+
FirebaseFirestore firestore = entry.getKey();
748762
Tasks.await(firestore.terminate());
749763
FlutterFirebaseFirestorePlugin.destroyCachedFirebaseFirestoreInstanceForKey(
750-
app.getName());
764+
firestore);
751765
}
752766

753767
removeEventListeners();

packages/cloud_firestore/cloud_firestore/example/integration_test/e2e_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'snapshot_metadata_e2e.dart';
2121
import 'timestamp_e2e.dart';
2222
import 'transaction_e2e.dart';
2323
import 'write_batch_e2e.dart';
24+
import 'second_database.dart';
2425

2526
bool kUseFirestoreEmulator = true;
2627

@@ -52,5 +53,6 @@ void main() {
5253
runWriteBatchTests();
5354
runLoadBundleTests();
5455
runSecondAppTests();
56+
runSecondDatabaseTests();
5557
});
5658
}

0 commit comments

Comments
 (0)