UNPKG

ra-data-firebase

Version:

Firebase data provider for React Admin

181 lines (155 loc) 5.62 kB
import firebase from 'firebase' import methods from './methods' import deepAssign from 'deep-assign' import { GET_LIST, GET_ONE, GET_MANY, GET_MANY_REFERENCE, CREATE, UPDATE, DELETE } from 'react-admin' const baseConfig = { initialQuerytimeout: 10000, metaFieldNames: { createdAt: 'createdAt', updatedAt: 'updatedAt', createdBy: 'createdBy' }, admin: { path: 'users', config: {}, validate: () => true }, debug: false, trackedResources: [], firebaseSaveFilter: (data) => data, firebaseGetFilter: (data) => data } export default (options = {}) => { options = deepAssign({}, baseConfig, methods, options) const { metaFieldNames, trackedResources, initialQuerytimeout, debug, admin, firebaseSaveFilter, firebaseGetFilter, upload, save, del, getItemID, getOne, getMany } = options const resourcesStatus = {} const resourcesReferences = {} const resourcesData = {} const resourcesPaths = {} const resourcesUploadFields = {} // Sanitize Resources trackedResources.forEach((resource, index) => { if (typeof resource === 'string') { resource = { name: resource, path: resource, uploadFields: [] } trackedResources[index] = resource } const { name, path, uploadFields } = resource if (!name) { throw new Error(`name is missing from resource ${resource}`) } resourcesUploadFields[name] = uploadFields || [] resourcesPaths[name] = path || name resourcesData[name] = {} }) const initializeResource = ({name, isPublic}, resolve) => { let ref = resourcesReferences[name] = firebase.database().ref(resourcesPaths[name]) resourcesData[name] = [] if (isPublic) { subscribeResource(ref, name, resolve) } else { firebase.auth().onAuthStateChanged(auth => { if (auth) { subscribeResource(ref, name, resolve) } }) } setTimeout(resolve, initialQuerytimeout) return true } const subscribeResource = (ref, name, resolve) => { ref.once('value', function (snapshot) { /** Uses "value" to fetch initial data. Avoid the AOR to show no results */ if (snapshot.key === name) { const entries = snapshot.val() || {} Object.keys(entries).forEach(key => { resourcesData[name][key] = firebaseGetFilter(entries[key], name) }) Object.keys(resourcesData[name]).forEach(itemKey => { resourcesData[name][itemKey].id = itemKey resourcesData[name][itemKey].key = itemKey }) resolve() } }) ref.on('child_added', function (snapshot) { resourcesData[name][snapshot.key] = firebaseGetFilter(Object.assign({}, { id: snapshot.key, key: snapshot.key }, snapshot.val()), name) }) ref.on('child_removed', function (oldsnapshot) { if (resourcesData[name][oldsnapshot.key]) { delete resourcesData[name][oldsnapshot.key] } }) ref.on('child_changed', function (snapshot) { resourcesData[name][snapshot.key] = snapshot.val() }) } trackedResources.forEach(resource => { resourcesStatus[resource.name] = new Promise(resolve => { initializeResource(resource, resolve) }) }) /** * @param {string} type Request type, e.g GET_LIST * @param {string} resourceName Resource name, e.g. "posts" * @param {Object} payload Request parameters. Depends on the request type * @returns {Promise} the Promise for a REST response */ return async (type, resourceName, params) => { debug && console.log(type, resourceName, params) await resourcesStatus[resourceName] let result = null let itemId = null switch (type) { case GET_LIST: case GET_MANY: case GET_MANY_REFERENCE: result = getMany(params, resourceName, resourcesData[resourceName]) return result case GET_ONE: result = getOne(params, resourceName, resourcesData[resourceName]) return result case DELETE: var uploadFields = resourcesUploadFields[resourceName] ? resourcesUploadFields[resourceName] : [] result = await del(params.id, resourceName, resourcesPaths[resourceName], uploadFields) return result case UPDATE: case CREATE: if (admin && admin.path === resourceName && type === CREATE && params.data && params.data.email && params.data.password && admin.validate(params.data)) { try { let app = firebase.initializeApp(admin.config, 'user-admin') let user = await app.auth().createUserWithEmailAndPassword(params.data.email, params.data.password) itemId = user.uid app.auth().signOut() } catch (error) { return Promise.reject(new Error(error)) } } else { itemId = getItemID(params, type, resourceName, resourcesPaths[resourceName], resourcesData[resourceName]) } var uploads = resourcesUploadFields[resourceName] ? resourcesUploadFields[resourceName] .map(field => upload(field, params.data, itemId, resourceName, resourcesPaths[resourceName])) : [] var currentData = resourcesData[resourceName][itemId] || {} var uploadResults = await Promise.all(uploads) result = await save(itemId, params.data, currentData, resourceName, resourcesPaths[resourceName], firebaseSaveFilter, uploadResults, type === CREATE, metaFieldNames) return result default: debug && console.error('Undocumented method: ', type) return { data: [] } } } }