diff --git a/packages/api/src/shell/copyStream.js b/packages/api/src/shell/copyStream.js index 60e49deb7..637eb0bb9 100644 --- a/packages/api/src/shell/copyStream.js +++ b/packages/api/src/shell/copyStream.js @@ -1,18 +1,47 @@ const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream'); +const Stream = require('stream'); +const ColumnMapTransformStream = require('../utility/ColumnMapTransformStream'); + +function copyStream(input, output, options) { + const { columns } = options || {}; + + const transforms = []; + if (columns) { + transforms.push(new ColumnMapTransformStream(columns)); + } + if (output.requireFixedStructure) { + transforms.push(new EnsureStreamHeaderStream()); + } + + // return new Promise((resolve, reject) => { + // Stream.pipeline(input, ...transforms, output, err => { + // if (err) { + // reject(err); + // } else { + // resolve(); + // } + // }); + // }); -function copyStream(input, output) { return new Promise((resolve, reject) => { const finisher = output['finisher'] || output; finisher.on('finish', resolve); finisher.on('error', reject); - if (output.requireFixedStructure) { - const ensureHeader = new EnsureStreamHeaderStream(); - input.pipe(ensureHeader); - ensureHeader.pipe(output); - } else { - input.pipe(output); + let lastStream = input; + for (const tran of transforms) { + lastStream.pipe(tran); + lastStream = tran; } + lastStream.pipe(output); + + // if (output.requireFixedStructure) { + // const ensureHeader = new EnsureStreamHeaderStream(); + // input.pipe(ensureHeader); + // ensureHeader.pipe(output); + // } else { + // input.pipe(output); + // } }); } diff --git a/packages/api/src/utility/ColumnMapTransformStream.js b/packages/api/src/utility/ColumnMapTransformStream.js new file mode 100644 index 000000000..1bbd913df --- /dev/null +++ b/packages/api/src/utility/ColumnMapTransformStream.js @@ -0,0 +1,21 @@ +const stream = require('stream'); +const { transformRowUsingColumnMap } = require('dbgate-tools'); + +class ColumnMapTransformStream extends stream.Transform { + constructor(columns) { + super({ objectMode: true }); + this.columns = columns; + } + _transform(chunk, encoding, done) { + if (chunk.__isStreamHeader) { + // skip stream header + done(); + return; + } + + this.push(transformRowUsingColumnMap(chunk, this.columns)); + done(); + } +} + +module.exports = ColumnMapTransformStream; diff --git a/packages/tools/src/tableTransforms.ts b/packages/tools/src/tableTransforms.ts index 403426cff..54f46ed03 100644 --- a/packages/tools/src/tableTransforms.ts +++ b/packages/tools/src/tableTransforms.ts @@ -1,5 +1,7 @@ import { TableInfo } from 'dbgate-types'; import _cloneDeep from 'lodash/cloneDeep'; +import _fromPairs from 'lodash/fromPairs'; +import _get from 'lodash/get'; export function prepareTableForImport(table: TableInfo): TableInfo { const res = _cloneDeep(table); @@ -10,3 +12,12 @@ export function prepareTableForImport(table: TableInfo): TableInfo { if (res.primaryKey) res.primaryKey.constraintName = null; return res; } + +interface TransformColumnDefinition { + src: string; + dst: string; +} + +export function transformRowUsingColumnMap(row, columns: TransformColumnDefinition[]) { + return _fromPairs(columns.map(col => [col.dst, _get(row, col.src)])); +} diff --git a/packages/web/src/elements/TableControl.svelte b/packages/web/src/elements/TableControl.svelte index e68377345..d22a01524 100644 --- a/packages/web/src/elements/TableControl.svelte +++ b/packages/web/src/elements/TableControl.svelte @@ -24,6 +24,7 @@ export let selectedIndex = 0; export let clickable = false; export let disableFocusOutline = false; + export let emptyMessage = null; export let domTable = undefined; @@ -99,6 +100,11 @@ {/each} {/each} + {#if emptyMessage && rows.length == 0} + + {emptyMessage} + + {/if} diff --git a/packages/web/src/forms/TextField.svelte b/packages/web/src/forms/TextField.svelte index 59a0b0ed4..6ef2cccf0 100644 --- a/packages/web/src/forms/TextField.svelte +++ b/packages/web/src/forms/TextField.svelte @@ -2,8 +2,8 @@ import { onMount } from 'svelte'; export let value; - export let focused; - export let domEditor; + export let focused = false; + export let domEditor = undefined; if (focused) onMount(() => domEditor.focus()); diff --git a/packages/web/src/impexp/ImportExportConfigurator.svelte b/packages/web/src/impexp/ImportExportConfigurator.svelte index dfbddfc4e..4ed7fcabc 100644 --- a/packages/web/src/impexp/ImportExportConfigurator.svelte +++ b/packages/web/src/impexp/ImportExportConfigurator.svelte @@ -42,11 +42,14 @@ + + + +
{header}
+ +
+ When no columns are defined in this mapping, source row is copied to target without any modifications +
+ + + + (value = (value || []).map((x, i) => (i == index ? { ...x, skip: !e.target.checked } : x)))} + /> + + + (value = (value || []).map((x, i) => (i == index ? { ...x, src: e.target.value } : x)))} + /> + + + (value = (value || []).map((x, i) => (i == index ? { ...x, dst: e.target.value } : x)))} + /> + + + { + value = value.filter((x, i) => i != index); + }}>Remove + + + + + { + closeCurrentModal(); + onConfirm(!value || value.length == 0 ? null : value); + }} + /> + + { + value = [...(value || []), {}]; + }} + /> + { + value = []; + }} + /> + +
+