mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 03:53:57 +00:00
csv delimiter configuration
This commit is contained in:
@@ -43,6 +43,7 @@ async function csvReader({ fileName, encoding = 'utf-8', header = true, delimite
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
delimiter,
|
delimiter,
|
||||||
quoted,
|
quoted,
|
||||||
|
skip_lines_with_error: true,
|
||||||
to_line: limitRows ? limitRows + 1 : -1,
|
to_line: limitRows ? limitRows + 1 : -1,
|
||||||
});
|
});
|
||||||
const fileStream = fs.createReadStream(fileName, encoding);
|
const fileStream = fs.createReadStream(fileName, encoding);
|
||||||
|
|||||||
@@ -8,6 +8,20 @@ const csvFormat: FileFormatDefinition = {
|
|||||||
name: 'CSV',
|
name: 'CSV',
|
||||||
readerFunc: 'csvReader',
|
readerFunc: 'csvReader',
|
||||||
writerFunc: 'csvWriter',
|
writerFunc: 'csvWriter',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
name: 'delimiter',
|
||||||
|
label: 'Delimiter',
|
||||||
|
options: [
|
||||||
|
{ name: 'Comma (,)', value: ',' },
|
||||||
|
{ name: 'Semicolon (;)', value: ';' },
|
||||||
|
{ name: 'Tab', value: '\t' },
|
||||||
|
{ name: 'Pipe (|)', value: '|' },
|
||||||
|
],
|
||||||
|
apiName: 'delimiter',
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default csvFormat;
|
export default csvFormat;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface FileFormatDefinition {
|
|||||||
name: string;
|
name: string;
|
||||||
readerFunc?: string;
|
readerFunc?: string;
|
||||||
writerFunc?: string;
|
writerFunc?: string;
|
||||||
|
args?: any[];
|
||||||
addFilesToSourceList: (
|
addFilesToSourceList: (
|
||||||
file: {
|
file: {
|
||||||
full: string;
|
full: string;
|
||||||
|
|||||||
@@ -1,54 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import styled from 'styled-components';
|
|
||||||
import {
|
|
||||||
FormTextField,
|
|
||||||
FormSubmit,
|
|
||||||
FormArchiveFolderSelect,
|
|
||||||
FormRow,
|
|
||||||
FormLabel,
|
|
||||||
FormSelectField,
|
|
||||||
FormCheckboxField,
|
|
||||||
} from '../utility/forms';
|
|
||||||
import { Formik, Form, useFormikContext } from 'formik';
|
import { Formik, Form, useFormikContext } from 'formik';
|
||||||
|
import FormArgumentList from '../utility/FormArgumentList';
|
||||||
|
|
||||||
const MacroArgumentsWrapper = styled.div`
|
|
||||||
`;
|
|
||||||
|
|
||||||
function MacroArgument({ arg, namePrefix }) {
|
|
||||||
const name = `${namePrefix}${arg.name}`;
|
|
||||||
if (arg.type == 'text') {
|
|
||||||
return <FormTextField label={arg.label} name={name} />;
|
|
||||||
}
|
|
||||||
if (arg.type == 'checkbox') {
|
|
||||||
return <FormCheckboxField label={arg.label} name={name} />;
|
|
||||||
}
|
|
||||||
if (arg.type == 'select') {
|
|
||||||
return (
|
|
||||||
<FormSelectField label={arg.label} name={name}>
|
|
||||||
{arg.options.map((opt) =>
|
|
||||||
_.isString(opt) ? <option value={opt}>{opt}</option> : <option value={opt.value}>{opt.name}</option>
|
|
||||||
)}
|
|
||||||
</FormSelectField>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function MacroArgumentList({ args, onChangeValues, namePrefix }) {
|
|
||||||
const { values } = useFormikContext();
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (onChangeValues) onChangeValues(values);
|
|
||||||
}, [values]);
|
|
||||||
return (
|
|
||||||
<MacroArgumentsWrapper>
|
|
||||||
{' '}
|
|
||||||
{args.map((arg) => (
|
|
||||||
<MacroArgument arg={arg} key={arg.name} namePrefix={namePrefix} />
|
|
||||||
))}
|
|
||||||
</MacroArgumentsWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function MacroParameters({ args, onChangeValues, macroValues, namePrefix }) {
|
export default function MacroParameters({ args, onChangeValues, macroValues, namePrefix }) {
|
||||||
if (!args || args.length == 0) return null;
|
if (!args || args.length == 0) return null;
|
||||||
@@ -59,7 +13,7 @@ export default function MacroParameters({ args, onChangeValues, macroValues, nam
|
|||||||
return (
|
return (
|
||||||
<Formik initialValues={initialValues} onSubmit={() => {}}>
|
<Formik initialValues={initialValues} onSubmit={() => {}}>
|
||||||
<Form>
|
<Form>
|
||||||
<MacroArgumentList args={args} onChangeValues={onChangeValues} namePrefix={namePrefix} />
|
<FormArgumentList args={args} onChangeValues={onChangeValues} namePrefix={namePrefix} />
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { useUploadsProvider } from '../utility/UploadsProvider';
|
|||||||
import { FontIcon } from '../icons';
|
import { FontIcon } from '../icons';
|
||||||
import useTheme from '../theme/useTheme';
|
import useTheme from '../theme/useTheme';
|
||||||
import { fileformats, findFileFormat, getFileFormatDirections } from '../fileformats';
|
import { fileformats, findFileFormat, getFileFormatDirections } from '../fileformats';
|
||||||
|
import FormArgumentList from '../utility/FormArgumentList';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
// max-height: 50vh;
|
// max-height: 50vh;
|
||||||
@@ -192,6 +193,7 @@ function SourceTargetConfig({
|
|||||||
const storageType = values[storageTypeField];
|
const storageType = values[storageTypeField];
|
||||||
const dbinfo = useDatabaseInfo({ conid: values[connectionIdField], database: values[databaseNameField] });
|
const dbinfo = useDatabaseInfo({ conid: values[connectionIdField], database: values[databaseNameField] });
|
||||||
const archiveFiles = useArchiveFiles({ folder: values[archiveFolderField] });
|
const archiveFiles = useArchiveFiles({ folder: values[archiveFolderField] });
|
||||||
|
const format = findFileFormat(storageType);
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column>
|
||||||
{direction == 'source' && (
|
{direction == 'source' && (
|
||||||
@@ -297,7 +299,14 @@ function SourceTargetConfig({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isFileStorage(storageType) && direction == 'source' && <FilesInput />}
|
{!!format && direction == 'source' && <FilesInput />}
|
||||||
|
|
||||||
|
{format && format.args && (
|
||||||
|
<FormArgumentList
|
||||||
|
args={format.args.filter((arg) => !arg.direction || arg.direction == direction)}
|
||||||
|
namePrefix={`${direction}_${format.storageType}_`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -379,6 +388,18 @@ export default function ImportExportConfigurator({ uploadedFile = undefined, onC
|
|||||||
handleChangePreviewSource();
|
handleChangePreviewSource();
|
||||||
}, [previewSource, supportsPreview]);
|
}, [previewSource, supportsPreview]);
|
||||||
|
|
||||||
|
const oldValues = React.useRef({});
|
||||||
|
React.useEffect(() => {
|
||||||
|
const changed = _.pickBy(
|
||||||
|
values,
|
||||||
|
(v, k) => k.startsWith(`source_${values.sourceStorageType}_`) && oldValues.current[k] != v
|
||||||
|
);
|
||||||
|
if (!_.isEmpty(changed)) {
|
||||||
|
handleChangePreviewSource();
|
||||||
|
}
|
||||||
|
oldValues.current = values;
|
||||||
|
}, [values]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export default function PreviewDataGrid({ reader, ...other }) {
|
|||||||
setGrider(null);
|
setGrider(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setErrorMessage(null);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const resp = await axios.post('runners/load-reader', reader);
|
const resp = await axios.post('runners/load-reader', reader);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ export function isFileStorage(storageType) {
|
|||||||
return !!findFileFormat(storageType);
|
return !!findFileFormat(storageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractApiParameters(values, direction, format) {
|
||||||
|
const pairs = (format.args || [])
|
||||||
|
.filter((arg) => arg.apiName)
|
||||||
|
.map((arg) => [arg.apiName, values[`${direction}_${format.storageType}_${arg.name}`]])
|
||||||
|
.filter((x) => x[1]);
|
||||||
|
return _.fromPairs(pairs);
|
||||||
|
}
|
||||||
|
|
||||||
async function getConnection(storageType, conid, database) {
|
async function getConnection(storageType, conid, database) {
|
||||||
if (storageType == 'database' || storageType == 'query') {
|
if (storageType == 'database' || storageType == 'query') {
|
||||||
const conn = await getConnectionInfo({ conid });
|
const conn = await getConnectionInfo({ conid });
|
||||||
@@ -58,7 +66,13 @@ function getSourceExpr(sourceName, values, sourceConnection, sourceDriver) {
|
|||||||
const sourceFile = values[`sourceFile_${sourceName}`];
|
const sourceFile = values[`sourceFile_${sourceName}`];
|
||||||
const format = findFileFormat(sourceStorageType);
|
const format = findFileFormat(sourceStorageType);
|
||||||
if (format && format.readerFunc) {
|
if (format && format.readerFunc) {
|
||||||
return [format.readerFunc, sourceFile];
|
return [
|
||||||
|
format.readerFunc,
|
||||||
|
{
|
||||||
|
...sourceFile,
|
||||||
|
...extractApiParameters(values, 'source', format),
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sourceStorageType == 'jsldata') {
|
if (sourceStorageType == 'jsldata') {
|
||||||
@@ -103,9 +117,9 @@ function getTargetExpr(sourceName, values, targetConnection, targetDriver) {
|
|||||||
format.writerFunc,
|
format.writerFunc,
|
||||||
{
|
{
|
||||||
fileName: getTargetName(sourceName, values),
|
fileName: getTargetName(sourceName, values),
|
||||||
|
...extractApiParameters(values, 'target', format),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
if (targetStorageType == 'database') {
|
if (targetStorageType == 'database') {
|
||||||
return [
|
return [
|
||||||
|
|||||||
50
packages/web/src/utility/FormArgumentList.js
Normal file
50
packages/web/src/utility/FormArgumentList.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import {
|
||||||
|
FormTextField,
|
||||||
|
FormSubmit,
|
||||||
|
FormArchiveFolderSelect,
|
||||||
|
FormRow,
|
||||||
|
FormLabel,
|
||||||
|
FormSelectField,
|
||||||
|
FormCheckboxField,
|
||||||
|
} from './forms';
|
||||||
|
import { Formik, Form, useFormikContext } from 'formik';
|
||||||
|
|
||||||
|
const FormArgumentsWrapper = styled.div``;
|
||||||
|
|
||||||
|
function FormArgument({ arg, namePrefix }) {
|
||||||
|
const name = `${namePrefix}${arg.name}`;
|
||||||
|
if (arg.type == 'text') {
|
||||||
|
return <FormTextField label={arg.label} name={name} />;
|
||||||
|
}
|
||||||
|
if (arg.type == 'checkbox') {
|
||||||
|
return <FormCheckboxField label={arg.label} name={name} />;
|
||||||
|
}
|
||||||
|
if (arg.type == 'select') {
|
||||||
|
return (
|
||||||
|
<FormSelectField label={arg.label} name={name}>
|
||||||
|
{arg.options.map((opt) =>
|
||||||
|
_.isString(opt) ? <option value={opt}>{opt}</option> : <option value={opt.value}>{opt.name}</option>
|
||||||
|
)}
|
||||||
|
</FormSelectField>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FormArgumentList({ args, onChangeValues = undefined, namePrefix }) {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (onChangeValues) onChangeValues(values);
|
||||||
|
}, [values]);
|
||||||
|
return (
|
||||||
|
<FormArgumentsWrapper>
|
||||||
|
{' '}
|
||||||
|
{args.map((arg) => (
|
||||||
|
<FormArgument arg={arg} key={arg.name} namePrefix={namePrefix} />
|
||||||
|
))}
|
||||||
|
</FormArgumentsWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user