[js][android] database sync tree implementation with adjusted tests

This commit is contained in:
Salakar
2017-08-15 21:29:50 +01:00
parent 67985f8e90
commit e4d27029b9
14 changed files with 647 additions and 233 deletions
@@ -77,20 +77,21 @@ public class Utils {
}
/**
* @param name
* @param path
* @param dataSnapshot
* @param refId
* @param registration
* @param previousChildName
* @return
*/
public static WritableMap snapshotToMap(String name, String path, DataSnapshot dataSnapshot, @Nullable String previousChildName, int refId) {
public static WritableMap snapshotToMap(DataSnapshot dataSnapshot, @Nullable String previousChildName) {
WritableMap result = Arguments.createMap();
WritableMap snapshot = Arguments.createMap();
WritableMap eventMap = Arguments.createMap();
snapshot.putString("key", dataSnapshot.getKey());
snapshot.putBoolean("exists", dataSnapshot.exists());
snapshot.putBoolean("hasChildren", dataSnapshot.hasChildren());
snapshot.putDouble("childrenCount", dataSnapshot.getChildrenCount());
snapshot.putArray("childKeys", Utils.getChildKeys(dataSnapshot));
mapPutValue("priority", dataSnapshot.getPriority(), snapshot);
if (!dataSnapshot.hasChildren()) {
mapPutValue("value", dataSnapshot.getValue(), snapshot);
@@ -103,16 +104,50 @@ public class Utils {
}
}
snapshot.putArray("childKeys", Utils.getChildKeys(dataSnapshot));
mapPutValue("priority", dataSnapshot.getPriority(), snapshot);
eventMap.putInt("refId", refId);
eventMap.putString("path", path);
eventMap.putMap("snapshot", snapshot);
eventMap.putString("eventName", name);
eventMap.putString("previousChildName", previousChildName);
result.putMap("snapshot", snapshot);
result.putString("previousChildName", previousChildName);
return result;
}
return eventMap;
/**
*
* @param map
* @return
*/
public static WritableMap readableMapToWritableMap(ReadableMap map) {
WritableMap writableMap = Arguments.createMap();
ReadableMapKeySetIterator iterator = map.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ReadableType type = map.getType(key);
switch (type) {
case Null:
writableMap.putNull(key);
break;
case Boolean:
writableMap.putBoolean(key, map.getBoolean(key));
break;
case Number:
writableMap.putDouble(key, map.getDouble(key));
break;
case String:
writableMap.putString(key, map.getString(key));
break;
case Map:
writableMap.putMap(key, readableMapToWritableMap(map.getMap(key)));
break;
case Array:
// TODO writableMap.putArray(key, readableArrayToWritableArray(map.getArray(key)));
break;
default:
throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
}
}
return writableMap;
}
/**
@@ -35,7 +35,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
private static final String TAG = "RNFirebaseDatabase";
private HashMap<String, ChildEventListener> childEventListeners;
private HashMap<String, ValueEventListener> valueEventListeners;
private SparseArray<RNFirebaseDatabaseReference> references = new SparseArray<>();
private HashMap<String, RNFirebaseDatabaseReference> references = new HashMap<String, RNFirebaseDatabaseReference>();
private SparseArray<RNFirebaseTransactionHandler> transactionHandlers = new SparseArray<>();
RNFirebaseDatabase(ReactApplicationContext reactContext) {
@@ -103,10 +103,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* @param state
*/
@ReactMethod
public void keepSynced(String appName, int id, String path, ReadableArray modifiers, Boolean state) {
getInternalReferenceForApp(appName, id, path, modifiers, false)
.getQuery()
.keepSynced(state);
public void keepSynced(String appName, String key, String path, ReadableArray modifiers, Boolean state) {
getInternalReferenceForApp(appName, key, path, modifiers).getQuery().keepSynced(state);
}
@@ -410,34 +408,36 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* Subscribe once to a firebase reference.
*
* @param appName
* @param refId
* @param key
* @param path
* @param modifiers
* @param eventName
* @param promise
*/
@ReactMethod
public void once(String appName, int refId, String path, ReadableArray modifiers, String eventName, Promise promise) {
getInternalReferenceForApp(appName, refId, path, modifiers, false).once(eventName, promise);
public void once(String appName, String key, String path, ReadableArray modifiers, String eventName, Promise promise) {
getInternalReferenceForApp(appName, key, path, modifiers).once(eventName, promise);
}
/**
* Subscribe to real time events for the specified database path + modifiers
*
* @param appName String
* @param args ReadableMap
* @param props ReadableMap
*/
@ReactMethod
public void on(String appName, ReadableMap args) {
RNFirebaseDatabaseReference ref = getInternalReferenceForApp(
appName,
args.getInt("id"),
args.getString("path"),
args.getArray("modifiers"),
true
public void on(String appName, ReadableMap props) {
getInternalReferenceForApp(appName, props).on(
this,
props.getString("eventType"),
props.getMap("registration")
);
}
@ReactMethod
public void off(String appName, ReadableMap args) {
ref.on(this, args.getString("eventType"), args.getString("eventQueryKey"));
}
/*
@@ -511,25 +511,49 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
* Return an existing or create a new RNFirebaseDatabaseReference instance.
*
* @param appName
* @param refId
* @param key
* @param path
* @param modifiers
* @param keep
* @return
*/
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, int refId, String path, ReadableArray modifiers, Boolean keep) {
RNFirebaseDatabaseReference existingRef = references.get(refId);
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, String key, String path, ReadableArray modifiers) {
RNFirebaseDatabaseReference existingRef = references.get(key);
if (existingRef == null) {
existingRef = new RNFirebaseDatabaseReference(
getReactApplicationContext(),
appName,
refId,
key,
path,
modifiers
);
}
return existingRef;
}
/**
* @param appName
* @param props
* @return
*/
private RNFirebaseDatabaseReference getInternalReferenceForApp(String appName, ReadableMap props) {
String key = props.getString("key");
String path = props.getString("path");
ReadableArray modifiers = props.getArray("modifiers");
RNFirebaseDatabaseReference existingRef = references.get(key);
if (existingRef == null) {
existingRef = new RNFirebaseDatabaseReference(
getReactApplicationContext(),
appName,
key,
path,
modifiers
);
if (keep) references.put(refId, existingRef);
references.put(key, existingRef);
}
return existingRef;
@@ -1,16 +1,14 @@
package io.invertase.firebase.database;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import android.util.Log;
import android.support.annotation.Nullable;
import android.util.SparseArray;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
@@ -24,10 +22,10 @@ import com.google.firebase.database.ValueEventListener;
import io.invertase.firebase.Utils;
public class RNFirebaseDatabaseReference {
class RNFirebaseDatabaseReference {
private static final String TAG = "RNFirebaseDBReference";
private int refId;
private String key;
private Query query;
private String path;
private String appName;
@@ -41,12 +39,12 @@ public class RNFirebaseDatabaseReference {
/**
* @param context
* @param app
* @param id
* @param refKey
* @param refPath
* @param modifiersArray
*/
RNFirebaseDatabaseReference(ReactContext context, String app, int id, String refPath, ReadableArray modifiersArray) {
refId = id;
RNFirebaseDatabaseReference(ReactContext context, String app, String refKey, String refPath, ReadableArray modifiersArray) {
key = refKey;
appName = app;
path = refPath;
reactContext = context;
@@ -62,7 +60,7 @@ public class RNFirebaseDatabaseReference {
ValueEventListener onceValueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
WritableMap data = Utils.snapshotToMap("value", path, dataSnapshot, null, refId);
WritableMap data = Utils.snapshotToMap(dataSnapshot, null);
promise.resolve(data);
}
@@ -74,7 +72,7 @@ public class RNFirebaseDatabaseReference {
query.addListenerForSingleValueEvent(onceValueEventListener);
Log.d(TAG, "Added OnceValueEventListener for refId: " + refId);
Log.d(TAG, "Added OnceValueEventListener for key: " + key);
}
/**
@@ -89,7 +87,7 @@ public class RNFirebaseDatabaseReference {
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_added".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_added", path, dataSnapshot, previousChildName, refId);
WritableMap data = Utils.snapshotToMap(dataSnapshot, previousChildName);
promise.resolve(data);
}
}
@@ -98,7 +96,7 @@ public class RNFirebaseDatabaseReference {
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_changed".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_changed", path, dataSnapshot, previousChildName, refId);
WritableMap data = Utils.snapshotToMap(dataSnapshot, previousChildName);
promise.resolve(data);
}
}
@@ -107,7 +105,7 @@ public class RNFirebaseDatabaseReference {
public void onChildRemoved(DataSnapshot dataSnapshot) {
if ("child_removed".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_removed", path, dataSnapshot, null, refId);
WritableMap data = Utils.snapshotToMap(dataSnapshot, null);
promise.resolve(data);
}
}
@@ -116,7 +114,7 @@ public class RNFirebaseDatabaseReference {
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_moved".equals(eventName)) {
query.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_moved", path, dataSnapshot, previousChildName, refId);
WritableMap data = Utils.snapshotToMap(dataSnapshot, previousChildName);
promise.resolve(data);
}
}
@@ -135,13 +133,14 @@ public class RNFirebaseDatabaseReference {
/**
* Handles a React Native JS 'on' request and initializes listeners.
*
* @param eventName
* @param database
* @param registration
*/
void on(RNFirebaseDatabase database, String eventName, String queryKey) {
if (eventName.equals("value")) {
addValueEventListener(queryKey, database);
void on(RNFirebaseDatabase database, String eventType, ReadableMap registration) {
if (eventType.equals("value")) {
addValueEventListener(registration, database);
} else {
addChildEventListener(queryKey, database, eventName);
addChildEventListener(registration, eventType, database);
}
}
@@ -161,94 +160,100 @@ public class RNFirebaseDatabaseReference {
/**
* @param queryKey
* @param eventName
* @param registration
* @param eventType
* @param database
*/
private void addChildEventListener(final String queryKey, final RNFirebaseDatabase database, final String eventName) {
if (!database.hasChildEventListener(queryKey)) {
private void addChildEventListener(final ReadableMap registration, final String eventType, final RNFirebaseDatabase database) {
final String eventRegistrationKey = registration.getString("eventRegistrationKey");
final String registrationCancellationKey = registration.getString("registrationCancellationKey");
if (!database.hasChildEventListener(eventRegistrationKey)) {
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_added".equals(eventName)) {
handleDatabaseEvent("child_added", queryKey, dataSnapshot, previousChildName);
if ("child_added".equals(eventType)) {
handleDatabaseEvent("child_added", registration, dataSnapshot, previousChildName);
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_changed".equals(eventName)) {
handleDatabaseEvent("child_changed", queryKey, dataSnapshot, previousChildName);
if ("child_changed".equals(eventType)) {
handleDatabaseEvent("child_changed", registration, dataSnapshot, previousChildName);
}
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
if ("child_removed".equals(eventName)) {
handleDatabaseEvent("child_removed", queryKey, dataSnapshot, null);
if ("child_removed".equals(eventType)) {
handleDatabaseEvent("child_removed", registration, dataSnapshot, null);
}
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_moved".equals(eventName)) {
handleDatabaseEvent("child_moved", queryKey, dataSnapshot, previousChildName);
if ("child_moved".equals(eventType)) {
handleDatabaseEvent("child_moved", registration, dataSnapshot, previousChildName);
}
}
@Override
public void onCancelled(DatabaseError error) {
query.removeEventListener(this);
database.removeChildEventListener(queryKey);
handleDatabaseError(queryKey, error);
database.removeChildEventListener(eventRegistrationKey);
handleDatabaseError(registration, error);
}
};
database.addChildEventListener(queryKey, childEventListener);
database.addChildEventListener(eventRegistrationKey, childEventListener);
query.addChildEventListener(childEventListener);
}
}
/**
* @param queryKey
* @param registration
*/
private void addValueEventListener(final String queryKey, final RNFirebaseDatabase database) {
if (!database.hasValueEventListener(queryKey)) {
private void addValueEventListener(final ReadableMap registration, final RNFirebaseDatabase database) {
final String eventRegistrationKey = registration.getString("eventRegistrationKey");
final String registrationCancellationKey = registration.getString("registrationCancellationKey");
if (!database.hasValueEventListener(eventRegistrationKey)) {
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
handleDatabaseEvent("value", queryKey, dataSnapshot, null);
handleDatabaseEvent("value", registration, dataSnapshot, null);
}
@Override
public void onCancelled(DatabaseError error) {
query.removeEventListener(this);
database.removeValueEventListener(queryKey);
handleDatabaseError(queryKey, error);
database.removeValueEventListener(eventRegistrationKey);
handleDatabaseError(registration, error);
}
};
database.addValueEventListener(queryKey, valueEventListener);
database.addValueEventListener(eventRegistrationKey, valueEventListener);
query.addValueEventListener(valueEventListener);
}
}
/**
* @param name
* @param eventType
* @param dataSnapshot
* @param previousChildName
*/
private void handleDatabaseEvent(final String name, String queryKey, final DataSnapshot dataSnapshot, @Nullable String previousChildName) {
WritableMap evt = Arguments.createMap();
WritableMap data = Utils.snapshotToMap(name, path, dataSnapshot, previousChildName, refId);
private void handleDatabaseEvent(String eventType, ReadableMap registration, DataSnapshot dataSnapshot, @Nullable String previousChildName) {
WritableMap event = Arguments.createMap();
WritableMap data = Utils.snapshotToMap(dataSnapshot, previousChildName);
evt.putMap("body", data);
evt.putInt("refId", refId);
evt.putString("eventName", name);
evt.putString("appName", appName);
evt.putString("queryKey", queryKey);
event.putMap("data", data);
event.putString("key", key);
event.putString("eventType", eventType);
event.putMap("registration", Utils.readableMapToWritableMap(registration));
Utils.sendEvent(reactContext, "database_on_event", evt);
Utils.sendEvent(reactContext, "database_sync_event", event);
}
/**
@@ -256,13 +261,14 @@ public class RNFirebaseDatabaseReference {
*
* @param error
*/
private void handleDatabaseError(String queryKey, DatabaseError error) {
WritableMap errMap = RNFirebaseDatabase.getJSError(error);
errMap.putInt("refId", refId);
errMap.putString("path", path);
errMap.putString("appName", appName);
errMap.putString("queryKey", queryKey);
Utils.sendEvent(reactContext, "database_cancel_event", errMap);
private void handleDatabaseError(ReadableMap registration, DatabaseError error) {
WritableMap event = Arguments.createMap();
event.putString("key", key);
event.putMap("error", RNFirebaseDatabase.getJSError(error));
event.putMap("registration", Utils.readableMapToWritableMap(registration));
Utils.sendEvent(reactContext, "database_sync_event", event);
}