Skip to content

Commit

Permalink
feat(firestore): support clearPersistence() & terminate() APIs (#3591)
Browse files Browse the repository at this point in the history
Co-authored-by: Mike Diarmid <mike.diarmid@gmail.com>

[publish]
  • Loading branch information
russellwheatley committed Jun 22, 2020
1 parent ca34cef commit 57ff900
Show file tree
Hide file tree
Showing 15 changed files with 6,396 additions and 5,247 deletions.
11,399 changes: 6,196 additions & 5,203 deletions docs/typedoc.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/typedoc.min.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ public void clearAll() {
getPreferences().edit().clear().apply();
}

public SharedPreferences.Editor remove(String key){
return getPreferences().edit().remove(key);
}

private SharedPreferences getPreferences() {
if (preferences == null) {
preferences = ReactNativeFirebaseApp
Expand Down
2 changes: 2 additions & 0 deletions packages/app/ios/RNFBApp/RNFBPreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

- (void)clearAll;

- (void)remove:(NSString *)key;

+ (RNFBPreferences *)shared;

@end
4 changes: 4 additions & 0 deletions packages/app/ios/RNFBApp/RNFBPreferences.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ - (void)clearAll {
[_userDefaults removePersistentDomainForName:RNFBDomainIdentifier];
}

- (void)remove:(NSString *)key {
[_userDefaults removeObjectForKey:key];
}

+ (RNFBPreferences *)shared {
return sharedInstance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,50 +20,63 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
import com.google.firebase.firestore.Query;

import java.util.HashMap;
import java.util.WeakHashMap;
import java.lang.ref.WeakReference;

import io.invertase.firebase.common.UniversalFirebasePreferences;

public class UniversalFirebaseFirestoreCommon {
private static HashMap<String, Boolean> settingsLock = new HashMap<>();
static WeakHashMap<String, WeakReference<FirebaseFirestore>> instanceCache = new WeakHashMap<>();

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

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

FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);

FirebaseFirestore instance = FirebaseFirestore.getInstance(firebaseApp);

setFirestoreSettings(instance, appName);

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

return instance;
}

private static void setFirestoreSettings(FirebaseFirestore firebaseFirestore, String appName) {
// Ensure not already been set
if (settingsLock.containsKey(appName)) return;

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;


int cacheSizeBytes = preferences.getIntValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_CACHE_SIZE + "_" + appName,
cacheSizeKey,
(int) firebaseFirestore.getFirestoreSettings().getCacheSizeBytes()
);

String host = preferences.getStringValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_HOST + "_" + appName,
hostKey,
firebaseFirestore.getFirestoreSettings().getHost()
);

boolean persistence = preferences.getBooleanValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_PERSISTENCE + "_" + appName,
persistenceKey,
firebaseFirestore.getFirestoreSettings().isPersistenceEnabled()
);

boolean ssl = preferences.getBooleanValue(
UniversalFirebaseFirestoreStatics.FIRESTORE_SSL + "_" + appName,
sslKey,
firebaseFirestore.getFirestoreSettings().isSslEnabled()
);

Expand All @@ -79,7 +92,8 @@ private static void setFirestoreSettings(FirebaseFirestore firebaseFirestore, St

firebaseFirestore.setFirestoreSettings(firestoreSettings.build());

settingsLock.put(appName, true);

preferences.remove(cacheSizeKey).remove(hostKey).remove(persistenceKey).remove(sslKey).apply();
}

static Query getQueryForFirestore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class UniversalFirebaseFirestoreException extends Exception {
UniversalFirebaseFirestoreException(FirebaseFirestoreException nativeException, Throwable cause) {
super(nativeException != null ? nativeException.getMessage() : "", cause);

String code = "unknown";
String code = null;
String message = "An unknown error occurred";

if (cause != null && cause.getMessage() != null && cause.getMessage().contains(":")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@
*/

import android.content.Context;

import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
import io.invertase.firebase.common.UniversalFirebaseModule;
import io.invertase.firebase.common.UniversalFirebasePreferences;

import java.util.Map;
import java.util.Objects;

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

public class UniversalFirebaseFirestoreModule extends UniversalFirebaseModule {

Expand All @@ -52,35 +53,44 @@ Task<Void> settings(String appName, Map<String, Object> settings) {

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

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

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

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

return null;
});
}

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

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

if (instanceCache.get(appName) != null) {
instanceCache.get(appName).clear();
instanceCache.remove(appName);
}

return firebaseFirestore.terminate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ public void setLogLevel(String logLevel) {
}
}

@ReactMethod
public void clearPersistence(String appName, Promise promise) {
module.clearPersistence(appName).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
promise.resolve(null);
} else {
rejectPromiseFirestoreException(promise, task.getException());
}
});
}

@ReactMethod
public void disableNetwork(String appName, Promise promise) {
module.disableNetwork(appName).addOnCompleteListener(task -> {
Expand Down Expand Up @@ -77,4 +88,15 @@ public void settings(String appName, ReadableMap settings, Promise promise) {
}
});
}

@ReactMethod
public void terminate(String appName, Promise promise) {
module.terminate(appName).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
promise.resolve(null);
} else {
rejectPromiseFirestoreException(promise, task.getException());
}
});
}
}
31 changes: 31 additions & 0 deletions packages/firestore/e2e/firestore.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,35 @@ describe('firestore()', () => {
}
});
});

describe('Clear cached data persistence', () => {
it('should clear any cached data', async () => {
const db = firebase.firestore();
const id = 'foobar';
const ref = db.doc(`v6/${id}`);
await ref.set({ foo: 'bar' });

try {
await db.clearPersistence();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.code.should.equal('firestore/failed-precondition');
}

const doc = await ref.get({ source: 'cache' });

should(doc.id).equal(id);

await db.terminate();
await db.clearPersistence();

try {
await ref.get({ source: 'cache' });
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.code.should.equal('firestore/unavailable');
return Promise.resolve();
}
});
});
});
1 change: 1 addition & 0 deletions packages/firestore/ios/RNFBFirestore/RNFBFirestoreCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ extern NSString *const FIRESTORE_CACHE_SIZE;
extern NSString *const FIRESTORE_HOST;
extern NSString *const FIRESTORE_PERSISTENCE;
extern NSString *const FIRESTORE_SSL;
extern NSMutableDictionary * instanceCache;
40 changes: 23 additions & 17 deletions packages/firestore/ios/RNFBFirestore/RNFBFirestoreCommon.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,27 @@
NSString *const FIRESTORE_PERSISTENCE = @"firebase_firestore_persistence";
NSString *const FIRESTORE_SSL = @"firebase_firestore_ssl";

__strong NSMutableDictionary *settingsLock;
NSMutableDictionary * instanceCache;

@implementation RNFBFirestoreCommon

+ (FIRFirestore *)getFirestoreForApp:(FIRApp *)app {
FIRFirestore *instance = [FIRFirestore firestoreForApp:app];
[self setFirestoreSettings:instance appName:[RNFBSharedUtils getAppJavaScriptName:app.name]];
return instance;
if(instanceCache == nil){
instanceCache = [[NSMutableDictionary alloc] init];
}

FIRFirestore * cachedInstance = instanceCache[[app name]];

if(cachedInstance){
return cachedInstance;
}

FIRFirestore *instance = [FIRFirestore firestoreForApp:app];

[self setFirestoreSettings:instance appName:[RNFBSharedUtils getAppJavaScriptName:app.name]];

instanceCache[[app name]] = instance;

return instance;
}

+ (dispatch_queue_t)getFirestoreQueue {
Expand All @@ -44,16 +57,6 @@ + (dispatch_queue_t)getFirestoreQueue {
}

+ (void)setFirestoreSettings:(FIRFirestore *)firestore appName:(NSString *)appName {
@synchronized(settingsLock) {
if (settingsLock == nil) {
settingsLock = [[NSMutableDictionary alloc] init];
}

// Prevent setting if already set
if (settingsLock[appName]) {
return;
}

FIRFirestoreSettings *firestoreSettings = [[FIRFirestoreSettings alloc] init];
RNFBPreferences *preferences = [RNFBPreferences shared];

Expand All @@ -79,9 +82,12 @@ + (void)setFirestoreSettings:(FIRFirestore *)firestore appName:(NSString *)appNa
NSString *sslKey = [NSString stringWithFormat:@"%@_%@", FIRESTORE_SSL, appName];
firestoreSettings.sslEnabled = (BOOL) [preferences getBooleanValue:sslKey defaultValue:firestore.settings.sslEnabled];

settingsLock[appName] = @(YES);
firestore.settings = firestoreSettings;
}

[preferences remove:cacheKey];
[preferences remove:hostKey];
[preferences remove:persistenceKey];
[preferences remove:sslKey];
}

+ (FIRDocumentReference *)getDocumentForFirestore:(FIRFirestore *)firestore path:(NSString *)path; {
Expand Down
32 changes: 32 additions & 0 deletions packages/firestore/ios/RNFBFirestore/RNFBFirestoreModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,36 @@ + (BOOL)requiresMainQueueSetup {
resolve([NSNull null]);
}

RCT_EXPORT_METHOD(clearPersistence:
(FIRApp *) firebaseApp
: (RCTPromiseResolveBlock) resolve
: (RCTPromiseRejectBlock)reject
) {
[[RNFBFirestoreCommon getFirestoreForApp:firebaseApp] clearPersistenceWithCompletion:^(NSError *error) {
if (error) {
[RNFBFirestoreCommon promiseRejectFirestoreException:reject error:error];
} else {
resolve(nil);
}
}];
}

RCT_EXPORT_METHOD(terminate:
(FIRApp *) firebaseApp
: (RCTPromiseResolveBlock) resolve
: (RCTPromiseRejectBlock)reject
) {
FIRFirestore *instance = [RNFBFirestoreCommon getFirestoreForApp:firebaseApp];

[instance terminateWithCompletion:^(NSError *error) {
if (error) {
[RNFBFirestoreCommon promiseRejectFirestoreException:reject error:error];
} else {
[instanceCache removeObjectForKey: [firebaseApp name]];
resolve(nil);
}
}];
}


@end
Loading

0 comments on commit 57ff900

Please sign in to comment.