From 91e003153b32a3a0604d169064c72abb20f2f573 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 12 Sep 2021 09:54:17 +0200 Subject: [PATCH] Updated logic to import servers, to not check the file type --- src/servers/services/ServersImporter.ts | 32 +++++---- test/servers/services/ServersImporter.test.ts | 67 ++++++++++++++++--- 2 files changed, 77 insertions(+), 22 deletions(-) diff --git a/src/servers/services/ServersImporter.ts b/src/servers/services/ServersImporter.ts index 7dcf8175..28f43b65 100644 --- a/src/servers/services/ServersImporter.ts +++ b/src/servers/services/ServersImporter.ts @@ -1,29 +1,37 @@ import { CsvJson } from 'csvjson'; import { ServerData } from '../data'; -interface CsvFile extends File { - type: 'text/csv' | 'text/comma-separated-values' | 'application/csv'; -} +const validateServer = (server: any): server is ServerData => + typeof server.url === 'string' && typeof server.apiKey === 'string' && typeof server.name === 'string'; -const CSV_MIME_TYPES = [ 'text/csv', 'text/comma-separated-values', 'application/csv' ]; -const isCsv = (file?: File | null): file is CsvFile => !!file && CSV_MIME_TYPES.includes(file.type); +const validateServers = (servers: any): servers is ServerData[] => + Array.isArray(servers) && servers.every(validateServer); export default class ServersImporter { - public constructor(private readonly csvjson: CsvJson, private readonly fileReaderFactory: () => FileReader) {} + public constructor(private readonly csvJson: CsvJson, private readonly fileReaderFactory: () => FileReader) {} public readonly importServersFromFile = async (file?: File | null): Promise => { - if (!isCsv(file)) { - throw new Error('No file provided or file is not a CSV'); + if (!file) { + throw new Error('No file provided'); } const reader = this.fileReaderFactory(); - return new Promise((resolve) => { + return new Promise((resolve, reject) => { reader.addEventListener('loadend', (e: ProgressEvent) => { - const content = e.target?.result?.toString() ?? ''; - const servers = this.csvjson.toObject(content); + try { + // TODO Read as stream, otherwise, if the file is too big, this will block the browser tab + const content = e.target?.result?.toString() ?? ''; + const servers = this.csvJson.toObject(content); - resolve(servers); + if (!validateServers(servers)) { + throw new Error('Provided file does not have the right format.'); + } + + resolve(servers); + } catch (e) { + reject(e); + } }); reader.readAsText(file); }); diff --git a/test/servers/services/ServersImporter.test.ts b/test/servers/services/ServersImporter.test.ts index 60e5dfc4..3f26ca96 100644 --- a/test/servers/services/ServersImporter.test.ts +++ b/test/servers/services/ServersImporter.test.ts @@ -21,23 +21,70 @@ describe('ServersImporter', () => { describe('importServersFromFile', () => { it('rejects with error if no file was provided', async () => { await expect(importer.importServersFromFile()).rejects.toEqual( - new Error('No file provided or file is not a CSV'), + new Error('No file provided'), ); }); - it('rejects with error if provided file is not a CSV', async () => { - await expect(importer.importServersFromFile(Mock.of({ type: 'text/html' }))).rejects.toEqual( - new Error('No file provided or file is not a CSV'), - ); + it('rejects with error if parsing the file fails', async () => { + const expectedError = new Error('Error parsing file'); + + toObject.mockImplementation(() => { + throw expectedError; + }); + + await expect(importer.importServersFromFile(Mock.of({ type: 'text/html' }))).rejects.toEqual(expectedError); }); it.each([ - [ 'text/csv' ], - [ 'text/comma-separated-values' ], - [ 'application/csv' ], - ])('reads file when a CSV is provided', async (type) => { - await importer.importServersFromFile(Mock.of({ type })); + [{}], + [ undefined ], + [[{ foo: 'bar' }]], + [ + [ + { + url: 1, + apiKey: 1, + name: 1, + }, + ], + ], + [ + [ + { + url: 'foo', + apiKey: 'foo', + name: 'foo', + }, + { bar: 'foo' }, + ], + ], + ])('rejects with error if provided file does not parse to valid list of servers', async (parsedObject) => { + toObject.mockReturnValue(parsedObject); + await expect(importer.importServersFromFile(Mock.of({ type: 'text/html' }))).rejects.toEqual( + new Error('Provided file does not have the right format.'), + ); + }); + + it('reads file when a CSV containing valid servers is provided', async () => { + const expectedServers = [ + { + url: 'foo', + apiKey: 'foo', + name: 'foo', + }, + { + url: 'bar', + apiKey: 'bar', + name: 'bar', + }, + ]; + + toObject.mockReturnValue(expectedServers); + + const result = await importer.importServersFromFile(Mock.all()); + + expect(result).toEqual(expectedServers); expect(readAsText).toHaveBeenCalledTimes(1); expect(toObject).toHaveBeenCalledTimes(1); });