[firestore] Add FieldPath support to DocumentReference and WriteBatch update methods
This commit is contained in:
parent
5e062868fc
commit
3dacb35291
|
@ -4,6 +4,8 @@
|
||||||
*/
|
*/
|
||||||
import CollectionReference from './CollectionReference';
|
import CollectionReference from './CollectionReference';
|
||||||
import DocumentSnapshot from './DocumentSnapshot';
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
|
import FieldPath from './FieldPath';
|
||||||
|
import { mergeFieldPathData } from './utils';
|
||||||
import { buildNativeMap } from './utils/serialize';
|
import { buildNativeMap } from './utils/serialize';
|
||||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||||
import { getLogger } from '../../utils/log';
|
import { getLogger } from '../../utils/log';
|
||||||
|
@ -192,10 +194,13 @@ export default class DocumentReference {
|
||||||
for (let i = 0; i < args.length; i += 2) {
|
for (let i = 0; i < args.length; i += 2) {
|
||||||
const key = args[i];
|
const key = args[i];
|
||||||
const value = args[i + 1];
|
const value = args[i + 1];
|
||||||
if (!isString(key)) {
|
if (isString(key)) {
|
||||||
throw new Error(`DocumentReference.update failed: Argument at index ${i} must be a string`);
|
data[key] = value;
|
||||||
|
} else if (key instanceof FieldPath) {
|
||||||
|
data = mergeFieldPathData(data, key._segments, value);
|
||||||
|
} else {
|
||||||
|
throw new Error(`DocumentReference.update failed: Argument at index ${i} must be a string or FieldPath`);
|
||||||
}
|
}
|
||||||
data[key] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const nativeData = buildNativeMap(data);
|
const nativeData = buildNativeMap(data);
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* @flow
|
* @flow
|
||||||
* WriteBatch representation wrapper
|
* WriteBatch representation wrapper
|
||||||
*/
|
*/
|
||||||
|
import FieldPath from './FieldPath';
|
||||||
|
import { mergeFieldPathData } from './utils';
|
||||||
import { buildNativeMap } from './utils/serialize';
|
import { buildNativeMap } from './utils/serialize';
|
||||||
import { isObject, isString } from '../../utils';
|
import { isObject, isString } from '../../utils';
|
||||||
import { getNativeModule } from '../../utils/native';
|
import { getNativeModule } from '../../utils/native';
|
||||||
|
@ -67,19 +69,22 @@ export default class WriteBatch {
|
||||||
let data = {};
|
let data = {};
|
||||||
if (args.length === 1) {
|
if (args.length === 1) {
|
||||||
if (!isObject(args[0])) {
|
if (!isObject(args[0])) {
|
||||||
throw new Error('DocumentReference.update failed: If using two arguments, the second must be an object.');
|
throw new Error('WriteBatch.update failed: If using two arguments, the second must be an object.');
|
||||||
}
|
}
|
||||||
data = args[0];
|
data = args[0];
|
||||||
} else if (args.length % 2 === 1) {
|
} else if (args.length % 2 === 1) {
|
||||||
throw new Error('DocumentReference.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.');
|
throw new Error('WriteBatch.update failed: Must have a document reference, followed by either a single object argument, or equal numbers of key/value pairs.');
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < args.length; i += 2) {
|
for (let i = 0; i < args.length; i += 2) {
|
||||||
const key = args[i];
|
const key = args[i];
|
||||||
const value = args[i + 1];
|
const value = args[i + 1];
|
||||||
if (!isString(key)) {
|
if (isString(key)) {
|
||||||
throw new Error(`DocumentReference.update failed: Argument at index ${i + 1} must be a string`);
|
data[key] = value;
|
||||||
|
} else if (key instanceof FieldPath) {
|
||||||
|
data = mergeFieldPathData(data, key._segments, value);
|
||||||
|
} else {
|
||||||
|
throw new Error(`WriteBatch.update failed: Argument at index ${i} must be a string or FieldPath`);
|
||||||
}
|
}
|
||||||
data[key] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
const buildFieldPathData = (segments: string[], value: any): Object => {
|
||||||
|
if (segments.length === 1) {
|
||||||
|
return {
|
||||||
|
[segments[0]]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
[segments[0]]: buildFieldPathData(segments.slice(1), value),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mergeFieldPathData = (data: Object, segments: string[], value: any): Object => {
|
||||||
|
if (segments.length === 1) {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
[segments[0]]: value,
|
||||||
|
};
|
||||||
|
} else if (data[segments[0]]) {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
[segments[0]]: mergeFieldPathData(data[segments[0]], segments.slice(1), value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
[segments[0]]: buildFieldPathData(segments.slice(1), value),
|
||||||
|
};
|
||||||
|
};
|
|
@ -66,9 +66,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
|
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
|
||||||
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
const currentDataValue = { name: 'doc1' };
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
@ -101,9 +99,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
|
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
|
||||||
it('allows binding multiple callbacks to the same ref', async () => {
|
it('allows binding multiple callbacks to the same ref', async () => {
|
||||||
// Setup
|
// Setup
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
@ -153,9 +149,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
unsubscribeA();
|
unsubscribeA();
|
||||||
unsubscribeB();
|
unsubscribeB();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
|
||||||
it('listener stops listening when unsubscribed', async () => {
|
it('listener stops listening when unsubscribed', async () => {
|
||||||
// Setup
|
// Setup
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
@ -228,9 +222,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
callbackA.should.be.calledTwice();
|
callbackA.should.be.calledTwice();
|
||||||
callbackB.should.be.calledThrice();
|
callbackB.should.be.calledThrice();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
|
||||||
it('supports options and callbacks', async () => {
|
it('supports options and callbacks', async () => {
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
const currentDataValue = { name: 'doc1' };
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
@ -266,9 +258,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
|
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
|
||||||
it('supports observer', async () => {
|
it('supports observer', async () => {
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
const currentDataValue = { name: 'doc1' };
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
@ -308,9 +298,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
|
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
|
||||||
it('supports options and observer', async () => {
|
it('supports options and observer', async () => {
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
const currentDataValue = { name: 'doc1' };
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
@ -361,9 +349,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
doc.data().name.should.equal('doc2');
|
doc.data().name.should.equal('doc2');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('set()', () => {
|
|
||||||
it('should merge Document', () => {
|
it('should merge Document', () => {
|
||||||
return firebase.native.firestore()
|
return firebase.native.firestore()
|
||||||
.doc('document-tests/doc1')
|
.doc('document-tests/doc1')
|
||||||
|
@ -374,9 +360,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
doc.data().merge.should.equal('merge');
|
doc.data().merge.should.equal('merge');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('set()', () => {
|
|
||||||
it('should overwrite Document', () => {
|
it('should overwrite Document', () => {
|
||||||
return firebase.native.firestore()
|
return firebase.native.firestore()
|
||||||
.doc('document-tests/doc1')
|
.doc('document-tests/doc1')
|
||||||
|
@ -398,9 +382,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
doc.data().name.should.equal('updated');
|
doc.data().name.should.equal('updated');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('update()', () => {
|
|
||||||
it('should update Document using key/value pairs', () => {
|
it('should update Document using key/value pairs', () => {
|
||||||
return firebase.native.firestore()
|
return firebase.native.firestore()
|
||||||
.doc('document-tests/doc1')
|
.doc('document-tests/doc1')
|
||||||
|
@ -410,6 +392,40 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
doc.data().name.should.equal('updated');
|
doc.data().name.should.equal('updated');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update Document using FieldPath/value pair', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc1')
|
||||||
|
.update(new firebase.native.firestore.FieldPath('name'), 'Name')
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||||
|
doc.data().name.should.equal('Name');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update Document using nested FieldPath and value pair', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc1')
|
||||||
|
.update(new firebase.native.firestore.FieldPath('nested', 'name'), 'Nested Name')
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||||
|
doc.data().nested.name.should.equal('Nested Name');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update Document using multiple FieldPath/value pairs', () => {
|
||||||
|
return firebase.native.firestore()
|
||||||
|
.doc('document-tests/doc1')
|
||||||
|
.update(
|
||||||
|
new firebase.native.firestore.FieldPath('nested', 'firstname'), 'First Name',
|
||||||
|
new firebase.native.firestore.FieldPath('nested', 'lastname'), 'Last Name',
|
||||||
|
)
|
||||||
|
.then(async () => {
|
||||||
|
const doc = await firebase.native.firestore().doc('document-tests/doc1').get();
|
||||||
|
doc.data().nested.firstname.should.equal('First Name');
|
||||||
|
doc.data().nested.lastname.should.equal('Last Name');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('types', () => {
|
context('types', () => {
|
||||||
|
@ -422,9 +438,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
const doc = await docRef.get();
|
const doc = await docRef.get();
|
||||||
should.equal(doc.data().field, true);
|
should.equal(doc.data().field, true);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('types', () => {
|
|
||||||
it('should handle Date field', async () => {
|
it('should handle Date field', async () => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
||||||
|
@ -437,9 +451,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
should.equal(doc.data().field.toISOString(), date.toISOString());
|
should.equal(doc.data().field.toISOString(), date.toISOString());
|
||||||
should.equal(doc.data().field.getTime(), date.getTime());
|
should.equal(doc.data().field.getTime(), date.getTime());
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('types', () => {
|
|
||||||
it('should handle DocumentReference field', async () => {
|
it('should handle DocumentReference field', async () => {
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
||||||
await docRef.set({
|
await docRef.set({
|
||||||
|
@ -449,9 +461,7 @@ function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
const doc = await docRef.get();
|
const doc = await docRef.get();
|
||||||
should.equal(doc.data().field.path, 'test/field');
|
should.equal(doc.data().field.path, 'test/field');
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
context('types', () => {
|
|
||||||
it('should handle GeoPoint field', async () => {
|
it('should handle GeoPoint field', async () => {
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
const docRef = firebase.native.firestore().doc('document-tests/reference');
|
||||||
await docRef.set({
|
await docRef.set({
|
||||||
|
|
|
@ -37,6 +37,13 @@ function firestoreTests({ describe, it, context, firebase }) {
|
||||||
.set(sfRef, { name: 'San Francisco' })
|
.set(sfRef, { name: 'San Francisco' })
|
||||||
.update(nycRef, { population: 1000000 })
|
.update(nycRef, { population: 1000000 })
|
||||||
.update(sfRef, 'name', 'San Fran')
|
.update(sfRef, 'name', 'San Fran')
|
||||||
|
.update(sfRef, new firebase.native.firestore.FieldPath('name'), 'San Fran FieldPath')
|
||||||
|
.update(sfRef, new firebase.native.firestore.FieldPath('nested', 'name'), 'Nested Nme')
|
||||||
|
.update(
|
||||||
|
sfRef,
|
||||||
|
new firebase.native.firestore.FieldPath('nested', 'firstname'), 'First Name',
|
||||||
|
new firebase.native.firestore.FieldPath('nested', 'lastname'), 'Last Name',
|
||||||
|
)
|
||||||
.set(lRef, { population: 3000000 }, { merge: true })
|
.set(lRef, { population: 3000000 }, { merge: true })
|
||||||
.delete(ayRef)
|
.delete(ayRef)
|
||||||
.commit()
|
.commit()
|
||||||
|
@ -53,7 +60,9 @@ function firestoreTests({ describe, it, context, firebase }) {
|
||||||
nycDoc.data().population.should.equal(1000000);
|
nycDoc.data().population.should.equal(1000000);
|
||||||
|
|
||||||
const sfDoc = await sfRef.get();
|
const sfDoc = await sfRef.get();
|
||||||
sfDoc.data().name.should.equal('San Fran');
|
sfDoc.data().name.should.equal('San Fran FieldPath');
|
||||||
|
sfDoc.data().nested.firstname.should.equal('First Name');
|
||||||
|
sfDoc.data().nested.lastname.should.equal('Last Name');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue