Skip to content

Commit ecd2915

Browse files
committed
Throw for sparse arrays
1 parent 18a0645 commit ecd2915

File tree

2 files changed

+61
-11
lines changed

2 files changed

+61
-11
lines changed

src/data-connect/data-connect-api-client-internal.ts

+5
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ export class DataConnectApiClient {
215215
return String(data);
216216
}
217217
if (validator.isArray(data)) {
218+
if (data.includes(null) || data.includes(undefined)) {
219+
throw new FirebaseDataConnectError(
220+
DATA_CONNECT_ERROR_CODE_MAPPING.INVALID_ARGUMENT,
221+
'Array elements cannot contain `null` or `undefined` values.');
222+
}
218223
const elements = data.map(item => this.objectToString(item)).join(', ');
219224
return `[${elements}]`;
220225
}

test/unit/data-connect/data-connect-api-client-internal.spec.ts

+56-11
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,14 @@ describe('DataConnectApiClient CRUD helpers', () => {
261261
},
262262
notes: undefined,
263263
releaseYear: undefined,
264-
extras: [1, undefined, 'hello', undefined, { a: 1, b: undefined }]
264+
extras: [1, 'hello', { a: 1, b: undefined }]
265265
};
266266

267+
const dataWithSparseArray = {
268+
genre: 'Action',
269+
extras: [1, undefined, 'hello', undefined, { a: 1, b: undefined }]
270+
}
271+
267272
const tableNames = ['movie', 'Movie', 'MOVIE', 'mOvIE', 'toybox', 'toyBox', 'toyBOX', 'ToyBox', 'TOYBOX'];
268273
const formatedTableNames = ['movie', 'movie', 'mOVIE', 'mOvIE', 'toybox', 'toyBox', 'toyBOX', 'toyBox', 'tOYBOX'];
269274

@@ -343,7 +348,7 @@ describe('DataConnectApiClient CRUD helpers', () => {
343348
title: "Die Hard",
344349
ratings: null,
345350
director: {},
346-
extras: [1, null, "hello", null, { a: 1 }]
351+
extras: [1, "hello", { a: 1 }]
347352
})
348353
}`;
349354
await apiClient.insert(tableName, dataWithUndefined);
@@ -369,6 +374,16 @@ describe('DataConnectApiClient CRUD helpers', () => {
369374
await expect(apiClientQueryError.insert(tableName, { data: 1 }))
370375
.to.be.rejectedWith(FirebaseDataConnectError, `${serverErrorString}. ${additionalErrorMessageForBulkImport}`);
371376
});
377+
378+
[
379+
{ a: [null] }, { a: [undefined] }, { a: [1, null, 2] }, { a: [1, undefined, 2] },
380+
{ a: ['a', null, 'b', undefined, 'd'] }, dataWithSparseArray
381+
].forEach((data) => {
382+
it('should throw FirebaseDataConnectError for arrays with null or undefined elements', async () => {
383+
await expect(apiClient.insert(tableName, data))
384+
.to.be.rejectedWith(FirebaseDataConnectError, /Array elements cannot contain `null` or `undefined` values./);
385+
});
386+
});
372387
});
373388

374389
// --- INSERT MANY TESTS ---
@@ -417,14 +432,14 @@ describe('DataConnectApiClient CRUD helpers', () => {
417432
title: "Die Hard",
418433
ratings: null,
419434
director: {},
420-
extras: [1, null, "hello", null, { a: 1 }]
435+
extras: [1, "hello", { a: 1 }]
421436
},
422437
{
423438
genre: "Action",
424439
title: "Die Hard",
425440
ratings: null,
426441
director: {},
427-
extras: [1, null, "hello", null, { a: 1 }]
442+
extras: [1, "hello", { a: 1 }]
428443
}])
429444
}`;
430445
await apiClient.insertMany(tableName, dataArray);
@@ -455,6 +470,16 @@ describe('DataConnectApiClient CRUD helpers', () => {
455470
await expect(apiClientQueryError.insertMany(tableName, [{ data: 1 }]))
456471
.to.be.rejectedWith(FirebaseDataConnectError, `${serverErrorString}. ${additionalErrorMessageForBulkImport}`);
457472
});
473+
474+
[
475+
[null], [undefined], [1, null, 2], [1, undefined, 2],
476+
['a', null, 'b', undefined, 'd'], [dataWithSparseArray]
477+
].forEach((data) => {
478+
it('should throw FirebaseDataConnectError for arrays with null or undefined elements', async () => {
479+
await expect(apiClient.insertMany(tableName, data))
480+
.to.be.rejectedWith(FirebaseDataConnectError, /Array elements cannot contain `null` or `undefined` values./);
481+
});
482+
});
458483
});
459484

460485
// --- UPSERT TESTS ---
@@ -476,10 +501,10 @@ describe('DataConnectApiClient CRUD helpers', () => {
476501
});
477502

478503
it('should call executeGraphql with the correct mutation for complex data', async () => {
479-
const complexData = { id: 'key2', active: false, items: [1, null], detail: { status: 'done/\\' } };
504+
const complexData = { id: 'key2', active: false, items: [1, 2], detail: { status: 'done/\\' } };
480505
const expectedMutation = `
481506
mutation { ${formatedTableName}_upsert(data:
482-
{ id: "key2", active: false, items: [1, null], detail: { status: "done/\\\\" } }) }`;
507+
{ id: "key2", active: false, items: [1, 2], detail: { status: "done/\\\\" } }) }`;
483508
await apiClient.upsert(tableName, complexData);
484509
expect(executeGraphqlStub).to.have.been.calledOnceWithExactly(normalizeGraphQLString(expectedMutation));
485510
});
@@ -492,7 +517,7 @@ describe('DataConnectApiClient CRUD helpers', () => {
492517
title: "Die Hard",
493518
ratings: null,
494519
director: {},
495-
extras: [1, null, "hello", null, { a: 1 }]
520+
extras: [1, "hello", { a: 1 }]
496521
})
497522
}`;
498523
await apiClient.upsert(tableName, dataWithUndefined);
@@ -518,6 +543,16 @@ describe('DataConnectApiClient CRUD helpers', () => {
518543
await expect(apiClientQueryError.upsert(tableName, { data: 1 }))
519544
.to.be.rejectedWith(FirebaseDataConnectError, `${serverErrorString}. ${additionalErrorMessageForBulkImport}`);
520545
});
546+
547+
[
548+
{ a: [null] }, { a: [undefined] }, { a: [1, null, 2] }, { a: [1, undefined, 2] },
549+
{ a: ['a', null, 'b', undefined, 'd'] }, dataWithSparseArray
550+
].forEach((data) => {
551+
it('should throw FirebaseDataConnectError for arrays with null or undefined elements', async () => {
552+
await expect(apiClient.upsert(tableName, data))
553+
.to.be.rejectedWith(FirebaseDataConnectError, /Array elements cannot contain `null` or `undefined` values./);
554+
});
555+
});
521556
});
522557

523558
// --- UPSERT MANY TESTS ---
@@ -542,11 +577,11 @@ describe('DataConnectApiClient CRUD helpers', () => {
542577
it('should call executeGraphql with the correct mutation for complex data array', async () => {
543578
const complexDataArray = [
544579
{ id: 'x', active: true, info: { nested: 'n1/\\"x' } },
545-
{ id: 'y', scores: [null, 2] }
580+
{ id: 'y', scores: [1, 2] }
546581
];
547582
const expectedMutation = `
548583
mutation { ${formatedTableName}_upsertMany(data:
549-
[{ id: "x", active: true, info: { nested: "n1/\\\\\\"x" } }, { id: "y", scores: [null, 2] }]) }`;
584+
[{ id: "x", active: true, info: { nested: "n1/\\\\\\"x" } }, { id: "y", scores: [1, 2] }]) }`;
550585
await apiClient.upsertMany(tableName, complexDataArray);
551586
expect(executeGraphqlStub).to.have.been.calledOnceWithExactly(normalizeGraphQLString(expectedMutation));
552587
});
@@ -563,14 +598,14 @@ describe('DataConnectApiClient CRUD helpers', () => {
563598
title: "Die Hard",
564599
ratings: null,
565600
director: {},
566-
extras: [1, null, "hello", null, { a: 1 }]
601+
extras: [1, "hello", { a: 1 }]
567602
},
568603
{
569604
genre: "Action",
570605
title: "Die Hard",
571606
ratings: null,
572607
director: {},
573-
extras: [1, null, "hello", null, { a: 1 }]
608+
extras: [1, "hello", { a: 1 }]
574609
}])
575610
}`;
576611
await apiClient.upsertMany(tableName, dataArray);
@@ -601,5 +636,15 @@ describe('DataConnectApiClient CRUD helpers', () => {
601636
await expect(apiClientQueryError.upsertMany(tableName, [{ data: 1 }]))
602637
.to.be.rejectedWith(FirebaseDataConnectError, `${serverErrorString}. ${additionalErrorMessageForBulkImport}`);
603638
});
639+
640+
[
641+
[null], [undefined], [1, null, 2], [1, undefined, 2],
642+
['a', null, 'b', undefined, 'd'], [dataWithSparseArray]
643+
].forEach((data) => {
644+
it('should throw FirebaseDataConnectError for arrays with null or undefined elements', async () => {
645+
await expect(apiClient.upsertMany(tableName, data))
646+
.to.be.rejectedWith(FirebaseDataConnectError, /Array elements cannot contain `null` or `undefined` values./);
647+
});
648+
});
604649
});
605650
});

0 commit comments

Comments
 (0)