RNFirebase - database - js
This commit is contained in:
parent
4d9255c3e9
commit
6a9b54124a
229
lib/modules/database/index.js
Normal file
229
lib/modules/database/index.js
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
* Database representation wrapper
|
||||||
|
*/
|
||||||
|
import { NativeModules, NativeEventEmitter } from 'react-native';
|
||||||
|
|
||||||
|
import { Base } from './../base';
|
||||||
|
import Snapshot from './snapshot.js';
|
||||||
|
import Reference from './reference.js';
|
||||||
|
import { promisify } from './../../utils';
|
||||||
|
|
||||||
|
const FirebaseDatabase = NativeModules.FirebaseDatabase;
|
||||||
|
const FirebaseDatabaseEvt = new NativeEventEmitter(FirebaseDatabase);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Database
|
||||||
|
*/
|
||||||
|
export default class Database extends Base {
|
||||||
|
constructor(firebase: Object, options: Object = {}) {
|
||||||
|
super(firebase, options);
|
||||||
|
this.subscriptions = {};
|
||||||
|
this.errorSubscriptions = {};
|
||||||
|
this.serverTimeOffset = 0;
|
||||||
|
this.persistenceEnabled = false;
|
||||||
|
this.namespace = 'firebase:database';
|
||||||
|
|
||||||
|
if (firebase.options.persistence === true) {
|
||||||
|
this._setPersistence(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.successListener = FirebaseDatabaseEvt.addListener(
|
||||||
|
'database_event',
|
||||||
|
event => this._handleDatabaseEvent(event)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.errorListener = FirebaseDatabaseEvt.addListener(
|
||||||
|
'database_error',
|
||||||
|
err => this._handleDatabaseError(err)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.offsetRef = this.ref('.info/serverTimeOffset');
|
||||||
|
|
||||||
|
this.offsetRef.on('value', (snapshot) => {
|
||||||
|
this.serverTimeOffset = snapshot.val() || this.serverTimeOffset;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.log.debug('Created new Database instance', this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://firebase.google.com/docs/reference/js/firebase.database.ServerValue
|
||||||
|
* @returns {{TIMESTAMP: (*|{[.sv]: string})}}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
get ServerValue(): Object {
|
||||||
|
return {
|
||||||
|
TIMESTAMP: FirebaseDatabase.serverValueTimestamp || { '.sv': 'timestamp' },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new firebase reference instance
|
||||||
|
* @param path
|
||||||
|
* @returns {Reference}
|
||||||
|
*/
|
||||||
|
ref(path: string) {
|
||||||
|
return new Reference(this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @param modifiersString
|
||||||
|
* @param modifiers
|
||||||
|
* @param eventName
|
||||||
|
* @param cb
|
||||||
|
* @param errorCb
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
on(path: string, modifiersString: string, modifiers: Array<string>, eventName: string, cb: () => void, errorCb: () => void) {
|
||||||
|
const handle = this._handle(path, modifiersString);
|
||||||
|
this.log.debug('adding on listener', handle);
|
||||||
|
|
||||||
|
if (!this.subscriptions[handle]) this.subscriptions[handle] = {};
|
||||||
|
if (!this.subscriptions[handle][eventName]) this.subscriptions[handle][eventName] = [];
|
||||||
|
this.subscriptions[handle][eventName].push(cb);
|
||||||
|
if (errorCb) {
|
||||||
|
if (!this.errorSubscriptions[handle]) this.errorSubscriptions[handle] = [];
|
||||||
|
this.errorSubscriptions[handle].push(errorCb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promisify('on', FirebaseDatabase)(path, modifiersString, modifiers, eventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @param modifiersString
|
||||||
|
* @param eventName
|
||||||
|
* @param origCB
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
off(path: string, modifiersString: string, eventName?: string, origCB?: () => void) {
|
||||||
|
const handle = this._handle(path, modifiersString);
|
||||||
|
this.log.debug('off() : ', handle, eventName);
|
||||||
|
|
||||||
|
if (!this.subscriptions[handle] || (eventName && !this.subscriptions[handle][eventName])) {
|
||||||
|
this.log.warn('off() called, but not currently listening at that location (bad path)', handle, eventName);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventName && origCB) {
|
||||||
|
const i = this.subscriptions[handle][eventName].indexOf(origCB);
|
||||||
|
|
||||||
|
if (i === -1) {
|
||||||
|
this.log.warn('off() called, but the callback specified is not listening at that location (bad path)', handle, eventName);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subscriptions[handle][eventName].splice(i, 1);
|
||||||
|
if (this.subscriptions[handle][eventName].length > 0) return Promise.resolve();
|
||||||
|
} else if (eventName) {
|
||||||
|
this.subscriptions[handle][eventName] = [];
|
||||||
|
} else {
|
||||||
|
this.subscriptions[handle] = {};
|
||||||
|
}
|
||||||
|
this.errorSubscriptions[handle] = [];
|
||||||
|
return promisify('off', FirebaseDatabase)(path, modifiersString, eventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all event handlers and their native subscriptions
|
||||||
|
* @returns {Promise.<*>}
|
||||||
|
*/
|
||||||
|
cleanup() {
|
||||||
|
const promises = [];
|
||||||
|
Object.keys(this.subscriptions).forEach((handle) => {
|
||||||
|
Object.keys(this.subscriptions[handle]).forEach((eventName) => {
|
||||||
|
const separator = handle.indexOf('|');
|
||||||
|
const path = handle.substring(0, separator);
|
||||||
|
const modifiersString = handle.substring(separator + 1);
|
||||||
|
promises.push(this.off(path, modifiersString, eventName));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
goOnline() {
|
||||||
|
FirebaseDatabase.goOnline();
|
||||||
|
}
|
||||||
|
|
||||||
|
goOffline() {
|
||||||
|
FirebaseDatabase.goOffline();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNALS
|
||||||
|
*/
|
||||||
|
_getServerTime() {
|
||||||
|
return new Date().getTime() + this.serverTimeOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enabled / disable database persistence
|
||||||
|
* @param enable
|
||||||
|
* @returns {*}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_setPersistence(enable: boolean = true) {
|
||||||
|
if (this.persistenceEnabled !== enable) {
|
||||||
|
this.log.debug(`${enable ? 'Enabling' : 'Disabling'} persistence`);
|
||||||
|
this.persistenceEnabled = enable;
|
||||||
|
return this.whenReady(promisify('enablePersistence', FirebaseDatabase)(enable));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.whenReady(Promise.resolve({ status: 'Already enabled' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @param modifiersString
|
||||||
|
* @returns {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_handle(path: string = '', modifiersString: string = '') {
|
||||||
|
return `${path}|${modifiersString}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_handleDatabaseEvent(event: Object) {
|
||||||
|
const body = event.body || {};
|
||||||
|
const { path, modifiersString, eventName, snapshot } = body;
|
||||||
|
const handle = this._handle(path, modifiersString);
|
||||||
|
|
||||||
|
this.log.debug('_handleDatabaseEvent: ', handle, eventName, snapshot && snapshot.key);
|
||||||
|
|
||||||
|
if (this.subscriptions[handle] && this.subscriptions[handle][eventName]) {
|
||||||
|
this.subscriptions[handle][eventName].forEach((cb) => {
|
||||||
|
cb(new Snapshot(new Reference(this, path, modifiersString.split('|')), snapshot), body);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
FirebaseDatabase.off(path, modifiersString, eventName, () => {
|
||||||
|
this.log.debug('_handleDatabaseEvent: No JS listener registered, removed native listener', handle, eventName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param err
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_handleDatabaseError(err: Object) {
|
||||||
|
const body = err.body || {};
|
||||||
|
const { path, modifiersString, eventName, msg } = body;
|
||||||
|
const handle = this._handle(path, modifiersString);
|
||||||
|
|
||||||
|
this.log.debug('_handleDatabaseError ->', handle, eventName, err);
|
||||||
|
|
||||||
|
if (this.errorSubscriptions[handle]) this.errorSubscriptions[handle].forEach((cb) => cb(new Error(msg)));
|
||||||
|
}
|
||||||
|
}
|
55
lib/modules/database/query.js
Normal file
55
lib/modules/database/query.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ReferenceBase } from './../base';
|
||||||
|
import Reference from './reference.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Query
|
||||||
|
*/
|
||||||
|
export default class Query extends ReferenceBase {
|
||||||
|
static ref: Reference;
|
||||||
|
|
||||||
|
static modifiers: Array<string>;
|
||||||
|
|
||||||
|
ref: Reference;
|
||||||
|
|
||||||
|
constructor(ref: Reference, path: string, existingModifiers?: Array<string>) {
|
||||||
|
super(ref.db, path);
|
||||||
|
this.log.debug('creating Query ', path, existingModifiers);
|
||||||
|
this.ref = ref;
|
||||||
|
this.modifiers = existingModifiers ? [...existingModifiers] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
setOrderBy(name: string, key?: string) {
|
||||||
|
if (key) {
|
||||||
|
this.modifiers.push(`${name}:${key}`);
|
||||||
|
} else {
|
||||||
|
this.modifiers.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLimit(name: string, limit: number) {
|
||||||
|
this.modifiers.push(`${name}:${limit}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilter(name: string, value: any, key?:string) {
|
||||||
|
if (key) {
|
||||||
|
this.modifiers.push(`${name}:${value}:${typeof value}:${key}`);
|
||||||
|
} else {
|
||||||
|
this.modifiers.push(`${name}:${value}:${typeof value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getModifiers(): Array<string> {
|
||||||
|
return [...this.modifiers];
|
||||||
|
}
|
||||||
|
|
||||||
|
getModifiersString(): string {
|
||||||
|
if (!this.modifiers || !Array.isArray(this.modifiers)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return this.modifiers.join('|');
|
||||||
|
}
|
||||||
|
}
|
331
lib/modules/database/reference.js
Normal file
331
lib/modules/database/reference.js
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
import { NativeModules } from 'react-native';
|
||||||
|
|
||||||
|
import Query from './query.js';
|
||||||
|
import Snapshot from './snapshot';
|
||||||
|
import Disconnect from './disconnect';
|
||||||
|
import { ReferenceBase } from './../base';
|
||||||
|
import { promisify, isFunction, isObject, tryJSONParse, tryJSONStringify, generatePushID } from './../../utils';
|
||||||
|
|
||||||
|
const FirebaseDatabase = NativeModules.FirebaseDatabase;
|
||||||
|
|
||||||
|
// https://firebase.google.com/docs/reference/js/firebase.database.Reference
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Reference
|
||||||
|
*/
|
||||||
|
export default class Reference extends ReferenceBase {
|
||||||
|
|
||||||
|
db: FirebaseDatabase;
|
||||||
|
query: Query;
|
||||||
|
|
||||||
|
constructor(db: FirebaseDatabase, path: string, existingModifiers?: Array<string>) {
|
||||||
|
super(db.firebase, path);
|
||||||
|
this.db = db;
|
||||||
|
this.namespace = 'firebase:db:ref';
|
||||||
|
this.query = new Query(this, path, existingModifiers);
|
||||||
|
this.log.debug('Created new Reference', this.db._handle(path, existingModifiers));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param bool
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
keepSynced(bool: boolean) {
|
||||||
|
const path = this._dbPath();
|
||||||
|
return promisify('keepSynced', FirebaseDatabase)(path, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
set(value: any) {
|
||||||
|
const path = this._dbPath();
|
||||||
|
const _value = this._serializeAnyType(value);
|
||||||
|
return promisify('set', FirebaseDatabase)(path, _value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param val
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
update(val: Object) {
|
||||||
|
const path = this._dbPath();
|
||||||
|
const value = this._serializeObject(val);
|
||||||
|
return promisify('update', FirebaseDatabase)(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
remove() {
|
||||||
|
return promisify('remove', FirebaseDatabase)(this._dbPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @param onComplete
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
push(value: any, onComplete: Function) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
const _path = this.path + '/' + generatePushID(this.db.serverTimeOffset);
|
||||||
|
return new Reference(this.db, _path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = this._dbPath();
|
||||||
|
const _value = this._serializeAnyType(value);
|
||||||
|
return promisify('push', FirebaseDatabase)(path, _value)
|
||||||
|
.then(({ ref }) => {
|
||||||
|
const newRef = new Reference(this.db, ref);
|
||||||
|
if (isFunction(onComplete)) return onComplete(null, newRef);
|
||||||
|
return newRef;
|
||||||
|
}).catch((e) => {
|
||||||
|
if (isFunction(onComplete)) return onComplete(e, null);
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
on(eventName: string, cb: () => any, errorCb: () => any) {
|
||||||
|
if (!isFunction(cb)) throw new Error('The specified callback must be a function');
|
||||||
|
if (errorCb && !isFunction(errorCb)) throw new Error('The specified error callback must be a function');
|
||||||
|
const path = this._dbPath();
|
||||||
|
const modifiers = this.query.getModifiers();
|
||||||
|
const modifiersString = this.query.getModifiersString();
|
||||||
|
this.log.debug('adding reference.on', path, modifiersString, eventName);
|
||||||
|
return this.db.on(path, modifiersString, modifiers, eventName, cb, errorCb);
|
||||||
|
}
|
||||||
|
|
||||||
|
once(eventName: string = 'once', cb: (snapshot: Object) => void) {
|
||||||
|
const path = this._dbPath();
|
||||||
|
const modifiers = this.query.getModifiers();
|
||||||
|
const modifiersString = this.query.getModifiersString();
|
||||||
|
return promisify('onOnce', FirebaseDatabase)(path, modifiersString, modifiers, eventName)
|
||||||
|
.then(({ snapshot }) => new Snapshot(this, snapshot))
|
||||||
|
.then((snapshot) => {
|
||||||
|
if (isFunction(cb)) cb(snapshot);
|
||||||
|
return snapshot;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
off(eventName?: string = '', origCB?: () => any) {
|
||||||
|
const path = this._dbPath();
|
||||||
|
const modifiersString = this.query.getModifiersString();
|
||||||
|
this.log.debug('ref.off(): ', path, modifiersString, eventName);
|
||||||
|
return this.db.off(path, modifiersString, eventName, origCB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.db, this.path, this.query.getModifiers());
|
||||||
|
newRef.query.setOrderBy(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.db, this.path, this.query.getModifiers());
|
||||||
|
newRef.query.setLimit(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.db, this.path, this.query.getModifiers());
|
||||||
|
newRef.query.setFilter(name, value, key);
|
||||||
|
return newRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDisconnect() {
|
||||||
|
return new Disconnect(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
child(path: string) {
|
||||||
|
return new Reference(this.db, this.path + '/' + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return this._dbPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GETTERS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent ref of the current ref i.e. a ref of /foo/bar would return a new ref to '/foo'
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
get parent(): Reference|null {
|
||||||
|
if (this.path === '/') return null;
|
||||||
|
return new Reference(this.db, this.path.substring(0, this.path.lastIndexOf('/')));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ref to the root of db - '/'
|
||||||
|
* @returns {Reference}
|
||||||
|
*/
|
||||||
|
get root(): Reference {
|
||||||
|
return new Reference(this.db, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNALS
|
||||||
|
*/
|
||||||
|
|
||||||
|
_dbPath(): string {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
86
lib/modules/database/snapshot.js
Normal file
86
lib/modules/database/snapshot.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
import Reference from './reference.js';
|
||||||
|
import { isObject, deepGet, deepExists } from './../../utils';
|
||||||
|
|
||||||
|
export default class Snapshot {
|
||||||
|
static key: String;
|
||||||
|
static value: Object;
|
||||||
|
static exists: boolean;
|
||||||
|
static hasChildren: boolean;
|
||||||
|
static childKeys: String[];
|
||||||
|
|
||||||
|
ref: Object;
|
||||||
|
key: string;
|
||||||
|
value: any;
|
||||||
|
exists: boolean;
|
||||||
|
priority: any;
|
||||||
|
childKeys: Array<string>;
|
||||||
|
|
||||||
|
constructor(ref: Reference, snapshot: Object) {
|
||||||
|
this.ref = ref;
|
||||||
|
this.key = snapshot.key;
|
||||||
|
this.value = snapshot.value;
|
||||||
|
this.exists = snapshot.exists || true;
|
||||||
|
this.priority = snapshot.priority === undefined ? null : snapshot.priority;
|
||||||
|
this.childKeys = snapshot.childKeys || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DEFAULT API METHODS
|
||||||
|
*/
|
||||||
|
|
||||||
|
val() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
child(path: string) {
|
||||||
|
const value = deepGet(this.value, path);
|
||||||
|
const childRef = this.ref.child(path);
|
||||||
|
return new Snapshot(childRef, {
|
||||||
|
value,
|
||||||
|
key: childRef.key,
|
||||||
|
exists: value !== null,
|
||||||
|
childKeys: isObject(value) ? Object.keys(value) : [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exists() {
|
||||||
|
return this.value !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(fn: (key: any) => any) {
|
||||||
|
return this.childKeys.forEach((key, i) => fn(this.child(key), i));
|
||||||
|
}
|
||||||
|
|
||||||
|
getPriority() {
|
||||||
|
return this.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChild(path: string) {
|
||||||
|
return deepExists(this.value, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChildren() {
|
||||||
|
return this.numChildren() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
numChildren() {
|
||||||
|
if (!isObject(this.value)) return 0;
|
||||||
|
return Object.keys(this.value).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EXTRA API METHODS
|
||||||
|
*/
|
||||||
|
map(fn: (key: string) => mixed) {
|
||||||
|
const arr = [];
|
||||||
|
this.forEach((item, i) => arr.push(fn(item, i)));
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
reverseMap(fn: (key: string) => mixed) {
|
||||||
|
return this.map(fn).reverse();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user