mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
generate shell script
This commit is contained in:
parent
f03a8c258a
commit
74aa90fd2a
@ -596,7 +596,19 @@ export default function DataGridCore(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exportGrid() {
|
function exportGrid() {
|
||||||
showModal((modalState) => <ImportExportModal modalState={modalState} />);
|
showModal((modalState) => (
|
||||||
|
<ImportExportModal
|
||||||
|
modalState={modalState}
|
||||||
|
initialValues={{
|
||||||
|
sourceStorageType: 'database',
|
||||||
|
sourceConnectionId: conid,
|
||||||
|
sourceDatabaseName: database,
|
||||||
|
sourceTables: [
|
||||||
|
`${display.baseTable && display.baseTable.schemaName}.${display.baseTable && display.baseTable.pureName}`,
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCellValue(chs, cell, value) {
|
function setCellValue(chs, cell, value) {
|
||||||
|
@ -4,7 +4,14 @@ import { Formik, Form, useFormik, useFormikContext } from 'formik';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import { FontIcon } from '../icons';
|
import { FontIcon } from '../icons';
|
||||||
import { FormButtonRow, FormSubmit, FormReactSelect, FormConnectionSelect, FormDatabaseSelect } from '../utility/forms';
|
import {
|
||||||
|
FormButtonRow,
|
||||||
|
FormSubmit,
|
||||||
|
FormReactSelect,
|
||||||
|
FormConnectionSelect,
|
||||||
|
FormDatabaseSelect,
|
||||||
|
FormTablesSelect,
|
||||||
|
} from '../utility/forms';
|
||||||
import { useConnectionList, useDatabaseList } from '../utility/metadataLoaders';
|
import { useConnectionList, useDatabaseList } from '../utility/metadataLoaders';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
@ -62,6 +69,7 @@ function SourceTargetConfig({
|
|||||||
storageTypeField,
|
storageTypeField,
|
||||||
connectionIdField,
|
connectionIdField,
|
||||||
databaseNameField,
|
databaseNameField,
|
||||||
|
tablesField,
|
||||||
}) {
|
}) {
|
||||||
const types = [
|
const types = [
|
||||||
{ value: 'database', label: 'Database' },
|
{ value: 'database', label: 'Database' },
|
||||||
@ -80,6 +88,8 @@ function SourceTargetConfig({
|
|||||||
<FormConnectionSelect name={connectionIdField} />
|
<FormConnectionSelect name={connectionIdField} />
|
||||||
<Label>Database</Label>
|
<Label>Database</Label>
|
||||||
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} />
|
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} />
|
||||||
|
<Label>Tables/views</Label>
|
||||||
|
<FormTablesSelect conidName={connectionIdField} databaseName={databaseNameField} name={tablesField} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
@ -88,23 +98,21 @@ function SourceTargetConfig({
|
|||||||
|
|
||||||
export default function ImportExportConfigurator() {
|
export default function ImportExportConfigurator() {
|
||||||
return (
|
return (
|
||||||
<Formik onSubmit={null} initialValues={{ sourceStorageType: 'database', targetStorageType: 'csv' }}>
|
<Wrapper>
|
||||||
<Form>
|
<SourceTargetConfig
|
||||||
<Wrapper>
|
isSource
|
||||||
<SourceTargetConfig
|
storageTypeField="sourceStorageType"
|
||||||
isSource
|
connectionIdField="sourceConnectionId"
|
||||||
storageTypeField="sourceStorageType"
|
databaseNameField="sourceDatabaseName"
|
||||||
connectionIdField="sourceConnectionId"
|
tablesField="sourceTables"
|
||||||
databaseNameField="sourceDatabaseName"
|
/>
|
||||||
/>
|
<SourceTargetConfig
|
||||||
<SourceTargetConfig
|
isTarget
|
||||||
isTarget
|
storageTypeField="targetStorageType"
|
||||||
storageTypeField="targetStorageType"
|
connectionIdField="targetConnectionId"
|
||||||
connectionIdField="targetConnectionId"
|
databaseNameField="targetDatabaseName"
|
||||||
databaseNameField="targetDatabaseName"
|
tablesField="targetTables"
|
||||||
/>
|
/>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
49
packages/web/src/impexp/ScriptCreator.js
Normal file
49
packages/web/src/impexp/ScriptCreator.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import ScriptWriter from './ScriptWriter';
|
||||||
|
|
||||||
|
export default class ScriptCreator {
|
||||||
|
constructor() {
|
||||||
|
this.varCount = 0;
|
||||||
|
this.commands = [];
|
||||||
|
}
|
||||||
|
allocVariable(prefix = 'var') {
|
||||||
|
this.varCount += 1;
|
||||||
|
return `${prefix}${this.varCount}`;
|
||||||
|
}
|
||||||
|
getCode() {
|
||||||
|
const writer = new ScriptWriter();
|
||||||
|
for (const command of this.commands) {
|
||||||
|
const { type } = command;
|
||||||
|
switch (type) {
|
||||||
|
case 'assign':
|
||||||
|
{
|
||||||
|
const { variableName, functionName, props } = command;
|
||||||
|
writer.assign(variableName, functionName, props);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'copyStream':
|
||||||
|
{
|
||||||
|
const { sourceVar, targetVar } = command;
|
||||||
|
writer.copyStream(sourceVar, targetVar);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.finish();
|
||||||
|
return writer.s;
|
||||||
|
}
|
||||||
|
assign(variableName, functionName, props) {
|
||||||
|
this.commands.push({
|
||||||
|
type: 'assign',
|
||||||
|
variableName,
|
||||||
|
functionName,
|
||||||
|
props,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
copyStream(sourceVar, targetVar) {
|
||||||
|
this.commands.push({
|
||||||
|
type: 'copyStream',
|
||||||
|
sourceVar,
|
||||||
|
targetVar,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
26
packages/web/src/impexp/ScriptWriter.js
Normal file
26
packages/web/src/impexp/ScriptWriter.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export default class ScriptWriter {
|
||||||
|
constructor() {
|
||||||
|
this.s = '';
|
||||||
|
this.put('const dbgateApi = require("@dbgate/api");');
|
||||||
|
this.put('async function run() {');
|
||||||
|
}
|
||||||
|
|
||||||
|
put(s) {
|
||||||
|
this.s += s;
|
||||||
|
this.s += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
this.put('await dbgateApi.copyStream(queryReader, csvWriter);');
|
||||||
|
this.put('dbgateApi.runScript(run);');
|
||||||
|
this.put('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
assign(variableName, functionName, props) {
|
||||||
|
this.put(`const ${variableName} = await dbgateApi.${functionName}(${JSON.stringify(props)});`);
|
||||||
|
}
|
||||||
|
|
||||||
|
copyStream(sourceVar, targetVar) {
|
||||||
|
this.put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
|
||||||
|
}
|
||||||
|
}
|
50
packages/web/src/impexp/createImpExpScript.js
Normal file
50
packages/web/src/impexp/createImpExpScript.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import ScriptCreator from './ScriptCreator';
|
||||||
|
import getAsArray from '../utility/getAsArray';
|
||||||
|
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||||
|
import engines from '@dbgate/engines';
|
||||||
|
|
||||||
|
function splitFullName(name) {
|
||||||
|
const i = name.indexOf('.');
|
||||||
|
if (i >= 0)
|
||||||
|
return {
|
||||||
|
schemaName: name.substr(0, i),
|
||||||
|
pureName: name.substr(i + 1),
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
schemaName: null,
|
||||||
|
pureName: name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function quoteFullName(dialect, { schemaName, pureName }) {
|
||||||
|
if (schemaName) return `${dialect.quoteIdentifier(schemaName)}.${dialect.quoteIdentifier(pureName)}`;
|
||||||
|
return `${dialect.quoteIdentifier(pureName)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function createImpExpScript(values) {
|
||||||
|
const script = new ScriptCreator();
|
||||||
|
if (values.sourceStorageType == 'database') {
|
||||||
|
const tables = getAsArray(values.sourceTables);
|
||||||
|
for (const table of tables) {
|
||||||
|
const sourceVar = script.allocVariable();
|
||||||
|
const connection = await getConnectionInfo({ conid: values.sourceConnectionId });
|
||||||
|
const driver = engines(connection);
|
||||||
|
|
||||||
|
const fullName = splitFullName(table);
|
||||||
|
script.assign(sourceVar, 'queryReader', {
|
||||||
|
connection: {
|
||||||
|
...connection,
|
||||||
|
database: values.sourceDatabaseName,
|
||||||
|
},
|
||||||
|
sql: `select * from ${quoteFullName(driver.dialect, fullName)}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetVar = script.allocVariable();
|
||||||
|
script.assign(targetVar, 'csvWriter', {
|
||||||
|
fileName: `${fullName.pureName}.csv`,
|
||||||
|
});
|
||||||
|
|
||||||
|
script.copyStream(sourceVar, targetVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return script.getCode();
|
||||||
|
}
|
@ -9,6 +9,7 @@ import 'ace-builds/src-noconflict/mode-sql';
|
|||||||
import 'ace-builds/src-noconflict/mode-mysql';
|
import 'ace-builds/src-noconflict/mode-mysql';
|
||||||
import 'ace-builds/src-noconflict/mode-pgsql';
|
import 'ace-builds/src-noconflict/mode-pgsql';
|
||||||
import 'ace-builds/src-noconflict/mode-sqlserver';
|
import 'ace-builds/src-noconflict/mode-sqlserver';
|
||||||
|
import 'ace-builds/src-noconflict/mode-javascript';
|
||||||
import 'ace-builds/src-noconflict/theme-github';
|
import 'ace-builds/src-noconflict/theme-github';
|
||||||
import 'ace-builds/src-noconflict/ext-searchbox';
|
import 'ace-builds/src-noconflict/ext-searchbox';
|
||||||
import 'ace-builds/src-noconflict/ext-language_tools';
|
import 'ace-builds/src-noconflict/ext-language_tools';
|
||||||
|
@ -10,17 +10,43 @@ import ModalFooter from './ModalFooter';
|
|||||||
import ModalContent from './ModalContent';
|
import ModalContent from './ModalContent';
|
||||||
import { useConnectionList, useDatabaseList } from '../utility/metadataLoaders';
|
import { useConnectionList, useDatabaseList } from '../utility/metadataLoaders';
|
||||||
import ImportExportConfigurator from '../impexp/ImportExportConfigurator';
|
import ImportExportConfigurator from '../impexp/ImportExportConfigurator';
|
||||||
|
import createImpExpScript from '../impexp/createImpExpScript';
|
||||||
|
import { openNewTab } from '../utility/common';
|
||||||
|
import { useSetOpenedTabs } from '../utility/globalState';
|
||||||
|
import { Formik, Form, useFormik, useFormikContext } from 'formik';
|
||||||
|
|
||||||
export default function ImportExportModal({ modalState }) {
|
export default function ImportExportModal({ modalState, initialValues }) {
|
||||||
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
|
|
||||||
|
const handleSubmit = async (values) => {
|
||||||
|
const code = await createImpExpScript(values);
|
||||||
|
openNewTab(setOpenedTabs, {
|
||||||
|
title: 'Shell',
|
||||||
|
icon: 'trigger.svg',
|
||||||
|
tabComponent: 'ShellTab',
|
||||||
|
props: {
|
||||||
|
initialScript: code,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
modalState.close();
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<ModalBase modalState={modalState}>
|
<ModalBase modalState={modalState}>
|
||||||
<ModalHeader modalState={modalState}>Import/Export</ModalHeader>
|
<Formik
|
||||||
<ModalContent>
|
onSubmit={handleSubmit}
|
||||||
<ImportExportConfigurator />
|
initialValues={{ sourceStorageType: 'database', targetStorageType: 'csv', ...initialValues }}
|
||||||
</ModalContent>
|
>
|
||||||
<ModalFooter>
|
<Form>
|
||||||
<FormStyledButton type="button" value="Close" onClick={modalState.close} />
|
<ModalHeader modalState={modalState}>Import/Export</ModalHeader>
|
||||||
</ModalFooter>
|
<ModalContent>
|
||||||
|
<ImportExportConfigurator />
|
||||||
|
</ModalContent>
|
||||||
|
<ModalFooter>
|
||||||
|
<FormStyledButton type="submit" value="Export" />
|
||||||
|
<FormStyledButton type="button" value="Close" onClick={modalState.close} />
|
||||||
|
</ModalFooter>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
</ModalBase>
|
</ModalBase>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
69
packages/web/src/sqleditor/JavaScriptEditor.js
Normal file
69
packages/web/src/sqleditor/JavaScriptEditor.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import AceEditor from 'react-ace';
|
||||||
|
import useDimensions from '../utility/useDimensions';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function JavaScriptEditor({
|
||||||
|
value = undefined,
|
||||||
|
readOnly = false,
|
||||||
|
onChange = undefined,
|
||||||
|
tabVisible = false,
|
||||||
|
onKeyDown = undefined,
|
||||||
|
editorRef = undefined,
|
||||||
|
focusOnCreate = false,
|
||||||
|
}) {
|
||||||
|
const [containerRef, { height, width }] = useDimensions();
|
||||||
|
const ownEditorRef = React.useRef(null);
|
||||||
|
|
||||||
|
const currentEditorRef = editorRef || ownEditorRef;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if ((tabVisible || focusOnCreate) && currentEditorRef.current && currentEditorRef.current.editor)
|
||||||
|
currentEditorRef.current.editor.focus();
|
||||||
|
}, [tabVisible, focusOnCreate]);
|
||||||
|
|
||||||
|
const handleKeyDown = React.useCallback(
|
||||||
|
async (data, hash, keyString, keyCode, event) => {
|
||||||
|
if (onKeyDown) onKeyDown(data, hash, keyString, keyCode, event);
|
||||||
|
},
|
||||||
|
[onKeyDown]
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if ((onKeyDown || !readOnly) && currentEditorRef.current) {
|
||||||
|
currentEditorRef.current.editor.keyBinding.addKeyboardHandler(handleKeyDown);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
currentEditorRef.current.editor.keyBinding.removeKeyboardHandler(handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [handleKeyDown]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper ref={containerRef}>
|
||||||
|
<AceEditor
|
||||||
|
ref={currentEditorRef}
|
||||||
|
mode="javascript"
|
||||||
|
theme="github"
|
||||||
|
onChange={onChange}
|
||||||
|
name="UNIQUE_ID_OF_DIV"
|
||||||
|
editorProps={{ $blockScrolling: true }}
|
||||||
|
setOptions={{
|
||||||
|
showPrintMargin: false,
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
readOnly={readOnly}
|
||||||
|
fontSize="11pt"
|
||||||
|
width={`${width}px`}
|
||||||
|
height={`${height}px`}
|
||||||
|
/>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
108
packages/web/src/tabs/ShellTab.js
Normal file
108
packages/web/src/tabs/ShellTab.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import axios from '../utility/axios';
|
||||||
|
import engines from '@dbgate/engines';
|
||||||
|
|
||||||
|
import { useConnectionInfo, getTableInfo, getConnectionInfo, getSqlObjectInfo } from '../utility/metadataLoaders';
|
||||||
|
import SqlEditor from '../sqleditor/SqlEditor';
|
||||||
|
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
|
||||||
|
import QueryToolbar from '../query/QueryToolbar';
|
||||||
|
import SessionMessagesView from '../query/SessionMessagesView';
|
||||||
|
import { TabPage } from '../widgets/TabControl';
|
||||||
|
import ResultTabs from '../sqleditor/ResultTabs';
|
||||||
|
import { VerticalSplitter } from '../widgets/Splitter';
|
||||||
|
import keycodes from '../utility/keycodes';
|
||||||
|
import { changeTab } from '../utility/common';
|
||||||
|
import useSocket from '../utility/SocketProvider';
|
||||||
|
import SaveSqlFileModal from '../modals/SaveSqlFileModal';
|
||||||
|
import useModalState from '../modals/useModalState';
|
||||||
|
import sqlFormatter from 'sql-formatter';
|
||||||
|
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
|
||||||
|
|
||||||
|
export default function ShellTab({
|
||||||
|
tabid,
|
||||||
|
conid,
|
||||||
|
database,
|
||||||
|
initialArgs,
|
||||||
|
tabVisible,
|
||||||
|
toolbarPortalRef,
|
||||||
|
initialScript,
|
||||||
|
storageKey,
|
||||||
|
...other
|
||||||
|
}) {
|
||||||
|
const localStorageKey = storageKey || `shell_${tabid}`;
|
||||||
|
const [shellText, setShellText] = React.useState(() => localStorage.getItem(localStorageKey) || initialScript || '');
|
||||||
|
const shellTextRef = React.useRef(shellText);
|
||||||
|
const [busy, setBusy] = React.useState(false);
|
||||||
|
|
||||||
|
const saveToStorage = React.useCallback(() => localStorage.setItem(localStorageKey, shellTextRef.current), [
|
||||||
|
localStorageKey,
|
||||||
|
shellTextRef,
|
||||||
|
]);
|
||||||
|
const saveToStorageDebounced = React.useMemo(() => _.debounce(saveToStorage, 5000), [saveToStorage]);
|
||||||
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
window.addEventListener('beforeunload', saveToStorage);
|
||||||
|
return () => {
|
||||||
|
saveToStorage();
|
||||||
|
window.removeEventListener('beforeunload', saveToStorage);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!storageKey)
|
||||||
|
changeTab(tabid, setOpenedTabs, (tab) => ({
|
||||||
|
...tab,
|
||||||
|
props: {
|
||||||
|
...tab.props,
|
||||||
|
storageKey: localStorageKey,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}, [storageKey]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
changeTab(tabid, setOpenedTabs, (tab) => ({ ...tab, busy }));
|
||||||
|
}, [busy]);
|
||||||
|
|
||||||
|
const editorRef = React.useRef(null);
|
||||||
|
|
||||||
|
useUpdateDatabaseForTab(tabVisible, conid, database);
|
||||||
|
const connection = useConnectionInfo({ conid });
|
||||||
|
|
||||||
|
const handleChange = (text) => {
|
||||||
|
if (text != null) shellTextRef.current = text;
|
||||||
|
setShellText(text);
|
||||||
|
saveToStorageDebounced();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExecute = async () => {};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
// axios.post('sessions/cancel', {
|
||||||
|
// sesid: sessionId,
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (data, hash, keyString, keyCode, event) => {
|
||||||
|
if (keyCode == keycodes.f5) {
|
||||||
|
event.preventDefault();
|
||||||
|
handleExecute();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<VerticalSplitter>
|
||||||
|
<JavaScriptEditor
|
||||||
|
value={shellText}
|
||||||
|
onChange={handleChange}
|
||||||
|
tabVisible={tabVisible}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
editorRef={editorRef}
|
||||||
|
/>
|
||||||
|
</VerticalSplitter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -2,6 +2,7 @@ import TableDataTab from './TableDataTab';
|
|||||||
import ViewDataTab from './ViewDataTab';
|
import ViewDataTab from './ViewDataTab';
|
||||||
import TableStructureTab from './TableStructureTab';
|
import TableStructureTab from './TableStructureTab';
|
||||||
import QueryTab from './QueryTab';
|
import QueryTab from './QueryTab';
|
||||||
|
import ShellTab from './ShellTab';
|
||||||
import InfoPageTab from './InfoPageTab';
|
import InfoPageTab from './InfoPageTab';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -10,4 +11,5 @@ export default {
|
|||||||
TableStructureTab,
|
TableStructureTab,
|
||||||
QueryTab,
|
QueryTab,
|
||||||
InfoPageTab,
|
InfoPageTab,
|
||||||
|
ShellTab,
|
||||||
};
|
};
|
||||||
|
@ -4,8 +4,9 @@ import Select from 'react-select';
|
|||||||
import { TextField, SelectField } from './inputs';
|
import { TextField, SelectField } from './inputs';
|
||||||
import { Field, useFormikContext } from 'formik';
|
import { Field, useFormikContext } from 'formik';
|
||||||
import FormStyledButton from '../widgets/FormStyledButton';
|
import FormStyledButton from '../widgets/FormStyledButton';
|
||||||
import { useConnectionList, useDatabaseList } from './metadataLoaders';
|
import { useConnectionList, useDatabaseList, useDatabaseInfo } from './metadataLoaders';
|
||||||
import useSocket from './SocketProvider';
|
import useSocket from './SocketProvider';
|
||||||
|
import getAsArray from './getAsArray';
|
||||||
|
|
||||||
export const FormRow = styled.div`
|
export const FormRow = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -84,14 +85,23 @@ export function FormRadioGroupItem({ name, text, value }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FormReactSelect({ options, name }) {
|
export function FormReactSelect({ options, name, isMulti = false }) {
|
||||||
const { setFieldValue, values } = useFormikContext();
|
const { setFieldValue, values } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
options={options}
|
options={options}
|
||||||
defaultValue={options.find((x) => x.value == values[name])}
|
defaultValue={
|
||||||
onChange={(item) => setFieldValue(name, item ? item.value : null)}
|
isMulti
|
||||||
|
? options.filter((x) => values[name] && values[name].includes(x.value))
|
||||||
|
: options.find((x) => x.value == values[name])
|
||||||
|
}
|
||||||
|
onChange={(item) =>
|
||||||
|
setFieldValue(name, isMulti ? getAsArray(item).map((x) => x.value) : item ? item.value : null)
|
||||||
|
}
|
||||||
menuPortalTarget={document.body}
|
menuPortalTarget={document.body}
|
||||||
|
isMulti={isMulti}
|
||||||
|
closeMenuOnSelect={!isMulti}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -126,3 +136,19 @@ export function FormDatabaseSelect({ conidName, name }) {
|
|||||||
if (databaseOptions.length == 0) return <div>Not available</div>;
|
if (databaseOptions.length == 0) return <div>Not available</div>;
|
||||||
return <FormReactSelect options={databaseOptions} name={name} />;
|
return <FormReactSelect options={databaseOptions} name={name} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FormTablesSelect({ conidName, databaseName, name }) {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const dbinfo = useDatabaseInfo({ conid: values[conidName], database: values[databaseName] });
|
||||||
|
const tablesOptions = React.useMemo(
|
||||||
|
() =>
|
||||||
|
[...((dbinfo && dbinfo.tables) || []), ...((dbinfo && dbinfo.views) || [])].map((x) => ({
|
||||||
|
value: `${x.schemaName}.${x.pureName}`,
|
||||||
|
label: x.pureName,
|
||||||
|
})),
|
||||||
|
[dbinfo]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tablesOptions.length == 0) return <div>Not available</div>;
|
||||||
|
return <FormReactSelect options={tablesOptions} name={name} isMulti />;
|
||||||
|
}
|
||||||
|
@ -23,3 +23,24 @@ async function run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbgateApi.runScript(run);
|
dbgateApi.runScript(run);
|
||||||
|
|
||||||
|
// dbgateApi.runBatch([
|
||||||
|
// {
|
||||||
|
// type: 'copyStream',
|
||||||
|
// source: {
|
||||||
|
// type: 'queryReader',
|
||||||
|
// connection: {
|
||||||
|
// server: 'localhost',
|
||||||
|
// engine: 'mysql',
|
||||||
|
// user: 'root',
|
||||||
|
// password: 'test',
|
||||||
|
// port: '3307',
|
||||||
|
// database: 'Chinook',
|
||||||
|
// },
|
||||||
|
// sql: 'SELECT * FROM Genre',
|
||||||
|
// },
|
||||||
|
// target: {
|
||||||
|
// type: 'csvWriter',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// ]);
|
||||||
|
Loading…
Reference in New Issue
Block a user