diff --git a/packages/api/src/controllers/runners.js b/packages/api/src/controllers/runners.js
index c6c28cd56..cdaaa36b1 100644
--- a/packages/api/src/controllers/runners.js
+++ b/packages/api/src/controllers/runners.js
@@ -107,6 +107,10 @@ module.exports = {
}
},
+ handle_progress(runid, { progressName, status }) {
+ socket.emit(`runner-progress-${runid}`, { progressName, status });
+ },
+
rejectRequest(runid, error) {
if (this.requests[runid]) {
const { reject } = this.requests[runid];
diff --git a/packages/api/src/shell/copyStream.js b/packages/api/src/shell/copyStream.js
index be0c8ea31..c73476d62 100644
--- a/packages/api/src/shell/copyStream.js
+++ b/packages/api/src/shell/copyStream.js
@@ -10,7 +10,15 @@ const streamPipeline = require('../utility/streamPipeline');
* @returns {Promise}
*/
async function copyStream(input, output, options) {
- const { columns } = options || {};
+ const { columns, progressName } = options || {};
+
+ if (progressName) {
+ process.send({
+ msgtype: 'progress',
+ progressName,
+ status: 'running',
+ });
+ }
const transforms = [];
if (columns) {
@@ -22,6 +30,14 @@ async function copyStream(input, output, options) {
try {
await streamPipeline(input, transforms, output);
+
+ if (progressName) {
+ process.send({
+ msgtype: 'progress',
+ progressName,
+ status: 'done',
+ });
+ }
} catch (err) {
process.send({
msgtype: 'copyStreamError',
@@ -30,6 +46,15 @@ async function copyStream(input, output, options) {
...err,
},
});
+
+ if (progressName) {
+ process.send({
+ msgtype: 'progress',
+ progressName,
+ status: 'error',
+ });
+ }
+
throw err;
}
}
diff --git a/packages/tools/src/ScriptWriter.ts b/packages/tools/src/ScriptWriter.ts
index 7c4f93b09..23afee0f4 100644
--- a/packages/tools/src/ScriptWriter.ts
+++ b/packages/tools/src/ScriptWriter.ts
@@ -41,12 +41,13 @@ export class ScriptWriter {
this.packageNames.push(packageName);
}
- copyStream(sourceVar, targetVar, colmapVar = null) {
- if (colmapVar) {
- this._put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar}, {columns: ${colmapVar}});`);
- } else {
- this._put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
- }
+ copyStream(sourceVar, targetVar, colmapVar = null, progressName?: string) {
+ let opts = '{';
+ if (colmapVar) opts += `columns: ${colmapVar}, `;
+ if (progressName) opts += `progressName: "${progressName}", `;
+ opts += '}';
+
+ this._put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar}, ${opts});`);
}
dumpDatabase(options) {
@@ -117,12 +118,13 @@ export class ScriptWriterJson {
});
}
- copyStream(sourceVar, targetVar, colmapVar = null) {
+ copyStream(sourceVar, targetVar, colmapVar = null, progressName?: string) {
this.commands.push({
type: 'copyStream',
sourceVar,
targetVar,
colmapVar,
+ progressName,
});
}
@@ -183,7 +185,7 @@ export function jsonScriptToJavascript(json) {
script.assignValue(cmd.variableName, cmd.jsonValue);
break;
case 'copyStream':
- script.copyStream(cmd.sourceVar, cmd.targetVar, cmd.colmapVar);
+ script.copyStream(cmd.sourceVar, cmd.targetVar, cmd.colmapVar, cmd.progressName);
break;
case 'endLine':
script.endLine();
diff --git a/packages/web/src/elements/TableControl.svelte b/packages/web/src/elements/TableControl.svelte
index 931f8f8ec..e8eb1051b 100644
--- a/packages/web/src/elements/TableControl.svelte
+++ b/packages/web/src/elements/TableControl.svelte
@@ -20,7 +20,7 @@
import { createEventDispatcher } from 'svelte';
import FontIcon from '../icons/FontIcon.svelte';
- export let columns: TableControlColumn[];
+ export let columns: (TableControlColumn | false)[];
export let rows;
export let focusOnCreate = false;
export let selectable = false;
diff --git a/packages/web/src/icons/FontIcon.svelte b/packages/web/src/icons/FontIcon.svelte
index c5ed2f18a..df808a488 100644
--- a/packages/web/src/icons/FontIcon.svelte
+++ b/packages/web/src/icons/FontIcon.svelte
@@ -149,6 +149,7 @@
'icon download': 'mdi mdi-download',
'icon text': 'mdi mdi-text',
'icon ai': 'mdi mdi-head-lightbulb',
+ 'icon wait': 'mdi mdi-timer-sand',
'icon run': 'mdi mdi-play',
'icon chevron-down': 'mdi mdi-chevron-down',
diff --git a/packages/web/src/impexp/ImportExportConfigurator.svelte b/packages/web/src/impexp/ImportExportConfigurator.svelte
index efb286ded..8889f763d 100644
--- a/packages/web/src/impexp/ImportExportConfigurator.svelte
+++ b/packages/web/src/impexp/ImportExportConfigurator.svelte
@@ -104,6 +104,7 @@
$: sourceList = $values.sourceList;
let targetEditKey = 0;
+ export let progressHolder = null;
const previewSource = writable(null);
@@ -231,11 +232,16 @@
header: 'Target',
slot: 1,
},
- {
+ supportsPreview && {
fieldName: 'preview',
header: 'Preview',
slot: 0,
},
+ !!progressHolder && {
+ fieldName: 'status',
+ header: 'Status',
+ slot: 3,
+ },
{
fieldName: 'columns',
header: 'Columns',
@@ -296,6 +302,17 @@
>{columnCount > 0 ? `(${columnCount} columns)` : '(copy from source)'}
+
+ {#if progressHolder[row]?.status == 'running'}
+ Running
+ {:else if progressHolder[row]?.status == 'error'}
+ Error
+ {:else if progressHolder[row]?.status == 'done'}
+ Done
+ {:else}
+ Queued
+ {/if}
+
{/key}
diff --git a/packages/web/src/impexp/createImpExpScript.ts b/packages/web/src/impexp/createImpExpScript.ts
index 0fa3c076c..c9961fb7b 100644
--- a/packages/web/src/impexp/createImpExpScript.ts
+++ b/packages/web/src/impexp/createImpExpScript.ts
@@ -233,7 +233,7 @@ export default async function createImpExpScript(extensions, values, forceScript
script.assignValue(colmapVar, colmap);
}
- script.copyStream(sourceVar, targetVar, colmapVar);
+ script.copyStream(sourceVar, targetVar, colmapVar, sourceName);
script.endLine();
}
return script.getScript(values.schedule);
diff --git a/packages/web/src/tabs/ImportExportTab.svelte b/packages/web/src/tabs/ImportExportTab.svelte
index 37e4321cc..ab027d607 100644
--- a/packages/web/src/tabs/ImportExportTab.svelte
+++ b/packages/web/src/tabs/ImportExportTab.svelte
@@ -65,6 +65,7 @@
export let savedFile;
export let savedFilePath;
+ let progressHolder = null;
const refreshArchiveFolderRef = createRef(null);
const formValues = writable({});
@@ -179,6 +180,7 @@
const handleExecute = async e => {
if (busy) return;
+ progressHolder = {};
const values = $formValues as any;
busy = true;
const script = await createImpExpScript($extensions, values);
@@ -228,6 +230,29 @@
title: `${getSourceTargetTitle('source', values)}->${getSourceTargetTitle('target', values)}(${values.sourceList?.length || 0})`,
}));
}
+
+ const handleProgress = progress => {
+ progressHolder = {
+ ...progressHolder,
+ [progress.progressName]: {
+ ...progressHolder[progress.progressName],
+ ...progress,
+ },
+ };
+ };
+
+ $: progressEffect = useEffect(() => {
+ if (runnerId) {
+ const eventName = `runner-progress-${runnerId}`;
+ apiOn(eventName, handleProgress);
+ return () => {
+ apiOff(eventName, handleProgress);
+ };
+ }
+ return () => {};
+ });
+
+ $progressEffect;
@@ -237,6 +262,7 @@