diff --git a/dist/index.js b/dist/index.js index 4989853..288a871 100644 --- a/dist/index.js +++ b/dist/index.js @@ -7,6 +7,148 @@ Object.defineProperty(exports, "__esModule", { var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.default = function (accountCredentials, backupPath, prettyPrintJSON) { + // Returns if a value is a string + var isString = function isString(value) { + if (typeof value === 'string' || value instanceof String) { + return { + value: value, + typeof: 'string' + }; + } + return false; + }; + + // Returns if a value is really a number + var isNumber = function isNumber(value) { + if (typeof value === 'number' && isFinite(value)) { + return { + value: value, + typeof: 'number' + }; + } + return false; + }; + + // Returns if a value is an array + var isArray = function isArray(value) { + if (value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.constructor === Array) { + return { + value: value, + typeof: 'array' + }; + } + return false; + }; + + // Returns if a value is an object + var isObject = function isObject(value) { + if (value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value.constructor === Object) { + return { + value: value, + typeof: 'object' + }; + } + return false; + }; + + // Returns if a value is null + var isNull = function isNull(value) { + if (value === null) { + return { + value: value, + typeof: 'null' + }; + } + return false; + }; + + // Returns if a value is undefined + var isUndefined = function isUndefined(value) { + if (typeof value === 'undefined') { + return { + value: value, + typeof: 'undefined' + }; + } + return false; + }; + + // Returns if a value is a boolean + var isBoolean = function isBoolean(value) { + if (typeof value === 'boolean') { + return { + value: value, + typeof: 'boolean' + }; + } + return false; + }; + + // Returns if value is a date object + var isDate = function isDate(value) { + if (value instanceof Date) { + return { + value: value, + typeof: 'date' + }; + } + return false; + }; + + var constructReferenceUrl = function constructReferenceUrl(reference) { + var referencePath = ''; + Object.keys(reference).forEach(function (key) { + Object.keys(reference[key]).forEach(function (subKey) { + if (subKey === 'segments') { + var pathArray = reference[key][subKey]; + pathArray.forEach(function (pathKey) { + referencePath = referencePath ? referencePath + '/' + pathKey : pathKey; + }); + } + }); + }); + return referencePath ? { value: referencePath, typeof: 'reference' } : referencePath; + }; + + var constructDocumentValue = function constructDocumentValue() { + var documentDataToStore = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var keys = arguments[1]; + var documentData = arguments[2]; + + keys.forEach(function (key) { + // Boolean - boolean + // Reference - reference + // Integer - number + // Array - array + // Object - object + // Float - number + // Geographical Point - todo + // Map = todo + // Null - null + // String - string + if (isBoolean(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, _defineProperty({}, key, isBoolean(documentData[key]))); + } else if (isDate(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, _defineProperty({}, key, isDate(documentData[key]))); + } else if (isNumber(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, _defineProperty({}, key, isNumber(documentData[key]))); + } else if (isArray(documentData[key])) { + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], { typeof: 'array' }); + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], constructDocumentValue({}, Object.keys(documentData[key]), documentData[key])); + } else if (isObject(documentData[key])) { + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], { typeof: 'object' }); + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], constructDocumentValue({}, Object.keys(documentData[key]), documentData[key])); + } else if (isNull(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, _defineProperty({}, key, isNull(documentData[key]))); + } else if (isString(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, _defineProperty({}, key, isString(documentData[key]))); + } else { + documentDataToStore = Object.assign({}, documentDataToStore, _defineProperty({}, key, constructReferenceUrl(documentData[key]))); + } + }); + return documentDataToStore; + }; + // from: https://hackernoon.com/functional-javascript-resolving-promises-sequentially-7aac18c4431e var promiseSerial = function promiseSerial(funcs) { return funcs.reduce(function (promise, func) { @@ -28,10 +170,14 @@ exports.default = function (accountCredentials, backupPath, prettyPrintJSON) { var fileContents = void 0; try { + var documentData = document.data(); + var keys = Object.keys(documentData); + var documentDataToStore = {}; + documentDataToStore = Object.assign({}, constructDocumentValue(documentDataToStore, keys, documentData)); if (prettyPrintJSON === true) { - fileContents = JSON.stringify(document.data(), null, 2); + fileContents = JSON.stringify(documentDataToStore, null, 2); } else { - fileContents = JSON.stringify(document.data()); + fileContents = JSON.stringify(documentDataToStore); } } catch (error) { throw new Error('Unable to serialize Document \'' + document.id + '\': ' + error); @@ -83,6 +229,7 @@ exports.default = function (accountCredentials, backupPath, prettyPrintJSON) { } else { throw new Error('No account credentials provided'); } + _firebaseAdmin2.default.initializeApp({ credential: _firebaseAdmin2.default.credential.cert(accountCredentialsContents) }); @@ -115,4 +262,6 @@ var _mkdirp = require('mkdirp'); var _mkdirp2 = _interopRequireDefault(_mkdirp); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } \ No newline at end of file +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } \ No newline at end of file diff --git a/dist/index.js.flow b/dist/index.js.flow index be1f950..9071e1c 100644 --- a/dist/index.js.flow +++ b/dist/index.js.flow @@ -5,8 +5,144 @@ import Firebase from 'firebase-admin' import fs from 'fs' import mkdirp from 'mkdirp' -export default function(accountCredentials: string | Object, backupPath: string, prettyPrintJSON: boolean) { -// from: https://hackernoon.com/functional-javascript-resolving-promises-sequentially-7aac18c4431e +export default function (accountCredentials: string | Object, backupPath: string, prettyPrintJSON: boolean) { + // Returns if a value is a string + const isString = (value) => { + if (typeof value === 'string' || value instanceof String) { + return { + value, + typeof: 'string' + } + } + return false + } + + // Returns if a value is really a number + const isNumber = (value) => { + if (typeof value === 'number' && isFinite(value)) { + return { + value, + typeof: 'number' + } + } + return false + } + + // Returns if a value is an array + const isArray = (value) => { + if (value && typeof value === 'object' && value.constructor === Array) { + return { + value, + typeof: 'array' + } + } + return false + } + + // Returns if a value is an object + const isObject = (value) => { + if (value && typeof value === 'object' && value.constructor === Object) { + return { + value, + typeof: 'object' + } + } + return false + } + + // Returns if a value is null + const isNull = (value) => { + if (value === null) { + return { + value, + typeof: 'null' + } + } + return false + } + + // Returns if a value is undefined + const isUndefined = (value) => { + if (typeof value === 'undefined') { + return { + value, + typeof: 'undefined' + } + } + return false + } + + // Returns if a value is a boolean + const isBoolean = (value) => { + if (typeof value === 'boolean') { + return { + value, + typeof: 'boolean' + } + } + return false + } + + // Returns if value is a date object + const isDate = (value) => { + if (value instanceof Date) { + return { + value, + typeof: 'date' + } + } + return false + } + + const constructReferenceUrl = (reference) => { + var referencePath = '' + Object.keys(reference).forEach(key => { + Object.keys(reference[key]).forEach(subKey => { + if (subKey === 'segments') { + const pathArray = reference[key][subKey] + pathArray.forEach(pathKey => { referencePath = referencePath ? `${referencePath}/${pathKey}` : pathKey }) + } + }) + }) + return referencePath ? { value: referencePath, typeof: 'reference' } : referencePath + } + + const constructDocumentValue = (documentDataToStore = {}, keys, documentData) => { + keys.forEach(key => { + // Boolean - boolean + // Reference - reference + // Integer - number + // Array - array + // Object - object + // Float - number + // Geographical Point - todo + // Map = todo + // Null - null + // String - string + if (isBoolean(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, { [key]: isBoolean(documentData[key]) }) + } else if (isDate(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, { [key]: isDate(documentData[key]) }) + } else if (isNumber(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, { [key]: isNumber(documentData[key]) }) + } else if (isArray(documentData[key])) { + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], { typeof: 'array' }) + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], constructDocumentValue({}, Object.keys(documentData[key]), documentData[key])) + } else if (isObject(documentData[key])) { + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], { typeof: 'object' }) + documentDataToStore[key] = Object.assign({}, documentDataToStore[key], constructDocumentValue({}, Object.keys(documentData[key]), documentData[key])) + } else if (isNull(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, { [key]: isNull(documentData[key]) }) + } else if (isString(documentData[key])) { + documentDataToStore = Object.assign({}, documentDataToStore, { [key]: isString(documentData[key]) }) + } else { + documentDataToStore = Object.assign({}, documentDataToStore, { [key]: constructReferenceUrl(documentData[key]) }) + } + }) + return documentDataToStore + } + + // from: https://hackernoon.com/functional-javascript-resolving-promises-sequentially-7aac18c4431e const promiseSerial = (funcs) => { return funcs.reduce( (promise, func) => { @@ -28,10 +164,14 @@ export default function(accountCredentials: string | Object, backupPath: string, let fileContents: string try { + const documentData = document.data() + const keys = Object.keys(documentData) + var documentDataToStore = {} + documentDataToStore = Object.assign({}, constructDocumentValue(documentDataToStore, keys, documentData)) if (prettyPrintJSON === true) { - fileContents = JSON.stringify(document.data(), null, 2) + fileContents = JSON.stringify(documentDataToStore, null, 2) } else { - fileContents = JSON.stringify(document.data()) + fileContents = JSON.stringify(documentDataToStore) } } catch (error) { throw new Error('Unable to serialize Document \'' + document.id + '\': ' + error) @@ -43,13 +183,13 @@ export default function(accountCredentials: string | Object, backupPath: string, } return document.ref.getCollections() - .then((collections) => { - return promiseSerial(collections.map((collection) => { - return () => { - return backupCollection(collection, backupPath + '/' + collection.id, logPath + document.id + '/') - } - })) - }) + .then((collections) => { + return promiseSerial(collections.map((collection) => { + return () => { + return backupCollection(collection, backupPath + '/' + collection.id, logPath + document.id + '/') + } + })) + }) } const backupCollection = (collection: Object, backupPath: string, logPath: string): Promise => { @@ -61,15 +201,15 @@ export default function(accountCredentials: string | Object, backupPath: string, } return collection.get() - .then((snapshots) => { - const backupFunctions = [] - snapshots.forEach((document) => { - backupFunctions.push(() => { - return backupDocument(document, backupPath + '/' + document.id, logPath + collection.id + '/') - }) + .then((snapshots) => { + const backupFunctions = [] + snapshots.forEach((document) => { + backupFunctions.push(() => { + return backupDocument(document, backupPath + '/' + document.id, logPath + collection.id + '/') }) - return promiseSerial(backupFunctions) }) + return promiseSerial(backupFunctions) + }) } let accountCredentialsContents: Object @@ -85,6 +225,7 @@ export default function(accountCredentials: string | Object, backupPath: string, } else { throw new Error('No account credentials provided') } + Firebase.initializeApp({ credential: Firebase.credential.cert(accountCredentialsContents) }) @@ -97,11 +238,11 @@ export default function(accountCredentials: string | Object, backupPath: string, const database = Firebase.firestore() database.getCollections() - .then((collections) => { - return promiseSerial(collections.map((collection) => { - return () => { - return backupCollection(collection, backupPath + '/' + collection.id, '/') - } - })) - }) + .then((collections) => { + return promiseSerial(collections.map((collection) => { + return () => { + return backupCollection(collection, backupPath + '/' + collection.id, '/') + } + })) + }) }