Skip to content

Commit 8d3c159

Browse files
author
Bobby
committed
fix: nested fields only supported w/ firestore.update
1 parent a0639de commit 8d3c159

File tree

2 files changed

+49
-29
lines changed

2 files changed

+49
-29
lines changed

src/utils/mutate.js

+37-21
Original file line numberDiff line numberDiff line change
@@ -83,30 +83,38 @@ const serverTimestamp = (firebase, key) =>
8383
* @returns
8484
*/
8585
function atomize(firebase, operation) {
86-
return Object.keys(operation).reduce((data, key) => {
87-
const clone = { ...data };
88-
const val = clone[key];
89-
if (!val) return clone;
90-
91-
const value =
92-
primaryValue(val) ||
93-
serverTimestamp(firebase, val[0]) ||
94-
arrayUnion(firebase, val[0], val[1]) ||
95-
arrayRemove(firebase, val[0], val[1]) ||
96-
increment(firebase, val[0], val[1]);
97-
98-
if (Array.isArray(val) && val.length > 0) {
99-
// eslint-disable-next-line no-param-reassign
100-
clone[key] = value;
101-
}
102-
return clone;
103-
}, cloneDeep(operation));
86+
let requiresUpdate = false;
87+
return [
88+
Object.keys(operation).reduce((data, key) => {
89+
const clone = { ...data };
90+
const val = clone[key];
91+
if (key.includes('.')) {
92+
requiresUpdate = true;
93+
}
94+
if (!val) return clone;
95+
96+
const value =
97+
primaryValue(val) ||
98+
serverTimestamp(firebase, val[0]) ||
99+
arrayUnion(firebase, val[0], val[1]) ||
100+
arrayRemove(firebase, val[0], val[1]) ||
101+
increment(firebase, val[0], val[1]);
102+
103+
if (Array.isArray(val) && val.length > 0) {
104+
// eslint-disable-next-line no-param-reassign
105+
clone[key] = value;
106+
}
107+
return clone;
108+
}, cloneDeep(operation)),
109+
requiresUpdate,
110+
];
104111
}
105112

106113
// ----- write functions -----
107114

108115
/**
109-
*
116+
* For details between set & udpate see:
117+
* https://firebase.google.com/docs/reference/js/firebase.firestore.Transaction#update
110118
* @param {object} firebase
111119
* @param {Mutation_v1 | Mutation_v2} operation
112120
* @param {Batch | Transaction} writer
@@ -115,15 +123,23 @@ function atomize(firebase, operation) {
115123
function write(firebase, operation = {}, writer = null) {
116124
const { collection, path, doc, id, data, ...rest } = operation;
117125
const ref = docRef(firebase.firestore(), path || collection, id || doc);
118-
const changes = atomize(firebase, data || rest);
126+
const [changes, useUpdate = false] = atomize(firebase, data || rest);
119127

120128
if (writer) {
121129
const writeType = writer.commit ? 'Batching' : 'Transaction.set';
122130
info(writeType, { id: ref.id, path: ref.parent.path, ...changes });
123-
writer.set(ref, changes, { merge: true });
131+
if (useUpdate) {
132+
writer.update(ref, changes);
133+
} else {
134+
writer.set(ref, changes, { merge: true });
135+
}
124136
return { id: ref.id, path: ref.parent.path, ...changes };
125137
}
126138
info('Writing', { id: ref.id, path: ref.parent.path, ...changes });
139+
if (useUpdate) {
140+
return ref.update(changes);
141+
}
142+
127143
return ref.set(changes, { merge: true });
128144
}
129145

test/unit/utils/mutate.spec.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ describe('firestore.mutate()', () => {
120120
const withConverter = sinon.spy(() => ({ get: firestoreGet }));
121121
const where = sinon.spy(() => ({ get: firestoreGet, withConverter }));
122122
const collection = sinon.spy(() => ({ doc, withConverter, where }));
123-
const set = sinon.spy();
123+
const update = sinon.spy();
124124
function* mock() {
125125
yield Promise.resolve({
126126
ref: { id: 'sprint-1', parent: { path: 'sprints' } },
@@ -139,7 +139,7 @@ describe('firestore.mutate()', () => {
139139
}
140140
const mocked = mock();
141141
const transactionGet = () => mocked.next().value;
142-
const transaction = { set, get: transactionGet };
142+
const transaction = { update, get: transactionGet };
143143
const runTransaction = sinon.spy((cb) => cb(transaction));
144144
const doc = sinon.spy((val) => ({
145145
doc: val,
@@ -172,6 +172,7 @@ describe('firestore.mutate()', () => {
172172
collection: 'orgs/tara-ai/tasks',
173173
doc: task.id,
174174
data: {
175+
'nested.field': 'new-value',
175176
nextSprintId:
176177
team?.sprintSettings.moveRemainingTasksTo === 'Backlog'
177178
? null
@@ -182,18 +183,18 @@ describe('firestore.mutate()', () => {
182183
},
183184
);
184185

185-
expect(set.calledTwice);
186+
expect(update.calledTwice);
186187
expect(
187-
set.calledWith(
188+
update.calledWith(
188189
{ doc: 'task-id-1' },
189-
{ nextSprintId: 'next-sprint-id-123' },
190+
{ nextSprintId: 'next-sprint-id-123', 'nested.field': 'new-value' },
190191
{ merge: true },
191192
),
192193
);
193194
expect(
194-
set.calledWith(
195+
update.calledWith(
195196
{ doc: 'task-id-2' },
196-
{ nextSprintId: 'next-sprint-id-123' },
197+
{ nextSprintId: 'next-sprint-id-123', 'nested.field': 'new-value' },
197198
{ merge: true },
198199
),
199200
);
@@ -279,8 +280,10 @@ describe('firestore.mutate()', () => {
279280

280281
it('handles stringified Field Values', async () => {
281282
const set = sinon.spy();
283+
const update = sinon.spy();
282284
const doc = sinon.spy(() => ({
283285
set,
286+
update,
284287
id: 'id',
285288
parent: { path: 'path' },
286289
}));
@@ -312,7 +315,8 @@ describe('firestore.mutate()', () => {
312315

313316
expect(collection.calledWith('orgs/tara-ai/teams'));
314317
expect(doc.calledWith('team-bravo'));
315-
expect(set.calledWith({ name: 'Bravo Team 🎄' }, { merge: true }));
318+
expect(update.calledWith({ name: 'Bravo Team 🎄' }));
319+
expect(set.notCalled);
316320
expect(firestore.FieldValue.serverTimestamp).to.have.been.calledOnce;
317321
expect(firestore.FieldValue.increment).to.have.been.calledOnce;
318322
expect(firestore.FieldValue.arrayRemove).to.have.been.calledOnce;

0 commit comments

Comments
 (0)