mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 11:16:01 +00:00
remove web
This commit is contained in:
@@ -1,580 +0,0 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import FormStyledButton from '../widgets/FormStyledButton';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
FormReactSelect,
|
||||
FormConnectionSelect,
|
||||
FormDatabaseSelect,
|
||||
FormTablesSelect,
|
||||
FormSchemaSelect,
|
||||
FormArchiveFolderSelect,
|
||||
FormArchiveFilesSelect,
|
||||
} from '../utility/forms';
|
||||
import { useArchiveFiles, useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||
import TableControl, { TableColumn } from '../utility/TableControl';
|
||||
import { TextField, SelectField, CheckboxField } from '../utility/inputs';
|
||||
import { createPreviewReader, getActionOptions, getTargetName } from './createImpExpScript';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import ErrorInfo from '../widgets/ErrorInfo';
|
||||
import getAsArray from '../utility/getAsArray';
|
||||
import LoadingInfo from '../widgets/LoadingInfo';
|
||||
import SqlEditor from '../sqleditor/SqlEditor';
|
||||
import { useUploadsProvider } from '../utility/UploadsProvider';
|
||||
import { FontIcon } from '../icons';
|
||||
import useTheme from '../theme/useTheme';
|
||||
import { findFileFormat, getFileFormatDirections } from '../utility/fileformats';
|
||||
import FormArgumentList from '../utility/FormArgumentList';
|
||||
import useExtensions from '../utility/useExtensions';
|
||||
import UploadButton from '../utility/UploadButton';
|
||||
import useShowModal from '../modals/showModal';
|
||||
import ChangeDownloadUrlModal from '../modals/ChangeDownloadUrlModal';
|
||||
import { useForm } from '../utility/FormProvider';
|
||||
|
||||
const Container = styled.div`
|
||||
// max-height: 50vh;
|
||||
// overflow-y: scroll;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const SourceListWrapper = styled.div`
|
||||
margin: 10px;
|
||||
`;
|
||||
|
||||
const Column = styled.div`
|
||||
margin: 10px;
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const Label = styled.div`
|
||||
margin: 5px;
|
||||
margin-top: 15px;
|
||||
color: ${props => props.theme.modal_font2};
|
||||
`;
|
||||
|
||||
const SourceNameWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const SourceNameButtons = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const IconButtonWrapper = styled.div`
|
||||
&:hover {
|
||||
background-color: ${props => props.theme.modal_background2};
|
||||
}
|
||||
cursor: pointer;
|
||||
color: ${props => props.theme.modal_font_blue[7]};
|
||||
margin-left: 5px;
|
||||
`;
|
||||
|
||||
const SqlWrapper = styled.div`
|
||||
position: relative;
|
||||
height: 100px;
|
||||
width: 20vw;
|
||||
`;
|
||||
|
||||
const DragWrapper = styled.div`
|
||||
padding: 10px;
|
||||
background: ${props => props.theme.modal_background2};
|
||||
`;
|
||||
|
||||
const ArrowWrapper = styled.div`
|
||||
font-size: 30px;
|
||||
color: ${props => props.theme.modal_font_blue[7]};
|
||||
align-self: center;
|
||||
`;
|
||||
|
||||
const Title = styled.div`
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 0px;
|
||||
`;
|
||||
|
||||
const ButtonsLine = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
function getFileFilters(extensions, storageType) {
|
||||
const res = [];
|
||||
const format = findFileFormat(extensions, storageType);
|
||||
if (format) res.push({ name: format.name, extensions: [format.extension] });
|
||||
res.push({ name: 'All Files', extensions: ['*'] });
|
||||
return res;
|
||||
}
|
||||
|
||||
async function addFileToSourceListDefault({ fileName, shortName, isDownload }, newSources, newValues) {
|
||||
const sourceName = shortName;
|
||||
newSources.push(sourceName);
|
||||
newValues[`sourceFile_${sourceName}`] = {
|
||||
fileName,
|
||||
isDownload,
|
||||
};
|
||||
}
|
||||
|
||||
async function addFilesToSourceList(extensions, files, values, setValues, preferedStorageType, setPreviewSource) {
|
||||
const newSources = [];
|
||||
const newValues = {};
|
||||
const storage = preferedStorageType || values.sourceStorageType;
|
||||
for (const file of getAsArray(files)) {
|
||||
const format = findFileFormat(extensions, storage);
|
||||
if (format) {
|
||||
await (format.addFileToSourceList || addFileToSourceListDefault)(file, newSources, newValues);
|
||||
}
|
||||
}
|
||||
newValues['sourceList'] = [...(values.sourceList || []).filter(x => !newSources.includes(x)), ...newSources];
|
||||
if (preferedStorageType && preferedStorageType != values.sourceStorageType) {
|
||||
newValues['sourceStorageType'] = preferedStorageType;
|
||||
}
|
||||
setValues({
|
||||
...values,
|
||||
...newValues,
|
||||
});
|
||||
if (setPreviewSource && newSources.length == 1) {
|
||||
setPreviewSource(newSources[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function ElectronFilesInput() {
|
||||
const { values, setValues } = useForm();
|
||||
const electron = getElectron();
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const extensions = useExtensions();
|
||||
|
||||
const handleClick = async () => {
|
||||
const files = electron.remote.dialog.showOpenDialogSync(electron.remote.getCurrentWindow(), {
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
filters: getFileFilters(extensions, values.sourceStorageType),
|
||||
});
|
||||
if (files) {
|
||||
const path = window.require('path');
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await addFilesToSourceList(
|
||||
extensions,
|
||||
files.map(full => ({
|
||||
fileName: full,
|
||||
shortName: path.parse(full).name,
|
||||
})),
|
||||
values,
|
||||
setValues
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormStyledButton type="button" value="Add file(s)" onClick={handleClick} />
|
||||
{isLoading && <LoadingInfo message="Anaysing input files" />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function extractUrlName(url, values) {
|
||||
const match = url.match(/\/([^/]+)($|\?)/);
|
||||
if (match) {
|
||||
const res = match[1];
|
||||
if (res.includes('.')) {
|
||||
return res.slice(0, res.indexOf('.'));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return `url${values && values.sourceList ? values.sourceList.length + 1 : '1'}`;
|
||||
}
|
||||
|
||||
function FilesInput({ setPreviewSource = undefined }) {
|
||||
const theme = useTheme();
|
||||
const electron = getElectron();
|
||||
const showModal = useShowModal();
|
||||
const { values, setValues } = useForm();
|
||||
const extensions = useExtensions();
|
||||
const doAddUrl = url => {
|
||||
addFilesToSourceList(
|
||||
extensions,
|
||||
[
|
||||
{
|
||||
fileName: url,
|
||||
shortName: extractUrlName(url, values),
|
||||
isDownload: true,
|
||||
},
|
||||
],
|
||||
values,
|
||||
setValues,
|
||||
null,
|
||||
setPreviewSource
|
||||
);
|
||||
};
|
||||
const handleAddUrl = () =>
|
||||
showModal(modalState => <ChangeDownloadUrlModal modalState={modalState} onConfirm={doAddUrl} />);
|
||||
return (
|
||||
<>
|
||||
<ButtonsLine>
|
||||
{electron ? <ElectronFilesInput /> : <UploadButton />}
|
||||
<FormStyledButton value="Add web URL" onClick={handleAddUrl} />
|
||||
</ButtonsLine>
|
||||
<DragWrapper theme={theme}>Drag & drop imported files here</DragWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SourceTargetConfig({
|
||||
direction,
|
||||
storageTypeField,
|
||||
connectionIdField,
|
||||
databaseNameField,
|
||||
archiveFolderField,
|
||||
schemaNameField,
|
||||
tablesField = undefined,
|
||||
engine = undefined,
|
||||
setPreviewSource = undefined,
|
||||
}) {
|
||||
const extensions = useExtensions();
|
||||
const theme = useTheme();
|
||||
const { values, setFieldValue } = useForm();
|
||||
const types =
|
||||
values[storageTypeField] == 'jsldata'
|
||||
? [{ value: 'jsldata', label: 'Query result data', directions: ['source'] }]
|
||||
: [
|
||||
{ value: 'database', label: 'Database', directions: ['source', 'target'] },
|
||||
...extensions.fileFormats.map(format => ({
|
||||
value: format.storageType,
|
||||
label: `${format.name} files(s)`,
|
||||
directions: getFileFormatDirections(format),
|
||||
})),
|
||||
{ value: 'query', label: 'SQL Query', directions: ['source'] },
|
||||
{ value: 'archive', label: 'Archive', directions: ['source', 'target'] },
|
||||
];
|
||||
const storageType = values[storageTypeField];
|
||||
const dbinfo = useDatabaseInfo({ conid: values[connectionIdField], database: values[databaseNameField] });
|
||||
const archiveFiles = useArchiveFiles({ folder: values[archiveFolderField] });
|
||||
const format = findFileFormat(extensions, storageType);
|
||||
return (
|
||||
<Column>
|
||||
{direction == 'source' && (
|
||||
<Title theme={theme}>
|
||||
<FontIcon icon="icon import" /> Source configuration
|
||||
</Title>
|
||||
)}
|
||||
{direction == 'target' && (
|
||||
<Title theme={theme}>
|
||||
<FontIcon icon="icon export" /> Target configuration
|
||||
</Title>
|
||||
)}
|
||||
<FormReactSelect options={types.filter(x => x.directions.includes(direction))} name={storageTypeField} />
|
||||
{(storageType == 'database' || storageType == 'query') && (
|
||||
<>
|
||||
<Label theme={theme}>Server</Label>
|
||||
<FormConnectionSelect name={connectionIdField} />
|
||||
<Label theme={theme}>Database</Label>
|
||||
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} />
|
||||
</>
|
||||
)}
|
||||
{storageType == 'database' && (
|
||||
<>
|
||||
<Label theme={theme}>Schema</Label>
|
||||
<FormSchemaSelect conidName={connectionIdField} databaseName={databaseNameField} name={schemaNameField} />
|
||||
{tablesField && (
|
||||
<>
|
||||
<Label theme={theme}>Tables/views</Label>
|
||||
<FormTablesSelect
|
||||
conidName={connectionIdField}
|
||||
schemaName={schemaNameField}
|
||||
databaseName={databaseNameField}
|
||||
name={tablesField}
|
||||
/>
|
||||
<div>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="All tables"
|
||||
onClick={() =>
|
||||
setFieldValue(
|
||||
'sourceList',
|
||||
_.uniq([...(values.sourceList || []), ...(dbinfo && dbinfo.tables.map(x => x.pureName))])
|
||||
)
|
||||
}
|
||||
/>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="All views"
|
||||
onClick={() =>
|
||||
setFieldValue(
|
||||
'sourceList',
|
||||
_.uniq([...(values.sourceList || []), ...(dbinfo && dbinfo.views.map(x => x.pureName))])
|
||||
)
|
||||
}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Remove all" onClick={() => setFieldValue('sourceList', [])} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{storageType == 'query' && (
|
||||
<>
|
||||
<Label theme={theme}>Query</Label>
|
||||
<SqlWrapper>
|
||||
<SqlEditor
|
||||
value={values.sourceSql}
|
||||
onChange={value => setFieldValue('sourceSql', value)}
|
||||
engine={engine}
|
||||
focusOnCreate
|
||||
/>
|
||||
</SqlWrapper>
|
||||
</>
|
||||
)}
|
||||
|
||||
{storageType == 'archive' && (
|
||||
<>
|
||||
<Label theme={theme}>Archive folder</Label>
|
||||
<FormArchiveFolderSelect
|
||||
name={archiveFolderField}
|
||||
additionalFolders={_.compact([values[archiveFolderField]])}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{storageType == 'archive' && direction == 'source' && (
|
||||
<>
|
||||
<Label theme={theme}>Source files</Label>
|
||||
<FormArchiveFilesSelect folderName={values[archiveFolderField]} name={tablesField} />
|
||||
<div>
|
||||
<FormStyledButton
|
||||
type="button"
|
||||
value="All files"
|
||||
onClick={() =>
|
||||
setFieldValue(
|
||||
'sourceList',
|
||||
_.uniq([...(values.sourceList || []), ...(archiveFiles && archiveFiles.map(x => x.name))])
|
||||
)
|
||||
}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Remove all" onClick={() => setFieldValue('sourceList', [])} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!!format && direction == 'source' && <FilesInput setPreviewSource={setPreviewSource} />}
|
||||
|
||||
{format && format.args && (
|
||||
<FormArgumentList
|
||||
args={format.args.filter(arg => !arg.direction || arg.direction == direction)}
|
||||
namePrefix={`${direction}_${format.storageType}_`}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
function SourceName({ name }) {
|
||||
const { values, setFieldValue } = useForm();
|
||||
const theme = useTheme();
|
||||
const showModal = useShowModal();
|
||||
const obj = values[`sourceFile_${name}`];
|
||||
const handleDelete = () => {
|
||||
setFieldValue(
|
||||
'sourceList',
|
||||
values.sourceList.filter(x => x != name)
|
||||
);
|
||||
};
|
||||
const doChangeUrl = url => {
|
||||
setFieldValue(`sourceFile_${name}`, { fileName: url, isDownload: true });
|
||||
};
|
||||
const handleChangeUrl = () => {
|
||||
showModal(modalState => (
|
||||
<ChangeDownloadUrlModal modalState={modalState} url={obj.fileName} onConfirm={doChangeUrl} />
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<SourceNameWrapper>
|
||||
<div>{name}</div>
|
||||
<SourceNameButtons>
|
||||
{obj && !!obj.isDownload && (
|
||||
<IconButtonWrapper onClick={handleChangeUrl} theme={theme} title={obj && obj.fileName}>
|
||||
<FontIcon icon="icon web" />
|
||||
</IconButtonWrapper>
|
||||
)}
|
||||
<IconButtonWrapper onClick={handleDelete} theme={theme}>
|
||||
<FontIcon icon="icon delete" />
|
||||
</IconButtonWrapper>
|
||||
</SourceNameButtons>
|
||||
</SourceNameWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ImportExportConfigurator({
|
||||
uploadedFile = undefined,
|
||||
openedFile = undefined,
|
||||
onChangePreview = undefined,
|
||||
}) {
|
||||
const { values, setFieldValue, setValues } = useForm();
|
||||
const targetDbinfo = useDatabaseInfo({ conid: values.targetConnectionId, database: values.targetDatabaseName });
|
||||
const sourceConnectionInfo = useConnectionInfo({ conid: values.sourceConnectionId });
|
||||
const { engine: sourceEngine } = sourceConnectionInfo || {};
|
||||
const { sourceList } = values;
|
||||
const { setUploadListener } = useUploadsProvider();
|
||||
const theme = useTheme();
|
||||
const [previewSource, setPreviewSource] = React.useState(null);
|
||||
const extensions = useExtensions();
|
||||
|
||||
const handleUpload = React.useCallback(
|
||||
file => {
|
||||
addFilesToSourceList(
|
||||
extensions,
|
||||
[
|
||||
{
|
||||
fileName: file.filePath,
|
||||
shortName: file.shortName,
|
||||
},
|
||||
],
|
||||
values,
|
||||
setValues,
|
||||
!sourceList || sourceList.length == 0 ? file.storageType : null,
|
||||
setPreviewSource
|
||||
);
|
||||
// setFieldValue('sourceList', [...(sourceList || []), file.originalName]);
|
||||
},
|
||||
[extensions, setFieldValue, sourceList, values]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
setUploadListener(() => handleUpload);
|
||||
return () => {
|
||||
setUploadListener(null);
|
||||
};
|
||||
}, [handleUpload]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (uploadedFile) {
|
||||
handleUpload(uploadedFile);
|
||||
}
|
||||
if (openedFile) {
|
||||
addFilesToSourceList(
|
||||
extensions,
|
||||
[
|
||||
{
|
||||
fileName: openedFile.filePath,
|
||||
shortName: openedFile.shortName,
|
||||
},
|
||||
],
|
||||
values,
|
||||
setValues,
|
||||
!sourceList || sourceList.length == 0 ? openedFile.storageType : null,
|
||||
setPreviewSource
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const supportsPreview =
|
||||
!!findFileFormat(extensions, values.sourceStorageType) || values.sourceStorageType == 'archive';
|
||||
const previewFileName =
|
||||
previewSource && values[`sourceFile_${previewSource}`] && values[`sourceFile_${previewSource}`].fileName;
|
||||
|
||||
const handleChangePreviewSource = async () => {
|
||||
if (previewSource && supportsPreview) {
|
||||
const reader = await createPreviewReader(extensions, values, previewSource);
|
||||
if (onChangePreview) onChangePreview(reader);
|
||||
} else {
|
||||
onChangePreview(null);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
handleChangePreviewSource();
|
||||
}, [previewSource, supportsPreview, previewFileName]);
|
||||
|
||||
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 (
|
||||
<Container>
|
||||
<Wrapper>
|
||||
<SourceTargetConfig
|
||||
direction="source"
|
||||
storageTypeField="sourceStorageType"
|
||||
connectionIdField="sourceConnectionId"
|
||||
databaseNameField="sourceDatabaseName"
|
||||
archiveFolderField="sourceArchiveFolder"
|
||||
schemaNameField="sourceSchemaName"
|
||||
tablesField="sourceList"
|
||||
engine={sourceEngine}
|
||||
setPreviewSource={setPreviewSource}
|
||||
/>
|
||||
<ArrowWrapper theme={theme}>
|
||||
<FontIcon icon="icon arrow-right" />
|
||||
</ArrowWrapper>
|
||||
<SourceTargetConfig
|
||||
direction="target"
|
||||
storageTypeField="targetStorageType"
|
||||
connectionIdField="targetConnectionId"
|
||||
databaseNameField="targetDatabaseName"
|
||||
archiveFolderField="targetArchiveFolder"
|
||||
schemaNameField="targetSchemaName"
|
||||
/>
|
||||
</Wrapper>
|
||||
<SourceListWrapper>
|
||||
<Title>
|
||||
<FontIcon icon="icon tables" /> Map source tables/files
|
||||
</Title>
|
||||
<TableControl rows={sourceList || []}>
|
||||
<TableColumn fieldName="source" header="Source" formatter={row => <SourceName name={row} />} />
|
||||
<TableColumn
|
||||
fieldName="action"
|
||||
header="Action"
|
||||
formatter={row => (
|
||||
<SelectField
|
||||
options={getActionOptions(extensions, row, values, targetDbinfo)}
|
||||
value={values[`actionType_${row}`] || getActionOptions(extensions, row, values, targetDbinfo)[0].value}
|
||||
onChange={e => setFieldValue(`actionType_${row}`, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<TableColumn
|
||||
fieldName="target"
|
||||
header="Target"
|
||||
formatter={row => (
|
||||
<TextField
|
||||
value={getTargetName(extensions, row, values)}
|
||||
onChange={e => setFieldValue(`targetName_${row}`, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<TableColumn
|
||||
fieldName="preview"
|
||||
header="Preview"
|
||||
formatter={row =>
|
||||
supportsPreview ? (
|
||||
<CheckboxField
|
||||
checked={previewSource == row}
|
||||
onChange={e => {
|
||||
if (e.target.checked) setPreviewSource(row);
|
||||
else setPreviewSource(null);
|
||||
}}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</TableControl>
|
||||
{(sourceList || []).length == 0 && <ErrorInfo message="No source tables/files" icon="img alert" />}
|
||||
</SourceListWrapper>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import { createGridCache, createGridConfig, FreeTableGridDisplay } from 'dbgate-datalib';
|
||||
import React from 'react';
|
||||
import DataGridCore from '../datagrid/DataGridCore';
|
||||
import RowsArrayGrider from '../datagrid/RowsArrayGrider';
|
||||
import axios from '../utility/axios';
|
||||
import ErrorInfo from '../widgets/ErrorInfo';
|
||||
import LoadingInfo from '../widgets/LoadingInfo';
|
||||
|
||||
export default function PreviewDataGrid({ reader, ...other }) {
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [errorMessage, setErrorMessage] = React.useState(null);
|
||||
const [model, setModel] = React.useState(null);
|
||||
const [config, setConfig] = React.useState(createGridConfig());
|
||||
const [cache, setCache] = React.useState(createGridCache());
|
||||
const [grider, setGrider] = React.useState(null);
|
||||
|
||||
const handleLoadInitialData = async () => {
|
||||
try {
|
||||
if (!reader) {
|
||||
setModel(null);
|
||||
setGrider(null);
|
||||
return;
|
||||
}
|
||||
setErrorMessage(null);
|
||||
setIsLoading(true);
|
||||
const resp = await axios.post('runners/load-reader', reader);
|
||||
// @ts-ignore
|
||||
setModel(resp.data);
|
||||
setGrider(new RowsArrayGrider(resp.data.rows));
|
||||
setIsLoading(false);
|
||||
} catch (err) {
|
||||
setIsLoading(false);
|
||||
const errorMessage = (err && err.response && err.response.data && err.response.data.error) || 'Loading failed';
|
||||
setErrorMessage(errorMessage);
|
||||
console.error(err.response);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
handleLoadInitialData();
|
||||
}, [reader]);
|
||||
|
||||
const display = React.useMemo(() => new FreeTableGridDisplay(model, config, setConfig, cache, setCache), [
|
||||
model,
|
||||
config,
|
||||
cache,
|
||||
grider,
|
||||
]);
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingInfo wrapper message="Loading data" />;
|
||||
}
|
||||
if (errorMessage) {
|
||||
return <ErrorInfo message={errorMessage} />;
|
||||
}
|
||||
|
||||
if (!grider) return null;
|
||||
|
||||
return <DataGridCore {...other} grider={grider} display={display} />;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import { extractShellApiFunctionName, extractShellApiPlugins } from 'dbgate-tools';
|
||||
|
||||
export default class ScriptWriter {
|
||||
constructor(varCount = '0') {
|
||||
this.s = '';
|
||||
this.packageNames = [];
|
||||
// this.engines = [];
|
||||
this.varCount = parseInt(varCount) || 0;
|
||||
}
|
||||
|
||||
allocVariable(prefix = 'var') {
|
||||
this.varCount += 1;
|
||||
return `${prefix}${this.varCount}`;
|
||||
}
|
||||
|
||||
put(s = '') {
|
||||
this.s += s;
|
||||
this.s += '\n';
|
||||
}
|
||||
|
||||
assign(variableName, functionName, props) {
|
||||
this.put(`const ${variableName} = await ${extractShellApiFunctionName(functionName)}(${JSON.stringify(props)});`);
|
||||
this.packageNames.push(...extractShellApiPlugins(functionName, props));
|
||||
}
|
||||
|
||||
requirePackage(packageName) {
|
||||
this.packageNames.push(packageName);
|
||||
}
|
||||
|
||||
copyStream(sourceVar, targetVar) {
|
||||
this.put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
|
||||
}
|
||||
|
||||
comment(s) {
|
||||
this.put(`// ${s}`);
|
||||
}
|
||||
|
||||
getScript(schedule = null) {
|
||||
const packageNames = this.packageNames;
|
||||
let prefix = _.uniq(packageNames)
|
||||
.map(packageName => `// @require ${packageName}\n`)
|
||||
.join('');
|
||||
if (schedule) prefix += `// @schedule ${schedule}`;
|
||||
if (prefix) prefix += '\n';
|
||||
|
||||
return prefix + this.s;
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import ScriptWriter from './ScriptWriter';
|
||||
import getAsArray from '../utility/getAsArray';
|
||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||
import { findEngineDriver, findObjectLike } from 'dbgate-tools';
|
||||
import { findFileFormat } from '../utility/fileformats';
|
||||
|
||||
export function getTargetName(extensions, source, values) {
|
||||
const key = `targetName_${source}`;
|
||||
if (values[key]) return values[key];
|
||||
const format = findFileFormat(extensions, values.targetStorageType);
|
||||
if (format) {
|
||||
const res = format.getDefaultOutputName ? format.getDefaultOutputName(source, values) : null;
|
||||
if (res) return res;
|
||||
return `${source}.${format.extension}`;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
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] != null);
|
||||
return _.fromPairs(pairs);
|
||||
}
|
||||
|
||||
async function getConnection(extensions, storageType, conid, database) {
|
||||
if (storageType == 'database' || storageType == 'query') {
|
||||
const conn = await getConnectionInfo({ conid });
|
||||
const driver = findEngineDriver(conn, extensions);
|
||||
return [
|
||||
{
|
||||
..._.omit(conn, ['_id', 'displayName']),
|
||||
database,
|
||||
},
|
||||
driver,
|
||||
];
|
||||
}
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
function getSourceExpr(extensions, sourceName, values, sourceConnection, sourceDriver) {
|
||||
const { sourceStorageType } = values;
|
||||
if (sourceStorageType == 'database') {
|
||||
const fullName = { schemaName: values.sourceSchemaName, pureName: sourceName };
|
||||
return [
|
||||
'tableReader',
|
||||
{
|
||||
connection: sourceConnection,
|
||||
...fullName,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (sourceStorageType == 'query') {
|
||||
return [
|
||||
'queryReader',
|
||||
{
|
||||
connection: sourceConnection,
|
||||
sql: values.sourceSql,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (findFileFormat(extensions, sourceStorageType)) {
|
||||
const sourceFile = values[`sourceFile_${sourceName}`];
|
||||
const format = findFileFormat(extensions, sourceStorageType);
|
||||
if (format && format.readerFunc) {
|
||||
return [
|
||||
format.readerFunc,
|
||||
{
|
||||
..._.omit(sourceFile, ['isDownload']),
|
||||
...extractApiParameters(values, 'source', format),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
if (sourceStorageType == 'jsldata') {
|
||||
return ['jslDataReader', { jslid: values.sourceJslId }];
|
||||
}
|
||||
if (sourceStorageType == 'archive') {
|
||||
return [
|
||||
'archiveReader',
|
||||
{
|
||||
folderName: values.sourceArchiveFolder,
|
||||
fileName: sourceName,
|
||||
},
|
||||
];
|
||||
}
|
||||
throw new Error(`Unknown source storage type: ${sourceStorageType}`);
|
||||
}
|
||||
|
||||
function getFlagsFroAction(action) {
|
||||
switch (action) {
|
||||
case 'dropCreateTable':
|
||||
return {
|
||||
createIfNotExists: true,
|
||||
dropIfExists: true,
|
||||
};
|
||||
case 'truncate':
|
||||
return {
|
||||
createIfNotExists: true,
|
||||
truncate: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
createIfNotExists: true,
|
||||
};
|
||||
}
|
||||
|
||||
function getTargetExpr(extensions, sourceName, values, targetConnection, targetDriver) {
|
||||
const { targetStorageType } = values;
|
||||
const format = findFileFormat(extensions, targetStorageType);
|
||||
if (format && format.writerFunc) {
|
||||
const outputParams = format.getOutputParams && format.getOutputParams(sourceName, values);
|
||||
return [
|
||||
format.writerFunc,
|
||||
{
|
||||
...(outputParams
|
||||
? outputParams
|
||||
: {
|
||||
fileName: getTargetName(extensions, sourceName, values),
|
||||
}),
|
||||
...extractApiParameters(values, 'target', format),
|
||||
},
|
||||
];
|
||||
}
|
||||
if (targetStorageType == 'database') {
|
||||
return [
|
||||
'tableWriter',
|
||||
{
|
||||
connection: targetConnection,
|
||||
schemaName: values.targetSchemaName,
|
||||
pureName: getTargetName(extensions, sourceName, values),
|
||||
...getFlagsFroAction(values[`actionType_${sourceName}`]),
|
||||
},
|
||||
];
|
||||
}
|
||||
if (targetStorageType == 'archive') {
|
||||
return [
|
||||
'archiveWriter',
|
||||
{
|
||||
folderName: values.targetArchiveFolder,
|
||||
fileName: getTargetName(extensions, sourceName, values),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
throw new Error(`Unknown target storage type: ${targetStorageType}`);
|
||||
}
|
||||
|
||||
export default async function createImpExpScript(extensions, values, addEditorInfo = true) {
|
||||
const script = new ScriptWriter(values.startVariableIndex || 0);
|
||||
|
||||
const [sourceConnection, sourceDriver] = await getConnection(
|
||||
extensions,
|
||||
values.sourceStorageType,
|
||||
values.sourceConnectionId,
|
||||
values.sourceDatabaseName
|
||||
);
|
||||
const [targetConnection, targetDriver] = await getConnection(
|
||||
extensions,
|
||||
values.targetStorageType,
|
||||
values.targetConnectionId,
|
||||
values.targetDatabaseName
|
||||
);
|
||||
|
||||
const sourceList = getAsArray(values.sourceList);
|
||||
for (const sourceName of sourceList) {
|
||||
const sourceVar = script.allocVariable();
|
||||
// @ts-ignore
|
||||
script.assign(sourceVar, ...getSourceExpr(extensions, sourceName, values, sourceConnection, sourceDriver));
|
||||
|
||||
const targetVar = script.allocVariable();
|
||||
// @ts-ignore
|
||||
script.assign(targetVar, ...getTargetExpr(extensions, sourceName, values, targetConnection, targetDriver));
|
||||
|
||||
script.copyStream(sourceVar, targetVar);
|
||||
script.put();
|
||||
}
|
||||
if (addEditorInfo) {
|
||||
script.comment('@ImportExportConfigurator');
|
||||
script.comment(JSON.stringify(values));
|
||||
}
|
||||
return script.getScript(values.schedule);
|
||||
}
|
||||
|
||||
export function getActionOptions(extensions, source, values, targetDbinfo) {
|
||||
const res = [];
|
||||
const targetName = getTargetName(extensions, source, values);
|
||||
if (values.targetStorageType == 'database') {
|
||||
let existing = findObjectLike(
|
||||
{ schemaName: values.targetSchemaName, pureName: targetName },
|
||||
targetDbinfo,
|
||||
'tables'
|
||||
);
|
||||
if (existing) {
|
||||
res.push({
|
||||
label: 'Append data',
|
||||
value: 'appendData',
|
||||
});
|
||||
res.push({
|
||||
label: 'Truncate and import',
|
||||
value: 'truncate',
|
||||
});
|
||||
res.push({
|
||||
label: 'Drop and create table',
|
||||
value: 'dropCreateTable',
|
||||
});
|
||||
} else {
|
||||
res.push({
|
||||
label: 'Create table',
|
||||
value: 'createTable',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.push({
|
||||
label: 'Create file',
|
||||
value: 'createFile',
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function createPreviewReader(extensions, values, sourceName) {
|
||||
const [sourceConnection, sourceDriver] = await getConnection(
|
||||
extensions,
|
||||
values.sourceStorageType,
|
||||
values.sourceConnectionId,
|
||||
values.sourceDatabaseName
|
||||
);
|
||||
const [functionName, props] = getSourceExpr(extensions, sourceName, values, sourceConnection, sourceDriver);
|
||||
return {
|
||||
functionName,
|
||||
props: {
|
||||
...props,
|
||||
limitRows: 100,
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user