shell script runner

This commit is contained in:
Jan Prochazka
2020-06-07 09:51:53 +02:00
parent 617548cd50
commit f37524f76f
14 changed files with 203 additions and 89 deletions

View File

@@ -1,49 +0,0 @@
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,
});
}
}

View File

@@ -1,9 +1,12 @@
export default class ScriptWriter {
constructor() {
this.s = '';
this.put('const dbgateApi = require("@dbgate/api");');
this.put();
this.put('async function run() {');
this.varCount = 0;
}
allocVariable(prefix = 'var') {
this.varCount += 1;
return `${prefix}${this.varCount}`;
}
put(s = '') {
@@ -11,17 +14,11 @@ export default class ScriptWriter {
this.s += '\n';
}
finish() {
this.put('}');
this.put();
this.put('dbgateApi.runScript(run);');
}
assign(variableName, functionName, props) {
this.put(` const ${variableName} = await dbgateApi.${functionName}(${JSON.stringify(props)});`);
this.put(`const ${variableName} = await dbgateApi.${functionName}(${JSON.stringify(props)});`);
}
copyStream(sourceVar, targetVar) {
this.put(` await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
this.put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
}
}

View File

@@ -1,12 +1,12 @@
import _ from 'lodash';
import ScriptCreator from './ScriptCreator';
import ScriptWriter from './ScriptWriter';
import getAsArray from '../utility/getAsArray';
import { getConnectionInfo } from '../utility/metadataLoaders';
import engines from '@dbgate/engines';
import { quoteFullName, fullNameFromString } from '@dbgate/datalib';
export default async function createImpExpScript(values) {
const script = new ScriptCreator();
const script = new ScriptWriter();
if (values.sourceStorageType == 'database') {
const tables = getAsArray(values.sourceTables);
for (const table of tables) {
@@ -29,7 +29,8 @@ export default async function createImpExpScript(values) {
});
script.copyStream(sourceVar, targetVar);
script.put();
}
}
return script.getCode();
return script.s;
}

View File

@@ -0,0 +1,15 @@
import React from 'react';
import ToolbarButton from '../widgets/ToolbarButton';
export default function ShellToolbar({ execute, cancel, busy}) {
return (
<>
<ToolbarButton disabled={busy} onClick={execute} icon="fas fa-play">
Execute
</ToolbarButton>
<ToolbarButton disabled={!busy} onClick={cancel} icon="fas fa-times">
Cancel
</ToolbarButton>
</>
);
}

View File

@@ -3,7 +3,7 @@ import React from 'react';
import MessagesView from './MessagesView';
import useSocket from '../utility/SocketProvider';
export default function SessionMessagesView({ sessionId, onMessageClick, executeNumber }) {
export default function SocketMessagesView({ eventName, onMessageClick = undefined, executeNumber }) {
const [displayedMessages, setDisplayedMessages] = React.useState([]);
const cachedMessagesRef = React.useRef([]);
const socket = useSocket();
@@ -27,13 +27,13 @@ export default function SessionMessagesView({ sessionId, onMessageClick, execute
}, [executeNumber]);
React.useEffect(() => {
if (sessionId && socket) {
socket.on(`session-info-${sessionId}`, handleInfo);
if (eventName && socket) {
socket.on(eventName, handleInfo);
return () => {
socket.off(`session-info-${sessionId}`, handleInfo);
socket.off(eventName, handleInfo);
};
}
}, [sessionId, socket]);
}, [eventName, socket]);
return <MessagesView items={displayedMessages} onMessageClick={onMessageClick} />;
}

View File

@@ -8,7 +8,7 @@ import { useConnectionInfo, getTableInfo, getConnectionInfo, getSqlObjectInfo }
import SqlEditor from '../sqleditor/SqlEditor';
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
import QueryToolbar from '../query/QueryToolbar';
import SessionMessagesView from '../query/SessionMessagesView';
import SocketMessagesView from '../query/SocketMessagesView';
import { TabPage } from '../widgets/TabControl';
import ResultTabs from '../sqleditor/ResultTabs';
import { VerticalSplitter } from '../widgets/Splitter';
@@ -143,6 +143,7 @@ export default function QueryTab({
};
const handleExecute = async () => {
if (busy) return;
setExecuteNumber((num) => num + 1);
const selectedText = editorRef.current.editor.getSelectedText();
@@ -204,8 +205,8 @@ export default function QueryTab({
{sessionId && (
<ResultTabs sessionId={sessionId} executeNumber={executeNumber}>
<TabPage label="Messages" key="messages">
<SessionMessagesView
sessionId={sessionId}
<SocketMessagesView
eventName={sessionId ? `session-info-${sessionId}` : null}
onMessageClick={handleMesageClick}
executeNumber={executeNumber}
/>

View File

@@ -8,7 +8,7 @@ import { useConnectionInfo, getTableInfo, getConnectionInfo, getSqlObjectInfo }
import SqlEditor from '../sqleditor/SqlEditor';
import { useUpdateDatabaseForTab, useSetOpenedTabs, useOpenedTabs } from '../utility/globalState';
import QueryToolbar from '../query/QueryToolbar';
import SessionMessagesView from '../query/SessionMessagesView';
import SocketMessagesView from '../query/SocketMessagesView';
import { TabPage } from '../widgets/TabControl';
import ResultTabs from '../sqleditor/ResultTabs';
import { VerticalSplitter } from '../widgets/Splitter';
@@ -19,11 +19,10 @@ import SaveSqlFileModal from '../modals/SaveSqlFileModal';
import useModalState from '../modals/useModalState';
import sqlFormatter from 'sql-formatter';
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
import ShellToolbar from '../query/ShellToolbar';
export default function ShellTab({
tabid,
conid,
database,
initialArgs,
tabVisible,
toolbarPortalRef,
@@ -43,6 +42,11 @@ export default function ShellTab({
const saveToStorageDebounced = React.useMemo(() => _.debounce(saveToStorage, 5000), [saveToStorage]);
const setOpenedTabs = useSetOpenedTabs();
const [executeNumber, setExecuteNumber] = React.useState(0);
const [runnerId, setRunnerId] = React.useState(null);
const socket = useSocket();
React.useEffect(() => {
window.addEventListener('beforeunload', saveToStorage);
return () => {
@@ -68,8 +72,18 @@ export default function ShellTab({
const editorRef = React.useRef(null);
useUpdateDatabaseForTab(tabVisible, conid, database);
const connection = useConnectionInfo({ conid });
const handleRunnerDone = React.useCallback(() => {
setBusy(false);
}, []);
React.useEffect(() => {
if (runnerId && socket) {
socket.on(`runner-done-${runnerId}`, handleRunnerDone);
return () => {
socket.off(`runner-done-${runnerId}`, handleRunnerDone);
};
}
}, [runnerId, socket]);
const handleChange = (text) => {
if (text != null) shellTextRef.current = text;
@@ -77,12 +91,24 @@ export default function ShellTab({
saveToStorageDebounced();
};
const handleExecute = async () => {};
const handleExecute = async () => {
if (busy) return;
setExecuteNumber((num) => num + 1);
const selectedText = editorRef.current.editor.getSelectedText();
let runid = runnerId;
const resp = await axios.post('runners/start', {
script: selectedText || shellText,
});
runid = resp.data.runid;
setRunnerId(runid);
setBusy(true);
};
const handleCancel = () => {
// axios.post('sessions/cancel', {
// sesid: sessionId,
// });
axios.post('runners/cancel', {
runid: runnerId,
});
};
const handleKeyDown = (data, hash, keyString, keyCode, event) => {
@@ -102,7 +128,19 @@ export default function ShellTab({
onKeyDown={handleKeyDown}
editorRef={editorRef}
/>
<SocketMessagesView eventName={runnerId ? `runner-info-${runnerId}` : null} executeNumber={executeNumber} />
</VerticalSplitter>
{toolbarPortalRef &&
toolbarPortalRef.current &&
tabVisible &&
ReactDOM.createPortal(
<ShellToolbar
execute={handleExecute}
busy={busy}
cancel={handleCancel}
/>,
toolbarPortalRef.current
)}
</>
);
}