diff --git a/packages/web/src/DragAndDropFileTarget.js b/packages/web/src/DragAndDropFileTarget.js index fae06f3f8..41e0211f0 100644 --- a/packages/web/src/DragAndDropFileTarget.js +++ b/packages/web/src/DragAndDropFileTarget.js @@ -1,5 +1,6 @@ import React from 'react'; import styled from 'styled-components'; +import { fileformats } from './fileformats'; import { FontIcon } from './icons'; import useTheme from './theme/useTheme'; @@ -47,7 +48,13 @@ export default function DragAndDropFileTarget({ isDragActive, inputProps }) { Drop the files to upload to DbGate - Supported file types: csv, MS Excel, json-lines + + Supported file types:{' '} + {fileformats + .filter((x) => x.readerFunc) + .map((x) => x.name) + .join(', ')} + diff --git a/packages/web/src/fileformats/csv.ts b/packages/web/src/fileformats/csv.ts index 5e6ec2b90..410a70d38 100644 --- a/packages/web/src/fileformats/csv.ts +++ b/packages/web/src/fileformats/csv.ts @@ -1,7 +1,13 @@ -export default { +import fileFormatBase from './fileFormatBase'; +import { FileFormatDefinition } from './types'; + +const csvFormat: FileFormatDefinition = { + ...fileFormatBase, storageType: 'csv', extension: 'csv', - name: 'CSV files', + name: 'CSV', readerFunc: 'csvReader', writerFunc: 'csvWriter', }; + +export default csvFormat; diff --git a/packages/web/src/fileformats/excel.ts b/packages/web/src/fileformats/excel.ts index d73eab3bd..1ae48e0f9 100644 --- a/packages/web/src/fileformats/excel.ts +++ b/packages/web/src/fileformats/excel.ts @@ -1,6 +1,27 @@ -export default { +import { DatabaseInfo } from 'dbgate-types'; +import axios from '../utility/axios'; +import fileFormatBase from './fileFormatBase'; +import { FileFormatDefinition } from './types'; + +const excelFormat: FileFormatDefinition = { + ...fileFormatBase, storageType: 'excel', extension: 'xlsx', - name: 'MS Excel files', + name: 'MS Excel', readerFunc: 'excelSheetReader', + + addFilesToSourceList: async (file, newSources, newValues) => { + const resp = await axios.get(`files/analyse-excel?filePath=${encodeURIComponent(file.full)}`); + const structure: DatabaseInfo = resp.data; + for (const table of structure.tables) { + const sourceName = table.pureName; + newSources.push(sourceName); + newValues[`sourceFile_${sourceName}`] = { + fileName: file.full, + sheetName: table.pureName, + }; + } + }, }; + +export default excelFormat; diff --git a/packages/web/src/fileformats/fileFormatBase.ts b/packages/web/src/fileformats/fileFormatBase.ts new file mode 100644 index 000000000..cb7bc1265 --- /dev/null +++ b/packages/web/src/fileformats/fileFormatBase.ts @@ -0,0 +1,11 @@ +const fileFormatBase = { + addFilesToSourceList: async (file, newSources, newValues) => { + const sourceName = file.name; + newSources.push(sourceName); + newValues[`sourceFile_${sourceName}`] = { + fileName: file.full, + }; + }, +}; + +export default fileFormatBase; diff --git a/packages/web/src/fileformats/jsonl.ts b/packages/web/src/fileformats/jsonl.ts index b58b00e23..02882140c 100644 --- a/packages/web/src/fileformats/jsonl.ts +++ b/packages/web/src/fileformats/jsonl.ts @@ -1,7 +1,13 @@ -export default { +import fileFormatBase from './fileFormatBase'; +import { FileFormatDefinition } from './types'; + +const jsonlFormat: FileFormatDefinition = { + ...fileFormatBase, storageType: 'jsonl', extension: 'jsonl', name: 'JSON lines', readerFunc: 'jsonLinesReader', writerFunc: 'jsonLinesWriter', }; + +export default jsonlFormat; diff --git a/packages/web/src/fileformats/types.ts b/packages/web/src/fileformats/types.ts index f6ba39495..b44e65f19 100644 --- a/packages/web/src/fileformats/types.ts +++ b/packages/web/src/fileformats/types.ts @@ -4,4 +4,13 @@ export interface FileFormatDefinition { name: string; readerFunc?: string; writerFunc?: string; + addFilesToSourceList: ( + file: { + full: string; + }, + newSources: string[], + newValues: { + [key: string]: any; + } + ) => void; } diff --git a/packages/web/src/impexp/ImportExportConfigurator.js b/packages/web/src/impexp/ImportExportConfigurator.js index d03fc40f0..f90172c4b 100644 --- a/packages/web/src/impexp/ImportExportConfigurator.js +++ b/packages/web/src/impexp/ImportExportConfigurator.js @@ -96,43 +96,31 @@ function getFileFilters(storageType) { return res; } -async function addFilesToSourceList(files, values, setFieldValue, preferedStorageType, setPreviewSource) { +async function addFilesToSourceList(files, values, setValues, preferedStorageType, setPreviewSource) { const newSources = []; + const newValues = {}; const storage = preferedStorageType || values.sourceStorageType; for (const file of getAsArray(files)) { - if (isFileStorage(storage)) { - if (storage == 'excel') { - const resp = await axios.get(`files/analyse-excel?filePath=${encodeURIComponent(file.full)}`); - /** @type {import('dbgate-types').DatabaseInfo} */ - const structure = resp.data; - for (const table of structure.tables) { - const sourceName = table.pureName; - newSources.push(sourceName); - setFieldValue(`sourceFile_${sourceName}`, { - fileName: file.full, - sheetName: table.pureName, - }); - } - } else { - const sourceName = file.name; - newSources.push(sourceName); - setFieldValue(`sourceFile_${sourceName}`, { - fileName: file.full, - }); - } + const format = findFileFormat(storage); + if (format && format.addFilesToSourceList) { + await format.addFilesToSourceList(file, newSources, newValues); } } - setFieldValue('sourceList', [...(values.sourceList || []).filter((x) => !newSources.includes(x)), ...newSources]); + newValues['sourceList'] = [...(values.sourceList || []).filter((x) => !newSources.includes(x)), ...newSources]; if (preferedStorageType && preferedStorageType != values.sourceStorageType) { - setFieldValue('sourceStorageType', preferedStorageType); + newValues['sourceStorageType'] = preferedStorageType; } + setValues({ + ...values, + ...newValues, + }); if (setPreviewSource && newSources.length == 1) { setPreviewSource(newSources[0]); } } function ElectronFilesInput() { - const { values, setFieldValue } = useFormikContext(); + const { values, setValues } = useFormikContext(); const electron = getElectron(); const [isLoading, setIsLoading] = React.useState(false); @@ -151,7 +139,7 @@ function ElectronFilesInput() { ...path.parse(full), })), values, - setFieldValue + setValues ); } finally { setIsLoading(false); @@ -195,7 +183,7 @@ function SourceTargetConfig({ { value: 'database', label: 'Database', directions: ['source', 'target'] }, ...fileformats.map((format) => ({ value: format.storageType, - label: format.name, + label: `${format.name} files(s)`, directions: getFileFormatDirections(format), })), { value: 'query', label: 'SQL Query', directions: ['source'] }, @@ -335,7 +323,7 @@ function SourceName({ name }) { } export default function ImportExportConfigurator({ uploadedFile = undefined, onChangePreview = undefined }) { - const { values, setFieldValue } = useFormikContext(); + const { values, setFieldValue, setValues } = useFormikContext(); const targetDbinfo = useDatabaseInfo({ conid: values.targetConnectionId, database: values.targetDatabaseName }); const sourceConnectionInfo = useConnectionInfo({ conid: values.sourceConnectionId }); const { engine: sourceEngine } = sourceConnectionInfo || {}; @@ -354,7 +342,7 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC }, ], values, - setFieldValue, + setValues, !sourceList || sourceList.length == 0 ? file.storageType : null, setPreviewSource );