diff --git a/packages/server/src/api/controllers/view/exporters.ts b/packages/server/src/api/controllers/view/exporters.ts index 76e5f865a3..94487e4dd1 100644 --- a/packages/server/src/api/controllers/view/exporters.ts +++ b/packages/server/src/api/controllers/view/exporters.ts @@ -26,7 +26,7 @@ export function csv( headers.map(header => { const val = row[header] if (typeof val === "object" && !(val instanceof Date)) { - return `"${JSON.stringify(val).replace(/"/g, "'")}"` + return `"${escapeCsvString(JSON.stringify(val))}"` } if (val !== undefined) { return `"${escapeCsvString(val.toString())}"` diff --git a/packages/server/src/api/routes/tests/application.spec.ts b/packages/server/src/api/routes/tests/application.spec.ts index cff205bbfa..e0b295f78e 100644 --- a/packages/server/src/api/routes/tests/application.spec.ts +++ b/packages/server/src/api/routes/tests/application.spec.ts @@ -1,19 +1,6 @@ import { DEFAULT_TABLES } from "../../../db/defaultData/datasource_bb_default" import { setEnv } from "../../../environment" -jest.mock("../../../utilities/redis", () => ({ - init: jest.fn(), - getLocksById: () => { - return {} - }, - doesUserHaveLock: () => { - return true - }, - updateLock: jest.fn(), - setDebounce: jest.fn(), - checkDebounce: jest.fn(), - shutdown: jest.fn(), -})) import { checkBuilderEndpoint } from "./utilities/TestFunctions" import * as setup from "./utilities" import { AppStatus } from "../../../db/utils" diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index b349a1df8a..3a732cc662 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2515,15 +2515,14 @@ if (descriptions.length) { csvString: exportedValue, }) - const stringified = (value: string) => - JSON.stringify(value).replace(/"/g, "'") + const stringified = (value: string) => JSON.stringify(value) const matchingObject = ( key: string, value: any, isArray: boolean ) => { - const objectMatcher = `{'${key}':'${value[key]}'.*?}` + const objectMatcher = `{"${key}":"${value[key]}".*?}` if (isArray) { return expect.stringMatching( new RegExp(`^\\[${objectMatcher}\\]$`) diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index 29b576d16a..6f790df9ad 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -1246,10 +1246,7 @@ if (descriptions.length) { }) describe.each([ - [ - RowExportFormat.CSV, - (val: any) => JSON.stringify(val).replace(/"/g, "'"), - ], + [RowExportFormat.CSV, (val: any) => JSON.stringify(val)], [RowExportFormat.JSON, (val: any) => val], ])("import validation (%s)", (_, userParser) => { const basicSchema: TableSchema = { diff --git a/packages/server/src/utilities/schema.ts b/packages/server/src/utilities/schema.ts index b13999c842..8309014a61 100644 --- a/packages/server/src/utilities/schema.ts +++ b/packages/server/src/utilities/schema.ts @@ -270,6 +270,7 @@ function parseJsonExport(value: any) { if (typeof value !== "string") { return value } + try { const parsed = JSON.parse(value) @@ -278,12 +279,17 @@ function parseJsonExport(value: any) { if ( e.message.startsWith("Expected property name or '}' in JSON at position ") ) { - // This was probably converted as CSV and it has single quotes instead of double ones + // In order to store JSON within CSVs what we used to do is replace double + // quotes with single quotes. This results in invalid JSON, so the line + // below is a workaround to parse it. However, this method of storing JSON + // was never valid, and we don't do it anymore. However, people may have + // exported data and stored it, hoping to be able to restore it later, so + // we leave this in place to support that. const parsed = JSON.parse(value.replace(/'/g, '"')) return parsed as T } - // It is no a valid JSON + // It is not valid JSON throw e } }