From 0d348392ebd83ec24bf3e5dce450f4f6ad8d0b47 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 14 Feb 2018 10:05:28 +0000 Subject: [PATCH] [types] More temp renames for capitalisation --- .../database/{query.js => query-temp.js} | 2 +- lib/modules/database/reference.js | 888 ------------------ 2 files changed, 1 insertion(+), 889 deletions(-) rename lib/modules/database/{query.js => query-temp.js} (97%) delete mode 100644 lib/modules/database/reference.js diff --git a/lib/modules/database/query.js b/lib/modules/database/query-temp.js similarity index 97% rename from lib/modules/database/query.js rename to lib/modules/database/query-temp.js index 974dba85..b58388be 100644 --- a/lib/modules/database/query.js +++ b/lib/modules/database/query-temp.js @@ -5,7 +5,7 @@ import { objectToUniqueId } from '../../utils'; import type { DatabaseModifier } from '../../types'; -import type Reference from './reference'; +import type Reference from './Reference'; // todo doc methods diff --git a/lib/modules/database/reference.js b/lib/modules/database/reference.js deleted file mode 100644 index b91e6f13..00000000 --- a/lib/modules/database/reference.js +++ /dev/null @@ -1,888 +0,0 @@ -/** - * @flow - * Database Reference representation wrapper - */ -import Query from './query'; -import Snapshot from './snapshot'; -import Disconnect from './disconnect'; -import { getLogger } from '../../utils/log'; -import { getNativeModule } from '../../utils/native'; -import ReferenceBase from '../../utils/ReferenceBase'; - -import { - promiseOrCallback, - isFunction, - isObject, - isString, - tryJSONParse, - tryJSONStringify, - generatePushID, -} from '../../utils'; - -import SyncTree from '../../utils/SyncTree'; - -import type Database from './'; -import type { DatabaseModifier, FirebaseError } from '../../types'; - -// track all event registrations by path -let listeners = 0; - -/** - * Enum for event types - * @readonly - * @enum {String} - */ -const ReferenceEventTypes = { - value: 'value', - child_added: 'child_added', - child_removed: 'child_removed', - child_changed: 'child_changed', - child_moved: 'child_moved', -}; - -type DatabaseListener = { - listenerId: number, - eventName: string, - successCallback: Function, - failureCallback?: Function, -}; - -/** - * @typedef {String} ReferenceLocation - Path to location in the database, relative - * to the root reference. Consists of a path where segments are separated by a - * forward slash (/) and ends in a ReferenceKey - except the root location, which - * has no ReferenceKey. - * - * @example - * // root reference location: '/' - * // non-root reference: '/path/to/referenceKey' - */ - -/** - * @typedef {String} ReferenceKey - Identifier for each location that is unique to that - * location, within the scope of its parent. The last part of a ReferenceLocation. - */ - -/** - * Represents a specific location in your Database that can be used for - * reading or writing data. - * - * You can reference the root using firebase.database().ref() or a child location - * by calling firebase.database().ref("child/path"). - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference - * @class Reference - * @extends ReferenceBase - */ -export default class Reference extends ReferenceBase { - _database: Database; - _promise: ?Promise<*>; - _query: Query; - _refListeners: { [listenerId: number]: DatabaseListener }; - - constructor( - database: Database, - path: string, - existingModifiers?: Array - ) { - super(path); - this._promise = null; - this._refListeners = {}; - this._database = database; - this._query = new Query(this, existingModifiers); - getLogger(database).debug('Created new Reference', this._getRefKey()); - } - - /** - * By calling `keepSynced(true)` on a location, the data for that location will - * automatically be downloaded and kept in sync, even when no listeners are - * attached for that location. Additionally, while a location is kept synced, - * it will not be evicted from the persistent disk cache. - * - * @link https://firebase.google.com/docs/reference/android/com/google/firebase/database/Query.html#keepSynced(boolean) - * @param bool - * @returns {*} - */ - keepSynced(bool: boolean): Promise { - return getNativeModule(this._database).keepSynced( - this._getRefKey(), - this.path, - this._query.getModifiers(), - bool - ); - } - - /** - * Writes data to this Database location. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#set - * @param value - * @param onComplete - * @returns {Promise} - */ - set(value: any, onComplete?: Function): Promise { - return promiseOrCallback( - getNativeModule(this._database).set( - this.path, - this._serializeAnyType(value) - ), - onComplete - ); - } - - /** - * Sets a priority for the data at this Database location. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#setPriority - * @param priority - * @param onComplete - * @returns {Promise} - */ - setPriority( - priority: string | number | null, - onComplete?: Function - ): Promise { - const _priority = this._serializeAnyType(priority); - - return promiseOrCallback( - getNativeModule(this._database).setPriority(this.path, _priority), - onComplete - ); - } - - /** - * Writes data the Database location. Like set() but also specifies the priority for that data. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#setWithPriority - * @param value - * @param priority - * @param onComplete - * @returns {Promise} - */ - setWithPriority( - value: any, - priority: string | number | null, - onComplete?: Function - ): Promise { - const _value = this._serializeAnyType(value); - const _priority = this._serializeAnyType(priority); - - return promiseOrCallback( - getNativeModule(this._database).setWithPriority( - this.path, - _value, - _priority - ), - onComplete - ); - } - - /** - * Writes multiple values to the Database at once. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#update - * @param val - * @param onComplete - * @returns {Promise} - */ - update(val: Object, onComplete?: Function): Promise { - const value = this._serializeObject(val); - - return promiseOrCallback( - getNativeModule(this._database).update(this.path, value), - onComplete - ); - } - - /** - * Removes the data at this Database location. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#remove - * @param onComplete - * @return {Promise} - */ - remove(onComplete?: Function): Promise { - return promiseOrCallback( - getNativeModule(this._database).remove(this.path), - onComplete - ); - } - - /** - * Atomically modifies the data at this location. - * - * @link https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction - * @param transactionUpdate - * @param onComplete - * @param applyLocally - */ - transaction( - transactionUpdate: Function, - onComplete: (error: ?Error, committed: boolean, snapshot: ?Snapshot) => *, - applyLocally: boolean = false - ) { - if (!isFunction(transactionUpdate)) { - return Promise.reject( - new Error('Missing transactionUpdate function argument.') - ); - } - - return new Promise((resolve, reject) => { - const onCompleteWrapper = (error, committed, snapshotData) => { - if (isFunction(onComplete)) { - if (error) { - onComplete(error, committed, null); - } else { - onComplete(null, committed, new Snapshot(this, snapshotData)); - } - } - - if (error) return reject(error); - return resolve({ - committed, - snapshot: new Snapshot(this, snapshotData), - }); - }; - - // start the transaction natively - this._database._transactionHandler.add( - this, - transactionUpdate, - onCompleteWrapper, - applyLocally - ); - }); - } - - /** - * - * @param eventName - * @param successCallback - * @param cancelOrContext - * @param context - * @returns {Promise.} - */ - once( - eventName: string = 'value', - successCallback: (snapshot: Object) => void, - cancelOrContext: (error: FirebaseError) => void, - context?: Object - ) { - return getNativeModule(this._database) - .once(this._getRefKey(), this.path, this._query.getModifiers(), eventName) - .then(({ snapshot }) => { - const _snapshot = new Snapshot(this, snapshot); - - if (isFunction(successCallback)) { - if (isObject(cancelOrContext)) - successCallback.bind(cancelOrContext)(_snapshot); - if (context && isObject(context)) - successCallback.bind(context)(_snapshot); - successCallback(_snapshot); - } - - return _snapshot; - }) - .catch(error => { - if (isFunction(cancelOrContext)) return cancelOrContext(error); - return error; - }); - } - - /** - * - * @param value - * @param onComplete - * @returns {*} - */ - push(value: any, onComplete?: Function): Reference | Promise { - if (value === null || value === undefined) { - return new Reference( - this._database, - `${this.path}/${generatePushID(this._database._serverTimeOffset)}` - ); - } - - const newRef = new Reference( - this._database, - `${this.path}/${generatePushID(this._database._serverTimeOffset)}` - ); - const promise = newRef.set(value); - - // if callback provided then internally call the set promise with value - if (isFunction(onComplete)) { - return ( - promise - // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 - .then(() => onComplete(null, newRef)) - // $FlowBug: Reports that onComplete can change to null despite the null check: https://github.com/facebook/flow/issues/1655 - .catch(error => onComplete(error, null)) - ); - } - - // otherwise attach promise to 'thenable' reference and return the - // new reference - newRef._setThenable(promise); - return newRef; - } - - /** - * MODIFIERS - */ - - /** - * - * @returns {Reference} - */ - orderByKey(): Reference { - return this.orderBy('orderByKey'); - } - - /** - * - * @returns {Reference} - */ - orderByPriority(): Reference { - return this.orderBy('orderByPriority'); - } - - /** - * - * @returns {Reference} - */ - orderByValue(): Reference { - return this.orderBy('orderByValue'); - } - - /** - * - * @param key - * @returns {Reference} - */ - orderByChild(key: string): Reference { - return this.orderBy('orderByChild', key); - } - - /** - * - * @param name - * @param key - * @returns {Reference} - */ - orderBy(name: string, key?: string): Reference { - const newRef = new Reference( - this._database, - this.path, - this._query.getModifiers() - ); - newRef._query.orderBy(name, key); - return newRef; - } - - /** - * LIMITS - */ - - /** - * - * @param limit - * @returns {Reference} - */ - limitToLast(limit: number): Reference { - return this.limit('limitToLast', limit); - } - - /** - * - * @param limit - * @returns {Reference} - */ - limitToFirst(limit: number): Reference { - return this.limit('limitToFirst', limit); - } - - /** - * - * @param name - * @param limit - * @returns {Reference} - */ - limit(name: string, limit: number): Reference { - const newRef = new Reference( - this._database, - this.path, - this._query.getModifiers() - ); - newRef._query.limit(name, limit); - return newRef; - } - - /** - * FILTERS - */ - - /** - * - * @param value - * @param key - * @returns {Reference} - */ - equalTo(value: any, key?: string): Reference { - return this.filter('equalTo', value, key); - } - - /** - * - * @param value - * @param key - * @returns {Reference} - */ - endAt(value: any, key?: string): Reference { - return this.filter('endAt', value, key); - } - - /** - * - * @param value - * @param key - * @returns {Reference} - */ - startAt(value: any, key?: string): Reference { - return this.filter('startAt', value, key); - } - - /** - * - * @param name - * @param value - * @param key - * @returns {Reference} - */ - filter(name: string, value: any, key?: string): Reference { - const newRef = new Reference( - this._database, - this.path, - this._query.getModifiers() - ); - newRef._query.filter(name, value, key); - return newRef; - } - - /** - * - * @returns {Disconnect} - */ - onDisconnect(): Disconnect { - return new Disconnect(this); - } - - /** - * Creates a Reference to a child of the current Reference, using a relative path. - * No validation is performed on the path to ensure it has a valid format. - * @param {String} path relative to current ref's location - * @returns {!Reference} A new Reference to the path provided, relative to the current - * Reference - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#child} - */ - child(path: string): Reference { - return new Reference(this._database, `${this.path}/${path}`); - } - - /** - * Return the ref as a path string - * @returns {string} - */ - toString(): string { - return `${this._database.app.options.databaseURL}/${this.path}`; - } - - /** - * Returns whether another Reference represent the same location and are from the - * same instance of firebase.app.App - multiple firebase apps not currently supported. - * @param {Reference} otherRef - Other reference to compare to this one - * @return {Boolean} Whether otherReference is equal to this one - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#isEqual} - */ - isEqual(otherRef: Reference): boolean { - return ( - !!otherRef && - otherRef.constructor === Reference && - otherRef.key === this.key && - this._query.queryIdentifier() === otherRef._query.queryIdentifier() - ); - } - - /** - * GETTERS - */ - - /** - * The parent location of a Reference, or null for the root Reference. - * @type {Reference} - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#parent} - */ - get parent(): Reference | null { - if (this.path === '/') return null; - return new Reference( - this._database, - this.path.substring(0, this.path.lastIndexOf('/')) - ); - } - - /** - * A reference to itself - * @type {!Reference} - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#ref} - */ - get ref(): Reference { - return this; - } - - /** - * Reference to the root of the database: '/' - * @type {!Reference} - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#root} - */ - get root(): Reference { - return new Reference(this._database, '/'); - } - - /** - * Access then method of promise if set - * @return {*} - */ - then(fnResolve: any => any, fnReject: any => any) { - if (isFunction(fnResolve) && this._promise && this._promise.then) { - return this._promise.then.bind(this._promise)( - result => { - this._promise = null; - return fnResolve(result); - }, - possibleErr => { - this._promise = null; - - if (isFunction(fnReject)) { - return fnReject(possibleErr); - } - - throw possibleErr; - } - ); - } - - throw new Error("Cannot read property 'then' of undefined."); - } - - /** - * Access catch method of promise if set - * @return {*} - */ - catch(fnReject: any => any) { - if (isFunction(fnReject) && this._promise && this._promise.catch) { - return this._promise.catch.bind(this._promise)(possibleErr => { - this._promise = null; - return fnReject(possibleErr); - }); - } - - throw new Error("Cannot read property 'catch' of undefined."); - } - - /** - * INTERNALS - */ - - /** - * Generate a unique registration key. - * - * @return {string} - */ - _getRegistrationKey(eventType: string): string { - return `$${this._database.app.name}$/${ - this.path - }$${this._query.queryIdentifier()}$${listeners}$${eventType}`; - } - - /** - * Generate a string that uniquely identifies this - * combination of path and query modifiers - * - * @return {string} - * @private - */ - _getRefKey(): string { - return `$${this._database.app.name}$/${ - this.path - }$${this._query.queryIdentifier()}`; - } - - /** - * Set the promise this 'thenable' reference relates to - * @param promise - * @private - */ - _setThenable(promise: Promise<*>) { - this._promise = promise; - } - - /** - * - * @param obj - * @returns {Object} - * @private - */ - _serializeObject(obj: Object) { - if (!isObject(obj)) return obj; - - // json stringify then parse it calls toString on Objects / Classes - // that support it i.e new Date() becomes a ISO string. - return tryJSONParse(tryJSONStringify(obj)); - } - - /** - * - * @param value - * @returns {*} - * @private - */ - _serializeAnyType(value: any) { - if (isObject(value)) { - return { - type: 'object', - value: this._serializeObject(value), - }; - } - - return { - type: typeof value, - value, - }; - } - - /** - * Register a listener for data changes at the current ref's location. - * The primary method of reading data from a Database. - * - * Listeners can be unbound using {@link off}. - * - * Event Types: - * - * - value: {@link callback}. - * - child_added: {@link callback} - * - child_removed: {@link callback} - * - child_changed: {@link callback} - * - child_moved: {@link callback} - * - * @param {ReferenceEventType} eventType - Type of event to attach a callback for. - * @param {ReferenceEventCallback} callback - Function that will be called - * when the event occurs with the new data. - * @param {cancelCallbackOrContext=} cancelCallbackOrContext - Optional callback that is called - * if the event subscription fails. {@link cancelCallbackOrContext} - * @param {*=} context - Optional object to bind the callbacks to when calling them. - * @returns {ReferenceEventCallback} callback function, unmodified (unbound), for - * convenience if you want to pass an inline function to on() and store it later for - * removing using off(). - * - * {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on} - */ - on( - eventType: string, - callback: Snapshot => any, - cancelCallbackOrContext?: Object => any | Object, - context?: Object - ): Function { - if (!eventType) { - throw new Error( - 'Query.on failed: Function called with 0 arguments. Expects at least 2.' - ); - } - - if (!isString(eventType) || !ReferenceEventTypes[eventType]) { - throw new Error( - `Query.on failed: First argument must be a valid string event type: "${Object.keys( - ReferenceEventTypes - ).join(', ')}"` - ); - } - - if (!callback) { - throw new Error( - 'Query.on failed: Function called with 1 argument. Expects at least 2.' - ); - } - - if (!isFunction(callback)) { - throw new Error( - 'Query.on failed: Second argument must be a valid function.' - ); - } - - if ( - cancelCallbackOrContext && - !isFunction(cancelCallbackOrContext) && - !isObject(context) && - !isObject(cancelCallbackOrContext) - ) { - throw new Error( - 'Query.on failed: Function called with 3 arguments, but third optional argument `cancelCallbackOrContext` was not a function.' - ); - } - - if ( - cancelCallbackOrContext && - !isFunction(cancelCallbackOrContext) && - context - ) { - throw new Error( - 'Query.on failed: Function called with 4 arguments, but third optional argument `cancelCallbackOrContext` was not a function.' - ); - } - - const eventRegistrationKey = this._getRegistrationKey(eventType); - const registrationCancellationKey = `${eventRegistrationKey}$cancelled`; - const _context = - cancelCallbackOrContext && !isFunction(cancelCallbackOrContext) - ? cancelCallbackOrContext - : context; - const registrationObj = { - eventType, - ref: this, - path: this.path, - key: this._getRefKey(), - appName: this._database.app.name, - eventRegistrationKey, - }; - - SyncTree.addRegistration({ - ...registrationObj, - listener: _context ? callback.bind(_context) : callback, - }); - - if (cancelCallbackOrContext && isFunction(cancelCallbackOrContext)) { - // cancellations have their own separate registration - // as these are one off events, and they're not guaranteed - // to occur either, only happens on failure to register on native - SyncTree.addRegistration({ - ref: this, - once: true, - path: this.path, - key: this._getRefKey(), - appName: this._database.app.name, - eventType: `${eventType}$cancelled`, - eventRegistrationKey: registrationCancellationKey, - listener: _context - ? cancelCallbackOrContext.bind(_context) - : cancelCallbackOrContext, - }); - } - - // initialise the native listener if not already listening - getNativeModule(this._database).on({ - eventType, - path: this.path, - key: this._getRefKey(), - appName: this._database.app.name, - modifiers: this._query.getModifiers(), - hasCancellationCallback: isFunction(cancelCallbackOrContext), - registration: { - eventRegistrationKey, - key: registrationObj.key, - registrationCancellationKey, - }, - }); - - // increment number of listeners - just s short way of making - // every registration unique per .on() call - listeners += 1; - - // return original unbound successCallback for - // the purposes of calling .off(eventType, callback) at a later date - return callback; - } - - /** - * Detaches a callback previously attached with on(). - * - * Detach a callback previously attached with on(). Note that if on() was called - * multiple times with the same eventType and callback, the callback will be called - * multiple times for each event, and off() must be called multiple times to - * remove the callback. Calling off() on a parent listener will not automatically - * remove listeners registered on child nodes, off() must also be called on any - * child listeners to remove the callback. - * - * If a callback is not specified, all callbacks for the specified eventType will be removed. - * Similarly, if no eventType or callback is specified, all callbacks for the Reference will be removed. - * @param eventType - * @param originalCallback - */ - off(eventType?: string = '', originalCallback?: () => any) { - if (!arguments.length) { - // Firebase Docs: - // if no eventType or callback is specified, all callbacks for the Reference will be removed. - return SyncTree.removeListenersForRegistrations( - SyncTree.getRegistrationsByPath(this.path) - ); - } - - /* - * VALIDATE ARGS - */ - if ( - eventType && - (!isString(eventType) || !ReferenceEventTypes[eventType]) - ) { - throw new Error( - `Query.off failed: First argument must be a valid string event type: "${Object.keys( - ReferenceEventTypes - ).join(', ')}"` - ); - } - - if (originalCallback && !isFunction(originalCallback)) { - throw new Error( - 'Query.off failed: Function called with 2 arguments, but second optional argument was not a function.' - ); - } - - // Firebase Docs: - // Note that if on() was called - // multiple times with the same eventType and callback, the callback will be called - // multiple times for each event, and off() must be called multiple times to - // remove the callback. - // Remove only a single registration - if (eventType && originalCallback) { - const registration = SyncTree.getOneByPathEventListener( - this.path, - eventType, - originalCallback - ); - if (!registration) return []; - - // remove the paired cancellation registration if any exist - SyncTree.removeListenersForRegistrations([`${registration}$cancelled`]); - - // remove only the first registration to match firebase web sdk - // call multiple times to remove multiple registrations - return SyncTree.removeListenerRegistrations(originalCallback, [ - registration, - ]); - } - - // Firebase Docs: - // If a callback is not specified, all callbacks for the specified eventType will be removed. - const registrations = SyncTree.getRegistrationsByPathEvent( - this.path, - eventType - ); - - SyncTree.removeListenersForRegistrations( - SyncTree.getRegistrationsByPathEvent(this.path, `${eventType}$cancelled`) - ); - - return SyncTree.removeListenersForRegistrations(registrations); - } -}