From ca27c88c921de69e4ee358532f6c4b2481c35cc3 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 17 Jan 2026 02:07:35 +0000 Subject: [PATCH 1/2] Fix: Correctly serialize strings with special characters in DataConnect Replaces manual string escaping with JSON.stringify in DataConnect API client to properly handle newlines and other control characters in mutation arguments. Adds a regression test for verifying newline escaping. Fixes #3043 --- .../data-connect-api-client-internal.ts | 5 +---- .../data-connect-api-client-internal.spec.ts | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/data-connect/data-connect-api-client-internal.ts b/src/data-connect/data-connect-api-client-internal.ts index 28284db6a4..6292d023aa 100644 --- a/src/data-connect/data-connect-api-client-internal.ts +++ b/src/data-connect/data-connect-api-client-internal.ts @@ -413,10 +413,7 @@ export class DataConnectApiClient { */ private objectToString(data: unknown): string { if (typeof data === 'string') { - const escapedString = data - .replace(/\\/g, '\\\\') // Replace \ with \\ - .replace(/"/g, '\\"'); // Replace " with \" - return `"${escapedString}"`; + return JSON.stringify(data); } if (typeof data === 'number' || typeof data === 'boolean' || data === null) { return String(data); diff --git a/test/unit/data-connect/data-connect-api-client-internal.spec.ts b/test/unit/data-connect/data-connect-api-client-internal.spec.ts index 628608a9e9..00b5ce8b27 100644 --- a/test/unit/data-connect/data-connect-api-client-internal.spec.ts +++ b/test/unit/data-connect/data-connect-api-client-internal.spec.ts @@ -928,4 +928,24 @@ describe('DataConnectApiClient CRUD helpers', () => { .to.be.rejectedWith(FirebaseDataConnectError, `${serverErrorString}. ${additionalErrorMessageForBulkImport}`); }); }); + + // --- Issue #3043 --- + describe('Issue #3043: String serialization', () => { + it('should correctly escape newlines in strings during insert', async () => { + const data = { + content: 'Line 1\nLine 2', + }; + // JSON.stringify("Line 1\nLine 2") -> "Line 1\nLine 2" + // We expect the generated mutation to contain the string content with properly escaped newline. + // So the GraphQL argument should look like content: "Line 1\nLine 2" + // Note: in the query string, we want literal characters \ and n. + + await apiClient.insert(tableName, data); + const callArgs = executeGraphqlStub.firstCall.args[0]; + + // Expected part of the query: content: "Line 1\nLine 2" + // which means the string "Line 1\\nLine 2" should be present in the call args. + expect(callArgs).to.include('content: "Line 1\\nLine 2"'); + }); + }); }); From 3c739c5437950affce41d36ed993fbe5a05de348 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 17 Jan 2026 02:11:52 +0000 Subject: [PATCH 2/2] Fix: Address PR comments and add more tests - Removed specified lines. - Added tests for backslash, double quotes, tab character, and emoji string serialization. --- .../data-connect-api-client-internal.spec.ts | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/test/unit/data-connect/data-connect-api-client-internal.spec.ts b/test/unit/data-connect/data-connect-api-client-internal.spec.ts index 00b5ce8b27..ba51086e77 100644 --- a/test/unit/data-connect/data-connect-api-client-internal.spec.ts +++ b/test/unit/data-connect/data-connect-api-client-internal.spec.ts @@ -929,16 +929,11 @@ describe('DataConnectApiClient CRUD helpers', () => { }); }); - // --- Issue #3043 --- describe('Issue #3043: String serialization', () => { - it('should correctly escape newlines in strings during insert', async () => { + it('should correctly escape special characters in strings during insert', async () => { const data = { content: 'Line 1\nLine 2', }; - // JSON.stringify("Line 1\nLine 2") -> "Line 1\nLine 2" - // We expect the generated mutation to contain the string content with properly escaped newline. - // So the GraphQL argument should look like content: "Line 1\nLine 2" - // Note: in the query string, we want literal characters \ and n. await apiClient.insert(tableName, data); const callArgs = executeGraphqlStub.firstCall.args[0]; @@ -947,5 +942,56 @@ describe('DataConnectApiClient CRUD helpers', () => { // which means the string "Line 1\\nLine 2" should be present in the call args. expect(callArgs).to.include('content: "Line 1\\nLine 2"'); }); + + it('should correctly escape backslash', async () => { + const data = { + content: 'Backslash \\', + }; + + await apiClient.insert(tableName, data); + const callArgs = executeGraphqlStub.firstCall.args[0]; + + // "Backslash \\" + // Escaped for GraphQL: "Backslash \\\\" + expect(callArgs).to.include('content: "Backslash \\\\"'); + }); + + it('should correctly escape double quotes', async () => { + const data = { + content: 'Quote "test"', + }; + + await apiClient.insert(tableName, data); + const callArgs = executeGraphqlStub.firstCall.args[0]; + + // "Quote \"test\"" + // Escaped for GraphQL: "Quote \\"test\\"" + expect(callArgs).to.include('content: "Quote \\"test\\""'); + }); + + it('should correctly escape tab character', async () => { + const data = { + content: 'Tab\tCharacter', + }; + + await apiClient.insert(tableName, data); + const callArgs = executeGraphqlStub.firstCall.args[0]; + + // "Tab\tCharacter" + // Escaped for GraphQL: "Tab\\tCharacter" + expect(callArgs).to.include('content: "Tab\\tCharacter"'); + }); + + it('should correctly handle emojis', async () => { + const data = { + content: 'Emoji 😊', + }; + + await apiClient.insert(tableName, data); + const callArgs = executeGraphqlStub.firstCall.args[0]; + + // "Emoji 😊" + expect(callArgs).to.include('content: "Emoji 😊"'); + }); }); });