Skip to content

Commit

Permalink
v0.11.0
Browse files Browse the repository at this point in the history
* feat(core): add snapshot cache - #203 - @illuminist
* fix(query): fix uncaught error when using populates - #250
* chore(test): start tests for populate - #250
* fix(orderedReducer): preserve id on document for `DOCUMENT_MODIFIED` action - #252
* fix(test): add a test to cover preserving of id for `DOCUMENT_MODIFIED` action - #252
* build(deps): bump mixin-deep from 1.3.1 to 1.3.2 - @dependabot
  • Loading branch information
prescottprue committed Nov 25, 2019
1 parent 06e4bb4 commit b5d5f31
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 209 deletions.
2 changes: 1 addition & 1 deletion examples/complete/package.json
Expand Up @@ -27,7 +27,7 @@
"dependencies": {
"@material-ui/core": "^4.5.2",
"@material-ui/icons": "^4.5.1",
"firebase": "^7.2.3",
"firebase": "^7.5.0",
"history": "^4.10.1",
"lodash": "^4.17.15",
"prop-types": "^15.7.2",
Expand Down
8 changes: 4 additions & 4 deletions examples/complete/src/config.js
Expand Up @@ -9,10 +9,10 @@ export const env = 'dev'
// Config for firebase
export const firebase = {
apiKey: 'AIzaSyBTvAcJwsN8iygsnwAZyzIuy1uleYEpWIo',
authDomain: 'redux-firebasev3.firebaseapp.com',
databaseURL: 'https://redux-firebasev3.firebaseio.com',
projectId: 'redux-firebasev3',
storageBucket: 'redux-firebasev3.appspot.com'
authDomain: 'redux-firestore.firebaseapp.com',
databaseURL: 'https://redux-firestore.firebaseio.com',
projectId: 'redux-firestore',
storageBucket: 'redux-firestore.appspot.com'
}

// Config to override default reduxFirebase config in store/createStore
Expand Down
348 changes: 174 additions & 174 deletions examples/complete/yarn.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "redux-firestore",
"version": "0.10.0",
"version": "0.11.0",
"description": "Redux bindings for Firestore.",
"main": "lib/index.js",
"module": "es/index.js",
Expand All @@ -25,7 +25,7 @@
"watch:lib": "npm run build:lib -- --watch",
"watch:commonjs": "npm run build:commonjs -- --watch",
"test": "mocha -R spec ./test/unit/**",
"test:cov": "nyc mocha -R spec ./test/unit/** && nyc report --reporter=lcov",
"test:cov": "nyc --reporter=lcov --reporter=html npm run test",
"codecov": "cat coverage/lcov.info | codecov",
"lint": "eslint src test",
"lint:fix": "npm run lint -- --fix",
Expand Down
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
9 changes: 3 additions & 6 deletions src/reducers/orderedReducer.js
Expand Up @@ -66,12 +66,9 @@ function modifyDoc(collectionState, action) {
}

if (!action.meta.subcollections || action.meta.storeAs) {
return updateItemInArray(
collectionState,
action.meta.doc,
item =>
// Merge is no longer used to prevent removal of subcollections since this will change in v1
action.payload.data,
return updateItemInArray(collectionState, action.meta.doc, item =>
// Merge is no longer used to prevent removal of subcollections since this will change in v1
({ id: action.meta.doc, ...action.payload.data }),
);
}

Expand Down
41 changes: 35 additions & 6 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 Expand Up @@ -571,9 +601,8 @@ export function promisesForPopulate(
: populatesIn,
);

const dataHasPopulateChilds = populatesForData.some(
populatesForData,
populate => has(originalData, populate.child),
const dataHasPopulateChilds = populatesForData.some(populate =>
has(originalData, populate.child),
);
if (dataHasPopulateChilds) {
// Data is a single object, resolve populates directly
Expand Down
16 changes: 16 additions & 0 deletions test/unit/actions/firestore.spec.js
Expand Up @@ -603,6 +603,22 @@ describe('firestoreActions', () => {
});
});
});
describe('populates', () => {
it('calls success callback if provided', async () => {
listenerConfig = {
collection: 'test',
doc: '1',
populates: [{ root: 'users', child: 'asdf' }],
};
const instance = createFirestoreInstance(
fakeFirebase,
fakeConfig,
dispatchSpy,
);
await instance.test.setListener(listenerConfig);
expect(dispatchSpy).to.have.been.calledOnce;
});
});
});

describe('setListeners', () => {
Expand Down
19 changes: 18 additions & 1 deletion test/unit/reducers/orderedReducer.spec.js
Expand Up @@ -83,10 +83,27 @@ describe('orderedReducer', () => {
});

describe('DOCUMENT_MODIFIED', () => {
it('preserves id on existing document - #252', () => {
const collection = 'test1';
const doc = 'test2';
const someDoc = { some: 'value' };
const payload = {
data: someDoc,
};
const meta = { collection, doc };
action = { meta, payload, type: actionTypes.DOCUMENT_MODIFIED };
const fakeState = {
[collection]: [{ id: doc, ...someDoc }, { id: 'id1' }, { id: 'id2' }],
};
const result = orderedReducer(fakeState, action);
// ID
expect(result).to.have.nested.property(`${collection}.0.id`, doc);
});

it('moves document within collection - #230', () => {
const collection = 'test1';
const doc = 'test2';
const someDoc = { id: doc, some: 'value' };
const someDoc = { some: 'value' };
const otherDoc = { id: 'id1' };
const newIndex = 2;
const oldIndex = 0;
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 b5d5f31

Please sign in to comment.