Skip to content

Commit

Permalink
feat(firestore): support for second database (#7949)
Browse files Browse the repository at this point in the history
  • Loading branch information
russellwheatley authored Aug 9, 2024
1 parent 9387ff9 commit eec08a0
Show file tree
Hide file tree
Showing 33 changed files with 3,137 additions and 309 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/scripts/firestore.rules
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ service cloud.firestore {
}
match /firestore/{document=**} {
allow read, write: if true;
}
}
match /{path=**}/collectionGroup/{documentId} {
allow read, write: if true;
}
}
match /second-database/{document=**} {
// separate rules are not supported so we need to use the same rules for both databases to prove it is querying different databases
allow read, write: if database == "second-rnfb";
}
}
}
}
16 changes: 9 additions & 7 deletions packages/app/lib/internal/registry/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*
*/

import { isString } from '@react-native-firebase/app/lib/common';
import { isString } from '../../common';
import FirebaseApp from '../../FirebaseApp';
import SDK_VERSION from '../../version';
import { DEFAULT_APP_NAME, KNOWN_NAMESPACES } from '../constants';
Expand Down Expand Up @@ -93,19 +93,21 @@ function getOrCreateModuleForApp(app, moduleNamespace) {
);
}

// e.g. firebase.storage(customUrlOrRegion)
function firebaseModuleWithArgs(customUrlOrRegion) {
if (customUrlOrRegion !== undefined) {
// e.g. firebase.storage(customUrlOrRegion), firebase.functions(customUrlOrRegion), firebase.firestore(databaseId), firebase.database(url)
function firebaseModuleWithArgs(customUrlOrRegionOrDatabaseId) {
if (customUrlOrRegionOrDatabaseId !== undefined) {
if (!hasCustomUrlOrRegionSupport) {
// TODO throw Module does not support arguments error
}

if (!isString(customUrlOrRegion)) {
if (!isString(customUrlOrRegionOrDatabaseId)) {
// TODO throw Module first argument must be a string error
}
}

const key = customUrlOrRegion ? `${customUrlOrRegion}:${moduleNamespace}` : moduleNamespace;
const key = customUrlOrRegionOrDatabaseId
? `${customUrlOrRegionOrDatabaseId}:${moduleNamespace}`
: moduleNamespace;

if (!APP_MODULE_INSTANCE[app.name]) {
APP_MODULE_INSTANCE[app.name] = {};
Expand All @@ -115,7 +117,7 @@ function getOrCreateModuleForApp(app, moduleNamespace) {
APP_MODULE_INSTANCE[app.name][key] = new ModuleClass(
app,
NAMESPACE_REGISTRY[moduleNamespace],
customUrlOrRegion,
customUrlOrRegionOrDatabaseId,
);
}

Expand Down
5 changes: 4 additions & 1 deletion packages/app/lib/internal/registry/nativeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ function initialiseNativeModule(module) {
function subscribeToNativeModuleEvent(eventName) {
if (!NATIVE_MODULE_EVENT_SUBSCRIPTIONS[eventName]) {
RNFBNativeEventEmitter.addListener(eventName, event => {
if (event.appName) {
if (event.appName && event.databaseId) {
// Firestore requires both appName and databaseId to prefix
SharedEventEmitter.emit(`${event.appName}-${event.databaseId}-${eventName}`, event);
} else if (event.appName) {
// native event has an appName property - auto prefix and internally emit
SharedEventEmitter.emit(`${event.appName}-${eventName}`, event);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,41 @@
public class UniversalFirebaseFirestoreCommon {
static WeakHashMap<String, WeakReference<FirebaseFirestore>> instanceCache = new WeakHashMap<>();

static FirebaseFirestore getFirestoreForApp(String appName) {
WeakReference<FirebaseFirestore> cachedInstance = instanceCache.get(appName);
static String createFirestoreKey(String appName, String databaseId) {
return appName + ":" + databaseId;
}

static FirebaseFirestore getFirestoreForApp(String appName, String databaseId) {
String firestoreKey = createFirestoreKey(appName, databaseId);
WeakReference<FirebaseFirestore> cachedInstance = instanceCache.get(firestoreKey);

if (cachedInstance != null) {
return cachedInstance.get();
}

FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);

FirebaseFirestore instance = FirebaseFirestore.getInstance(firebaseApp);
FirebaseFirestore instance = FirebaseFirestore.getInstance(firebaseApp, databaseId);

setFirestoreSettings(instance, appName);
setFirestoreSettings(instance, firestoreKey);

instanceCache.put(appName, new WeakReference<FirebaseFirestore>(instance));

return instance;
}

private static void setFirestoreSettings(FirebaseFirestore firebaseFirestore, String appName) {
private static void setFirestoreSettings(
FirebaseFirestore firebaseFirestore, String firestoreKey) {

UniversalFirebasePreferences preferences = UniversalFirebasePreferences.getSharedInstance();
FirebaseFirestoreSettings.Builder firestoreSettings = new FirebaseFirestoreSettings.Builder();

String cacheSizeKey = UniversalFirebaseFirestoreStatics.FIRESTORE_CACHE_SIZE + "_" + appName;
String hostKey = UniversalFirebaseFirestoreStatics.FIRESTORE_HOST + "_" + appName;
String persistenceKey = UniversalFirebaseFirestoreStatics.FIRESTORE_PERSISTENCE + "_" + appName;
String sslKey = UniversalFirebaseFirestoreStatics.FIRESTORE_SSL + "_" + appName;
String cacheSizeKey =
UniversalFirebaseFirestoreStatics.FIRESTORE_CACHE_SIZE + "_" + firestoreKey;
String hostKey = UniversalFirebaseFirestoreStatics.FIRESTORE_HOST + "_" + firestoreKey;
String persistenceKey =
UniversalFirebaseFirestoreStatics.FIRESTORE_PERSISTENCE + "_" + firestoreKey;
String sslKey = UniversalFirebaseFirestoreStatics.FIRESTORE_SSL + "_" + firestoreKey;

int cacheSizeBytes =
preferences.getIntValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*
*/

import static io.invertase.firebase.firestore.UniversalFirebaseFirestoreCommon.createFirestoreKey;
import static io.invertase.firebase.firestore.UniversalFirebaseFirestoreCommon.getFirestoreForApp;
import static io.invertase.firebase.firestore.UniversalFirebaseFirestoreCommon.instanceCache;

Expand All @@ -40,27 +41,28 @@ public class UniversalFirebaseFirestoreModule extends UniversalFirebaseModule {
super(context, serviceName);
}

Task<Void> disableNetwork(String appName) {
return getFirestoreForApp(appName).disableNetwork();
Task<Void> disableNetwork(String appName, String databaseId) {
return getFirestoreForApp(appName, databaseId).disableNetwork();
}

Task<Void> enableNetwork(String appName) {
return getFirestoreForApp(appName).enableNetwork();
Task<Void> enableNetwork(String appName, String databaseId) {
return getFirestoreForApp(appName, databaseId).enableNetwork();
}

Task<Void> useEmulator(String appName, String host, int port) {
Task<Void> useEmulator(String appName, String databaseId, String host, int port) {
return Tasks.call(
getExecutor(),
() -> {
if (emulatorConfigs.get(appName) == null) {
emulatorConfigs.put(appName, "true");
getFirestoreForApp(appName).useEmulator(host, port);
String firestoreKey = createFirestoreKey(appName, databaseId);
if (emulatorConfigs.get(firestoreKey) == null) {
emulatorConfigs.put(firestoreKey, "true");
getFirestoreForApp(appName, databaseId).useEmulator(host, port);
}
return null;
});
}

Task<Void> settings(String appName, Map<String, Object> settings) {
Task<Void> settings(String firestoreKey, Map<String, Object> settings) {
return Tasks.call(
getExecutor(),
() -> {
Expand All @@ -70,31 +72,31 @@ Task<Void> settings(String appName, Map<String, Object> settings) {

UniversalFirebasePreferences.getSharedInstance()
.setIntValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_CACHE_SIZE + "_" + appName,
UniversalFirebaseFirestoreStatics.FIRESTORE_CACHE_SIZE + "_" + firestoreKey,
Objects.requireNonNull(cacheSizeBytesDouble).intValue());
}

// settings.host
if (settings.containsKey("host")) {
UniversalFirebasePreferences.getSharedInstance()
.setStringValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_HOST + "_" + appName,
UniversalFirebaseFirestoreStatics.FIRESTORE_HOST + "_" + firestoreKey,
(String) settings.get("host"));
}

// settings.persistence
if (settings.containsKey("persistence")) {
UniversalFirebasePreferences.getSharedInstance()
.setBooleanValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_PERSISTENCE + "_" + appName,
UniversalFirebaseFirestoreStatics.FIRESTORE_PERSISTENCE + "_" + firestoreKey,
(boolean) settings.get("persistence"));
}

// settings.ssl
if (settings.containsKey("ssl")) {
UniversalFirebasePreferences.getSharedInstance()
.setBooleanValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_SSL + "_" + appName,
UniversalFirebaseFirestoreStatics.FIRESTORE_SSL + "_" + firestoreKey,
(boolean) settings.get("ssl"));
}

Expand All @@ -104,33 +106,33 @@ Task<Void> settings(String appName, Map<String, Object> settings) {
.setStringValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_SERVER_TIMESTAMP_BEHAVIOR
+ "_"
+ appName,
+ firestoreKey,
(String) settings.get("serverTimestampBehavior"));
}

return null;
});
}

LoadBundleTask loadBundle(String appName, String bundle) {
LoadBundleTask loadBundle(String appName, String databaseId, String bundle) {
byte[] bundleData = bundle.getBytes(StandardCharsets.UTF_8);
return getFirestoreForApp(appName).loadBundle(bundleData);
return getFirestoreForApp(appName, databaseId).loadBundle(bundleData);
}

Task<Void> clearPersistence(String appName) {
return getFirestoreForApp(appName).clearPersistence();
Task<Void> clearPersistence(String appName, String databaseId) {
return getFirestoreForApp(appName, databaseId).clearPersistence();
}

Task<Void> waitForPendingWrites(String appName) {
return getFirestoreForApp(appName).waitForPendingWrites();
Task<Void> waitForPendingWrites(String appName, String databaseId) {
return getFirestoreForApp(appName, databaseId).waitForPendingWrites();
}

Task<Void> terminate(String appName) {
FirebaseFirestore firebaseFirestore = getFirestoreForApp(appName);

if (instanceCache.get(appName) != null) {
instanceCache.get(appName).clear();
instanceCache.remove(appName);
Task<Void> terminate(String appName, String databaseId) {
FirebaseFirestore firebaseFirestore = getFirestoreForApp(appName, databaseId);
String firestoreKey = createFirestoreKey(appName, databaseId);
if (instanceCache.get(firestoreKey) != null) {
instanceCache.get(firestoreKey).clear();
instanceCache.remove(firestoreKey);
}

return firebaseFirestore.terminate();
Expand Down
Loading

0 comments on commit eec08a0

Please sign in to comment.