Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(firestore): support clearPersistence() & terminate() APIs #3591

Merged
merged 30 commits into from
Jun 22, 2020
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4666a51
feat(firestore): clearPersistence API
russellwheatley May 1, 2020
6f4a3be
Merge branch 'master' into @russell/firestore-clear-persistence
Salakar May 5, 2020
6d0598e
Merge branch 'master' into @russell/firestore-clear-persistence
russellwheatley Jun 3, 2020
27aaa62
docs(firestore): update types & ios return null
russellwheatley Jun 3, 2020
fe1c80b
fix(firestore):update to void return types
russellwheatley Jun 3, 2020
2b8d228
feat(firestore): clearPersistence & terminate api
russellwheatley Jun 9, 2020
f70c71a
docs(firestore): terminate api
russellwheatley Jun 9, 2020
f640b7b
docs(firestore): update
russellwheatley Jun 9, 2020
1e85384
build(android): add gpu guest
russellwheatley Jun 10, 2020
c84d91c
Merge branch 'master' into @russell/firestore-clear-persistence
russellwheatley Jun 11, 2020
5a59883
Merge branch 'master' firestore-clear-persistence
russellwheatley Jun 17, 2020
671ff91
Merge branch '@russell/firestore-clear-persistence' of github.com:inv…
russellwheatley Jun 17, 2020
f85c11a
chore(firestore): clear cached settings
russellwheatley Jun 17, 2020
cd1392e
tests(firestore): update clearPersistence test
russellwheatley Jun 17, 2020
ca12019
fix(firestore, java): rm only pertinent keys
russellwheatley Jun 17, 2020
984384f
fix(firestore, ios): rm pertinent cache keys
russellwheatley Jun 17, 2020
766178c
docs(firestore): gen docs
russellwheatley Jun 17, 2020
cefb8c1
fix(firestore, ios): terminate bug
russellwheatley Jun 18, 2020
4d2dbaa
Merge branch 'master' into @russell/firestore-clear-persistence
russellwheatley Jun 18, 2020
49b4aa4
fix(firestore, android): apply changes
russellwheatley Jun 18, 2020
fdb1b59
Merge branch '@russell/firestore-clear-persistence' of github.com:inv…
russellwheatley Jun 18, 2020
2b15da6
chore(firestore): PR requests
russellwheatley Jun 19, 2020
0ec641b
chore(firestore): update to weakhashmap for cache
russellwheatley Jun 19, 2020
d70b7ba
Merge branch 'master' into @russell/firestore-clear-persistence
russellwheatley Jun 22, 2020
45c966c
Merge branch 'master' into @russell/firestore-clear-persistence
russellwheatley Jun 22, 2020
1a9aa74
Merge branch 'master' into @russell/firestore-clear-persistence
russellwheatley Jun 22, 2020
40f2043
Merge branch 'master' into clear-persistence
russellwheatley Jun 22, 2020
d1dec91
docs(firestore): update docs
russellwheatley Jun 22, 2020
cc1235e
Apply suggestions from code review
russellwheatley Jun 22, 2020
6fa4a7f
Merge branch 'master' into @russell/firestore-clear-persistence
russellwheatley Jun 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11,377 changes: 6,185 additions & 5,192 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,62 @@
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;
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved

import io.invertase.firebase.common.UniversalFirebasePreferences;

public class UniversalFirebaseFirestoreCommon {
private static HashMap<String, Boolean> settingsLock = new HashMap<>();
static WeakHashMap<String, FirebaseFirestore> instanceCache = new WeakHashMap<>();
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved

static FirebaseFirestore getFirestoreForApp(String appName) {
FirebaseFirestore cachedInstance = instanceCache.get(appName);
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved

if(cachedInstance != null){
return cachedInstance;
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved
}

FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);

FirebaseFirestore instance = FirebaseFirestore.getInstance(firebaseApp);

setFirestoreSettings(instance, appName);

instanceCache.put(appName, instance);
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved

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 +91,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,41 @@ 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();
Salakar marked this conversation as resolved.
Show resolved Hide resolved
}

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

instanceCache.remove(appName);
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved

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
22 changes: 22 additions & 0 deletions packages/firestore/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1939,6 +1939,28 @@ export namespace FirebaseFirestoreTypes {
* @param settings A `Settings` object.
*/
settings(settings: Settings): Promise<void>;
/**
* Aimed primarily at clearing up any data cached from running tests. Needs to be executed before any database calls
* are made.
*
* #### Example
*
*```js
* await firebase.firestore().clearPersistence();
* ```
*/
clearPersistence(): Promise<void>;
/**
* Typically called to ensure a new Firestore instance is initialized before calling
* `firebase.firestore().clearPersistence()`.
*
* #### Example
*
*```js
* await firebase.firestore().terminate();
* ```
*/
terminate(): Promise<void>;
}
}

Expand Down
Loading