Merge pull request #881 from pocketgems/multiple_databases_3.3.0_upstream

Implement multiple database shard support
This commit is contained in:
Chris Bianca 2018-03-23 08:35:01 +00:00 committed by GitHub
commit a605a703fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 250 additions and 117 deletions

View File

@ -53,16 +53,16 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param appName
*/
@ReactMethod
public void goOnline(String appName) {
getDatabaseForApp(appName).goOnline();
public void goOnline(String appName, String dbURL) {
getDatabaseForAppAndSetLogging(appName, dbURL).goOnline();
}
/**
* @param appName
*/
@ReactMethod
public void goOffline(String appName) {
getDatabaseForApp(appName).goOffline();
public void goOffline(String appName, String dbURL) {
getDatabaseForAppAndSetLogging(appName, dbURL).goOffline();
}
/**
@ -70,8 +70,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param state
*/
@ReactMethod
public void setPersistence(String appName, Boolean state) {
getDatabaseForApp(appName).setPersistenceEnabled(state);
public void setPersistence(String appName, String dbURL, Boolean state) {
getDatabaseForAppAndSetLogging(appName, dbURL).setPersistenceEnabled(state);
}
/**
@ -79,8 +79,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param size
*/
@ReactMethod
public void setPersistenceCacheSizeBytes(String appName, int size) {
getDatabaseForApp(appName).setPersistenceCacheSizeBytes((long) size);
public void setPersistenceCacheSizeBytes(String appName, String dbURL, int size) {
getDatabaseForAppAndSetLogging(appName, dbURL).setPersistenceCacheSizeBytes((long) size);
}
@ -116,8 +116,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param state
*/
@ReactMethod
public void keepSynced(String appName, String key, String path, ReadableArray modifiers, Boolean state) {
getInternalReferenceForApp(appName, key, path, modifiers).getQuery().keepSynced(state);
public void keepSynced(String appName, String dbURL, String key, String path, ReadableArray modifiers, Boolean state) {
getInternalReferenceForApp(appName, dbURL, key, path, modifiers).getQuery().keepSynced(state);
}
@ -130,7 +130,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param updates
*/
@ReactMethod
public void transactionTryCommit(String appName, int transactionId, ReadableMap updates) {
public void transactionTryCommit(String appName, String dbURL, int transactionId, ReadableMap updates) {
RNFirebaseTransactionHandler handler = transactionHandlers.get(transactionId);
if (handler != null) {
@ -147,16 +147,16 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param applyLocally
*/
@ReactMethod
public void transactionStart(final String appName, final String path, final int transactionId, final Boolean applyLocally) {
public void transactionStart(final String appName, final String dbURL, final String path, final int transactionId, final Boolean applyLocally) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
DatabaseReference reference = getReferenceForAppPath(appName, path);
DatabaseReference reference = getReferenceForAppPath(appName, dbURL, path);
reference.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(transactionId, appName);
final RNFirebaseTransactionHandler transactionHandler = new RNFirebaseTransactionHandler(transactionId, appName, dbURL);
transactionHandlers.put(transactionId, transactionHandler);
final WritableMap updatesMap = transactionHandler.createUpdateMap(mutableData);
@ -212,9 +212,9 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void onDisconnectSet(String appName, String path, ReadableMap props, final Promise promise) {
public void onDisconnectSet(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
String type = props.getString("type");
DatabaseReference ref = getReferenceForAppPath(appName, path);
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
OnDisconnect onDisconnect = ref.onDisconnect();
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@ -257,8 +257,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void onDisconnectUpdate(String appName, String path, ReadableMap props, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void onDisconnectUpdate(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
OnDisconnect ondDisconnect = ref.onDisconnect();
Map<String, Object> map = Utils.recursivelyDeconstructReadableMap(props);
@ -279,8 +279,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void onDisconnectRemove(String appName, String path, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void onDisconnectRemove(String appName, String dbURL, String path, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
OnDisconnect onDisconnect = ref.onDisconnect();
onDisconnect.removeValue(new DatabaseReference.CompletionListener() {
@ -299,8 +299,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void onDisconnectCancel(String appName, String path, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void onDisconnectCancel(String appName, String dbURL, String path, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
OnDisconnect onDisconnect = ref.onDisconnect();
onDisconnect.cancel(new DatabaseReference.CompletionListener() {
@ -318,8 +318,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void set(String appName, String path, ReadableMap props, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void set(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Object value = Utils.recursivelyDeconstructReadableMap(props).get("value");
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@ -339,8 +339,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void setPriority(String appName, String path, ReadableMap priority, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void setPriority(String appName, String dbURL, String path, ReadableMap priority, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Object priorityValue = Utils.recursivelyDeconstructReadableMap(priority).get("value");
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@ -361,8 +361,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void setWithPriority(String appName, String path, ReadableMap data, ReadableMap priority, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void setWithPriority(String appName, String dbURL, String path, ReadableMap data, ReadableMap priority, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Object dataValue = Utils.recursivelyDeconstructReadableMap(data).get("value");
Object priorityValue = Utils.recursivelyDeconstructReadableMap(priority).get("value");
@ -383,8 +383,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void update(String appName, String path, ReadableMap props, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void update(String appName, String dbURL, String path, ReadableMap props, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
Map<String, Object> updates = Utils.recursivelyDeconstructReadableMap(props);
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@ -403,8 +403,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void remove(String appName, String path, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, path);
public void remove(String appName, String dbURL, String path, final Promise promise) {
DatabaseReference ref = getReferenceForAppPath(appName, dbURL, path);
DatabaseReference.CompletionListener listener = new DatabaseReference.CompletionListener() {
@Override
@ -428,8 +428,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param promise
*/
@ReactMethod
public void once(String appName, String key, String path, ReadableArray modifiers, String eventType, Promise promise) {
getInternalReferenceForApp(appName, key, path, modifiers).once(eventType, promise);
public void once(String appName, String dbURL, String key, String path, ReadableArray modifiers, String eventType, Promise promise) {
getInternalReferenceForApp(appName, dbURL, key, path, modifiers).once(eventType, promise);
}
/**
@ -439,8 +439,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param props ReadableMap
*/
@ReactMethod
public void on(String appName, ReadableMap props) {
getCachedInternalReferenceForApp(appName, props)
public void on(String appName, String dbURL, ReadableMap props) {
getCachedInternalReferenceForApp(appName, dbURL, props)
.on(
props.getString("eventType"),
props.getMap("registration")
@ -494,11 +494,30 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* Get a database instance for a specific firebase app instance
*
* @param appName
* @param dbURL
* @return
*/
private FirebaseDatabase getDatabaseForApp(String appName) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
public static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
FirebaseDatabase firebaseDatabase;
if(dbURL != null && dbURL.length() > 0) {
firebaseDatabase = FirebaseDatabase.getInstance(dbURL);
} else {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
}
return firebaseDatabase;
}
/**
* Get a database instance for a specific firebase app instance and enable/disable logging
*
* @param appName
* @param dbURL
* @return
*/
private FirebaseDatabase getDatabaseForAppAndSetLogging(String appName, String dbURL) {
FirebaseDatabase firebaseDatabase = RNFirebaseDatabase.getDatabaseForApp(appName, dbURL);
Boolean logLevel = loggingLevelSet.get(firebaseDatabase.getApp().getName());
if (enableLogging && (logLevel == null || !logLevel)) {
@ -535,8 +554,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param path
* @return
*/
private DatabaseReference getReferenceForAppPath(String appName, String path) {
return getDatabaseForApp(appName).getReference(path);
private DatabaseReference getReferenceForAppPath(String appName, String dbURL, String path) {
return getDatabaseForAppAndSetLogging(appName, dbURL).getReference(path);
}
/**
@ -548,10 +567,11 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param modifiers
* @return
*/
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, String key, String path, ReadableArray modifiers) {
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, String dbURL, String key, String path, ReadableArray modifiers) {
return new RNFirebaseDatabaseReference(
getReactApplicationContext(),
appName,
dbURL,
key,
path,
modifiers
@ -565,7 +585,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param props
* @return
*/
private RNFirebaseDatabaseReference getCachedInternalReferenceForApp(String appName, ReadableMap props) {
private RNFirebaseDatabaseReference getCachedInternalReferenceForApp(String appName, String dbURL, ReadableMap props) {
String key = props.getString("key");
String path = props.getString("path");
ReadableArray modifiers = props.getArray("modifiers");
@ -573,7 +593,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
RNFirebaseDatabaseReference existingRef = references.get(key);
if (existingRef == null) {
existingRef = getInternalReferenceForApp(appName, key, path, modifiers);
existingRef = getInternalReferenceForApp(appName, dbURL, key, path, modifiers);
references.put(key, existingRef);
}

View File

@ -28,6 +28,7 @@ class RNFirebaseDatabaseReference {
private String key;
private Query query;
private String appName;
private String dbURL;
private ReactContext reactContext;
private static final String TAG = "RNFirebaseDBReference";
private HashMap<String, ChildEventListener> childEventListeners = new HashMap<>();
@ -43,10 +44,11 @@ class RNFirebaseDatabaseReference {
* @param refPath
* @param modifiersArray
*/
RNFirebaseDatabaseReference(ReactContext context, String app, String refKey, String refPath, ReadableArray modifiersArray) {
RNFirebaseDatabaseReference(ReactContext context, String app, String url, String refKey, String refPath, ReadableArray modifiersArray) {
key = refKey;
query = null;
appName = app;
dbURL = url;
reactContext = context;
buildDatabaseQueryAtPathAndModifiers(refPath, modifiersArray);
}
@ -346,9 +348,7 @@ class RNFirebaseDatabaseReference {
* @return
*/
private void buildDatabaseQueryAtPathAndModifiers(String path, ReadableArray modifiers) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance(firebaseApp);
FirebaseDatabase firebaseDatabase = RNFirebaseDatabase.getDatabaseForApp(appName, dbURL);
query = firebaseDatabase.getReference(path);
List<Object> modifiersList = Utils.recursivelyDeconstructReadableArray(modifiers);

View File

@ -21,6 +21,7 @@ import io.invertase.firebase.Utils;
public class RNFirebaseTransactionHandler {
private int transactionId;
private String appName;
private String dbURL;
private final ReentrantLock lock;
private final Condition condition;
private Map<String, Object> data;
@ -31,8 +32,9 @@ public class RNFirebaseTransactionHandler {
boolean abort = false;
boolean timeout = false;
RNFirebaseTransactionHandler(int id, String app) {
RNFirebaseTransactionHandler(int id, String app, String url) {
appName = app;
dbURL = url;
transactionId = id;
lock = new ReentrantLock();
condition = lock.newCondition();
@ -107,6 +109,7 @@ public class RNFirebaseTransactionHandler {
// all events get distributed js side based on app name
updatesMap.putString("appName", appName);
updatesMap.putString("dbURL", dbURL);
if (!updatesData.hasChildren()) {
Utils.mapPutValue("value", updatesData.getValue(), updatesMap);
@ -129,6 +132,7 @@ public class RNFirebaseTransactionHandler {
resultMap.putInt("id", transactionId);
resultMap.putString("appName", appName);
resultMap.putString("dbURL", dbURL);
resultMap.putBoolean("timeout", timeout);
resultMap.putBoolean("committed", committed);

View File

@ -17,6 +17,7 @@
+ (void)handlePromise:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject databaseError:(NSError *)databaseError;
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName;
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName URL:(NSString *)url;
+ (NSDictionary *)getJSError:(NSError *)nativeError;

View File

@ -22,22 +22,26 @@ RCT_EXPORT_MODULE();
return self;
}
RCT_EXPORT_METHOD(goOnline:(NSString *)appDisplayName) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOnline];
RCT_EXPORT_METHOD(goOnline:(NSString *)appDisplayName
dbURL:(NSString *)dbURL) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL] goOnline];
}
RCT_EXPORT_METHOD(goOffline:(NSString *)appDisplayName) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName] goOffline];
RCT_EXPORT_METHOD(goOffline:(NSString *)appDisplayName
dbURL:(NSString *)dbURL) {
[[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL] goOffline];
}
RCT_EXPORT_METHOD(setPersistence:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
state:(BOOL)state) {
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceEnabled = state;
[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL].persistenceEnabled = state;
}
RCT_EXPORT_METHOD(setPersistenceCacheSizeBytes:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
size:(NSInteger *)size) {
[RNFirebaseDatabase getDatabaseForApp:appDisplayName].persistenceCacheSizeBytes = (NSUInteger)size;
[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL].persistenceCacheSizeBytes = (NSUInteger)size;
}
RCT_EXPORT_METHOD(enableLogging:(BOOL)enabled) {
@ -45,15 +49,17 @@ RCT_EXPORT_METHOD(enableLogging:(BOOL)enabled) {
}
RCT_EXPORT_METHOD(keepSynced:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
key:(NSString *)key
path:(NSString *)path
modifiers:(NSArray *)modifiers
state:(BOOL)state) {
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers].query;
FIRDatabaseQuery *query = [self getInternalReferenceForApp:appDisplayName dbURL:dbURL key:key path:path modifiers:modifiers].query;
[query keepSynced:state];
}
RCT_EXPORT_METHOD(transactionTryCommit:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
transactionId:(nonnull NSNumber *)transactionId
updates:(NSDictionary *)updates) {
__block NSMutableDictionary *transactionState;
@ -83,6 +89,7 @@ RCT_EXPORT_METHOD(transactionTryCommit:(NSString *)appDisplayName
RCT_EXPORT_METHOD(transactionStart:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
transactionId:(nonnull NSNumber *)transactionId
applyLocally:(BOOL)applyLocally) {
@ -90,12 +97,12 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *)appDisplayName
NSMutableDictionary *transactionState = [NSMutableDictionary new];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
transactionState[@"semaphore"] = sema;
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref runTransactionBlock:^FIRTransactionResult *_Nonnull (FIRMutableData *_Nonnull currentData) {
dispatch_barrier_async(_transactionQueue, ^{
[_transactions setValue:transactionState forKey:[transactionId stringValue]];
NSDictionary *updateMap = [self createTransactionUpdateMap:appDisplayName transactionId:transactionId updatesData:currentData];
NSDictionary *updateMap = [self createTransactionUpdateMap:appDisplayName dbURL:dbURL transactionId:transactionId updatesData:currentData];
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:updateMap];
});
@ -120,123 +127,134 @@ RCT_EXPORT_METHOD(transactionStart:(NSString *)appDisplayName
return [FIRTransactionResult successWithValue:currentData];
}
} andCompletionBlock:^(NSError *_Nullable databaseError, BOOL committed, FIRDataSnapshot *_Nullable snapshot) {
NSDictionary *resultMap = [self createTransactionResultMap:appDisplayName transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
NSDictionary *resultMap = [self createTransactionResultMap:appDisplayName dbURL:dbURL transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
[RNFirebaseUtil sendJSEvent:self name:DATABASE_TRANSACTION_EVENT body:resultMap];
} withLocalEvents:applyLocally];
});
}
RCT_EXPORT_METHOD(onDisconnectSet:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
props:(NSDictionary *)props
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref onDisconnectSetValue:props[@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectUpdate:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
props:(NSDictionary *)props
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref onDisconnectUpdateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectRemove:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref onDisconnectRemoveValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectCancel:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref cancelDisconnectOperationsWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(set:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
props:(NSDictionary *)props
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref setValue:[props valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setPriority:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
priority:(NSDictionary *)priority
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref setPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setWithPriority:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
data:(NSDictionary *)data
priority:(NSDictionary *)priority
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref setValue:[data valueForKey:@"value"] andPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(update:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
props:(NSDictionary *)props
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref updateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(remove:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
path:(NSString *)path
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName path:path];
FIRDatabaseReference *ref = [self getReferenceForAppPath:appDisplayName dbURL:dbURL path:path];
[ref removeValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(once:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
key:(NSString *)key
path:(NSString *)path
modifiers:(NSArray *)modifiers
eventName:(NSString *)eventName
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appDisplayName key:key path:path modifiers:modifiers];
RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appDisplayName dbURL:dbURL key:key path:path modifiers:modifiers];
[ref once:eventName resolver:resolve rejecter:reject];
}
RCT_EXPORT_METHOD(on:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
props:(NSDictionary *)props) {
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appDisplayName props:props];
RNFirebaseDatabaseReference *ref = [self getCachedInternalReferenceForApp:appDisplayName dbURL:dbURL props:props];
[ref on:props[@"eventType"] registration:props[@"registration"]];
}
@ -271,23 +289,31 @@ RCT_EXPORT_METHOD(off:(NSString *)key
return [FIRDatabase databaseForApp:app];
}
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appDisplayName path:(NSString *)path {
return [[RNFirebaseDatabase getDatabaseForApp:appDisplayName] referenceWithPath:path];
+ (FIRDatabase *)getDatabaseForApp:(NSString *)appDisplayName URL:(NSString *)url {
if (url == nil) {
return [self getDatabaseForApp:appDisplayName];
}
FIRApp *app = [RNFirebaseUtil getApp:appDisplayName];
return [FIRDatabase databaseForApp:app URL:url];
}
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appDisplayName key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
- (FIRDatabaseReference *)getReferenceForAppPath:(NSString *)appDisplayName dbURL:(NSString *)dbURL path:(NSString *)path {
return [[RNFirebaseDatabase getDatabaseForApp:appDisplayName URL:dbURL] referenceWithPath:path];
}
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appDisplayName props:(NSDictionary *)props {
- (RNFirebaseDatabaseReference *)getInternalReferenceForApp:(NSString *)appDisplayName dbURL:(NSString *)dbURL key:(NSString *)key path:(NSString *)path modifiers:(NSArray *)modifiers {
return [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName dbURL:dbURL key:key refPath:path modifiers:modifiers];
}
- (RNFirebaseDatabaseReference *)getCachedInternalReferenceForApp:(NSString *)appDisplayName dbURL:(NSString *)dbURL props:(NSDictionary *)props {
NSString *key = props[@"key"];
NSString *path = props[@"path"];
NSDictionary *modifiers = props[@"modifiers"];
NSArray *modifiers = props[@"modifiers"];
RNFirebaseDatabaseReference *ref = _dbReferences[key];
if (ref == nil) {
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName key:key refPath:path modifiers:modifiers];
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self appDisplayName:appDisplayName dbURL:dbURL key:key refPath:path modifiers:modifiers];
_dbReferences[key] = ref;
}
return ref;
@ -375,20 +401,22 @@ RCT_EXPORT_METHOD(off:(NSString *)key
return errorMap;
}
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
- (NSDictionary *)createTransactionUpdateMap:(NSString *)appDisplayName dbURL:(NSString *)dbURL transactionId:(NSNumber *)transactionId updatesData:(FIRMutableData *)updatesData {
NSMutableDictionary *updatesMap = [[NSMutableDictionary alloc] init];
[updatesMap setValue:transactionId forKey:@"id"];
[updatesMap setValue:@"update" forKey:@"type"];
[updatesMap setValue:appDisplayName forKey:@"appName"];
[updatesMap setValue:dbURL forKey:@"dbURL"];
[updatesMap setValue:updatesData.value forKey:@"value"];
return updatesMap;
}
- (NSDictionary *)createTransactionResultMap:(NSString *)appDisplayName transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
- (NSDictionary *)createTransactionResultMap:(NSString *)appDisplayName dbURL:(NSString *)dbURL transactionId:(NSNumber *)transactionId error:(NSError *)error committed:(BOOL)committed snapshot:(FIRDataSnapshot *)snapshot {
NSMutableDictionary *resultMap = [[NSMutableDictionary alloc] init];
[resultMap setValue:transactionId forKey:@"id"];
[resultMap setValue:appDisplayName forKey:@"appName"];
[resultMap setValue:dbURL forKey:@"dbURL"];
// TODO: no timeout on iOS
[resultMap setValue:@(committed) forKey:@"committed"];
// TODO: no interrupted on iOS

View File

@ -13,11 +13,12 @@
@property RCTEventEmitter *emitter;
@property FIRDatabaseQuery *query;
@property NSString *appDisplayName;
@property NSString *dbURL;
@property NSString *key;
@property NSString *path;
@property NSMutableDictionary *listeners;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter appDisplayName:(NSString *)appDisplayName dbURL:(NSString *)dbURL key:(NSString *)key refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
- (void)on:(NSString *) eventName registration:(NSDictionary *) registration;
- (void)once:(NSString *) eventType resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)removeEventListener:(NSString *)eventRegistrationKey;

View File

@ -6,6 +6,7 @@
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
appDisplayName:(NSString *)appDisplayName
dbURL:(NSString *)dbURL
key:(NSString *)key
refPath:(NSString *)refPath
modifiers:(NSArray *)modifiers {
@ -13,6 +14,7 @@
if (self) {
_emitter = emitter;
_appDisplayName = appDisplayName;
_dbURL = dbURL;
_key = key;
_path = refPath;
_listeners = [[NSMutableDictionary alloc] init];
@ -123,7 +125,7 @@
- (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(NSString *)path
modifiers:(NSArray *)modifiers {
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_appDisplayName];
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_appDisplayName URL:_dbURL];
FIRDatabaseQuery *query = [[firebaseDatabase reference] child:path];
for (NSDictionary *modifier in modifiers) {

View File

@ -41,6 +41,7 @@ export default class AdMob extends ModuleBase {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});

View File

@ -34,6 +34,7 @@ export default class Analytics extends ModuleBase {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});
}

View File

@ -53,6 +53,7 @@ export default class Auth extends ModuleBase {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
hasShards: false,
namespace: NAMESPACE,
});
this._user = null;

View File

@ -32,6 +32,7 @@ export default class RemoteConfig extends ModuleBase {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});
this._developerModeEnabled = false;

View File

@ -16,6 +16,7 @@ export default class Crash extends ModuleBase {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});
}

View File

@ -497,7 +497,7 @@ export default class Reference extends ReferenceBase {
* @returns {string}
*/
toString(): string {
return `${this._database.app.options.databaseURL}/${this.path}`;
return `${this._database.databaseUrl}/${this.path}`;
}
/**
@ -606,7 +606,7 @@ export default class Reference extends ReferenceBase {
* @return {string}
*/
_getRegistrationKey(eventType: string): string {
return `$${this._database.app.name}$/${
return `$${this._database.databaseUrl}$/${
this.path
}$${this._query.queryIdentifier()}$${listeners}$${eventType}`;
}
@ -618,8 +618,8 @@ export default class Reference extends ReferenceBase {
* @return {string}
* @private
*/
_getRefKey(): string {
return `$${this._database.app.name}$/${
_getRefKey() {
return `$${this._database.databaseUrl}$/${
this.path
}$${this._query.queryIdentifier()}`;
}
@ -758,6 +758,7 @@ export default class Reference extends ReferenceBase {
path: this.path,
key: this._getRefKey(),
appName: this._database.app.name,
dbURL: this._database.databaseUrl,
eventRegistrationKey,
};
@ -776,6 +777,7 @@ export default class Reference extends ReferenceBase {
path: this.path,
key: this._getRefKey(),
appName: this._database.app.name,
dbURL: this._database.databaseUrl,
eventType: `${eventType}$cancelled`,
eventRegistrationKey: registrationCancellationKey,
listener: _context

View File

@ -10,6 +10,7 @@ import ModuleBase from '../../utils/ModuleBase';
import { getNativeModule } from '../../utils/native';
import type App from '../core/app';
import firebase from '../core/firebase';
const NATIVE_EVENTS = [
'database_transaction_event',
@ -26,14 +27,32 @@ export default class Database extends ModuleBase {
_offsetRef: Reference;
_serverTimeOffset: number;
_transactionHandler: TransactionHandler;
_serviceUrl: string;
constructor(app: App, options: Object = {}) {
super(app, {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
namespace: NAMESPACE,
});
constructor(appOrUrl: App | string, options: Object = {}) {
let app;
let serviceUrl;
if (typeof appOrUrl === 'string') {
app = firebase.app();
serviceUrl = appOrUrl;
} else {
app = appOrUrl;
serviceUrl = app.options.databaseURL;
}
super(
app,
{
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
hasShards: true,
namespace: NAMESPACE,
},
serviceUrl
);
this._serviceUrl = serviceUrl;
this._transactionHandler = new TransactionHandler(this);
if (options.persistence) {
@ -83,6 +102,14 @@ export default class Database extends ModuleBase {
ref(path: string): Reference {
return new Reference(this, path);
}
/**
* Returns the database url
* @returns {string}
*/
get databaseUrl(): string {
return this._serviceUrl;
}
}
export const statics = {

View File

@ -15,6 +15,7 @@ export default class Crashlytics extends ModuleBase {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});
}

View File

@ -58,6 +58,7 @@ export default class Firestore extends ModuleBase {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
hasShards: false,
namespace: NAMESPACE,
});

View File

@ -81,6 +81,7 @@ export default class Links extends ModuleBase {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});

View File

@ -42,6 +42,7 @@ export default class Messaging extends ModuleBase {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});

View File

@ -16,6 +16,7 @@ export default class PerformanceMonitoring extends ModuleBase {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});
}

View File

@ -30,6 +30,7 @@ export default class Storage extends ModuleBase {
events: NATIVE_EVENTS,
moduleName: MODULE_NAME,
multiApp: true,
hasShards: false,
namespace: NAMESPACE,
});

View File

@ -23,6 +23,7 @@ export default class RNFirebaseUtils extends ModuleBase {
super(app, {
moduleName: MODULE_NAME,
multiApp: false,
hasShards: false,
namespace: NAMESPACE,
});
}

View File

@ -50,6 +50,7 @@ export type FirebaseModuleConfig = {
events?: string[],
moduleName: FirebaseModuleName,
multiApp: boolean,
hasShards: boolean,
namespace: FirebaseNamespace,
};

View File

@ -9,6 +9,7 @@ import type { FirebaseModuleConfig, FirebaseNamespace } from '../types';
export default class ModuleBase {
_app: App;
_serviceUrl: ?string;
namespace: FirebaseNamespace;
/**
@ -16,7 +17,7 @@ export default class ModuleBase {
* @param app
* @param config
*/
constructor(app: App, config: FirebaseModuleConfig) {
constructor(app: App, config: FirebaseModuleConfig, serviceUrl: ?string) {
if (!config.moduleName) {
throw new Error('Missing module name');
}
@ -25,10 +26,11 @@ export default class ModuleBase {
}
const { moduleName } = config;
this._app = app;
this._serviceUrl = serviceUrl;
this.namespace = config.namespace;
// check if native module exists as all native
initialiseNativeModule(this, config);
initialiseNativeModule(this, config, serviceUrl);
initialiseLogger(
this,
`${app.name}:${moduleName.replace('RNFirebase', '')}`

View File

@ -49,9 +49,16 @@ export default {
namespace: FirebaseNamespace,
InstanceClass: Class<M>
): () => FirebaseModule {
return (): M => {
if (!APP_MODULES[app._name]) {
APP_MODULES[app._name] = {};
return (serviceUrl: ?string = null): M => {
if (serviceUrl && namespace !== 'database') {
throw new Error(
INTERNALS.STRINGS.ERROR_INIT_SERVICE_URL_UNSUPPORTED(namespace)
);
}
const appOrShardName = serviceUrl || app.name;
if (!APP_MODULES[appOrShardName]) {
APP_MODULES[appOrShardName] = {};
}
if (
@ -63,11 +70,14 @@ export default {
app.utils().checkPlayServicesAvailability();
}
if (!APP_MODULES[app._name][namespace]) {
APP_MODULES[app._name][namespace] = new InstanceClass(app, app.options);
if (!APP_MODULES[appOrShardName][namespace]) {
APP_MODULES[appOrShardName][namespace] = new InstanceClass(
serviceUrl || app,
app.options
);
}
return APP_MODULES[app._name][namespace];
return APP_MODULES[appOrShardName][namespace];
};
},
@ -163,8 +173,13 @@ export default {
statics: S,
moduleName: FirebaseModuleName
): FirebaseModuleAndStatics<M, S> {
const getModule = (app?: App): FirebaseModule => {
let _app = app;
const getModule = (appOrUrl?: App | string): FirebaseModule => {
let _app = appOrUrl;
let _serviceUrl: ?string = null;
if (typeof appOrUrl === 'string' && namespace === 'database') {
_app = null;
_serviceUrl = appOrUrl;
}
// throw an error if it's not a valid app instance
if (_app && !(_app instanceof App))
@ -178,7 +193,7 @@ export default {
}
// $FlowExpectedError: Flow doesn't support indexable signatures on classes: https://github.com/facebook/flow/issues/1323
const module = _app[namespace];
return module();
return module(_serviceUrl);
};
return Object.assign(getModule, statics, {

View File

@ -98,6 +98,13 @@ export default {
ERROR_INIT_STRING_NAME:
'Firebase.initializeApp(options, name <-- requires a valid string value.',
/**
* @return {string}
*/
ERROR_INIT_SERVICE_URL_UNSUPPORTED(namespace: string) {
return `${namespace} does not support URL as a param, please pass in an app.`;
},
/**
* @return {string}
*/

View File

@ -11,37 +11,40 @@ import type { FirebaseModuleConfig } from '../types';
const NATIVE_MODULES: { [string]: Object } = {};
/**
* Prepends appName arg to all native method calls
* @param appName
* Prepends all arguments in prependArgs to all native method calls
* @param NativeModule
* @param argToPrepend
*/
const nativeWithApp = (appName: string, NativeModule: Object): Object => {
const nativeWithArgs = (
NativeModule: Object,
argToPrepend: Array<mixed>
): Object => {
const native = {};
const methods = Object.keys(NativeModule);
for (let i = 0, len = methods.length; i < len; i++) {
const method = methods[i];
native[method] = (...args) => NativeModule[method](...[appName, ...args]);
native[method] = (...args) =>
NativeModule[method](...[...argToPrepend, ...args]);
}
return native;
};
const getModuleKey = (module: ModuleBase): string =>
`${module.app.name}:${module.namespace}`;
const nativeModuleKey = (module: ModuleBase): string =>
`${module._serviceUrl || module.app.name}:${module.namespace}`;
export const getNativeModule = (module: ModuleBase): Object => {
const key = getModuleKey(module);
return NATIVE_MODULES[key];
};
export const getNativeModule = (module: ModuleBase): Object =>
NATIVE_MODULES[nativeModuleKey(module)];
export const initialiseNativeModule = (
module: ModuleBase,
config: FirebaseModuleConfig
config: FirebaseModuleConfig,
serviceUrl: ?string
): Object => {
const { moduleName, multiApp, namespace } = config;
const { moduleName, multiApp, hasShards, namespace } = config;
const nativeModule = NativeModules[moduleName];
const key = getModuleKey(module);
const key = nativeModuleKey(module);
if (!nativeModule && namespace !== 'utils') {
throw new Error(
@ -51,8 +54,16 @@ export const initialiseNativeModule = (
// used by the modules that extend ModuleBase
// to access their native module counterpart
const argToPrepend = [];
if (multiApp) {
NATIVE_MODULES[key] = nativeWithApp(module.app.name, nativeModule);
argToPrepend.push(module.app.name);
}
if (hasShards) {
argToPrepend.push(serviceUrl);
}
if (argToPrepend.length) {
NATIVE_MODULES[key] = nativeWithArgs(nativeModule, argToPrepend);
} else {
NATIVE_MODULES[key] = nativeModule;
}