Skip to content

Commit

Permalink
feat(core): add snapshot cache - #203 - @illuminist
Browse files Browse the repository at this point in the history
  • Loading branch information
prescottprue committed Nov 25, 2019
2 parents 75e0995 + 26a3a92 commit acab96d
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 11 deletions.
17 changes: 11 additions & 6 deletions src/actions/firestore.js
Expand Up @@ -4,14 +4,15 @@ import { wrapInDispatch } from '../utils/actions';
import { actionTypes } from '../constants';
import {
attachListener,
detachListener,
orderedFromSnap,
dataByIdSnapshot,
getQueryConfig,
getQueryName,
firestoreRef,
detachListener,
dispatchListenerResponse,
firestoreRef,
getPopulateActions,
getQueryConfig,
getQueryName,
orderedFromSnap,
snapshotCache,
} from '../utils/query';

const pathListenerCounts = {};
Expand All @@ -34,7 +35,11 @@ export function add(firebase, dispatch, queryOption, ...args) {
actionTypes.ADD_REQUEST,
{
type: actionTypes.ADD_SUCCESS,
payload: snap => ({ id: snap.id, data: args[0] }),
payload: snap => {
const obj = { id: snap.id, data: args[0] };
snapshotCache.set(obj, snap);
return obj;
},
},
actionTypes.ADD_FAILURE,
],
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Expand Up @@ -4,6 +4,7 @@ import { firestoreActions } from './actions';
import createFirestoreInstance from './createFirestoreInstance';
import constants, { actionTypes } from './constants';
import middleware, { CALL_FIRESTORE } from './middleware';
import { getSnapshotByObject } from './utils/query';

// converted with transform-inline-environment-variables
export const version = process.env.npm_package_version;
Expand All @@ -16,6 +17,7 @@ export {
createFirestoreInstance,
firestoreActions as actions,
getFirestore,
getSnapshotByObject,
constants,
actionTypes,
middleware,
Expand All @@ -31,6 +33,7 @@ export default {
createFirestoreInstance,
actions: firestoreActions,
getFirestore,
getSnapshotByObject,
constants,
actionTypes,
middleware,
Expand Down
36 changes: 33 additions & 3 deletions src/utils/query.js
Expand Up @@ -12,6 +12,23 @@ import {
} from 'lodash';
import { actionTypes } from '../constants';

export const snapshotCache = new WeakMap();
/**
* Get DocumentSnapshot and QuerySnapshot with object from either data or
* ordered firestore state. If provided with doc data, it will return
* DocumentSnapshot, providing with a collection from data or an array from
* ordered state will return QuerySnapshot, except ordered state that generated
* as DocumentRef will return DocumentSnapshot
* Note: the cache is local and, not persistance. Passing an object from initial
* state or from SSR state will yield undefined.
* @param {object|Array} obj - The object from data or ordered state
* @returns {firebase.firestore.DocumentSnapshot|firebase.firestore.QuerySnapshot}
* DocumentSnapshot or QuerySnapshot depend on type of object provided
*/
export function getSnapshotByObject(obj) {
return snapshotCache.get(obj);
}

/**
* Add where claues to Cloud Firestore Reference handling invalid formats
* and multiple where statements (array of arrays)
Expand Down Expand Up @@ -443,15 +460,18 @@ export function orderedFromSnap(snap) {
const obj = isObject(snap.data())
? { id: snap.id, ...(snap.data() || snap.data) }
: { id: snap.id, data: snap.data() };
snapshotCache.set(obj, snap);
ordered.push(obj);
} else if (snap.forEach) {
snap.forEach(doc => {
const obj = isObject(doc.data())
? { id: doc.id, ...(doc.data() || doc.data) }
: { id: doc.id, data: doc.data() };
snapshotCache.set(obj, snap);
ordered.push(obj);
});
}
snapshotCache.set(ordered, snap);
return ordered;
}

Expand All @@ -464,13 +484,23 @@ export function orderedFromSnap(snap) {
export function dataByIdSnapshot(snap) {
const data = {};
if (snap.data) {
data[snap.id] = snap.exists ? snap.data() : null;
const snapData = snap.exists ? snap.data() : null;
if (snapData) {
snapshotCache.set(snapData, snap);
}
data[snap.id] = snapData;
} else if (snap.forEach) {
snap.forEach(doc => {
data[doc.id] = doc.data() || doc;
const snapData = doc.data() || doc;
snapshotCache.set(snapData, snap);
data[doc.id] = snapData;
});
}
return !!data && Object.keys(data).length ? data : null;
if (!!data && Object.keys(data).length) {
snapshotCache.set(data, snap);
return data;
}
return null;
}

/**
Expand Down
43 changes: 41 additions & 2 deletions test/unit/utils/query.spec.js
Expand Up @@ -6,6 +6,7 @@ import {
firestoreRef,
orderedFromSnap,
dataByIdSnapshot,
getSnapshotByObject,
} from 'utils/query';
import { actionTypes } from 'constants';

Expand Down Expand Up @@ -811,14 +812,14 @@ describe('query utils', () => {
describe('dataByIdSnapshot', () => {
it('sets data by id if valid', () => {
const id = 'someId';
const fakeData = 'some';
const fakeData = { some: 'thing' };
result = dataByIdSnapshot({ id, data: () => fakeData, exists: true });
expect(result).to.have.property(id, fakeData);
});

it('supports collection data', () => {
const id = 'someId';
const fakeData = 'some';
const fakeData = { some: 'thing' };
result = dataByIdSnapshot({
forEach: func => func({ data: () => fakeData, id }),
});
Expand All @@ -841,4 +842,42 @@ describe('query utils', () => {
expect(result).to.have.property(id, null);
});
});

describe('dataByIdSnapshot', () => {
it('retrieve snapshot with data from data state ', () => {
const id = 'someId';
const fakeData = { some: 'thing' };
const fakeSnap = { id, data: () => fakeData, exists: true };
result = dataByIdSnapshot(fakeSnap);
expect(getSnapshotByObject(result)).to.equal(fakeSnap);
});

it('retrieve snapshot with data from data collection state ', () => {
const id = 'someId';
const fakeData = { some: 'thing' };
const fakeDocSnap = { id, data: () => fakeData, exists: true };
const docArray = [fakeDocSnap];
const fakeSnap = { forEach: docArray.forEach.bind(docArray) };
result = dataByIdSnapshot(fakeSnap);
expect(getSnapshotByObject(result)).to.equal(fakeSnap);
});

it('retrieve snapshot with data from ordered state', () => {
const id = 'someId';
const fakeData = { some: 'thing' };
const fakeSnap = { id, data: () => fakeData, exists: true };
result = orderedFromSnap(fakeSnap);
expect(getSnapshotByObject(result)).to.equal(fakeSnap);
});

it('retrieve snapshot with data from ordered collection state ', () => {
const id = 'someId';
const fakeData = { some: 'thing' };
const fakeDocSnap = { id, data: () => fakeData, exists: true };
const docArray = [fakeDocSnap];
const fakeSnap = { forEach: docArray.forEach.bind(docArray) };
result = orderedFromSnap(fakeSnap);
expect(getSnapshotByObject(result)).to.equal(fakeSnap);
});
});
});

0 comments on commit acab96d

Please sign in to comment.