Skip to content

Commit

Permalink
fix(firestore): accept nested undefined array values
Browse files Browse the repository at this point in the history
Fixes #5437
  • Loading branch information
mikehardy committed Jul 21, 2021
1 parent 0f18b75 commit 224383f
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 53 deletions.
1 change: 1 addition & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jest.doMock('react-native', () => {
},
RNFBFirestoreModule: {
settings: jest.fn(),
documentSet: jest.fn(),
},
RNFBPerfModule: {},
RNFBStorageModule: {
Expand Down
34 changes: 33 additions & 1 deletion packages/firestore/__tests__/firestore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ describe('Storage', function () {
}
});

it('throws when nested undefined value provided and ignored undefined is false', async function () {
it('throws when nested undefined object value provided and ignored undefined is false', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: false });
const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
try {
Expand All @@ -262,5 +262,37 @@ describe('Storage', function () {
return expect(e.message).toEqual('Unsupported field value: undefined');
}
});

it('throws when nested undefined array value provided and ignored undefined is false', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: false });
const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
try {
await docRef.set({
myArray: [{ name: 'Tim', location: { state: undefined, country: 'United Kingdom' } }],
});
return Promise.reject(new Error('Expected set() to throw'));
} catch (e) {
return expect(e.message).toEqual('Unsupported field value: undefined');
}
});

it('does not throw when nested undefined array value provided and ignore undefined is true', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });
const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
await docRef.set({
myArray: [{ name: 'Tim', location: { state: undefined, country: 'United Kingdom' } }],
});
});

it('does not throw when nested undefined object value provided and ignore undefined is true', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });
const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
await docRef.set({
field1: 1,
field2: {
shouldNotWork: undefined,
},
});
});
});
});
82 changes: 82 additions & 0 deletions packages/firestore/e2e/DocumentReference/set.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,86 @@ describe('firestore.doc().set()', function () {
snapshot2.data().should.eql(jet.contextify(merged));
await ref.delete();
});

it('throws when nested undefined array value provided and ignored undefined is false', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: false });
const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
try {
await docRef.set({
myArray: [{ name: 'Tim', location: { state: undefined, country: 'United Kingdom' } }],
});
return Promise.reject(new Error('Expected set() to throw'));
} catch (error) {
error.message.should.containEql('Unsupported field value: undefined');
}
});

it('accepts undefined nested array values if ignoreUndefined is true', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });
const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
await docRef.set({
myArray: [{ name: 'Tim', location: { state: undefined, country: 'United Kingdom' } }],
});
});

it('does not throw when nested undefined object value provided and ignore undefined is true', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });
const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
await docRef.set({
field1: 1,
field2: {
shouldNotWork: undefined,
},
});
});

it('filters out undefined properties when setting enabled', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });

const docRef = firebase.firestore().doc(`${COLLECTION}/ignoreUndefinedTrueProp`);
await docRef.set({
field1: 1,
field2: undefined,
});

const snap = await docRef.get();
const snapData = snap.data();
if (!snapData) {
return Promise.reject(new Error('Snapshot not saved'));
}

snapData.field1.should.eql(1);
snapData.hasOwnProperty('field2').should.eql(false);
});

it('filters out nested undefined properties when setting enabled', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });

const docRef = firebase.firestore().doc(`${COLLECTION}/ignoreUndefinedTrueNestedProp`);
await docRef.set({
field1: 1,
field2: {
shouldBeMissing: undefined,
},
field3: [
{
shouldBeHere: 'Here',
shouldBeMissing: undefined,
},
],
});

const snap = await docRef.get();
const snapData = snap.data();
if (!snapData) {
return Promise.reject(new Error('Snapshot not saved'));
}

snapData.field1.should.eql(1);
snapData.hasOwnProperty('field2').should.eql(true);
snapData.field2.hasOwnProperty('shouldBeMissing').should.eql(false);
snapData.hasOwnProperty('field3').should.eql(true);
snapData.field3[0].shouldBeHere.should.eql('Here');
snapData.field3[0].hasOwnProperty('shouldBeMissing').should.eql(false);
});
});
44 changes: 0 additions & 44 deletions packages/firestore/e2e/firestore.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,50 +37,6 @@ describe('firestore()', function () {

describe('collection()', function () {});

describe('doc()', function () {
it('filters out undefined properties when setting enabled', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });

const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
await docRef.set({
field1: 1,
field2: undefined,
});

const snap = await docRef.get();
const snapData = snap.data();
if (!snapData) {
return Promise.reject(new Error('Snapshot not saved'));
}

snapData.field1.should.eql(1);
snapData.hasOwnProperty('field2').should.eql(false);
});

it('filters out nested undefined properties when setting enabled', async function () {
await firebase.firestore().settings({ ignoreUndefinedProperties: true });

const docRef = firebase.firestore().doc(`${COLLECTION}/bar`);
await docRef.set({
field1: 1,
field2: {
shouldBeMissing: undefined,
},
});

const snap = await docRef.get();
const snapData = snap.data();
if (!snapData) {
return Promise.reject(new Error('Snapshot not saved'));
}

snapData.field1.should.eql(1);
snapData.hasOwnProperty('field2').should.eql(true);

snapData.field2.hasOwnProperty('shouldBeMissing').should.eql(false);
});
});

describe('collectionGroup()', function () {
it('performs a collection group query', async function () {
const docRef1 = firebase.firestore().doc(`${COLLECTION}/collectionGroup1`);
Expand Down
25 changes: 17 additions & 8 deletions packages/firestore/lib/utils/serialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ export function buildNativeMap(data, ignoreUndefined) {
if (typeof data[key] === 'undefined') {
if (!ignoreUndefined) {
throw new Error('Unsupported field value: undefined');
} else {
continue;
}
} else {
const typeMap = generateNativeData(data[key], ignoreUndefined);
if (typeMap) {
nativeData[key] = typeMap;
}
}

const typeMap = generateNativeData(data[key], ignoreUndefined);
if (typeMap) {
nativeData[key] = typeMap;
}
}
}
Expand All @@ -76,12 +78,19 @@ export function buildNativeMap(data, ignoreUndefined) {
* @param array
* @returns {Array}
*/
export function buildNativeArray(array) {
export function buildNativeArray(array, ignoreUndefined) {
const nativeArray = [];
if (array) {
for (let i = 0; i < array.length; i++) {
const value = array[i];
const typeMap = generateNativeData(value);
if (typeof value === 'undefined') {
if (!ignoreUndefined) {
throw new Error('Unsupported field value: undefined');
} else {
continue;
}
}
const typeMap = generateNativeData(value, ignoreUndefined);
if (typeMap) {
nativeArray.push(typeMap);
}
Expand Down Expand Up @@ -144,7 +153,7 @@ export function generateNativeData(value, ignoreUndefined) {
}

if (isArray(value)) {
return getTypeMapInt('array', buildNativeArray(value));
return getTypeMapInt('array', buildNativeArray(value, ignoreUndefined));
}

if (isObject(value)) {
Expand Down

1 comment on commit 224383f

@vercel
Copy link

@vercel vercel bot commented on 224383f Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.