Skip to content

Commit

Permalink
Use csvtojson to get the headers of a csv instead of a custom method (#…
Browse files Browse the repository at this point in the history
…7392)

this way there will not be not matching headers in some scenarios
  • Loading branch information
daneryl authored Oct 21, 2024
1 parent 9a47d06 commit 910f6c7
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 24 deletions.
15 changes: 6 additions & 9 deletions app/api/csv/csv.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import readline from 'readline';

import csvtojson from 'csvtojson';

import { Readable } from 'stream';
Expand All @@ -8,18 +6,17 @@ import importFile, { ImportFile } from './importFile';
type CSVRow = { [k: string]: string };

const DELIMITERS = [',', ';'];
const DELIMITER_REGEX = new RegExp(`[${DELIMITERS.join('')}]`);

const peekHeaders = async (readSource: Readable | string): Promise<string[]> => {
const readStream =
typeof readSource === 'string' ? await importFile(readSource).readStream() : readSource;
let headers: string[] = [];
const rl = readline.createInterface({ input: readStream });
const line = (await rl[Symbol.asyncIterator]().next()).value;
headers = line.split(DELIMITER_REGEX);
rl.close();
readStream.unpipe();
readStream.destroy();
const stream = csvtojson().fromStream(readStream);
await stream.on('header', async h => {
headers = h;
await stream.end();
});

return headers;
};

Expand Down
10 changes: 9 additions & 1 deletion app/api/csv/importFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { createError } from 'api/utils';
import zipFile from 'api/utils/zipFile';
// eslint-disable-next-line node/no-restricted-import
import { createReadStream } from 'fs';
// eslint-disable-next-line node/no-restricted-import
import { readFile } from 'fs/promises';

const extractFromZip = async (zipPath: string, fileName: string) => {
const readStream = await zipFile(zipPath).findReadStream(entry => entry === fileName);
Expand All @@ -16,14 +18,19 @@ const extractFromZip = async (zipPath: string, fileName: string) => {
return readStream;
};

export class ImportFile {
class ImportFile {
filePath: string;

constructor(filePath: string) {
this.filePath = filePath;
}

private async checkFileExists() {
await readFile(this.filePath);
}

async readStream(fileName = 'import.csv') {
await this.checkFileExists();
if (path.extname(this.filePath) === '.zip') {
return extractFromZip(this.filePath, fileName);
}
Expand All @@ -46,4 +53,5 @@ export class ImportFile {

const importFile = (filePath: string) => new ImportFile(filePath);

export { ImportFile };
export default importFile;
10 changes: 5 additions & 5 deletions app/api/csv/specs/csv.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('peekHeaders()', () => {
title1, text1, text1_es, text1_en`;
const { mockedFile, mockedFileStream } = mockFileStream(content);
const headers = await peekHeaders(mockedFileStream);
expect(headers).toEqual(['title', ' textprop', ' textprop__es', ' textprop__en']);
expect(headers).toEqual(['title', 'textprop', 'textprop__es', 'textprop__en']);
mockedFile.mockRestore();
});

Expand All @@ -27,10 +27,10 @@ describe('peekHeaders()', () => {
'title',
'unrelated_property',
'select_property__en',
' Select Property__es',
'Select Property__es',
'Multiselect Property__en',
' multiselect_property__es',
' no_new_value_select',
'multiselect_property__es',
'no_new_value_select',
]);
});
});
Expand Down Expand Up @@ -60,7 +60,7 @@ describe('validateFormat()', () => {
message: 'Expected 3 columns, but found 2.',
},
{
content: 'title,textprop\ntitle1',
content: 'title,textprop\ntitle1,',
columns: 1,
message: 'Expected 1 columns, but found 2.',
},
Expand Down
8 changes: 6 additions & 2 deletions app/api/csv/specs/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import path from 'path';
import yazl from 'yazl';
import { Readable } from 'stream';
// eslint-disable-next-line node/no-restricted-import
import fsPromises from 'fs/promises';
// eslint-disable-next-line node/no-restricted-import
import fs from 'fs';

const createTestingZip = (filesToZip, fileName, directory = __dirname) =>
Expand Down Expand Up @@ -37,7 +39,9 @@ class ReadableString extends Readable {

const stream = string => new ReadableString(string);

const mockCsvFileReadStream = str =>
jest.spyOn(fs, 'createReadStream').mockImplementation(() => stream(str));
const mockCsvFileReadStream = str => {
jest.spyOn(fsPromises, 'readFile').mockImplementation(() => {});
return jest.spyOn(fs, 'createReadStream').mockImplementation(() => stream(str));
};

export { stream, createTestingZip, mockCsvFileReadStream };
3 changes: 3 additions & 0 deletions app/api/csv/typeParsers/multiselect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ function labelNotNull(label: string | null): label is string {
function splitMultiselectLabels(labelString: string): {
labelInfos: LabelInfo[];
} {
if (!labelString) {
return { labelInfos: [] };
}
const labels = labelString
.split(csvConstants.multiValueSeparator)
.map(l => l.trim())
Expand Down
8 changes: 1 addition & 7 deletions app/api/i18n/specs/translations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,7 @@ describe('translations', () => {
});

it('should throw error when translation is not available', async () => {
const readFileMock = jest
.spyOn(fs.promises, 'readFile')
.mockRejectedValue({ code: 'ENOENT' });

await expect(translations.importPredefined('zh')).rejects.toThrowError(
await expect(translations.importPredefined('non-existent')).rejects.toBeInstanceOf(
UITranslationNotAvailable
);

Expand All @@ -441,8 +437,6 @@ describe('translations', () => {
expect(ZHTranslations.Password).toBe('Password');
expect(ZHTranslations.Account).toBe('Account');
expect(ZHTranslations.Age).toBe('Age');

readFileMock.mockRestore();
});
});
});

0 comments on commit 910f6c7

Please sign in to comment.