diff --git a/android/src/main/java/io/invertase/firebase/firestore/FirestoreSerialize.java b/android/src/main/java/io/invertase/firebase/firestore/FirestoreSerialize.java index b4929ee5..6a0ef635 100644 --- a/android/src/main/java/io/invertase/firebase/firestore/FirestoreSerialize.java +++ b/android/src/main/java/io/invertase/firebase/firestore/FirestoreSerialize.java @@ -20,6 +20,7 @@ public class FirestoreSerialize { private static final String KEY_DOC_CHANGE_OLD_INDEX = "oldIndex"; private static final String KEY_DOC_CHANGE_TYPE = "type"; private static final String KEY_DOCUMENTS = "documents"; + private static final String KEY_METADATA = "metadata"; private static final String KEY_PATH = "path"; /** @@ -35,10 +36,13 @@ public class FirestoreSerialize { if (documentSnapshot.exists()) { documentMap.putMap(KEY_DATA, objectMapToWritable(documentSnapshot.getData())); } - // Missing fields from web SDK - // createTime - // readTime - // updateTime + // metadata + if (documentSnapshot.getMetadata() != null) { + WritableMap metadata = Arguments.createMap(); + metadata.putBoolean("fromCache", documentSnapshot.getMetadata().isFromCache()); + metadata.putBoolean("hasPendingWrites", documentSnapshot.getMetadata().hasPendingWrites()); + documentMap.putMap(KEY_METADATA, metadata); + } return documentMap; } @@ -57,6 +61,14 @@ public class FirestoreSerialize { } queryMap.putArray(KEY_DOCUMENTS, documents); + // metadata + if (querySnapshot.getMetadata() != null) { + WritableMap metadata = Arguments.createMap(); + metadata.putBoolean("fromCache", querySnapshot.getMetadata().isFromCache()); + metadata.putBoolean("hasPendingWrites", querySnapshot.getMetadata().hasPendingWrites()); + queryMap.putMap(KEY_METADATA, metadata); + } + return queryMap; } diff --git a/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestore.java b/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestore.java index 180d286a..1fc4d4d1 100644 --- a/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestore.java +++ b/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestore.java @@ -65,7 +65,7 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule { @ReactMethod public void documentBatch(final String appName, final ReadableArray writes, - final ReadableMap commitOptions, final Promise promise) { + final Promise promise) { FirebaseFirestore firestore = getFirestoreForApp(appName); WriteBatch batch = firestore.batch(); final List writesArray = Utils.recursivelyDeconstructReadableArray(writes); @@ -100,16 +100,10 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule { @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { - Log.d(TAG, "set:onComplete:success"); - WritableArray result = Arguments.createArray(); - for (Object w : writesArray) { - // Missing fields from web SDK - // writeTime - result.pushMap(Arguments.createMap()); - } - promise.resolve(result); + Log.d(TAG, "documentBatch:onComplete:success"); + promise.resolve(null); } else { - Log.e(TAG, "set:onComplete:failure", task.getException()); + Log.e(TAG, "documentBatch:onComplete:failure", task.getException()); RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); } } @@ -129,9 +123,9 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule { } @ReactMethod - public void documentDelete(String appName, String path, ReadableMap options, final Promise promise) { + public void documentDelete(String appName, String path, final Promise promise) { RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path); - ref.delete(options, promise); + ref.delete(promise); } @ReactMethod @@ -249,63 +243,67 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule { break; case CANCELLED: code = ErrorUtils.getCodeWithService(service, "cancelled"); - message = ErrorUtils.getMessageWithService("Cancelled.", service, code); + message = ErrorUtils.getMessageWithService("The operation was cancelled.", service, code); break; case UNKNOWN: code = ErrorUtils.getCodeWithService(service, "unknown"); - message = ErrorUtils.getMessageWithService("An unknown error occurred.", service, code); + message = ErrorUtils.getMessageWithService("Unknown error or an error from a different error domain.", service, code); break; case INVALID_ARGUMENT: code = ErrorUtils.getCodeWithService(service, "invalid-argument"); - message = ErrorUtils.getMessageWithService("Invalid argument.", service, code); + message = ErrorUtils.getMessageWithService("Client specified an invalid argument.", service, code); + break; + case DEADLINE_EXCEEDED: + code = ErrorUtils.getCodeWithService(service, "deadline-exceeded"); + message = ErrorUtils.getMessageWithService("Deadline expired before operation could complete.", service, code); break; case NOT_FOUND: code = ErrorUtils.getCodeWithService(service, "not-found"); - message = ErrorUtils.getMessageWithService("Not found.", service, code); + message = ErrorUtils.getMessageWithService("Some requested document was not found.", service, code); break; case ALREADY_EXISTS: code = ErrorUtils.getCodeWithService(service, "already-exists"); - message = ErrorUtils.getMessageWithService("Already exists.", service, code); + message = ErrorUtils.getMessageWithService("Some document that we attempted to create already exists.", service, code); break; case PERMISSION_DENIED: code = ErrorUtils.getCodeWithService(service, "permission-denied"); - message = ErrorUtils.getMessageWithService("Permission denied.", service, code); + message = ErrorUtils.getMessageWithService("The caller does not have permission to execute the specified operation.", service, code); break; case RESOURCE_EXHAUSTED: code = ErrorUtils.getCodeWithService(service, "resource-exhausted"); - message = ErrorUtils.getMessageWithService("Resource exhausted.", service, code); + message = ErrorUtils.getMessageWithService("Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space.", service, code); break; case FAILED_PRECONDITION: code = ErrorUtils.getCodeWithService(service, "failed-precondition"); - message = ErrorUtils.getMessageWithService("Failed precondition.", service, code); + message = ErrorUtils.getMessageWithService("Operation was rejected because the system is not in a state required for the operation`s execution.", service, code); break; case ABORTED: code = ErrorUtils.getCodeWithService(service, "aborted"); - message = ErrorUtils.getMessageWithService("Aborted.", service, code); + message = ErrorUtils.getMessageWithService("The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.", service, code); break; case OUT_OF_RANGE: code = ErrorUtils.getCodeWithService(service, "out-of-range"); - message = ErrorUtils.getMessageWithService("Out of range.", service, code); + message = ErrorUtils.getMessageWithService("Operation was attempted past the valid range.", service, code); break; case UNIMPLEMENTED: code = ErrorUtils.getCodeWithService(service, "unimplemented"); - message = ErrorUtils.getMessageWithService("Unimplemented.", service, code); + message = ErrorUtils.getMessageWithService("Operation is not implemented or not supported/enabled.", service, code); break; case INTERNAL: code = ErrorUtils.getCodeWithService(service, "internal"); - message = ErrorUtils.getMessageWithService("Internal.", service, code); + message = ErrorUtils.getMessageWithService("Internal errors.", service, code); break; case UNAVAILABLE: code = ErrorUtils.getCodeWithService(service, "unavailable"); - message = ErrorUtils.getMessageWithService("Unavailable.", service, code); + message = ErrorUtils.getMessageWithService("The service is currently unavailable.", service, code); break; case DATA_LOSS: code = ErrorUtils.getCodeWithService(service, "data-loss"); - message = ErrorUtils.getMessageWithService("Data loss.", service, code); + message = ErrorUtils.getMessageWithService("Unrecoverable data loss or corruption.", service, code); break; case UNAUTHENTICATED: code = ErrorUtils.getCodeWithService(service, "unauthenticated"); - message = ErrorUtils.getMessageWithService("Unauthenticated.", service, code); + message = ErrorUtils.getMessageWithService("The request does not have valid authentication credentials for the operation.", service, code); break; default: code = ErrorUtils.getCodeWithService(service, "unknown"); diff --git a/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestoreDocumentReference.java b/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestoreDocumentReference.java index 8167f281..f35d2056 100644 --- a/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestoreDocumentReference.java +++ b/android/src/main/java/io/invertase/firebase/firestore/RNFirebaseFirestoreDocumentReference.java @@ -47,15 +47,13 @@ public class RNFirebaseFirestoreDocumentReference { // Not supported on Android out of the box } - public void delete(final ReadableMap options, final Promise promise) { + public void delete(final Promise promise) { this.ref.delete().addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { Log.d(TAG, "delete:onComplete:success"); - // Missing fields from web SDK - // writeTime - promise.resolve(Arguments.createMap()); + promise.resolve(null); } else { Log.e(TAG, "delete:onComplete:failure", task.getException()); RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); @@ -122,9 +120,7 @@ public class RNFirebaseFirestoreDocumentReference { public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { Log.d(TAG, "set:onComplete:success"); - // Missing fields from web SDK - // writeTime - promise.resolve(Arguments.createMap()); + promise.resolve(null); } else { Log.e(TAG, "set:onComplete:failure", task.getException()); RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); @@ -140,9 +136,7 @@ public class RNFirebaseFirestoreDocumentReference { public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { Log.d(TAG, "update:onComplete:success"); - // Missing fields from web SDK - // writeTime - promise.resolve(Arguments.createMap()); + promise.resolve(null); } else { Log.e(TAG, "update:onComplete:failure", task.getException()); RNFirebaseFirestore.promiseRejectException(promise, (FirebaseFirestoreException)task.getException()); diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestore.m b/ios/RNFirebase/firestore/RNFirebaseFirestore.m index e5261c52..9a7df690 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestore.m +++ b/ios/RNFirebase/firestore/RNFirebaseFirestore.m @@ -49,7 +49,6 @@ RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appName RCT_EXPORT_METHOD(documentBatch:(NSString *) appName writes:(NSArray *) writes - commitOptions:(NSDictionary *) commitOptions resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appName]; @@ -80,13 +79,7 @@ RCT_EXPORT_METHOD(documentBatch:(NSString *) appName if (error) { [RNFirebaseFirestore promiseRejectException:reject error:error]; } else { - NSMutableArray *result = [[NSMutableArray alloc] init]; - for (NSDictionary *write in writes) { - // Missing fields from web SDK - // writeTime - [result addObject:@{}]; - } - resolve(result); + resolve(nil); } }]; } @@ -108,10 +101,9 @@ RCT_EXPORT_METHOD(documentCreate:(NSString *) appName RCT_EXPORT_METHOD(documentDelete:(NSString *) appName path:(NSString *) path - options:(NSDictionary *) options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) { - [[self getDocumentForAppPath:appName path:path] delete:options resolver:resolve rejecter:reject]; + [[self getDocumentForAppPath:appName path:path] delete:resolve rejecter:reject]; } RCT_EXPORT_METHOD(documentGet:(NSString *) appName @@ -197,8 +189,75 @@ RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName NSString *message; NSString *service = @"Firestore"; - // TODO: Proper error codes switch (nativeError.code) { + case FIRFirestoreErrorCodeOK: + code = [RNFirebaseFirestore getCodeWithService:service code:@"ok"]; + message = [RNFirebaseFirestore getMessageWithService:@"Ok." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeCancelled: + code = [RNFirebaseFirestore getCodeWithService:service code:@"cancelled"]; + message = [RNFirebaseFirestore getMessageWithService:@"The operation was cancelled." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeUnknown: + code = [RNFirebaseFirestore getCodeWithService:service code:@"unknown"]; + message = [RNFirebaseFirestore getMessageWithService:@"Unknown error or an error from a different error domain." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeInvalidArgument: + code = [RNFirebaseFirestore getCodeWithService:service code:@"invalid-argument"]; + message = [RNFirebaseFirestore getMessageWithService:@"Client specified an invalid argument." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeDeadlineExceeded: + code = [RNFirebaseFirestore getCodeWithService:service code:@"deadline-exceeded"]; + message = [RNFirebaseFirestore getMessageWithService:@"Deadline expired before operation could complete." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeNotFound: + code = [RNFirebaseFirestore getCodeWithService:service code:@"not-found"]; + message = [RNFirebaseFirestore getMessageWithService:@"Some requested document was not found." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeAlreadyExists: + code = [RNFirebaseFirestore getCodeWithService:service code:@"already-exists"]; + message = [RNFirebaseFirestore getMessageWithService:@"Some document that we attempted to create already exists." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodePermissionDenied: + code = [RNFirebaseFirestore getCodeWithService:service code:@"permission-denied"]; + message = [RNFirebaseFirestore getMessageWithService:@"The caller does not have permission to execute the specified operation." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeResourceExhausted: + code = [RNFirebaseFirestore getCodeWithService:service code:@"resource-exhausted"]; + message = [RNFirebaseFirestore getMessageWithService:@"Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeFailedPrecondition: + code = [RNFirebaseFirestore getCodeWithService:service code:@"failed-precondition"]; + message = [RNFirebaseFirestore getMessageWithService:@"Operation was rejected because the system is not in a state required for the operation`s execution." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeAborted: + code = [RNFirebaseFirestore getCodeWithService:service code:@"aborted"]; + message = [RNFirebaseFirestore getMessageWithService:@"The operation was aborted, typically due to a concurrency issue like transaction aborts, etc." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeOutOfRange: + code = [RNFirebaseFirestore getCodeWithService:service code:@"out-of-range"]; + message = [RNFirebaseFirestore getMessageWithService:@"Operation was attempted past the valid range." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeUnimplemented: + code = [RNFirebaseFirestore getCodeWithService:service code:@"unimplemented"]; + message = [RNFirebaseFirestore getMessageWithService:@"Operation is not implemented or not supported/enabled." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeInternal: + code = [RNFirebaseFirestore getCodeWithService:service code:@"internal"]; + message = [RNFirebaseFirestore getMessageWithService:@"Internal errors." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeUnavailable: + code = [RNFirebaseFirestore getCodeWithService:service code:@"unavailable"]; + message = [RNFirebaseFirestore getMessageWithService:@"The service is currently unavailable." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeDataLoss: + code = [RNFirebaseFirestore getCodeWithService:service code:@"data-loss"]; + message = [RNFirebaseFirestore getMessageWithService:@"Unrecoverable data loss or corruption." service:service fullCode:code]; + break; + case FIRFirestoreErrorCodeUnauthenticated: + code = [RNFirebaseFirestore getCodeWithService:service code:@"unauthenticated"]; + message = [RNFirebaseFirestore getMessageWithService:@"The request does not have valid authentication credentials for the operation." service:service fullCode:code]; + break; default: code = [RNFirebaseFirestore getCodeWithService:service code:@"unknown"]; message = [RNFirebaseFirestore getMessageWithService:@"An unknown error occurred." service:service fullCode:code]; diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m index 69388409..1cc0e3ab 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m @@ -157,6 +157,12 @@ static NSMutableDictionary *_listeners; NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init]; [snapshot setValue:[self documentChangesToArray:querySnapshot.documentChanges] forKey:@"changes"]; [snapshot setValue:[self documentSnapshotsToArray:querySnapshot.documents] forKey:@"documents"]; + if (querySnapshot.metadata) { + NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init]; + [metadata setValue:@(querySnapshot.metadata.fromCache) forKey:@"fromCache"]; + [metadata setValue:@(querySnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"]; + [snapshot setValue:metadata forKey:@"metadata"]; + } return snapshot; } diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h index 51f6d257..53fdab87 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h @@ -19,7 +19,7 @@ - (id)initWithPath:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path; - (void)collections:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; - (void)create:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; -- (void)delete:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; +- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; - (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; + (void)offSnapshot:(NSString *)listenerId; - (void)onSnapshot:(NSString *)listenerId; diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m index 4e51a028..3f3c0d16 100644 --- a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m @@ -34,8 +34,7 @@ static NSMutableDictionary *_listeners; // Not supported on iOS out of the box } -- (void)delete:(NSDictionary *)options - resolver:(RCTPromiseResolveBlock) resolve +- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject { [_ref deleteDocumentWithCompletion:^(NSError * _Nullable error) { [RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject]; @@ -115,9 +114,7 @@ static NSMutableDictionary *_listeners; if (error) { [RNFirebaseFirestore promiseRejectException:reject error:error]; } else { - // Missing fields from web SDK - // writeTime - resolve(@{}); + resolve(nil); } } @@ -127,11 +124,12 @@ static NSMutableDictionary *_listeners; if (documentSnapshot.exists) { [snapshot setValue:documentSnapshot.data forKey:@"data"]; } - // Missing fields from web SDK - // createTime - // readTime - // updateTime - + if (documentSnapshot.metadata) { + NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init]; + [metadata setValue:@(documentSnapshot.metadata.fromCache) forKey:@"fromCache"]; + [metadata setValue:@(documentSnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"]; + [snapshot setValue:metadata forKey:@"metadata"]; + } return snapshot; } diff --git a/lib/modules/firestore/CollectionReference.js b/lib/modules/firestore/CollectionReference.js index 3c53be1d..01fe57e6 100644 --- a/lib/modules/firestore/CollectionReference.js +++ b/lib/modules/firestore/CollectionReference.js @@ -37,7 +37,7 @@ export default class CollectionReference { return parentPath ? new DocumentReference(this._firestore, parentPath) : null; } - add(data: { [string]: any }): Promise { + add(data: Object): Promise { const documentRef = this.doc(); return documentRef.set(data) .then(() => Promise.resolve(documentRef)); diff --git a/lib/modules/firestore/DocumentReference.js b/lib/modules/firestore/DocumentReference.js index 9873c2cd..c5cc9634 100644 --- a/lib/modules/firestore/DocumentReference.js +++ b/lib/modules/firestore/DocumentReference.js @@ -5,21 +5,12 @@ import CollectionReference from './CollectionReference'; import DocumentSnapshot from './DocumentSnapshot'; import Path from './Path'; -import INTERNALS from './../../internals'; import { firestoreAutoId } from '../../utils'; -export type DeleteOptions = { - lastUpdateTime?: string, -} - export type WriteOptions = { merge?: boolean, } -export type WriteResult = { - writeTime: string, -} - /** * @class DocumentReference */ @@ -40,9 +31,9 @@ export default class DocumentReference { return this._documentPath.id; } - get parent(): CollectionReference | null { + get parent(): CollectionReference { const parentPath = this._documentPath.parent(); - return parentPath ? new CollectionReference(this._firestore, parentPath) : null; + return new CollectionReference(this._firestore, parentPath); } get path(): string { @@ -58,15 +49,9 @@ export default class DocumentReference { return new CollectionReference(this._firestore, path); } - create(data: { [string]: any }): Promise { - /* return this._firestore._native - .documentCreate(this.path, data); */ - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('DocumentReference', 'create')); - } - - delete(deleteOptions?: DeleteOptions): Promise { + delete(): Promise { return this._firestore._native - .documentDelete(this.path, deleteOptions); + .documentDelete(this.path); } get(): Promise { @@ -75,21 +60,6 @@ export default class DocumentReference { .then(result => new DocumentSnapshot(this._firestore, result)); } - getCollections(): Promise { - /* return this._firestore._native - .documentCollections(this.path) - .then((collectionIds) => { - const collections = []; - - for (const collectionId of collectionIds) { - collections.push(this.collection(collectionId)); - } - - return collections; - }); */ - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('DocumentReference', 'getCollections')); - } - onSnapshot(onNext: Function, onError?: Function): () => void { // TODO: Validation const listenerId = firestoreAutoId(); @@ -121,13 +91,12 @@ export default class DocumentReference { return this._offDocumentSnapshot.bind(this, listenerId, listener); } - set(data: { [string]: any }, writeOptions?: WriteOptions): Promise { + set(data: Object, writeOptions?: WriteOptions): Promise { return this._firestore._native .documentSet(this.path, data, writeOptions); } - // TODO: Update to new update method signature - update(data: { [string]: any }): Promise { + update(data: Object): Promise { return this._firestore._native .documentUpdate(this.path, data); } diff --git a/lib/modules/firestore/DocumentSnapshot.js b/lib/modules/firestore/DocumentSnapshot.js index f3bc8823..3548f359 100644 --- a/lib/modules/firestore/DocumentSnapshot.js +++ b/lib/modules/firestore/DocumentSnapshot.js @@ -4,37 +4,30 @@ */ import DocumentReference from './DocumentReference'; import Path from './Path'; -import INTERNALS from './../../internals'; + +export type SnapshotMetadata = { + fromCache: boolean, + hasPendingWrites: boolean, +} export type DocumentSnapshotNativeData = { - createTime: string, data: Object, + metadata: SnapshotMetadata, path: string, - readTime: string, - updateTime: string, } /** * @class DocumentSnapshot */ export default class DocumentSnapshot { - _createTime: string; _data: Object; - _readTime: string; + _metadata: SnapshotMetadata; _ref: DocumentReference; - _updateTime: string; constructor(firestore: Object, nativeData: DocumentSnapshotNativeData) { - this._createTime = nativeData.createTime; this._data = nativeData.data; + this._metadata = nativeData.metadata; this._ref = new DocumentReference(firestore, Path.fromName(nativeData.path)); - this._readTime = nativeData.readTime; - this._updateTime = nativeData.updateTime; - } - - get createTime(): string { - // return this._createTime; - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'createTime')); } get exists(): boolean { @@ -45,20 +38,14 @@ export default class DocumentSnapshot { return this._ref.id; } - get readTime(): string { - // return this._readTime; - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'readTime')); + get metadata(): SnapshotMetadata { + return _metadata; } get ref(): DocumentReference { return this._ref; } - get updateTime(): string { - // return this._updateTime; - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_PROPERTY('DocumentSnapshot', 'updateTime')); - } - data(): Object { return this._data; } diff --git a/lib/modules/firestore/Query.js b/lib/modules/firestore/Query.js index c4f271f5..cdf5daa5 100644 --- a/lib/modules/firestore/Query.js +++ b/lib/modules/firestore/Query.js @@ -104,31 +104,18 @@ export default class Query { .then(nativeData => new QuerySnapshot(this._firestore, this, nativeData)); } - limit(n: number): Query { + limit(limit: number): Query { // TODO: Validation // validate.isInteger('n', n); const options = { ...this._queryOptions, - limit: n, + limit, }; return new Query(this.firestore, this._referencePath, this._fieldFilters, this._fieldOrders, options); } - offset(n: number): Query { - // TODO: Validation - // validate.isInteger('n', n); - - /* const options = { - ...this._queryOptions, - offset: n, - }; - return new Query(this.firestore, this._referencePath, this._fieldFilters, - this._fieldOrders, options); */ - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'offset')); - } - onSnapshot(onNext: () => any, onError?: () => any): () => void { // TODO: Validation const listenerId = firestoreAutoId(); @@ -185,31 +172,6 @@ export default class Query { combinedOrders, this._queryOptions); } - select(varArgs: string[]): Query { - /* - varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments); - const fieldReferences = []; - - if (varArgs.length === 0) { - fieldReferences.push(DOCUMENT_NAME_FIELD); - } else { - for (let i = 0; i < varArgs.length; ++i) { - // TODO: Validation - // validate.isFieldPath(i, args[i]); - fieldReferences.push(varArgs[i]); - } - } - - const options = { - ...this._queryOptions, - selectFields: fieldReferences, - }; - - return new Query(this.firestore, this._referencePath, this._fieldFilters, - this._fieldOrders, options);*/ - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'select')); - } - startAfter(fieldValues: any): Query { fieldValues = [].slice.call(arguments); // TODO: Validation @@ -234,10 +196,6 @@ export default class Query { this._fieldOrders, options); } - stream(): Stream { - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'stream')); - } - where(fieldPath: string, opStr: Operator, value: any): Query { // TODO: Validation // validate.isFieldPath('fieldPath', fieldPath); diff --git a/lib/modules/firestore/QuerySnapshot.js b/lib/modules/firestore/QuerySnapshot.js index 4b3f4b3e..26b910d6 100644 --- a/lib/modules/firestore/QuerySnapshot.js +++ b/lib/modules/firestore/QuerySnapshot.js @@ -7,12 +7,12 @@ import DocumentSnapshot from './DocumentSnapshot'; import Query from './Query'; import type { DocumentChangeNativeData } from './DocumentChange'; -import type { DocumentSnapshotNativeData } from './DocumentSnapshot'; +import type { DocumentSnapshotNativeData, SnapshotMetadata } from './DocumentSnapshot'; type QuerySnapshotNativeData = { changes: DocumentChangeNativeData[], documents: DocumentSnapshotNativeData[], - readTime: string, + metadata: SnapshotMetadata, } /** @@ -21,14 +21,14 @@ type QuerySnapshotNativeData = { export default class QuerySnapshot { _changes: DocumentChange[]; _docs: DocumentSnapshot[]; + _metadata: SnapshotMetadata; _query: Query; - _readTime: string; constructor(firestore: Object, query: Query, nativeData: QuerySnapshotNativeData) { this._changes = nativeData.changes.map(change => new DocumentChange(change)); this._docs = nativeData.documents.map(doc => new DocumentSnapshot(firestore, doc)); + this._metadata = nativeData.metadata; this._query = query; - this._readTime = nativeData.readTime; } get docChanges(): DocumentChange[] { @@ -47,8 +47,8 @@ export default class QuerySnapshot { return this._query; } - get readTime(): string { - return this._readTime; + get metadata(): SnapshotMetadata { + return this._metadata; } get size(): number { diff --git a/lib/modules/firestore/WriteBatch.js b/lib/modules/firestore/WriteBatch.js index 6fd1fd85..7ada92d9 100644 --- a/lib/modules/firestore/WriteBatch.js +++ b/lib/modules/firestore/WriteBatch.js @@ -4,11 +4,7 @@ */ import DocumentReference from './DocumentReference'; -import type { DeleteOptions, WriteOptions, WriteResult } from './DocumentReference'; - -type CommitOptions = { - transactionId: string, -} +import type { WriteOptions } from './DocumentReference'; type DocumentWrite = { data?: Object, @@ -29,28 +25,16 @@ export default class WriteBatch { this._writes = []; } - get firestore(): Object { - return this._firestore; + commit(): Promise { + return this._firestore._native + .documentBatch(this._writes); } - get isEmpty(): boolean { - return this._writes.length === 0; - } - - create(docRef: DocumentReference, data: Object): WriteBatch { - // TODO: Validation - // validate.isDocumentReference('docRef', docRef); - // validate.isDocument('data', data); - - return this.set(docRef, data, { exists: false }); - } - - delete(docRef: DocumentReference, deleteOptions?: DeleteOptions): WriteBatch { + delete(docRef: DocumentReference): WriteBatch { // TODO: Validation // validate.isDocumentReference('docRef', docRef); // validate.isOptionalPrecondition('deleteOptions', deleteOptions); this._writes.push({ - options: deleteOptions, path: docRef.path, type: 'DELETE', }); @@ -75,7 +59,7 @@ export default class WriteBatch { } // TODO: Update to new method signature - update(docRef: DocumentReference, data: { [string]: any }): WriteBatch { + update(docRef: DocumentReference, data: Object): WriteBatch { // TODO: Validation // validate.isDocumentReference('docRef', docRef); // validate.isDocument('data', data, true); @@ -88,9 +72,4 @@ export default class WriteBatch { return this; } - - commit(commitOptions?: CommitOptions): Promise { - return this._firestore._native - .documentBatch(this._writes, commitOptions); - } } diff --git a/lib/modules/firestore/index.js b/lib/modules/firestore/index.js index 4f1ae343..5d593f1b 100644 --- a/lib/modules/firestore/index.js +++ b/lib/modules/firestore/index.js @@ -13,9 +13,6 @@ import Path from './Path'; import WriteBatch from './WriteBatch'; import INTERNALS from './../../internals'; -const unquotedIdentifier_ = '(?:[A-Za-z_][A-Za-z_0-9]*)'; -const UNQUOTED_IDENTIFIER_REGEX = new RegExp(`^${unquotedIdentifier_}$`); - type CollectionSyncEvent = { appName: string, querySnapshot?: QuerySnapshot, @@ -92,50 +89,20 @@ export default class Firestore extends ModuleBase { return new DocumentReference(this, path); } - getAll(varArgs: DocumentReference[]): Promise { - /*varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments); - - const documents = []; - varArgs.forEach((document) => { - // TODO: Validation - // validate.isDocumentReference(i, varArgs[i]); - documents.push(document.path); - }); - return this._native - .documentGetAll(documents) - .then(results => results.map(result => new DocumentSnapshot(this, result)));*/ - throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD('Query', 'offset')); + enablePersistence(): Promise { + throw new Error('Persistence is enabled by default on the Firestore SDKs'); } - getCollections(): Promise { - const rootDocument = new DocumentReference(this, this._referencePath); - return rootDocument.getCollections(); + runTransaction(updateFunction): Promise { + throw new Error('firebase.firestore().runTransaction() coming soon'); } - runTransaction(updateFunction, transactionOptions?: Object): Promise { - + setLogLevel(logLevel: 'debug' | 'error' | 'silent'): void { + throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_MODULE_METHOD(Firestore, 'setLogLevel')); } - static geoPoint(latitude, longitude): GeoPoint { - return new GeoPoint(latitude, longitude); - } - - static fieldPath(varArgs: string[]): string { - varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments); - - let fieldPath = ''; - - for (let i = 0; i < varArgs.length; ++i) { - let component = varArgs[i]; - // TODO: Validation - // validate.isString(i, component); - if (!UNQUOTED_IDENTIFIER_REGEX.test(component)) { - component = `\`${component.replace(/[`\\]/g, '\\$&')} \``; - } - fieldPath += i !== 0 ? `.${component}` : component; - } - - return fieldPath; + settings(settings: Object): void { + throw new Error('firebase.firestore().settings() coming soon'); } /** @@ -174,4 +141,5 @@ export const statics = { delete: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.deleteFieldValue || {}, serverTimestamp: () => NativeModules.RNFirebaseFirestore && NativeModules.RNFirebaseFirestore.serverTimestampFieldValue || {} }, + GeoPoint, }; diff --git a/package.json b/package.json index cfea54d9..396be3c4 100644 --- a/package.json +++ b/package.json @@ -88,8 +88,11 @@ "rnpm": { "android": { "buildPatch": " compile(project(':react-native-firebase')) {\n transitive = false\n }\n", - "packageImportPath": "import io.invertase.firebase.RNFirebasePackage;\nimport io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;", - "packageInstance": "new RNFirebasePackage(),\n new RNFirebaseAnalyticsPackage()" + "packageImportPath": "import io.invertase.firebase.RNFirebasePackage;", + "packageInstance": "new RNFirebasePackage()" + }, + "commands": { + "postlink": "node node_modules/react-native-firebase/scripts/rnpm-postlink" } } } diff --git a/scripts/rnpm-postlink.js b/scripts/rnpm-postlink.js new file mode 100644 index 00000000..048c7ac7 --- /dev/null +++ b/scripts/rnpm-postlink.js @@ -0,0 +1,26 @@ +const fs = require('fs'); +const path = require('path'); + +const appBuildGradlePath = path.join('android', 'app', 'build.gradle'); + +const defaultCompileStatement = "compile project(':react-native-firebase')"; +const requiredCompileStatement = "compile(project(':react-native-firebase')) {\n transitive = false\n }"; + +// android/build.gradle +// 1) TODO: Add Google Play maven repository + +// 2) TODO: Add google-services dependency if required + +// android/app/build.gradle +// 0) Load the file +let buildGradleContents = fs.readFileSync(appBuildGradlePath, 'utf8'); + +// 1) Check that react-native-firebase compile statement is the correct format +buildGradleContents = buildGradleContents.replace(defaultCompileStatement, requiredCompileStatement); + +// 2) TODO: Add firebase-core and play-services-base dependencies + +// 3) TODO: Add google-services plugin + +// 4) Write file +fs.writeFileSync(appBuildGradlePath, buildGradleContents);