diff --git a/packages/builder/src/components/backend/TableNavigator/utils.js b/packages/builder/src/components/backend/TableNavigator/utils.js index 5f13f9df33..2889d90cc0 100644 --- a/packages/builder/src/components/backend/TableNavigator/utils.js +++ b/packages/builder/src/components/backend/TableNavigator/utils.js @@ -58,7 +58,7 @@ export const parseFile = e => { resolveRows(rows) }) .catch(() => { - reject("cannot parse csv.") + reject("cannot parse csv") }) } }) diff --git a/packages/server/src/utilities/csv.ts b/packages/server/src/utilities/csv.ts index 8540b62941..1e139f36fa 100644 --- a/packages/server/src/utilities/csv.ts +++ b/packages/server/src/utilities/csv.ts @@ -7,40 +7,51 @@ export async function jsonFromCsvString(csvString: string) { let numOfHeaders: number | undefined = undefined let headerMismatch = false - const castedWithEmptyValues = await csv({ - ignoreEmpty: true, - delimiter: possibleDelimeters[i], - }).fromString(csvString) + try { + const castedWithEmptyValues = await csv({ + ignoreEmpty: true, + delimiter: possibleDelimeters[i], + }).fromString(csvString) - // By default the csvtojson library casts empty values as empty strings. This - // is causing issues on conversion. ignoreEmpty will remove the key completly - // if empty, so creating this empty object will ensure we return the values - // with the keys but empty values - const result = await csv({ - ignoreEmpty: false, - delimiter: possibleDelimeters[i], - }).fromString(csvString) - for (const [i, r] of result.entries()) { - const columns = Object.keys(r) - if (numOfHeaders == null) { - numOfHeaders = columns.length + // By default the csvtojson library casts empty values as empty strings. This + // is causing issues on conversion. ignoreEmpty will remove the key completly + // if empty, so creating this empty object will ensure we return the values + // with the keys but empty values + const result = await csv({ + ignoreEmpty: false, + delimiter: possibleDelimeters[i], + }).fromString(csvString) + for (const [i, r] of result.entries()) { + // The purpose of this is to find rows that have been split + // into the wrong number of columns - Any valid .CSV file will have + // the same number of colums in each row + // If the number of columms in each row is different to + // the number of headers, this isn't the right delimiter + const columns = Object.keys(r) + if (numOfHeaders == null) { + numOfHeaders = columns.length + } + if (numOfHeaders === 1 || numOfHeaders !== columns.length) { + headerMismatch = true + break + } + for (const [key] of Object.entries(r).filter( + ([, value]) => value === "" + )) { + if (castedWithEmptyValues[i][key] === undefined) { + r[key] = null + } + } } - if (numOfHeaders !== columns.length) { - headerMismatch = true - break + if (headerMismatch) { + continue + } else { + return result } - for (const [key] of Object.entries(r).filter( - ([, value]) => value === "" - )) { - // if (castedWithEmptyValues[i][key] === undefined) { - // r[key] = null - // } - } - } - if (headerMismatch) { + } catch (err) { + // Splitting on the wrong delimiter sometimes throws CSV parsing error + // (eg unterminated strings), which tells us we've picked the wrong delimiter continue - } else { - return result } } throw new Error("Unable to determine delimiter")