From 5acea6d2183c9558ac9b5163dcde07db09641b7a Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 4 Apr 2017 17:58:20 +0100 Subject: [PATCH 1/2] initial commit: basic flow errors --- lib/firebase.js | 20 ++++++++++++-- lib/flow.js | 10 +++++++ lib/modules/auth/index.js | 7 ++++- lib/modules/crash/index.js | 2 +- lib/modules/database/disconnect.js | 1 + lib/modules/database/index.js | 3 +- lib/modules/database/reference.js | 12 ++++++-- lib/modules/database/snapshot.js | 12 ++++---- lib/modules/database/transaction.js | 11 ++++++-- lib/utils/index.js | 43 +++++++++++++++++++++-------- 10 files changed, 93 insertions(+), 28 deletions(-) diff --git a/lib/firebase.js b/lib/firebase.js index 65685af2..c6662227 100644 --- a/lib/firebase.js +++ b/lib/firebase.js @@ -15,7 +15,7 @@ import Messaging, { statics as MessagingStatics } from './modules/messaging'; import Analytics from './modules/analytics'; import Crash from './modules/crash'; -const instances = { default: null }; +const instances: Object = { default: null }; const FirebaseModule = NativeModules.RNFirebase; /** @@ -34,6 +34,19 @@ export default class Firebase { _remoteConfig: ?Object; _crash: ?Object; + auth: Function; + storage: Function; + database: Function; + messaging: Function; + + eventHandlers: Object; + debug: boolean; + options: { + errorOnMissingPlayServices: boolean, + debug?: boolean, + persistence?: boolean + }; + /** * * @param options @@ -127,14 +140,17 @@ export default class Firebase { * @returns {function()} * @private */ - _staticsOrInstance(name, statics, InstanceClass) { + _staticsOrInstance(name, statics, InstanceClass): Function { const getInstance = () => { const internalPropName = `_${name}`; + // $FlowFixMe if (!this[internalPropName]) { + // $FlowFixMe this[internalPropName] = new InstanceClass(this); } + // $FlowFixMe return this[internalPropName]; }; diff --git a/lib/flow.js b/lib/flow.js index ec6ada1f..ab295056 100644 --- a/lib/flow.js +++ b/lib/flow.js @@ -21,3 +21,13 @@ declare type GoogleApiAvailabilityType = { isUserResolvableError?: boolean, error?: string }; + +declare class FirebaseError { + message: string, + name: string, + code: string, + stack: string, + path: string, + details: string, + modifiers: string +}; diff --git a/lib/modules/auth/index.js b/lib/modules/auth/index.js index bd00d445..b04cd89a 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -42,7 +42,12 @@ export default class Auth extends Base { if (auth && auth.user && !this._user) this._user = new User(this, auth); else if ((!auth || !auth.user) && this._user) this._user = null; else if (this._user) this._user._updateValues(auth); - if (emit) this.emit('onAuthStateChanged', this._authResult.user || null); + if (emit) { + this.emit( + 'onAuthStateChanged', + this._authResult && this._authResult.user || null + ); + } return auth ? this._user : null; } diff --git a/lib/modules/crash/index.js b/lib/modules/crash/index.js index 436952e9..7c4f7664 100644 --- a/lib/modules/crash/index.js +++ b/lib/modules/crash/index.js @@ -33,7 +33,7 @@ export default class Analytics extends Base { * @param {Error} error * @param maxStackSize */ - report(error: Error, maxStackSize: Number = 10): void { + report(error: FirebaseError, maxStackSize: number = 10): void { if (!error || !error.code || !error.message) return; let errorMessage = `Message: ${error.message}\r\n`; diff --git a/lib/modules/database/disconnect.js b/lib/modules/database/disconnect.js index f79795eb..8b5c1f93 100644 --- a/lib/modules/database/disconnect.js +++ b/lib/modules/database/disconnect.js @@ -12,6 +12,7 @@ const FirebaseDatabase = NativeModules.RNFirebaseDatabase; */ export default class Disconnect { ref: Reference; + path: string; /** * diff --git a/lib/modules/database/index.js b/lib/modules/database/index.js index df6df3b6..e5baf473 100644 --- a/lib/modules/database/index.js +++ b/lib/modules/database/index.js @@ -218,7 +218,8 @@ export default class Database extends Base { firebaseMessage = `${firebaseMessage} at /${path}\r\n`; } - const firebaseError = new Error(firebaseMessage); + // $FlowFixMe + const firebaseError: FirebaseError = new Error(firebaseMessage); firebaseError.code = code; firebaseError.path = path; diff --git a/lib/modules/database/reference.js b/lib/modules/database/reference.js index cc17a609..6fb43b1b 100644 --- a/lib/modules/database/reference.js +++ b/lib/modules/database/reference.js @@ -157,19 +157,25 @@ export default class Reference extends ReferenceBase { * @param onComplete * @param applyLocally */ - transaction(transactionUpdate, onComplete?: () => any, applyLocally: boolean = false) { + transaction( + transactionUpdate: Function, + onComplete?: (?Error, any, ?Snapshot) => any, + 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 (error) { - if (isFunction(onComplete)) onComplete(error, committed, null); + if (typeof onComplete === 'function') { + onComplete(error, committed, null); + } return reject(error); } const snapshot = new Snapshot(this, snapshotData); - if (isFunction(onComplete)) { + if (typeof onComplete === 'function') { onComplete(null, committed, snapshot); } diff --git a/lib/modules/database/snapshot.js b/lib/modules/database/snapshot.js index d81ae220..97c28546 100644 --- a/lib/modules/database/snapshot.js +++ b/lib/modules/database/snapshot.js @@ -62,7 +62,7 @@ export default class Snapshot { * @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#exists * @returns {boolean} */ - exists(): Boolean { + exists(): boolean { return this._value !== null; } @@ -71,7 +71,7 @@ export default class Snapshot { * @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach * @param action */ - forEach(action: (key: any) => any): Boolean { + forEach(action: (key: any) => any): boolean { if (!this._childKeys.length) return false; let cancelled = false; @@ -94,7 +94,7 @@ export default class Snapshot { * @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#getPriority * @returns {String|Number|null} */ - getPriority(): String|Number|null { + getPriority(): string | number | null { return this._priority; } @@ -104,7 +104,7 @@ export default class Snapshot { * @param path * @returns {Boolean} */ - hasChild(path: string): Boolean { + hasChild(path: string): boolean { return deepExists(this._value, path); } @@ -113,7 +113,7 @@ export default class Snapshot { * @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#hasChildren * @returns {boolean} */ - hasChildren(): Boolean { + hasChildren(): boolean { return this.numChildren() > 0; } @@ -122,7 +122,7 @@ export default class Snapshot { * @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#numChildren * @returns {Number} */ - numChildren(): Number { + numChildren(): number { if (!isObject(this._value)) return 0; return Object.keys(this._value).length; } diff --git a/lib/modules/database/transaction.js b/lib/modules/database/transaction.js index f4a2fdaa..22e4e654 100644 --- a/lib/modules/database/transaction.js +++ b/lib/modules/database/transaction.js @@ -13,7 +13,7 @@ const FirebaseDatabase = NativeModules.RNFirebaseDatabase; * @class Database */ export default class TransactionHandler extends Base { - constructor(firebase: Object, database: Object, FirebaseDatabaseEvt) { + constructor(firebase: Object, database: Object, FirebaseDatabaseEvt: Object) { super(firebase, {}); this.transactions = {}; this.database = database; @@ -32,7 +32,12 @@ export default class TransactionHandler extends Base { * @param onComplete * @param applyLocally */ - add(reference, transactionUpdater, onComplete, applyLocally = false) { + add( + reference: Object, + transactionUpdater: Function, + onComplete?: Function, + applyLocally?: boolean = false + ) { const id = this._generateTransactionId(); this.transactions[id] = { @@ -57,7 +62,7 @@ export default class TransactionHandler extends Base { * @returns {string} * @private */ - _generateTransactionId() { + _generateTransactionId(): string { return generatePushID(this.database.serverTimeOffset); } diff --git a/lib/utils/index.js b/lib/utils/index.js index 72a75693..74fa3cd9 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1,3 +1,6 @@ +/** + * @flow + */ // modeled after base64 web-safe chars, but ordered by ASCII const PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'; const hasOwnProperty = Object.hasOwnProperty; @@ -8,7 +11,8 @@ const _handler = (resolve, reject, errorPrefix, err, resp) => { // resolve / reject after events etc setImmediate(() => { if (err) { - const firebaseError = new Error(err.message); + // $FlowFixMe + const firebaseError: FirebaseError = new Error(err.message); if (isObject(err)) { Object.keys(err).forEach(key => Object.defineProperty(firebaseError, key, { value: err[key] })); @@ -23,7 +27,7 @@ const _handler = (resolve, reject, errorPrefix, err, resp) => { }); }; -export function toWebSDKErrorCode(code, prefix) { +export function toWebSDKErrorCode(code: any, prefix: string): string { if (!code || typeof code !== 'string') return ''; return code.toLowerCase().replace('error_', prefix).replace(/_/g, '-'); } @@ -36,7 +40,11 @@ export function toWebSDKErrorCode(code, prefix) { * @param joiner * @returns {*} */ -export function deepGet(object, path, joiner = '/') { +export function deepGet( + object: Object, + path: string, + joiner?: string = '/' +): any { const keys = path.split(joiner); let i = 0; @@ -60,7 +68,11 @@ export function deepGet(object, path, joiner = '/') { * @param joiner * @returns {*} */ -export function deepExists(object, path, joiner = '/') { +export function deepExists( + object: Object, + path: string, + joiner?: string = '/' +): boolean { const keys = path.split(joiner); let i = 0; @@ -81,7 +93,7 @@ export function deepExists(object, path, joiner = '/') { * @param item * @returns {boolean} */ -export function isObject(item) { +export function isObject(item: any): boolean { return (item && typeof item === 'object' && !Array.isArray(item) && item !== null); } @@ -90,8 +102,8 @@ export function isObject(item) { * @param item * @returns {*|boolean} */ -export function isFunction(item) { - return (item && typeof item === 'function'); +export function isFunction(item?: any): boolean { + return Boolean(item && typeof item === 'function'); } /** @@ -112,7 +124,7 @@ export function tryJSONParse(string) { * @param data * @returns {*} */ -export function tryJSONStringify(data) { +export function tryJSONStringify(data: any): string | null { try { return JSON.stringify(data); } catch (jsonError) { @@ -149,7 +161,11 @@ export function noop(): void { * @param NativeModule * @param errorPrefix */ -export function promisify(fn: Function, NativeModule: Object, errorPrefix): Function { +export function promisify( + fn: Function | string, + NativeModule: Object, + errorPrefix?: string +): (any) => Promise<> { return (...args) => { return new Promise((resolve, reject) => { const _fn = typeof fn === 'function' ? fn : NativeModule[fn]; @@ -168,7 +184,12 @@ export function promisify(fn: Function, NativeModule: Object, errorPrefix): Func * @param callback * @private */ -function _delayChunk(collection, chunkSize, operation, callback): void { +function _delayChunk( + collection: Array<*>, + chunkSize: number, + operation: Function, + callback: Function +): void { const length = collection.length; const iterations = Math.ceil(length / chunkSize); @@ -210,7 +231,7 @@ export function each(array: Array, chunkSize?: number, iterator: Function, cb: F }, cb); } -export function typeOf(value) { +export function typeOf(value: any): string { if (value === null) return 'null'; if (Array.isArray(value)) return 'array'; return typeof value; From 51a07fc4eebc49454646b899be8a7abfd6146a11 Mon Sep 17 00:00:00 2001 From: Ben Styles Date: Tue, 4 Apr 2017 17:58:47 +0100 Subject: [PATCH 2/2] flow fixes that change logic slightly --- lib/modules/database/transaction.js | 4 ++-- lib/utils/index.js | 32 ++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/modules/database/transaction.js b/lib/modules/database/transaction.js index 22e4e654..8a4b529a 100644 --- a/lib/modules/database/transaction.js +++ b/lib/modules/database/transaction.js @@ -50,7 +50,7 @@ export default class TransactionHandler extends Base { started: true, }; - FirebaseDatabase.startTransaction(reference.path, id, applyLocally || false); + FirebaseDatabase.startTransaction(reference.path, id, applyLocally); } /** @@ -77,7 +77,7 @@ export default class TransactionHandler extends Base { case 'update': return this._handleUpdate(event); case 'error': - return this._handleError(error); + return this._handleError(event); case 'complete': return this._handleComplete(event); default: diff --git a/lib/utils/index.js b/lib/utils/index.js index 74fa3cd9..bedb5a48 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -111,9 +111,9 @@ export function isFunction(item?: any): boolean { * @param string * @returns {*} */ -export function tryJSONParse(string) { +export function tryJSONParse(string: string | null): any { try { - return JSON.parse(string); + return string && JSON.parse(string); } catch (jsonError) { return string; } @@ -217,18 +217,25 @@ function _delayChunk( * @param iterator * @param cb */ -export function each(array: Array, chunkSize?: number, iterator: Function, cb: Function): void { +export function each( + array: Array<*>, + chunkSize: number | Function, + iterator: Function, + cb?: Function +): void { if (typeof chunkSize === 'function') { cb = iterator; iterator = chunkSize; chunkSize = DEFAULT_CHUNK_SIZE; } - _delayChunk(array, chunkSize, (slice, start) => { - for (let ii = 0, jj = slice.length; ii < jj; ii += 1) { - iterator(slice[ii], start + ii); - } - }, cb); + if (cb) { + _delayChunk(array, chunkSize, (slice, start) => { + for (let ii = 0, jj = slice.length; ii < jj; ii += 1) { + iterator(slice[ii], start + ii); + } + }, cb); + } } export function typeOf(value: any): string { @@ -245,7 +252,12 @@ export function typeOf(value: any): string { * @param cb * @returns {*} */ -export function map(array: Array, chunkSize?: number, iterator: Function, cb: Function): void { +export function map( + array: Array<*>, + chunkSize: number | Function, + iterator: Function, + cb?: Function +): void { if (typeof chunkSize === 'function') { cb = iterator; iterator = chunkSize; @@ -258,7 +270,7 @@ export function map(array: Array, chunkSize?: number, iterator: Function, cb: Fu result.push(iterator(slice[ii], start + ii, array)); } return result; - }, () => cb(result)); + }, () => cb && cb(result)); }