diff --git a/packages/app/lib/FirebaseApp.js b/packages/app/lib/FirebaseApp.js index be7ea00120..7ff26f28c6 100644 --- a/packages/app/lib/FirebaseApp.js +++ b/packages/app/lib/FirebaseApp.js @@ -61,8 +61,7 @@ export default class FirebaseApp { } extendApp(extendedProps) { - // this method has no modular alternative, send true for param 'noAlternative' - warnIfNotModularCall(arguments, '', true); + warnIfNotModularCall(arguments); this._checkDestroyed(); Object.assign(this, extendedProps); } @@ -74,8 +73,7 @@ export default class FirebaseApp { } toString() { - // this method has no modular alternative, send true for param 'noAlternative' - warnIfNotModularCall(arguments, '', true); + warnIfNotModularCall(arguments); return this.name; } } diff --git a/packages/app/lib/common/index.js b/packages/app/lib/common/index.js index 9f7cb63bb6..159ae0c40f 100644 --- a/packages/app/lib/common/index.js +++ b/packages/app/lib/common/index.js @@ -108,17 +108,96 @@ const NO_REPLACEMENT = true; const mapOfDeprecationReplacements = { crashlytics: { - checkForUnsentReports: 'checkForUnsentReports()', - crash: 'crash()', - deleteUnsentReports: 'deleteUnsentReports()', - didCrashOnPreviousExecution: 'didCrashOnPreviousExecution()', - log: 'log()', - setAttribute: 'setAttribute()', - setAttributes: 'setAttributes()', - setUserId: 'setUserId()', - recordError: 'recordError()', - sendUnsentReports: 'sendUnsentReports()', - setCrashlyticsCollectionEnabled: 'setCrashlyticsCollectionEnabled()', + default: { + checkForUnsentReports: 'checkForUnsentReports()', + crash: 'crash()', + deleteUnsentReports: 'deleteUnsentReports()', + didCrashOnPreviousExecution: 'didCrashOnPreviousExecution()', + log: 'log()', + setAttribute: 'setAttribute()', + setAttributes: 'setAttributes()', + setUserId: 'setUserId()', + recordError: 'recordError()', + sendUnsentReports: 'sendUnsentReports()', + setCrashlyticsCollectionEnabled: 'setCrashlyticsCollectionEnabled()', + }, + }, + firestore: { + default: { + batch: 'writeBatch()', + loadBundle: 'loadBundle()', + namedQuery: 'namedQuery()', + clearPersistence: 'clearIndexedDbPersistence()', + waitForPendingWrites: 'waitForPendingWrites()', + terminate: 'terminate()', + useEmulator: 'connectFirestoreEmulator()', + collection: 'collection()', + collectionGroup: 'collectionGroup()', + disableNetwork: 'disableNetwork()', + doc: 'doc()', + enableNetwork: 'enableNetwork()', + runTransaction: 'runTransaction()', + settings: 'settings()', + persistentCacheIndexManager: 'getPersistentCacheIndexManager()', + }, + statics: { + setLogLevel: 'setLogLevel()', + Filter: 'where()', + FieldValue: 'FieldValue', + Timestamp: 'Timestamp', + GeoPoint: 'GeoPoint', + Blob: 'Bytes', + FieldPath: 'FieldPath', + }, + FirestoreCollectionReference: { + count: 'getCountFromServer()', + countFromServer: 'getCountFromServer()', + endAt: 'endAt()', + endBefore: 'endBefore()', + get: 'getDocs()', + isEqual: NO_REPLACEMENT, + limit: 'limit()', + limitToLast: 'limitToLast()', + onSnapshot: 'onSnapshot()', + orderBy: 'orderBy()', + startAfter: 'startAfter()', + startAt: 'startAt()', + where: 'where()', + add: 'addDoc()', + doc: 'doc()', + }, + FirestoreDocumentReference: { + collection: 'collection()', + delete: 'deleteDoc()', + get: 'getDoc()', + isEqual: NO_REPLACEMENT, + onSnapshot: 'onSnapshot()', + set: 'setDoc()', + update: 'updateDoc()', + }, + FirestoreDocumentSnapshot: { + isEqual: NO_REPLACEMENT, + }, + FirestoreFieldValue: { + arrayRemove: 'arrayRemove()', + arrayUnion: 'arrayUnion()', + delete: 'deleteField()', + increment: 'increment()', + serverTimestamp: 'serverTimestamp()', + }, + Filter: { + or: 'or()', + and: 'and()', + }, + FirestorePersistentCacheIndexManager: { + enableIndexAutoCreation: 'enablePersistentCacheIndexAutoCreation()', + disableIndexAutoCreation: 'disablePersistentCacheIndexAutoCreation()', + deleteAllIndexes: 'deleteAllPersistentCacheIndexes()', + }, + FirestoreTimestamp: { + seconds: NO_REPLACEMENT, + nanoseconds: NO_REPLACEMENT, + }, }, }; @@ -126,15 +205,14 @@ const v8deprecationMessage = 'This v8 method is deprecated and will be removed in the next major release ' + 'as part of move to match Firebase Web modular v9 SDK API.'; -export function deprecationConsoleWarning(moduleName, methodName, isModularMethod) { +export function deprecationConsoleWarning(nameSpace, methodName, instanceName, isModularMethod) { if (!isModularMethod) { - const moduleMap = mapOfDeprecationReplacements[moduleName]; + const moduleMap = mapOfDeprecationReplacements[nameSpace]; if (moduleMap) { - const replacementMethodName = moduleMap[methodName]; - // only warn if it is mapped and purposefully deprecated - if (replacementMethodName) { - const message = createMessage(moduleName, methodName); - + const instanceMap = moduleMap[instanceName]; + const deprecatedMethod = instanceMap[methodName]; + if (instanceMap && deprecatedMethod) { + const message = createMessage(nameSpace, methodName, instanceName); // eslint-disable-next-line no-console console.warn(message); } @@ -142,16 +220,23 @@ export function deprecationConsoleWarning(moduleName, methodName, isModularMetho } } -export function createMessage(moduleName, methodName, uniqueMessage = '') { - if (uniqueMessage.length > 0) { +export function createMessage( + nameSpace, + methodName, + instanceName = 'default', + uniqueMessage = null, +) { + if (uniqueMessage) { // Unique deprecation message used for testing return uniqueMessage; } - const moduleMap = mapOfDeprecationReplacements[moduleName]; + const moduleMap = mapOfDeprecationReplacements[nameSpace]; if (moduleMap) { - const replacementMethodName = moduleMap[methodName]; - if (replacementMethodName) { + const instance = moduleMap[instanceName]; + if (instance) { + const replacementMethodName = instance[methodName]; + if (replacementMethodName !== NO_REPLACEMENT) { return v8deprecationMessage + ` Please use \`${replacementMethodName}\` instead.`; } else { @@ -161,9 +246,98 @@ export function createMessage(moduleName, methodName, uniqueMessage = '') { } } +function getNamespace(target) { + if (target.GeoPoint) { + // target is statics object. GeoPoint is a static class on Firestore + return 'firestore'; + } + if (target._config && target._config.namespace) { + return target._config.namespace; + } + const className = target.name ? target.name : target.constructor.name; + return Object.keys(mapOfDeprecationReplacements).find(key => { + if (mapOfDeprecationReplacements[key][className]) { + return key; + } + }); +} + +function getInstanceName(target) { + if (target.GeoPoint) { + // target is statics object. GeoPoint is a static class on Firestore + return 'statics'; + } + if (target._config) { + // module class instance, we use default to store map of deprecated methods + return 'default'; + } + if (target.name) { + // It's a function which has a name property unlike classes + return target.name; + } + // It's a class instance + return target.constructor.name; +} + +export function createDeprecationProxy(instance) { + return new Proxy(instance, { + construct(target, args) { + // needed for Timestamp which we pass as static, when we construct new instance, we need to wrap it in proxy again. + return createDeprecationProxy(new target(...args)); + }, + get(target, prop, receiver) { + const originalMethod = target[prop]; + + if (prop === 'constructor') { + return Reflect.get(target, prop, receiver); + } + + if (target && target.constructor && target.constructor.name === 'FirestoreTimestamp') { + deprecationConsoleWarning('firestore', prop, 'FirestoreTimestamp', false); + return Reflect.get(target, prop, receiver); + } + + if (target && target.name === 'firebaseModuleWithApp') { + // statics + if ( + prop === 'Filter' || + prop === 'FieldValue' || + prop === 'Timestamp' || + prop === 'GeoPoint' || + prop === 'Blob' || + prop === 'FieldPath' + ) { + deprecationConsoleWarning('firestore', prop, 'statics', false); + } + if (prop !== 'setLogLevel') { + // we want to capture setLogLevel function call which we do below + return Reflect.get(target, prop, receiver); + } + } + + if (typeof originalMethod === 'function') { + return function (...args) { + const isModularMethod = args.includes(MODULAR_DEPRECATION_ARG); + const instanceName = getInstanceName(target); + const nameSpace = getNamespace(target); + + deprecationConsoleWarning(nameSpace, prop, instanceName, isModularMethod); + + return originalMethod.apply(target, filterModularArgument(args)); + }; + } + return Reflect.get(target, prop, receiver); + }, + }); +} + export const MODULAR_DEPRECATION_ARG = 'react-native-firebase-modular-method-call'; -export function warnIfNotModularCall(args, replacementMethodName, noAlternative) { +export function filterModularArgument(list) { + return list.filter(arg => arg !== MODULAR_DEPRECATION_ARG); +} + +export function warnIfNotModularCall(args, replacementMethodName = '') { for (let i = 0; i < args.length; i++) { if (args[i] === MODULAR_DEPRECATION_ARG) { return; @@ -173,7 +347,7 @@ export function warnIfNotModularCall(args, replacementMethodName, noAlternative) 'This v8 method is deprecated and will be removed in the next major release ' + 'as part of move to match Firebase Web modular v9 SDK API.'; - if (!noAlternative) { + if (replacementMethodName.length > 0) { message += ` Please use \`${replacementMethodName}\` instead.`; } diff --git a/packages/app/lib/common/unitTestUtils.ts b/packages/app/lib/common/unitTestUtils.ts index 7196fd99b4..9372074ad4 100644 --- a/packages/app/lib/common/unitTestUtils.ts +++ b/packages/app/lib/common/unitTestUtils.ts @@ -18,25 +18,29 @@ export const checkV9Deprecation = (modularFunction: () => void, nonModularFuncti export type CheckV9DeprecationFunction = ( modularFunction: () => void, nonModularFunction: () => void, - methodName: string, + methodNameKey: string, uniqueMessage: string = '', ) => void; -export const createCheckV9Deprecation = (moduleName: string): CheckV9DeprecationFunction => { +export const createCheckV9Deprecation = (moduleNames: string[]): CheckV9DeprecationFunction => { return ( modularFunction: () => void, nonModularFunction: () => void, - methodName: string, - uniqueMessage = '', + methodNameKey: string, + uniqueMessage: string?, ) => { + const moduleName = moduleNames[0]; // firestore, database, etc + const instanceName = moduleNames[1] || 'default'; // default, FirestoreCollectionReference, etc const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + // Do not call `mockRestore()` as it removes the spy consoleWarnSpy.mockReset(); modularFunction(); expect(consoleWarnSpy).not.toHaveBeenCalled(); consoleWarnSpy.mockReset(); + consoleWarnSpy.mockRestore(); const consoleWarnSpy2 = jest.spyOn(console, 'warn').mockImplementation(warnMessage => { - const message = createMessage(moduleName, methodName, uniqueMessage); - expect(message).toMatch(warnMessage); + const message = createMessage(moduleName, methodNameKey, instanceName, uniqueMessage); + expect(warnMessage).toMatch(message); }); nonModularFunction(); diff --git a/packages/app/lib/internal/registry/namespace.js b/packages/app/lib/internal/registry/namespace.js index 6ee73bf20b..361852073c 100644 --- a/packages/app/lib/internal/registry/namespace.js +++ b/packages/app/lib/internal/registry/namespace.js @@ -15,7 +15,7 @@ * */ -import { isString, MODULAR_DEPRECATION_ARG, deprecationConsoleWarning } from '../../common'; +import { isString, createDeprecationProxy } from '../../common'; import FirebaseApp from '../../FirebaseApp'; import SDK_VERSION from '../../version'; import { DEFAULT_APP_NAME, KNOWN_NAMESPACES } from '../constants'; @@ -114,11 +114,11 @@ function getOrCreateModuleForApp(app, moduleNamespace) { } if (!APP_MODULE_INSTANCE[app.name][key]) { - APP_MODULE_INSTANCE[app.name][key] = new ModuleClass( - app, - NAMESPACE_REGISTRY[moduleNamespace], - customUrlOrRegionOrDatabaseId, + const module = createDeprecationProxy( + new ModuleClass(app, NAMESPACE_REGISTRY[moduleNamespace], customUrlOrRegionOrDatabaseId), ); + + APP_MODULE_INSTANCE[app.name][key] = module; } return APP_MODULE_INSTANCE[app.name][key]; @@ -180,8 +180,9 @@ function getOrCreateModuleForRoot(moduleNamespace) { } Object.assign(firebaseModuleWithApp, statics || {}); - Object.freeze(firebaseModuleWithApp); - MODULE_GETTER_FOR_ROOT[moduleNamespace] = firebaseModuleWithApp; + // Object.freeze(firebaseModuleWithApp); + // Wrap around statics, e.g. firebase.firestore.FieldValue, removed freeze as it stops proxy working. it is deprecated anyway + MODULE_GETTER_FOR_ROOT[moduleNamespace] = createDeprecationProxy(firebaseModuleWithApp); return MODULE_GETTER_FOR_ROOT[moduleNamespace]; } @@ -277,28 +278,6 @@ export function getFirebaseRoot() { return createFirebaseRoot(); } -function createDeprecationProxy(instance) { - return new Proxy(instance, { - get(target, prop, receiver) { - const originalMethod = target[prop]; - if (prop === 'constructor') { - return target.constructor; - } - if (typeof originalMethod === 'function') { - return function (...args) { - const isModularMethod = args.includes(MODULAR_DEPRECATION_ARG); - const moduleName = receiver._config.namespace; - - deprecationConsoleWarning(moduleName, prop, isModularMethod); - - return originalMethod.apply(target, args); - }; - } - return Reflect.get(target, prop, receiver); - }, - }); -} - /** * * @param options diff --git a/packages/crashlytics/__tests__/crashlytics.test.ts b/packages/crashlytics/__tests__/crashlytics.test.ts index 7e9775445e..b6a18aebd2 100644 --- a/packages/crashlytics/__tests__/crashlytics.test.ts +++ b/packages/crashlytics/__tests__/crashlytics.test.ts @@ -89,7 +89,7 @@ describe('Crashlytics', function () { let checkV9Deprecation: CheckV9DeprecationFunction; beforeEach(function () { - checkV9Deprecation = createCheckV9Deprecation('crashlytics'); + checkV9Deprecation = createCheckV9Deprecation(['crashlytics']); // @ts-ignore test jest.spyOn(FirebaseModule.prototype, 'native', 'get').mockImplementation(() => { diff --git a/packages/firestore/__tests__/firestore.test.ts b/packages/firestore/__tests__/firestore.test.ts index 29f661fe9b..b7005478f1 100644 --- a/packages/firestore/__tests__/firestore.test.ts +++ b/packages/firestore/__tests__/firestore.test.ts @@ -1,7 +1,23 @@ -import { describe, expect, it } from '@jest/globals'; +import { describe, expect, it, jest, beforeEach } from '@jest/globals'; +// @ts-ignore test +import { createDeprecationProxy } from '../../app/lib/common'; +// @ts-ignore test +import FirebaseModule from '../../app/lib/internal/FirebaseModule'; +// @ts-ignore test +import FirestoreQuery from '../lib/FirestoreQuery'; +// @ts-ignore test +import FirestoreDocumentSnapshot from '../lib/FirestoreDocumentSnapshot'; +// @ts-ignore test +import * as nativeModule from '@react-native-firebase/app/lib/internal/nativeModuleAndroidIos'; + +import { + createCheckV9Deprecation, + CheckV9DeprecationFunction, +} from '../../app/lib/common/unitTestUtils'; import firestore, { firebase, + connectFirestoreEmulator, Filter, getFirestore, getAggregateFromServer, @@ -17,6 +33,7 @@ import firestore, { enableNetwork, disableNetwork, clearPersistence, + clearIndexedDbPersistence, terminate, waitForPendingWrites, initializeFirestore, @@ -696,4 +713,689 @@ describe('Firestore', function () { expect(nullIndexManagerModular).toBeNull(); }); }); + + describe('test `console.warn` is called for RNFB v8 API & not called for v9 API', function () { + let collectionRefV9Deprecation: CheckV9DeprecationFunction; + let docRefV9Deprecation: CheckV9DeprecationFunction; + let fieldValueV9Deprecation: CheckV9DeprecationFunction; + let filterV9Deprecation: CheckV9DeprecationFunction; + let persistentCacheIndexManagerV9Deprecation: CheckV9DeprecationFunction; + let firestoreRefV9Deprecation: CheckV9DeprecationFunction; + let staticsV9Deprecation: CheckV9DeprecationFunction; + let timestampV9Deprecation: CheckV9DeprecationFunction; + + beforeEach(function () { + firestoreRefV9Deprecation = createCheckV9Deprecation(['firestore']); + collectionRefV9Deprecation = createCheckV9Deprecation([ + 'firestore', + 'FirestoreCollectionReference', + ]); + + docRefV9Deprecation = createCheckV9Deprecation(['firestore', 'FirestoreDocumentReference']); + + fieldValueV9Deprecation = createCheckV9Deprecation(['firestore', 'FirestoreFieldValue']); + filterV9Deprecation = createCheckV9Deprecation(['firestore', 'Filter']); + persistentCacheIndexManagerV9Deprecation = createCheckV9Deprecation([ + 'firestore', + 'FirestorePersistentCacheIndexManager', + ]); + + staticsV9Deprecation = createCheckV9Deprecation(['firestore', 'statics']); + + timestampV9Deprecation = createCheckV9Deprecation(['firestore', 'FirestoreTimestamp']); + + // @ts-ignore test + jest.spyOn(FirebaseModule.prototype, 'native', 'get').mockImplementation(() => { + return new Proxy( + {}, + { + get: () => + jest.fn().mockResolvedValue({ + source: 'cache', + changes: [], + documents: [], + metadata: {}, + path: 'foo', + } as never), + }, + ); + }); + + jest + .spyOn(FirestoreQuery.prototype, '_handleQueryCursor') + // @ts-ignore test + .mockImplementation(() => { + return []; + }); + }); + + describe('Firestore', function () { + it('firestore.batch()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => writeBatch(firestore), + () => firestore.batch(), + 'batch', + ); + }); + + it('firestore.loadBundle()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => loadBundle(firestore, 'some bundle'), + () => firestore.loadBundle('some bundle'), + 'loadBundle', + ); + }); + + it('firestore.namedQuery()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => namedQuery(firestore, 'some name'), + () => firestore.namedQuery('some name'), + 'namedQuery', + ); + }); + + it('firestore.clearPersistence()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => clearIndexedDbPersistence(firestore), + () => firestore.clearPersistence(), + 'clearPersistence', + ); + // Deprecating the modular method clearPersistence() as it doesn't exist on firebase-js-sdk + firestoreRefV9Deprecation( + () => clearIndexedDbPersistence(firestore), + () => clearPersistence(firestore), + 'clearPersistence', + ); + }); + + it('firestore.waitForPendingWrites()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => waitForPendingWrites(firestore), + () => firestore.waitForPendingWrites(), + 'waitForPendingWrites', + ); + }); + + it('firestore.terminate()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => terminate(firestore), + () => firestore.terminate(), + 'terminate', + ); + }); + + it('firestore.useEmulator()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => connectFirestoreEmulator(firestore, 'localhost', 8080), + () => firestore.useEmulator('localhost', 8080), + 'useEmulator', + ); + }); + + it('firestore.collection()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => collection(firestore, 'collection'), + () => firestore.collection('collection'), + 'collection', + ); + }); + + it('firestore.collectionGroup()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => collectionGroup(firestore, 'collection'), + () => firestore.collectionGroup('collection'), + 'collectionGroup', + ); + }); + + it('firestore.disableNetwork()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => disableNetwork(firestore), + () => firestore.disableNetwork(), + 'disableNetwork', + ); + }); + + it('firestore.doc()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => doc(firestore, 'collection/path'), + () => firestore.doc('collection/path'), + 'doc', + ); + }); + + it('firestore.enableNetwork()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => enableNetwork(firestore), + () => firestore.enableNetwork(), + 'enableNetwork', + ); + }); + + it('firestore.runTransaction()', function () { + const firestore = getFirestore(); + firestoreRefV9Deprecation( + () => runTransaction(firestore, async () => {}), + () => firestore.runTransaction(async () => {}), + 'runTransaction', + ); + }); + + it('firestore.settings()', function () { + const firestore = getFirestore(); + const app = firebase.app(); + firestoreRefV9Deprecation( + // no equivalent settings method for firebase-js-sdk + () => initializeFirestore(app, {}), + () => firestore.settings({}), + 'settings', + ); + }); + }); + + describe('CollectionReference', function () { + it('CollectionReference.count()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => getCountFromServer(query), + () => query.count(), + 'count', + ); + }); + + it('CollectionReference.countFromServer()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => getCountFromServer(query), + () => query.countFromServer(), + 'count', + ); + }); + + it('CollectionReference.endAt()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => endAt('foo'), + () => query.endAt('foo'), + 'endAt', + ); + }); + + it('CollectionReference.endBefore()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => endBefore('foo'), + () => query.endBefore('foo'), + 'endBefore', + ); + }); + + it('CollectionReference.get()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => getDocs(query), + () => query.get(), + 'get', + ); + }); + + it('CollectionReference.isEqual()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + // no equivalent method + () => {}, + () => query.isEqual(query), + 'isEqual', + ); + }); + + it('CollectionReference.limit()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => limit(9), + () => query.limit(9), + 'limit', + ); + }); + + it('CollectionReference.limitToLast()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => limitToLast(9), + () => query.limitToLast(9), + 'limitToLast', + ); + }); + + it('CollectionReference.onSnapshot()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => onSnapshot(query, () => {}), + () => query.onSnapshot(() => {}), + 'onSnapshot', + ); + }); + + it('CollectionReference.orderBy()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => orderBy('foo', 'asc'), + () => query.orderBy('foo', 'asc'), + 'orderBy', + ); + }); + + it('CollectionReference.startAfter()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => startAfter('foo'), + () => query.startAfter('foo'), + 'startAfter', + ); + }); + + it('CollectionReference.startAt()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => startAt('foo'), + () => query.startAt('foo'), + 'startAt', + ); + }); + + it('CollectionReference.where()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => where('foo', '==', 'bar'), + () => query.where('foo', '==', 'bar'), + 'where', + ); + }); + + it('CollectionReference.add()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => addDoc(query, { foo: 'bar' }), + () => query.add({ foo: 'bar' }), + 'add', + ); + }); + + it('CollectionReference.doc()', function () { + const firestore = getFirestore(); + + const query = collection(firestore, 'test'); + + collectionRefV9Deprecation( + () => doc(query, 'bar'), + () => query.doc('foo'), + 'doc', + ); + }); + }); + + describe('DocumentReference', function () { + it('DocumentReference.collection()', function () { + const firestore = getFirestore(); + + const docRef = firestore.doc('some/foo'); + + docRefV9Deprecation( + () => collection(firestore, 'bar'), + () => docRef.collection('bar'), + 'collection', + ); + }); + + it('DocumentReference.delete()', function () { + const firestore = getFirestore(); + + const docRef = firestore.doc('some/foo'); + + docRefV9Deprecation( + () => deleteDoc(docRef), + () => docRef.delete(), + 'delete', + ); + }); + + it('DocumentReference.get()', function () { + const firestore = getFirestore(); + + const docRef = firestore.doc('some/foo'); + + docRefV9Deprecation( + () => getDoc(docRef), + () => docRef.get(), + 'get', + ); + }); + + it('DocumentReference.isEqual()', function () { + const firestore = getFirestore(); + + const docRef = firestore.doc('some/foo'); + + docRefV9Deprecation( + // no equivalent method + () => {}, + () => docRef.isEqual(docRef), + 'isEqual', + ); + }); + + it('DocumentReference.onSnapshot()', function () { + const firestore = getFirestore(); + + const docRef = firestore.doc('some/foo'); + + docRefV9Deprecation( + () => onSnapshot(docRef, () => {}), + () => docRef.onSnapshot(() => {}), + 'onSnapshot', + ); + }); + + it('DocumentReference.set()', function () { + const firestore = getFirestore(); + + const docRef = firestore.doc('some/foo'); + + docRefV9Deprecation( + () => setDoc(docRef, { foo: 'bar' }), + () => docRef.set({ foo: 'bar' }), + 'set', + ); + }); + + it('DocumentReference.update()', function () { + const firestore = getFirestore(); + + const docRef = firestore.doc('some/foo'); + + docRefV9Deprecation( + () => updateDoc(docRef, { foo: 'bar' }), + () => docRef.update({ foo: 'bar' }), + 'update', + ); + }); + }); + + it('FirestoreDocumentSnapshot.isEqual()', function () { + const firestore = getFirestore(); + // Every `FirestoreDocumentSnapshot` has been wrapped in deprecation proxy, so we use constructor directly + // for ease of mocking + const snapshot = createDeprecationProxy( + new FirestoreDocumentSnapshot(firestore, { + source: 'cache', + changes: [], + documents: [], + metadata: {}, + path: 'foo', + }), + ); + + docRefV9Deprecation( + // no equivalent method + () => {}, + () => snapshot.isEqual(snapshot), + 'isEqual', + ); + }); + + describe('FieldValue', function () { + it('FieldValue.delete()', function () { + const fieldValue = firestore.FieldValue; + fieldValueV9Deprecation( + () => deleteField(), + () => fieldValue.delete(), + 'delete', + ); + }); + + it('FieldValue.increment()', function () { + const fieldValue = firestore.FieldValue; + fieldValueV9Deprecation( + () => increment(3), + () => fieldValue.increment(4), + 'increment', + ); + }); + + it('FieldValue.serverTimestamp()', function () { + const fieldValue = firestore.FieldValue; + fieldValueV9Deprecation( + () => serverTimestamp(), + () => fieldValue.serverTimestamp(), + 'serverTimestamp', + ); + }); + + it('FieldValue.arrayUnion()', function () { + const fieldValue = firestore.FieldValue; + fieldValueV9Deprecation( + () => arrayUnion('foo'), + () => fieldValue.arrayUnion('bar'), + 'arrayUnion', + ); + }); + + it('FieldValue.arrayRemove()', function () { + const fieldValue = firestore.FieldValue; + fieldValueV9Deprecation( + () => arrayRemove('foo'), + () => fieldValue.arrayRemove('bar'), + 'arrayRemove', + ); + }); + }); + + describe('statics', function () { + it('Firestore.setLogLevel()', function () { + // @ts-ignore test + jest + .spyOn(nativeModule, 'getReactNativeModule') + .mockReturnValue({ setLogLevel: jest.fn() }); + + staticsV9Deprecation( + () => setLogLevel('debug'), + () => firestore.setLogLevel('debug'), + 'setLogLevel', + ); + }); + + it('Filter static', function () { + staticsV9Deprecation( + // no corresponding method + () => {}, + () => firestore.Filter, + 'Filter', + ); + }); + + it('Timestamp static', function () { + staticsV9Deprecation( + () => Timestamp, + () => firestore.Timestamp, + 'Timestamp', + ); + }); + + it('FieldValue static', function () { + staticsV9Deprecation( + () => FieldValue, + () => firestore.FieldValue, + 'FieldValue', + ); + }); + + it('GeoPoint static', function () { + staticsV9Deprecation( + () => GeoPoint, + () => firestore.GeoPoint, + 'GeoPoint', + ); + }); + + it('Blob static', function () { + staticsV9Deprecation( + () => Blob, + () => firestore.Blob, + 'Blob', + ); + }); + + it('FieldPath static', function () { + staticsV9Deprecation( + () => FieldPath, + () => firestore.FieldPath, + 'FieldPath', + ); + }); + }); + + describe('Filter', function () { + it('Filter.or()', function () { + const filter = firestore.Filter; + filterV9Deprecation( + () => or(where('foo.bar', '==', null), where('foo.bar', '==', null)), + () => filter.or(filter('foo', '==', 'bar'), filter('baz', '==', 'qux')), + 'or', + ); + }); + + it('Filter.and()', function () { + const filter = firestore.Filter; + filterV9Deprecation( + () => and(where('foo.bar', '==', null), where('foo.bar', '==', null)), + () => filter.and(filter('foo', '==', 'bar'), filter('baz', '==', 'qux')), + 'and', + ); + }); + }); + + describe('FirestorePersistentCacheIndexManager', function () { + it('firestore.persistentCacheIndexManager()', function () { + const firestore = getFirestore(); + + firestoreRefV9Deprecation( + () => getPersistentCacheIndexManager(firestore), + () => firestore.persistentCacheIndexManager(), + 'persistentCacheIndexManager', + ); + }); + + it('FirestorePersistentCacheIndexManager.enableIndexAutoCreation()', function () { + const firestore = getFirestore(); + // @ts-ignore test + firestore._settings.persistence = true; + const indexManager = firestore.persistentCacheIndexManager(); + persistentCacheIndexManagerV9Deprecation( + () => enablePersistentCacheIndexAutoCreation(indexManager!), + () => indexManager!.enableIndexAutoCreation(), + 'enableIndexAutoCreation', + ); + }); + + it('FirestorePersistentCacheIndexManager.disableIndexAutoCreation()', function () { + const firestore = getFirestore(); + // @ts-ignore test + firestore._settings.persistence = true; + const indexManager = firestore.persistentCacheIndexManager(); + persistentCacheIndexManagerV9Deprecation( + () => disablePersistentCacheIndexAutoCreation(indexManager!), + () => indexManager!.disableIndexAutoCreation(), + 'disableIndexAutoCreation', + ); + }); + + it('FirestorePersistentCacheIndexManager.deleteAllIndexes()', function () { + const firestore = getFirestore(); + // @ts-ignore test + firestore._settings.persistence = true; + const indexManager = firestore.persistentCacheIndexManager(); + persistentCacheIndexManagerV9Deprecation( + () => deleteAllPersistentCacheIndexes(indexManager!), + () => indexManager!.deleteAllIndexes(), + 'deleteAllIndexes', + ); + }); + }); + + describe('Timestamp', function () { + it('Timestamp.seconds', function () { + const timestamp = new firestore.Timestamp(2, 3); + timestampV9Deprecation( + // no corresponding method + () => {}, + () => timestamp.seconds, + 'seconds', + ); + }); + + it('Timestamp.nanoseconds', function () { + const timestamp = new firestore.Timestamp(2000, 3000000); + timestampV9Deprecation( + // no corresponding method + () => {}, + () => timestamp.nanoseconds, + 'nanoseconds', + ); + }); + }); + }); }); diff --git a/packages/firestore/e2e/WriteBatch/commit.e2e.js b/packages/firestore/e2e/WriteBatch/commit.e2e.js index 43cc9d3278..67566e3ed5 100644 --- a/packages/firestore/e2e/WriteBatch/commit.e2e.js +++ b/packages/firestore/e2e/WriteBatch/commit.e2e.js @@ -254,17 +254,17 @@ describe('firestore.WriteBatch.commit()', function () { }); it('should set & commit', async function () { - const { getFirestore, writeBatch, doc, setDoc, getDoc, deleteDoc } = firestoreModular; + const { getFirestore, writeBatch, doc, getDoc, deleteDoc } = firestoreModular; const db = getFirestore(); const lRef = doc(db, `${COLLECTION}/LON`); const nycRef = doc(db, `${COLLECTION}/NYC`); const sfRef = doc(db, `${COLLECTION}/SF`); const batch = writeBatch(db); - - setDoc(batch, lRef, { name: 'London' }); - setDoc(batch, nycRef, { name: 'New York' }); - setDoc(batch, sfRef, { name: 'San Francisco' }); + // This is the correct way of setting batch for modular API + batch.set(lRef, { name: 'London' }); + batch.set(nycRef, { name: 'New York' }); + batch.set(sfRef, { name: 'San Francisco' }); await batch.commit(); diff --git a/packages/firestore/lib/FirestoreDocumentChange.js b/packages/firestore/lib/FirestoreDocumentChange.js index e2cb2e7c02..9aa66833b6 100644 --- a/packages/firestore/lib/FirestoreDocumentChange.js +++ b/packages/firestore/lib/FirestoreDocumentChange.js @@ -14,7 +14,7 @@ * limitations under the License. * */ - +import { createDeprecationProxy } from '@react-native-firebase/app/lib/common'; import FirestoreDocumentSnapshot from './FirestoreDocumentSnapshot'; const TYPE_MAP = { @@ -31,7 +31,9 @@ export default class FirestoreDocumentChange { } get doc() { - return new FirestoreDocumentSnapshot(this._firestore, this._nativeData.doc); + return createDeprecationProxy( + new FirestoreDocumentSnapshot(this._firestore, this._nativeData.doc), + ); } get newIndex() { diff --git a/packages/firestore/lib/FirestoreDocumentReference.js b/packages/firestore/lib/FirestoreDocumentReference.js index 0ffe715f95..094239acb9 100644 --- a/packages/firestore/lib/FirestoreDocumentReference.js +++ b/packages/firestore/lib/FirestoreDocumentReference.js @@ -15,7 +15,13 @@ * */ -import { isObject, isString, isUndefined } from '@react-native-firebase/app/lib/common'; +import { + isObject, + isString, + isUndefined, + createDeprecationProxy, + filterModularArgument, +} from '@react-native-firebase/app/lib/common'; import NativeError from '@react-native-firebase/app/lib/internal/NativeFirebaseError'; import { parseSetOptions, parseSnapshotArgs, parseUpdateArgs } from './utils'; import { buildNativeMap, provideDocumentReferenceClass } from './utils/serialize'; @@ -103,7 +109,7 @@ export default class FirestoreDocumentReference { return this._firestore.native .documentGet(this.path, options) - .then(data => new FirestoreDocumentSnapshot(this._firestore, data)); + .then(data => createDeprecationProxy(new FirestoreDocumentSnapshot(this._firestore, data))); } isEqual(other) { @@ -154,9 +160,8 @@ export default class FirestoreDocumentReference { if (event.body.error) { handleError(NativeError.fromEvent(event.body.error, 'firestore')); } else { - const documentSnapshot = new FirestoreDocumentSnapshot( - this._firestore, - event.body.snapshot, + const documentSnapshot = createDeprecationProxy( + new FirestoreDocumentSnapshot(this._firestore, event.body.snapshot), ); handleSuccess(documentSnapshot); } @@ -193,7 +198,8 @@ export default class FirestoreDocumentReference { } update(...args) { - if (args.length === 0) { + const updatedArgs = filterModularArgument(args); + if (updatedArgs.length === 0) { throw new Error( 'firebase.firestore().doc().update(*) expected at least 1 argument but was called with 0 arguments.', ); @@ -201,7 +207,7 @@ export default class FirestoreDocumentReference { let data; try { - data = parseUpdateArgs(args); + data = parseUpdateArgs(updatedArgs); } catch (e) { throw new Error(`firebase.firestore().doc().update(*) ${e.message}`); } diff --git a/packages/firestore/lib/FirestoreQuery.js b/packages/firestore/lib/FirestoreQuery.js index cd5f6664d6..a8de1042dd 100644 --- a/packages/firestore/lib/FirestoreQuery.js +++ b/packages/firestore/lib/FirestoreQuery.js @@ -21,6 +21,8 @@ import { isObject, isString, isUndefined, + filterModularArgument, + createDeprecationProxy, } from '@react-native-firebase/app/lib/common'; import NativeError from '@react-native-firebase/app/lib/internal/NativeFirebaseError'; import { FirestoreAggregateQuery } from './FirestoreAggregate'; @@ -133,33 +135,35 @@ export default class FirestoreQuery { } count() { - return new FirestoreAggregateQuery( - this._firestore, - this, - this._collectionPath, - this._modifiers, + return createDeprecationProxy( + new FirestoreAggregateQuery(this._firestore, this, this._collectionPath, this._modifiers), ); } countFromServer() { + // deprecation warning called in count() return this.count(); } endAt(docOrField, ...fields) { - return new FirestoreQuery( - this._firestore, - this._collectionPath, - this._handleQueryCursor('endAt', docOrField, fields), - this._queryName, + return createDeprecationProxy( + new FirestoreQuery( + this._firestore, + this._collectionPath, + this._handleQueryCursor('endAt', docOrField, filterModularArgument(fields)), + this._queryName, + ), ); } endBefore(docOrField, ...fields) { - return new FirestoreQuery( - this._firestore, - this._collectionPath, - this._handleQueryCursor('endBefore', docOrField, fields), - this._queryName, + return createDeprecationProxy( + new FirestoreQuery( + this._firestore, + this._collectionPath, + this._handleQueryCursor('endBefore', docOrField, filterModularArgument(fields)), + this._queryName, + ), ); } @@ -250,7 +254,9 @@ export default class FirestoreQuery { const modifiers = this._modifiers._copy().limit(limit); - return new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName); + return createDeprecationProxy( + new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName), + ); } limitToLast(limitToLast) { @@ -262,7 +268,9 @@ export default class FirestoreQuery { const modifiers = this._modifiers._copy().limitToLast(limitToLast); - return new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName); + return createDeprecationProxy( + new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName), + ); } onSnapshot(...args) { @@ -274,7 +282,7 @@ export default class FirestoreQuery { this._modifiers.validatelimitToLast(); try { - const options = parseSnapshotArgs(args); + const options = parseSnapshotArgs(filterModularArgument(args)); snapshotListenOptions = options.snapshotListenOptions; callback = options.callback; onNext = options.onNext; @@ -386,24 +394,30 @@ export default class FirestoreQuery { throw new Error(`firebase.firestore().collection().orderBy() ${e.message}`); } - return new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName); + return createDeprecationProxy( + new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName), + ); } startAfter(docOrField, ...fields) { - return new FirestoreQuery( - this._firestore, - this._collectionPath, - this._handleQueryCursor('startAfter', docOrField, fields), - this._queryName, + return createDeprecationProxy( + new FirestoreQuery( + this._firestore, + this._collectionPath, + this._handleQueryCursor('startAfter', docOrField, filterModularArgument(fields)), + this._queryName, + ), ); } startAt(docOrField, ...fields) { - return new FirestoreQuery( - this._firestore, - this._collectionPath, - this._handleQueryCursor('startAt', docOrField, fields), - this._queryName, + return createDeprecationProxy( + new FirestoreQuery( + this._firestore, + this._collectionPath, + this._handleQueryCursor('startAt', docOrField, filterModularArgument(fields)), + this._queryName, + ), ); } @@ -487,6 +501,8 @@ export default class FirestoreQuery { throw new Error(`firebase.firestore().collection().where() ${e.message}`); } - return new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName); + return createDeprecationProxy( + new FirestoreQuery(this._firestore, this._collectionPath, modifiers, this._queryName), + ); } } diff --git a/packages/firestore/lib/FirestoreQuerySnapshot.js b/packages/firestore/lib/FirestoreQuerySnapshot.js index 1ea59b9d0e..a730038ac7 100644 --- a/packages/firestore/lib/FirestoreQuerySnapshot.js +++ b/packages/firestore/lib/FirestoreQuerySnapshot.js @@ -20,6 +20,7 @@ import { isFunction, isObject, isUndefined, + createDeprecationProxy, } from '@react-native-firebase/app/lib/common'; import FirestoreDocumentChange from './FirestoreDocumentChange'; import FirestoreDocumentSnapshot from './FirestoreDocumentSnapshot'; @@ -31,7 +32,9 @@ export default class FirestoreQuerySnapshot { this._source = nativeData.source; this._excludesMetadataChanges = nativeData.excludesMetadataChanges; this._changes = nativeData.changes.map($ => new FirestoreDocumentChange(firestore, $)); - this._docs = nativeData.documents.map($ => new FirestoreDocumentSnapshot(firestore, $)); + this._docs = nativeData.documents.map($ => + createDeprecationProxy(new FirestoreDocumentSnapshot(firestore, $)), + ); this._metadata = new FirestoreSnapshotMetadata(nativeData.metadata); } diff --git a/packages/firestore/lib/FirestoreStatics.js b/packages/firestore/lib/FirestoreStatics.js index 8f98c714a8..19f921160b 100644 --- a/packages/firestore/lib/FirestoreStatics.js +++ b/packages/firestore/lib/FirestoreStatics.js @@ -15,6 +15,7 @@ * */ +import { createDeprecationProxy } from '@react-native-firebase/app/lib/common'; import { getReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; import FirestoreBlob from './FirestoreBlob'; import FirestoreFieldPath from './FirestoreFieldPath'; @@ -25,10 +26,10 @@ import { Filter } from './FirestoreFilter'; export default { Blob: FirestoreBlob, FieldPath: FirestoreFieldPath, - FieldValue: FirestoreFieldValue, + FieldValue: createDeprecationProxy(FirestoreFieldValue), GeoPoint: FirestoreGeoPoint, - Timestamp: FirestoreTimestamp, - Filter: Filter, + Timestamp: createDeprecationProxy(FirestoreTimestamp), + Filter: createDeprecationProxy(Filter), CACHE_SIZE_UNLIMITED: -1, diff --git a/packages/firestore/lib/FirestoreTransaction.js b/packages/firestore/lib/FirestoreTransaction.js index d6d710b974..f496ddb6b8 100644 --- a/packages/firestore/lib/FirestoreTransaction.js +++ b/packages/firestore/lib/FirestoreTransaction.js @@ -15,7 +15,7 @@ * */ -import { isObject } from '@react-native-firebase/app/lib/common'; +import { isObject, createDeprecationProxy } from '@react-native-firebase/app/lib/common'; import FirestoreDocumentReference from './FirestoreDocumentReference'; import FirestoreDocumentSnapshot from './FirestoreDocumentSnapshot'; import { parseSetOptions, parseUpdateArgs } from './utils'; @@ -52,7 +52,7 @@ export default class FirestoreTransaction { this._calledGetCount++; return this._firestore.native .transactionGetDocument(this._meta.id, documentRef.path) - .then(data => new FirestoreDocumentSnapshot(this._firestore, data)); + .then(data => createDeprecationProxy(new FirestoreDocumentSnapshot(this._firestore, data))); } /** diff --git a/packages/firestore/lib/index.js b/packages/firestore/lib/index.js index 08a5cfafea..e95cb29713 100644 --- a/packages/firestore/lib/index.js +++ b/packages/firestore/lib/index.js @@ -23,6 +23,7 @@ import { isString, isUndefined, isAndroid, + createDeprecationProxy, } from '@react-native-firebase/app/lib/common'; import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; import { @@ -175,7 +176,7 @@ class FirebaseFirestoreModule extends FirebaseModule { ); } - return new FirestoreCollectionReference(this, path); + return createDeprecationProxy(new FirestoreCollectionReference(this, path)); } collectionGroup(collectionId) { @@ -197,11 +198,13 @@ class FirebaseFirestoreModule extends FirebaseModule { ); } - return new FirestoreQuery( - this, - this._referencePath.child(collectionId), - new FirestoreQueryModifiers().asCollectionGroupQuery(), - undefined, + return createDeprecationProxy( + new FirestoreQuery( + this, + this._referencePath.child(collectionId), + new FirestoreQueryModifiers().asCollectionGroupQuery(), + undefined, + ), ); } @@ -224,7 +227,7 @@ class FirebaseFirestoreModule extends FirebaseModule { throw new Error("firebase.firestore().doc(*) 'documentPath' must point to a document."); } - return new FirestoreDocumentReference(this, path); + return createDeprecationProxy(new FirestoreDocumentReference(this, path)); } async enableNetwork() { @@ -377,7 +380,7 @@ class FirebaseFirestoreModule extends FirebaseModule { if (this._settings.persistence === false) { return null; } - return new FirestorePersistentCacheIndexManager(this); + return createDeprecationProxy(new FirestorePersistentCacheIndexManager(this)); } } diff --git a/packages/firestore/lib/modular/index.d.ts b/packages/firestore/lib/modular/index.d.ts index df51ad2ace..b91d31b5a4 100644 --- a/packages/firestore/lib/modular/index.d.ts +++ b/packages/firestore/lib/modular/index.d.ts @@ -96,6 +96,9 @@ export type WithFieldValue = ? { [K in keyof T]: WithFieldValue | FieldValue } : never); +export type EmulatorMockTokenOptions = ({ user_id: string } | { sub: string }) & + Partial; + /** * Returns the existing default {@link Firestore} instance that is associated with the * default {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new @@ -131,6 +134,24 @@ export function getFirestore(app?: FirebaseApp): Firestore; */ export declare function getFirestore(app?: FirebaseApp, databaseId?: string): Firestore; +/** + * Modify this instance to communicate with the Cloud Firestore emulator. + * + * @param firestore - A reference to the root `Firestore` instance. + * instance is associated with. + * @param host: emulator host (eg, 'localhost') + * @param port: emulator port (eg, 8080) + * @param options.mockUserToken - the mock auth token to use for unit testing + * @returns void. + */ +export declare function connectFirestoreEmulator( + firestore: Firestore, + host: string, + port: number, + options?: { + mockUserToken?: EmulatorMockTokenOptions | string; + }, +): void; /** * Gets a `DocumentReference` instance that refers to the document at the * specified absolute path. @@ -373,10 +394,19 @@ export function disableNetwork(firestore: Firestore): Promise; * Aimed primarily at clearing up any data cached from running tests. Needs to be executed before any database calls * are made. * + * Deprecated, please use `clearIndexedDbPersistence` instead. * @param firestore - A reference to the root `Firestore` instance. */ export function clearPersistence(firestore: Firestore): Promise; +/** + * Aimed primarily at clearing up any data cached from running tests. Needs to be executed before any database calls + * are made. + * + * @param firestore - A reference to the root `Firestore` instance. + */ +export function clearIndexedDbPersistence(firestore: Firestore): Promise; + /** * Terminates the provided {@link Firestore} instance. * @@ -503,6 +533,62 @@ interface AggregateSpec { [field: string]: AggregateFieldType; } +interface FirebaseIdToken { + // Always set to https://securetoken.google.com/PROJECT_ID + iss: string; + + // Always set to PROJECT_ID + aud: string; + + // The user's unique ID + sub: string; + + // The token issue time, in seconds since epoch + iat: number; + + // The token expiry time, normally 'iat' + 3600 + exp: number; + + // The user's unique ID. Must be equal to 'sub' + user_id: string; + + // The time the user authenticated, normally 'iat' + auth_time: number; + + // The sign in provider, only set when the provider is 'anonymous' + provider_id?: 'anonymous'; + + // The user's primary email + email?: string; + + // The user's email verification status + email_verified?: boolean; + + // The user's primary phone number + phone_number?: string; + + // The user's display name + name?: string; + + // The user's profile photo URL + picture?: string; + + // Information on all identities linked to this user + firebase: { + // The primary sign-in provider + sign_in_provider: FirebaseSignInProvider; + + // A map of providers to the user's list of unique identifiers from + // each provider + identities?: { [provider in FirebaseSignInProvider]?: string[] }; + }; + + // Custom claims set by the developer + [claim: string]: unknown; + + uid?: never; // Try to catch a common mistake of "uid" (should be "sub" instead). +} + /** * The union of all `AggregateField` types that are supported by Firestore. */ diff --git a/packages/firestore/lib/modular/index.js b/packages/firestore/lib/modular/index.js index 4242e983eb..e291587e48 100644 --- a/packages/firestore/lib/modular/index.js +++ b/packages/firestore/lib/modular/index.js @@ -21,6 +21,7 @@ import { fieldPathFromArgument, } from '../FirestoreAggregate'; import FirestoreQuery from '../FirestoreQuery'; +import { MODULAR_DEPRECATION_ARG } from '../../../app/lib/common'; /** * @param {FirebaseApp?} app @@ -53,7 +54,7 @@ export function doc(parent, path, ...pathSegments) { path = path + '/' + pathSegments.map(e => e.replace(/^\/|\/$/g, '')).join('/'); } - return parent.doc(path); + return parent.doc.call(parent, path, MODULAR_DEPRECATION_ARG); } /** @@ -67,7 +68,7 @@ export function collection(parent, path, ...pathSegments) { path = path + '/' + pathSegments.map(e => e.replace(/^\/|\/$/g, '')).join('/'); } - return parent.collection(path); + return parent.collection.call(parent, path, MODULAR_DEPRECATION_ARG); } /** @@ -76,7 +77,7 @@ export function collection(parent, path, ...pathSegments) { * @returns {Query} */ export function collectionGroup(firestore, collectionId) { - return firestore.collectionGroup(collectionId); + return firestore.collectionGroup.call(firestore, collectionId, MODULAR_DEPRECATION_ARG); } /** @@ -86,7 +87,7 @@ export function collectionGroup(firestore, collectionId) { * @returns {Promise} */ export function setDoc(reference, data, options) { - return reference.set(data, options); + return reference.set.call(reference, data, options, MODULAR_DEPRECATION_ARG); } /** @@ -99,18 +100,24 @@ export function setDoc(reference, data, options) { export function updateDoc(reference, fieldOrUpdateData, value, ...moreFieldsAndValues) { if (!fieldOrUpdateData) { // @ts-ignore - return reference.update(); + return reference.update.call(reference, MODULAR_DEPRECATION_ARG); } if (!value) { - return reference.update(fieldOrUpdateData); + return reference.update.call(reference, fieldOrUpdateData, MODULAR_DEPRECATION_ARG); } if (!moreFieldsAndValues || !Array.isArray(moreFieldsAndValues)) { - return reference.update(fieldOrUpdateData, value); + return reference.update.call(reference, fieldOrUpdateData, value, MODULAR_DEPRECATION_ARG); } - return reference.update(fieldOrUpdateData, value, ...moreFieldsAndValues); + return reference.update.call( + reference, + fieldOrUpdateData, + value, + ...moreFieldsAndValues, + MODULAR_DEPRECATION_ARG, + ); } /** @@ -119,7 +126,7 @@ export function updateDoc(reference, fieldOrUpdateData, value, ...moreFieldsAndV * @returns {Promise} */ export function addDoc(reference, data) { - return reference.add(data); + return reference.add.call(reference, data, MODULAR_DEPRECATION_ARG); } /** @@ -127,7 +134,7 @@ export function addDoc(reference, data) { * @returns {Promise} */ export function enableNetwork(firestore) { - return firestore.enableNetwork(); + return firestore.enableNetwork.call(firestore, MODULAR_DEPRECATION_ARG); } /** @@ -135,7 +142,7 @@ export function enableNetwork(firestore) { * @returns {Promise} */ export function disableNetwork(firestore) { - return firestore.disableNetwork(); + return firestore.disableNetwork.call(firestore, MODULAR_DEPRECATION_ARG); } /** @@ -143,15 +150,23 @@ export function disableNetwork(firestore) { * @returns {Promise} */ export function clearPersistence(firestore) { + // this will call deprecation warning as it isn't part of firebase-js-sdk API return firestore.clearPersistence(); } +/** + * @param {Firestore} firestore + * @returns {Promise} + */ +export function clearIndexedDbPersistence(firestore) { + return firestore.clearPersistence.call(firestore, MODULAR_DEPRECATION_ARG); +} /** * @param {Firestore} firestore * @returns {Promise} */ export function terminate(firestore) { - return firestore.terminate(); + return firestore.terminate.call(firestore, MODULAR_DEPRECATION_ARG); } /** @@ -159,7 +174,7 @@ export function terminate(firestore) { * @returns {Promise} */ export function waitForPendingWrites(firestore) { - return firestore.waitForPendingWrites(); + return firestore.waitForPendingWrites.call(firestore, MODULAR_DEPRECATION_ARG); } /** @@ -171,16 +186,20 @@ export function waitForPendingWrites(firestore) { export async function initializeFirestore(app, settings /* databaseId */) { // TODO(exaby73): implement 2nd database once it's supported const firestore = firebase.firestore(app); - await firestore.settings(settings); + await firestore.settings.call(firestore, settings, MODULAR_DEPRECATION_ARG); return firestore; } +export function connectFirestoreEmulator(firestore, host, port, options) { + return firestore.useEmulator.call(firestore, host, port, options, MODULAR_DEPRECATION_ARG); +} + /** * @param {import('./').LogLevel} logLevel * @returns {void} */ export function setLogLevel(logLevel) { - return firebase.firestore.setLogLevel(logLevel); + return firebase.firestore.setLogLevel.call(null, logLevel, MODULAR_DEPRECATION_ARG); } /** @@ -189,7 +208,7 @@ export function setLogLevel(logLevel) { * @returns {Promise} */ export function runTransaction(firestore, updateFunction) { - return firestore.runTransaction(updateFunction); + return firestore.runTransaction.call(firestore, updateFunction, MODULAR_DEPRECATION_ARG); } /** @@ -197,7 +216,7 @@ export function runTransaction(firestore, updateFunction) { * @returns {Promise} */ export function getCountFromServer(query) { - return query.count().get(); + return query.count.call(query, MODULAR_DEPRECATION_ARG).get(); } export function getAggregateFromServer(query, aggregateSpec) { @@ -291,7 +310,7 @@ export function count() { * @returns {import('.').LoadBundleTask} */ export function loadBundle(firestore, bundleData) { - return firestore.loadBundle(bundleData); + return firestore.loadBundle.call(firestore, bundleData, MODULAR_DEPRECATION_ARG); } /** @@ -300,7 +319,7 @@ export function loadBundle(firestore, bundleData) { * @returns {Query} */ export function namedQuery(firestore, name) { - return firestore.namedQuery(name); + return firestore.namedQuery.call(firestore, name, MODULAR_DEPRECATION_ARG); } /** @@ -308,7 +327,7 @@ export function namedQuery(firestore, name) { * @returns {FirebaseFirestoreTypes.WriteBatch} */ export function writeBatch(firestore) { - return firestore.batch(); + return firestore.batch.call(firestore, MODULAR_DEPRECATION_ARG); } /** @@ -320,7 +339,7 @@ export function writeBatch(firestore) { * @returns {PersistentCacheIndexManager | null} */ export function getPersistentCacheIndexManager(firestore) { - return firestore.persistentCacheIndexManager(); + return firestore.persistentCacheIndexManager.call(firestore, MODULAR_DEPRECATION_ARG); } /** @@ -331,7 +350,7 @@ export function getPersistentCacheIndexManager(firestore) { * @returns {Promise} */ export function getDoc(reference) { - return reference.get({ source: 'default' }); + return reference.get.call(reference, { source: 'default' }, MODULAR_DEPRECATION_ARG); } /** @@ -182,7 +182,7 @@ export function getDoc(reference) { * @returns {Promise} */ export function getDocFromCache(reference) { - return reference.get({ source: 'cache' }); + return reference.get.call(reference, { source: 'cache' }, MODULAR_DEPRECATION_ARG); } /** @@ -190,7 +190,7 @@ export function getDocFromCache(reference) { * @returns {Promise} */ export function getDocFromServer(reference) { - return reference.get({ source: 'server' }); + return reference.get.call(reference, { source: 'server' }, MODULAR_DEPRECATION_ARG); } /** @@ -198,7 +198,7 @@ export function getDocFromServer(reference) { * @returns {Promise} */ export function getDocs(query) { - return query.get({ source: 'default' }); + return query.get.call(query, { source: 'default' }, MODULAR_DEPRECATION_ARG); } /** @@ -206,7 +206,7 @@ export function getDocs(query) { * @returns {Promise} */ export function getDocsFromCache(query) { - return query.get({ source: 'cache' }); + return query.get.call(query, { source: 'cache' }, MODULAR_DEPRECATION_ARG); } /** @@ -214,7 +214,7 @@ export function getDocsFromCache(query) { * @returns {Promise} */ export function getDocsFromServer(query) { - return query.get({ source: 'server' }); + return query.get.call(query, { source: 'server' }, MODULAR_DEPRECATION_ARG); } /** @@ -222,5 +222,5 @@ export function getDocsFromServer(query) { * @returns {Promise} */ export function deleteDoc(reference) { - return reference.delete(); + return reference.delete.call(reference, MODULAR_DEPRECATION_ARG); } diff --git a/packages/firestore/lib/modular/snapshot.js b/packages/firestore/lib/modular/snapshot.js index 3293ac0980..50fb006ed7 100644 --- a/packages/firestore/lib/modular/snapshot.js +++ b/packages/firestore/lib/modular/snapshot.js @@ -4,11 +4,13 @@ * @typedef {import('snapshot').Unsubscribe} Unsubscribe */ +import { MODULAR_DEPRECATION_ARG } from '../../../app/lib/common'; + /** * @param {Query | DocumentReference} reference * @param {unknown} args * @returns {Promise} */ export function onSnapshot(reference, ...args) { - return reference.onSnapshot(...args); + return reference.onSnapshot.call(reference, ...args, MODULAR_DEPRECATION_ARG); }