[firestore][android] Finish type mapping work for Android
This commit is contained in:
parent
1c81da466c
commit
76d77da2e5
|
@ -6,6 +6,7 @@ import com.facebook.react.bridge.Arguments;
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||||
|
import com.facebook.react.bridge.ReadableType;
|
||||||
import com.facebook.react.bridge.WritableArray;
|
import com.facebook.react.bridge.WritableArray;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.google.firebase.firestore.DocumentChange;
|
import com.google.firebase.firestore.DocumentChange;
|
||||||
|
@ -15,12 +16,20 @@ import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.firestore.GeoPoint;
|
import com.google.firebase.firestore.GeoPoint;
|
||||||
import com.google.firebase.firestore.QuerySnapshot;
|
import com.google.firebase.firestore.QuerySnapshot;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.invertase.firebase.Utils;
|
||||||
|
|
||||||
public class FirestoreSerialize {
|
public class FirestoreSerialize {
|
||||||
private static final String TAG = "FirestoreSerialize";
|
private static final String TAG = "FirestoreSerialize";
|
||||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
|
||||||
private static final String KEY_CHANGES = "changes";
|
private static final String KEY_CHANGES = "changes";
|
||||||
private static final String KEY_DATA = "data";
|
private static final String KEY_DATA = "data";
|
||||||
private static final String KEY_DOC_CHANGE_DOCUMENT = "document";
|
private static final String KEY_DOC_CHANGE_DOCUMENT = "document";
|
||||||
|
@ -143,38 +152,12 @@ public class FirestoreSerialize {
|
||||||
* @param array Object[]
|
* @param array Object[]
|
||||||
* @return WritableArray
|
* @return WritableArray
|
||||||
*/
|
*/
|
||||||
static WritableArray objectArrayToWritable(Object[] array) {
|
private static WritableArray objectArrayToWritable(Object[] array) {
|
||||||
WritableArray writableArray = Arguments.createArray();
|
WritableArray writableArray = Arguments.createArray();
|
||||||
|
|
||||||
for (Object item : array) {
|
for (Object item : array) {
|
||||||
if (item == null) {
|
WritableMap typeMap = buildTypeMap(item);
|
||||||
writableArray.pushNull();
|
writableArray.pushMap(typeMap);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Class itemClass = item.getClass();
|
|
||||||
|
|
||||||
if (itemClass == Boolean.class) {
|
|
||||||
writableArray.pushBoolean((Boolean) item);
|
|
||||||
} else if (itemClass == Integer.class) {
|
|
||||||
writableArray.pushDouble(((Integer) item).doubleValue());
|
|
||||||
} else if (itemClass == Long.class) {
|
|
||||||
writableArray.pushDouble(((Long) item).doubleValue());
|
|
||||||
} else if (itemClass == Double.class) {
|
|
||||||
writableArray.pushDouble((Double) item);
|
|
||||||
} else if (itemClass == Float.class) {
|
|
||||||
writableArray.pushDouble(((Float) item).doubleValue());
|
|
||||||
} else if (itemClass == String.class) {
|
|
||||||
writableArray.pushString(item.toString());
|
|
||||||
} else if (Map.class.isAssignableFrom(itemClass)) {
|
|
||||||
writableArray.pushMap((objectMapToWritable((Map<String, Object>) item)));
|
|
||||||
} else if (List.class.isAssignableFrom(itemClass)) {
|
|
||||||
List<Object> list = (List<Object>) item;
|
|
||||||
Object[] listAsArray = list.toArray(new Object[list.size()]);
|
|
||||||
writableArray.pushArray(objectArrayToWritable(listAsArray));
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Cannot convert object of type " + itemClass);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return writableArray;
|
return writableArray;
|
||||||
|
@ -191,44 +174,47 @@ public class FirestoreSerialize {
|
||||||
typeMap.putString("type", "null");
|
typeMap.putString("type", "null");
|
||||||
typeMap.putNull("value");
|
typeMap.putNull("value");
|
||||||
} else {
|
} else {
|
||||||
Class valueClass = value.getClass();
|
if (value instanceof Boolean) {
|
||||||
|
|
||||||
if (valueClass == Boolean.class) {
|
|
||||||
typeMap.putString("type", "boolean");
|
typeMap.putString("type", "boolean");
|
||||||
typeMap.putBoolean("value", (Boolean) value);
|
typeMap.putBoolean("value", (Boolean) value);
|
||||||
} else if (valueClass == Integer.class) {
|
} else if (value instanceof Integer) {
|
||||||
map.putDouble(key, ((Integer) value).doubleValue());
|
typeMap.putString("type", "number");
|
||||||
} else if (valueClass == Long.class) {
|
typeMap.putDouble("value", ((Integer) value).doubleValue());
|
||||||
map.putDouble(key, ((Long) value).doubleValue());
|
} else if (value instanceof Long) {
|
||||||
} else if (valueClass == Double.class) {
|
typeMap.putString("type", "number");
|
||||||
|
typeMap.putDouble("value", ((Long) value).doubleValue());
|
||||||
|
} else if (value instanceof Double) {
|
||||||
typeMap.putString("type", "number");
|
typeMap.putString("type", "number");
|
||||||
typeMap.putDouble("value", (Double) value);
|
typeMap.putDouble("value", (Double) value);
|
||||||
} else if (valueClass == Float.class) {
|
} else if (value instanceof Float) {
|
||||||
typeMap.putString("type", "number");
|
typeMap.putString("type", "number");
|
||||||
typeMap.putDouble("value", ((Float) value).doubleValue());
|
typeMap.putDouble("value", ((Float) value).doubleValue());
|
||||||
} else if (valueClass == String.class) {
|
} else if (value instanceof String) {
|
||||||
map.putString(key, value.toString());
|
typeMap.putString("type", "string");
|
||||||
} else if (Map.class.isAssignableFrom(valueClass)) {
|
typeMap.putString("value", (String) value);
|
||||||
map.putMap(key, (objectMapToWritable((Map<String, Object>) value)));
|
} else if (Map.class.isAssignableFrom(value.getClass())) {
|
||||||
} else if (List.class.isAssignableFrom(valueClass)) {
|
typeMap.putString("type", "object");
|
||||||
|
typeMap.putMap("value", objectMapToWritable((Map<String, Object>) value));
|
||||||
|
} else if (List.class.isAssignableFrom(value.getClass())) {
|
||||||
|
typeMap.putString("type", "array");
|
||||||
List<Object> list = (List<Object>) value;
|
List<Object> list = (List<Object>) value;
|
||||||
Object[] array = list.toArray(new Object[list.size()]);
|
Object[] array = list.toArray(new Object[list.size()]);
|
||||||
typeMap.putArray("value", objectArrayToWritable(array));
|
typeMap.putArray("value", objectArrayToWritable(array));
|
||||||
} else if (valueClass == DocumentReference.class) {
|
} else if (value instanceof DocumentReference) {
|
||||||
typeMap.putString("type", "reference");
|
typeMap.putString("type", "reference");
|
||||||
typeMap.putString("value", ((DocumentReference) value).getPath());
|
typeMap.putString("value", ((DocumentReference) value).getPath());
|
||||||
} else if (valueClass == GeoPoint.class) {
|
} else if (value instanceof GeoPoint) {
|
||||||
typeMap.putString("type", "geopoint");
|
typeMap.putString("type", "geopoint");
|
||||||
WritableMap geoPoint = Arguments.createMap();
|
WritableMap geoPoint = Arguments.createMap();
|
||||||
geoPoint.putDouble("latitude", ((GeoPoint) value).getLatitude());
|
geoPoint.putDouble("latitude", ((GeoPoint) value).getLatitude());
|
||||||
geoPoint.putDouble("longitude", ((GeoPoint) value).getLongitude());
|
geoPoint.putDouble("longitude", ((GeoPoint) value).getLongitude());
|
||||||
typeMap.putMap("value", geoPoint);
|
typeMap.putMap("value", geoPoint);
|
||||||
} else if (valueClass == Date.class) {
|
} else if (value instanceof Date) {
|
||||||
typeMap.putString("type", "date");
|
typeMap.putString("type", "date");
|
||||||
typeMap.putString("value", DATE_FORMAT.format((Date) value));
|
typeMap.putString("value", DATE_FORMAT.format((Date) value));
|
||||||
} else {
|
} else {
|
||||||
// TODO: Changed to log an error rather than crash - is this correct?
|
// TODO: Changed to log an error rather than crash - is this correct?
|
||||||
Log.e(TAG, "buildTypeMap", new RuntimeException("Cannot convert object of type " + valueClass));
|
Log.e(TAG, "buildTypeMap: Cannot convert object of type " + value.getClass());
|
||||||
typeMap.putString("type", "null");
|
typeMap.putString("type", "null");
|
||||||
typeMap.putNull("value");
|
typeMap.putNull("value");
|
||||||
}
|
}
|
||||||
|
@ -249,7 +235,7 @@ public class FirestoreSerialize {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Object> parseReadableArray(FirebaseFirestore firestore, ReadableArray readableArray) {
|
private static List<Object> parseReadableArray(FirebaseFirestore firestore, ReadableArray readableArray) {
|
||||||
List<Object> list = new ArrayList<>();
|
List<Object> list = new ArrayList<>();
|
||||||
if (readableArray != null) {
|
if (readableArray != null) {
|
||||||
for (int i = 0; i < readableArray.size(); i++) {
|
for (int i = 0; i < readableArray.size(); i++) {
|
||||||
|
@ -259,7 +245,7 @@ public class FirestoreSerialize {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object parseTypeMap(FirebaseFirestore firestore, ReadableMap typeMap) {
|
private static Object parseTypeMap(FirebaseFirestore firestore, ReadableMap typeMap) {
|
||||||
String type = typeMap.getString("type");
|
String type = typeMap.getString("type");
|
||||||
if ("boolean".equals(type)) {
|
if ("boolean".equals(type)) {
|
||||||
return typeMap.getBoolean("value");
|
return typeMap.getBoolean("value");
|
||||||
|
@ -292,4 +278,23 @@ public class FirestoreSerialize {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Object> parseDocumentBatches(FirebaseFirestore firestore, ReadableArray readableArray) {
|
||||||
|
List<Object> writes = new ArrayList<>(readableArray.size());
|
||||||
|
for (int i = 0; i < readableArray.size(); i++) {
|
||||||
|
Map<String, Object> write = new HashMap<>();
|
||||||
|
ReadableMap map = readableArray.getMap(i);
|
||||||
|
if (map.hasKey("data")) {
|
||||||
|
write.put("data", parseReadableMap(firestore, map.getMap("data")));
|
||||||
|
}
|
||||||
|
if (map.hasKey("options")) {
|
||||||
|
write.put("options", Utils.recursivelyDeconstructReadableMap(map.getMap("options")));
|
||||||
|
}
|
||||||
|
write.put("path", map.getString("path"));
|
||||||
|
write.put("type", map.getString("type"));
|
||||||
|
|
||||||
|
writes.add(write);
|
||||||
|
}
|
||||||
|
return writes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
|
||||||
final Promise promise) {
|
final Promise promise) {
|
||||||
FirebaseFirestore firestore = getFirestoreForApp(appName);
|
FirebaseFirestore firestore = getFirestoreForApp(appName);
|
||||||
WriteBatch batch = firestore.batch();
|
WriteBatch batch = firestore.batch();
|
||||||
final List<Object> writesArray = Utils.recursivelyDeconstructReadableArray(writes);
|
final List<Object> writesArray = FirestoreSerialize.parseDocumentBatches(firestore, writes);
|
||||||
|
|
||||||
for (Object w : writesArray) {
|
for (Object w : writesArray) {
|
||||||
Map<String, Object> write = (Map) w;
|
Map<String, Object> write = (Map) w;
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
*/
|
*/
|
||||||
import CollectionReference from './CollectionReference';
|
import CollectionReference from './CollectionReference';
|
||||||
import DocumentSnapshot from './DocumentSnapshot';
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
import GeoPoint from './GeoPoint';
|
|
||||||
import Path from './Path';
|
import Path from './Path';
|
||||||
import { firestoreAutoId, isFunction, isObject, isString, typeOf } from '../../utils';
|
import { buildNativeMap } from './utils/serialize';
|
||||||
|
import { firestoreAutoId, isFunction, isObject, isString } from '../../utils';
|
||||||
|
|
||||||
export type WriteOptions = {
|
export type WriteOptions = {
|
||||||
merge?: boolean,
|
merge?: boolean,
|
||||||
|
@ -153,7 +153,7 @@ export default class DocumentReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
set(data: Object, writeOptions?: WriteOptions): Promise<void> {
|
set(data: Object, writeOptions?: WriteOptions): Promise<void> {
|
||||||
const nativeData = this._buildNativeMap(data);
|
const nativeData = buildNativeMap(data);
|
||||||
return this._firestore._native
|
return this._firestore._native
|
||||||
.documentSet(this.path, nativeData, writeOptions);
|
.documentSet(this.path, nativeData, writeOptions);
|
||||||
}
|
}
|
||||||
|
@ -177,8 +177,9 @@ export default class DocumentReference {
|
||||||
data[key] = value;
|
data[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const nativeData = buildNativeMap(data);
|
||||||
return this._firestore._native
|
return this._firestore._native
|
||||||
.documentUpdate(this.path, data);
|
.documentUpdate(this.path, nativeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -196,59 +197,4 @@ export default class DocumentReference {
|
||||||
this._firestore._native
|
this._firestore._native
|
||||||
.documentOffSnapshot(this.path, listenerId);
|
.documentOffSnapshot(this.path, listenerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildNativeMap(data: Object): Object {
|
|
||||||
const nativeData = {};
|
|
||||||
if (data) {
|
|
||||||
Object.keys(data).forEach((key) => {
|
|
||||||
nativeData[key] = this._buildTypeMap(data[key]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return nativeData;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildNativeArray(array: Object[]): any[] {
|
|
||||||
const nativeArray = [];
|
|
||||||
if (array) {
|
|
||||||
array.forEach((value) => {
|
|
||||||
nativeArray.push(this._buildTypeMap(value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return nativeArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildTypeMap(value: any): any {
|
|
||||||
const typeMap = {};
|
|
||||||
const type = typeOf(value);
|
|
||||||
if (value === null) {
|
|
||||||
typeMap.type = 'null';
|
|
||||||
typeMap.value = null;
|
|
||||||
} else if (type === 'boolean' || type === 'number' || type === 'string') {
|
|
||||||
typeMap.type = type;
|
|
||||||
typeMap.value = value;
|
|
||||||
} else if (type === 'array') {
|
|
||||||
typeMap.type = type;
|
|
||||||
typeMap.value = this._buildNativeArray(value);
|
|
||||||
} else if (type === 'object') {
|
|
||||||
if (value instanceof DocumentReference) {
|
|
||||||
typeMap.type = 'reference';
|
|
||||||
typeMap.value = value.path;
|
|
||||||
} else if (value instanceof GeoPoint) {
|
|
||||||
typeMap.type = 'geopoint';
|
|
||||||
typeMap.value = {
|
|
||||||
latititude: value.latitude,
|
|
||||||
longitude: value.longitude,
|
|
||||||
};
|
|
||||||
} else if (value instanceof Date) {
|
|
||||||
typeMap.type = 'date';
|
|
||||||
typeMap.value = value.toISOString();
|
|
||||||
} else {
|
|
||||||
typeMap.type = 'object';
|
|
||||||
typeMap.value = this._buildNativeMap(value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn(`Unknown data type received ${type}`);
|
|
||||||
}
|
|
||||||
return typeMap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
* DocumentSnapshot representation wrapper
|
* DocumentSnapshot representation wrapper
|
||||||
*/
|
*/
|
||||||
import DocumentReference from './DocumentReference';
|
import DocumentReference from './DocumentReference';
|
||||||
import GeoPoint from './GeoPoint';
|
|
||||||
import Path from './Path';
|
import Path from './Path';
|
||||||
|
import { parseNativeMap } from './utils/serialize';
|
||||||
|
|
||||||
export type SnapshotMetadata = {
|
export type SnapshotMetadata = {
|
||||||
fromCache: boolean,
|
fromCache: boolean,
|
||||||
|
@ -17,23 +17,16 @@ export type DocumentSnapshotNativeData = {
|
||||||
path: string,
|
path: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
type TypeMap = {
|
|
||||||
type: 'array' | 'boolean' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string',
|
|
||||||
value: any,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class DocumentSnapshot
|
* @class DocumentSnapshot
|
||||||
*/
|
*/
|
||||||
export default class DocumentSnapshot {
|
export default class DocumentSnapshot {
|
||||||
_data: Object;
|
_data: Object;
|
||||||
_firestore: Object;
|
|
||||||
_metadata: SnapshotMetadata;
|
_metadata: SnapshotMetadata;
|
||||||
_ref: DocumentReference;
|
_ref: DocumentReference;
|
||||||
|
|
||||||
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) {
|
constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) {
|
||||||
this._data = this._parseNativeMap(nativeData.data);
|
this._data = parseNativeMap(firestore, nativeData.data);
|
||||||
this._firestore = firestore;
|
|
||||||
this._metadata = nativeData.metadata;
|
this._metadata = nativeData.metadata;
|
||||||
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
|
this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path));
|
||||||
}
|
}
|
||||||
|
@ -61,47 +54,4 @@ export default class DocumentSnapshot {
|
||||||
get(fieldPath: string): any {
|
get(fieldPath: string): any {
|
||||||
return this._data[fieldPath];
|
return this._data[fieldPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* INTERNALS
|
|
||||||
*/
|
|
||||||
|
|
||||||
_parseNativeMap(nativeData: Object): Object {
|
|
||||||
const data = {};
|
|
||||||
if (nativeData) {
|
|
||||||
Object.keys(nativeData).forEach((key) => {
|
|
||||||
data[key] = this._parseTypeMap(nativeData[key]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_parseNativeArray(nativeArray: Object[]): any[] {
|
|
||||||
const array = [];
|
|
||||||
if (nativeArray) {
|
|
||||||
nativeArray.forEach((typeMap) => {
|
|
||||||
array.push(this._parseTypeMap(typeMap));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
_parseTypeMap(typeMap: TypeMap): any {
|
|
||||||
const { type, value } = typeMap;
|
|
||||||
if (type === 'boolean' || type === 'number' || type === 'string' || type === 'null') {
|
|
||||||
return value;
|
|
||||||
} else if (type === 'array') {
|
|
||||||
return this._parseNativeArray(value);
|
|
||||||
} else if (type === 'object') {
|
|
||||||
return this._parseNativeMap(value);
|
|
||||||
} else if (type === 'reference') {
|
|
||||||
return new DocumentReference(this._firestore, Path.fromName(value));
|
|
||||||
} else if (type === 'geopoint') {
|
|
||||||
return new GeoPoint(value.latitude, value.longitude);
|
|
||||||
} else if (type === 'date') {
|
|
||||||
return new Date(value);
|
|
||||||
}
|
|
||||||
console.warn(`Unknown data type received ${type}`);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* WriteBatch representation wrapper
|
* WriteBatch representation wrapper
|
||||||
*/
|
*/
|
||||||
import DocumentReference from './DocumentReference';
|
import DocumentReference from './DocumentReference';
|
||||||
|
import { buildNativeMap } from './utils/serialize';
|
||||||
import { isObject, isString } from '../../utils';
|
import { isObject, isString } from '../../utils';
|
||||||
|
|
||||||
import type { WriteOptions } from './DocumentReference';
|
import type { WriteOptions } from './DocumentReference';
|
||||||
|
@ -48,9 +49,9 @@ export default class WriteBatch {
|
||||||
// validate.isDocumentReference('docRef', docRef);
|
// validate.isDocumentReference('docRef', docRef);
|
||||||
// validate.isDocument('data', data);
|
// validate.isDocument('data', data);
|
||||||
// validate.isOptionalPrecondition('writeOptions', writeOptions);
|
// validate.isOptionalPrecondition('writeOptions', writeOptions);
|
||||||
|
const nativeData = buildNativeMap(data);
|
||||||
this._writes.push({
|
this._writes.push({
|
||||||
data,
|
data: nativeData,
|
||||||
options: writeOptions,
|
options: writeOptions,
|
||||||
path: docRef.path,
|
path: docRef.path,
|
||||||
type: 'SET',
|
type: 'SET',
|
||||||
|
@ -81,8 +82,9 @@ export default class WriteBatch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nativeData = buildNativeMap(data);
|
||||||
this._writes.push({
|
this._writes.push({
|
||||||
data,
|
data: nativeData,
|
||||||
path: docRef.path,
|
path: docRef.path,
|
||||||
type: 'UPDATE',
|
type: 'UPDATE',
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import DocumentReference from '../DocumentReference';
|
||||||
|
import GeoPoint from '../GeoPoint';
|
||||||
|
import Path from '../Path';
|
||||||
|
import { typeOf } from '../../../utils';
|
||||||
|
|
||||||
|
type TypeMap = {
|
||||||
|
type: 'array' | 'boolean' | 'geopoint' | 'null' | 'number' | 'object' | 'reference' | 'string',
|
||||||
|
value: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions that build up the data needed to represent
|
||||||
|
* the different types available within Firestore
|
||||||
|
* for transmission to the native side
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const buildNativeMap = (data: Object): Object => {
|
||||||
|
const nativeData = {};
|
||||||
|
if (data) {
|
||||||
|
Object.keys(data).forEach((key) => {
|
||||||
|
nativeData[key] = buildTypeMap(data[key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return nativeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildNativeArray = (array: Object[]): any[] => {
|
||||||
|
const nativeArray = [];
|
||||||
|
if (array) {
|
||||||
|
array.forEach((value) => {
|
||||||
|
nativeArray.push(buildTypeMap(value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return nativeArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildTypeMap = (value: any): any => {
|
||||||
|
const typeMap = {};
|
||||||
|
const type = typeOf(value);
|
||||||
|
if (value === null) {
|
||||||
|
typeMap.type = 'null';
|
||||||
|
typeMap.value = null;
|
||||||
|
} else if (type === 'boolean' || type === 'number' || type === 'string') {
|
||||||
|
typeMap.type = type;
|
||||||
|
typeMap.value = value;
|
||||||
|
} else if (type === 'array') {
|
||||||
|
typeMap.type = type;
|
||||||
|
typeMap.value = buildNativeArray(value);
|
||||||
|
} else if (type === 'object') {
|
||||||
|
if (value instanceof DocumentReference) {
|
||||||
|
typeMap.type = 'reference';
|
||||||
|
typeMap.value = value.path;
|
||||||
|
} else if (value instanceof GeoPoint) {
|
||||||
|
typeMap.type = 'geopoint';
|
||||||
|
typeMap.value = {
|
||||||
|
latitude: value.latitude,
|
||||||
|
longitude: value.longitude,
|
||||||
|
};
|
||||||
|
} else if (value instanceof Date) {
|
||||||
|
typeMap.type = 'date';
|
||||||
|
typeMap.value = value.toISOString();
|
||||||
|
} else {
|
||||||
|
typeMap.type = 'object';
|
||||||
|
typeMap.value = buildNativeMap(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`Unknown data type received ${type}`);
|
||||||
|
}
|
||||||
|
return typeMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions that parse the received from the native
|
||||||
|
* side and converts to the correct Firestore JS types
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const parseNativeMap = (firestore: Object, nativeData: Object): Object => {
|
||||||
|
let data;
|
||||||
|
if (nativeData) {
|
||||||
|
data = {};
|
||||||
|
Object.keys(nativeData).forEach((key) => {
|
||||||
|
data[key] = parseTypeMap(firestore, nativeData[key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseNativeArray = (firestore: Object, nativeArray: Object[]): any[] => {
|
||||||
|
const array = [];
|
||||||
|
if (nativeArray) {
|
||||||
|
nativeArray.forEach((typeMap) => {
|
||||||
|
array.push(parseTypeMap(firestore, typeMap));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseTypeMap = (firestore: Object, typeMap: TypeMap): any => {
|
||||||
|
const { type, value } = typeMap;
|
||||||
|
if (type === 'boolean' || type === 'number' || type === 'string' || type === 'null') {
|
||||||
|
return value;
|
||||||
|
} else if (type === 'array') {
|
||||||
|
return parseNativeArray(firestore, value);
|
||||||
|
} else if (type === 'object') {
|
||||||
|
return parseNativeMap(firestore, value);
|
||||||
|
} else if (type === 'reference') {
|
||||||
|
return new DocumentReference(firestore, Path.fromName(value));
|
||||||
|
} else if (type === 'geopoint') {
|
||||||
|
return new GeoPoint(value.latitude, value.longitude);
|
||||||
|
} else if (type === 'date') {
|
||||||
|
return new Date(value);
|
||||||
|
}
|
||||||
|
console.warn(`Unknown data type received ${type}`);
|
||||||
|
return value;
|
||||||
|
};
|
|
@ -2,6 +2,7 @@ import sinon from 'sinon';
|
||||||
import 'should-sinon';
|
import 'should-sinon';
|
||||||
import should from 'should';
|
import should from 'should';
|
||||||
|
|
||||||
|
|
||||||
function documentReferenceTests({ describe, it, context, firebase }) {
|
function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
describe('DocumentReference', () => {
|
describe('DocumentReference', () => {
|
||||||
context('class', () => {
|
context('class', () => {
|
||||||
|
@ -28,16 +29,6 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('get()', () => {
|
|
||||||
it('should return DocumentReference field', async () => {
|
|
||||||
const docRef = firebase.native.firestore().doc('users/6hyiyxQ00JzdWlKFyH3E');
|
|
||||||
const doc = await docRef.get();
|
|
||||||
console.log('Doc', doc);
|
|
||||||
should.equal(doc.exists, true);
|
|
||||||
await docRef.set(doc.data());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('calls callback with the initial data and then when value changes', async () => {
|
it('calls callback with the initial data and then when value changes', async () => {
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
@ -420,6 +411,43 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context('types', () => {
|
||||||
|
it('should handle Date field', async () => {
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
||||||
|
await docRef.set({
|
||||||
|
field: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const doc = await docRef.get();
|
||||||
|
doc.data().field.should.be.instanceof(Date);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('types', () => {
|
||||||
|
it('should handle DocumentReference field', async () => {
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
||||||
|
await docRef.set({
|
||||||
|
field: firebase.native.firestore().doc('test/field'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const doc = await docRef.get();
|
||||||
|
should.equal(doc.data().field.path, 'test/field');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('types', () => {
|
||||||
|
it('should handle GeoPoint field', async () => {
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
||||||
|
await docRef.set({
|
||||||
|
field: new firebase.native.firestore.GeoPoint(1.01, 1.02),
|
||||||
|
});
|
||||||
|
|
||||||
|
const doc = await docRef.get();
|
||||||
|
should.equal(doc.data().field.latitude, 1.01);
|
||||||
|
should.equal(doc.data().field.longitude, 1.02);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue