mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
import export
This commit is contained in:
parent
c2c54856ff
commit
2063005d5c
@ -109,6 +109,15 @@
|
|||||||
onClick: () => get(currentDataGrid).copyToClipboard(),
|
onClick: () => get(currentDataGrid).copyToClipboard(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'dataGrid.export',
|
||||||
|
category: 'Data grid',
|
||||||
|
name: 'Export',
|
||||||
|
keyText: 'Ctrl+E',
|
||||||
|
enabledStore: derived(currentDataGrid, grid => grid != null && grid.exportEnabled()),
|
||||||
|
onClick: () => get(currentDataGrid).exportGrid(),
|
||||||
|
});
|
||||||
|
|
||||||
function getRowCountInfo(selectedCells, grider, realColumnUniqueNames, selectedRowData, allRowCount) {
|
function getRowCountInfo(selectedCells, grider, realColumnUniqueNames, selectedRowData, allRowCount) {
|
||||||
if (selectedCells.length > 1 && selectedCells.every(x => _.isNumber(x[0]) && _.isNumber(x[1]))) {
|
if (selectedCells.length > 1 && selectedCells.every(x => _.isNumber(x[0]) && _.isNumber(x[1]))) {
|
||||||
let sum = _.sumBy(selectedCells, cell => {
|
let sum = _.sumBy(selectedCells, cell => {
|
||||||
@ -180,6 +189,7 @@
|
|||||||
export let onReferenceClick = undefined;
|
export let onReferenceClick = undefined;
|
||||||
export let onSave;
|
export let onSave;
|
||||||
export let focusOnVisible = false;
|
export let focusOnVisible = false;
|
||||||
|
export let onExportGrid = null;
|
||||||
|
|
||||||
export let isLoadedAll;
|
export let isLoadedAll;
|
||||||
export let loadedTime;
|
export let loadedTime;
|
||||||
@ -231,6 +241,14 @@
|
|||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function exportGrid() {
|
||||||
|
if (onExportGrid) onExportGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportEnabled() {
|
||||||
|
return !!onExportGrid;
|
||||||
|
}
|
||||||
|
|
||||||
export function revertRowChanges() {
|
export function revertRowChanges() {
|
||||||
grider.beginUpdate();
|
grider.beginUpdate();
|
||||||
for (const index of getSelectedRowIndexes()) {
|
for (const index of getSelectedRowIndexes()) {
|
||||||
@ -813,6 +831,7 @@
|
|||||||
return [
|
return [
|
||||||
{ command: 'dataGrid.refresh' },
|
{ command: 'dataGrid.refresh' },
|
||||||
{ command: 'dataGrid.copyToClipboard' },
|
{ command: 'dataGrid.copyToClipboard' },
|
||||||
|
{ command: 'dataGrid.export' },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{ command: 'dataGrid.save' },
|
{ command: 'dataGrid.save' },
|
||||||
{ command: 'dataGrid.revertRowChanges' },
|
{ command: 'dataGrid.revertRowChanges' },
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
import { scriptToSql } from 'dbgate-sqltree';
|
import { scriptToSql } from 'dbgate-sqltree';
|
||||||
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
|
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
|
||||||
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||||
|
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
|
|
||||||
import axiosInstance from '../utility/axiosInstance';
|
import axiosInstance from '../utility/axiosInstance';
|
||||||
@ -98,6 +99,16 @@
|
|||||||
engine: display.engine,
|
engine: display.engine,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function exportGrid() {
|
||||||
|
const initialValues: any = {};
|
||||||
|
initialValues.sourceStorageType = 'query';
|
||||||
|
initialValues.sourceConnectionId = conid;
|
||||||
|
initialValues.sourceDatabaseName = database;
|
||||||
|
initialValues.sourceSql = display.getExportQuery();
|
||||||
|
initialValues.sourceList = display.baseTable ? [display.baseTable.pureName] : [];
|
||||||
|
showModal(ImportExportModal, { initialValues });
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LoadingDataGridCore
|
<LoadingDataGridCore
|
||||||
@ -105,6 +116,7 @@
|
|||||||
{loadDataPage}
|
{loadDataPage}
|
||||||
{dataPageAvailable}
|
{dataPageAvailable}
|
||||||
{loadRowCount}
|
{loadRowCount}
|
||||||
|
onExportGrid={exportGrid}
|
||||||
bind:loadedRows
|
bind:loadedRows
|
||||||
{grider}
|
{grider}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
|
54
packages/web/src/elements/LargeButton.svelte
Normal file
54
packages/web/src/elements/LargeButton.svelte
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
export let icon;
|
||||||
|
export let disabled = false;
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
if (!disabled) dispatch('click');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="button" on:click={handleClick} class:disabled>
|
||||||
|
<div class="icon">
|
||||||
|
<FontIcon {icon} />
|
||||||
|
</div>
|
||||||
|
<div class="inner">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.button {
|
||||||
|
padding: 5px 15px;
|
||||||
|
color: var(--theme-font-1);
|
||||||
|
border: 1px solid var(--theme-border);
|
||||||
|
width: 120px;
|
||||||
|
height: 60px;
|
||||||
|
background-color: var(--theme-bg-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:not(.disabled):hover {
|
||||||
|
background-color: var(--theme-bg-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:not(.disabled):active {
|
||||||
|
background-color: var(--theme-bg-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.disabled {
|
||||||
|
color: var(--theme-font-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
17
packages/web/src/forms/LargeFormButton.svelte
Normal file
17
packages/web/src/forms/LargeFormButton.svelte
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script>
|
||||||
|
import { getFormContext } from './FormProviderCore.svelte';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import LargeButton from '../elements/LargeButton.svelte';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
const { values } = getFormContext();
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
dispatch('click', $values);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<LargeButton on:click={handleClick} {...$$props}>
|
||||||
|
<slot />
|
||||||
|
</LargeButton>
|
17
packages/web/src/impexp/FormConnectionSelect.svelte
Normal file
17
packages/web/src/impexp/FormConnectionSelect.svelte
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||||
|
|
||||||
|
import { useConnectionList } from '../utility/metadataLoaders';
|
||||||
|
|
||||||
|
$: connections = useConnectionList();
|
||||||
|
$: connectionOptions = ($connections || []).map(conn => ({
|
||||||
|
value: conn._id,
|
||||||
|
label: conn.displayName || conn.server,
|
||||||
|
}));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if connectionOptions.length == 0}
|
||||||
|
<div>Not available</div>
|
||||||
|
{:else}
|
||||||
|
<FormSelectField {...$$restProps} options={connectionOptions} />
|
||||||
|
{/if}
|
21
packages/web/src/impexp/FormDatabaseSelect.svelte
Normal file
21
packages/web/src/impexp/FormDatabaseSelect.svelte
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||||
|
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||||
|
import { useDatabaseList } from '../utility/metadataLoaders';
|
||||||
|
|
||||||
|
export let conidName;
|
||||||
|
|
||||||
|
const { values } = getFormContext();
|
||||||
|
$: databases = useDatabaseList({ conid: $values[conidName] });
|
||||||
|
|
||||||
|
$: databaseOptions = ($databases || []).map(db => ({
|
||||||
|
value: db.name,
|
||||||
|
label: db.name,
|
||||||
|
}));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if databaseOptions.length == 0}
|
||||||
|
<div>Not available</div>
|
||||||
|
{:else}
|
||||||
|
<FormSelectField {...$$restProps} options={databaseOptions} />
|
||||||
|
{/if}
|
41
packages/web/src/impexp/ImportExportConfigurator.svelte
Normal file
41
packages/web/src/impexp/ImportExportConfigurator.svelte
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
|
||||||
|
import SourceTargetConfig from './SourceTargetConfig.svelte';
|
||||||
|
|
||||||
|
// engine={sourceEngine}
|
||||||
|
// {setPreviewSource}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex1">
|
||||||
|
<div class="flex">
|
||||||
|
<SourceTargetConfig
|
||||||
|
direction="source"
|
||||||
|
storageTypeField="sourceStorageType"
|
||||||
|
connectionIdField="sourceConnectionId"
|
||||||
|
databaseNameField="sourceDatabaseName"
|
||||||
|
archiveFolderField="sourceArchiveFolder"
|
||||||
|
schemaNameField="sourceSchemaName"
|
||||||
|
tablesField="sourceList"
|
||||||
|
/>
|
||||||
|
<div class="arrow">
|
||||||
|
<FontIcon icon="icon arrow-right" />
|
||||||
|
</div>
|
||||||
|
<SourceTargetConfig
|
||||||
|
direction="target"
|
||||||
|
storageTypeField="targetStorageType"
|
||||||
|
connectionIdField="targetConnectionId"
|
||||||
|
databaseNameField="targetDatabaseName"
|
||||||
|
archiveFolderField="targetArchiveFolder"
|
||||||
|
schemaNameField="targetSchemaName"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.arrow {
|
||||||
|
font-size: 30px;
|
||||||
|
color: var(--theme-icon-blue);
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
</style>
|
50
packages/web/src/impexp/ScriptWriter.ts
Normal file
50
packages/web/src/impexp/ScriptWriter.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { extractShellApiFunctionName, extractShellApiPlugins } from 'dbgate-tools';
|
||||||
|
|
||||||
|
export default class ScriptWriter {
|
||||||
|
s = '';
|
||||||
|
packageNames: string[] = [];
|
||||||
|
varCount = 0;
|
||||||
|
|
||||||
|
constructor(varCount = '0') {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
79
packages/web/src/impexp/SourceTargetConfig.svelte
Normal file
79
packages/web/src/impexp/SourceTargetConfig.svelte
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||||
|
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||||
|
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import { findFileFormat, getFileFormatDirections } from '../plugins/fileformats';
|
||||||
|
import { extensions } from '../stores';
|
||||||
|
import { useArchiveFiles, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||||
|
import FormConnectionSelect from './FormConnectionSelect.svelte';
|
||||||
|
import FormDatabaseSelect from './FormDatabaseSelect.svelte';
|
||||||
|
|
||||||
|
export let direction;
|
||||||
|
export let storageTypeField;
|
||||||
|
|
||||||
|
export let connectionIdField;
|
||||||
|
export let databaseNameField;
|
||||||
|
export let archiveFolderField;
|
||||||
|
export let schemaNameField;
|
||||||
|
export let tablesField = undefined;
|
||||||
|
export let engine = undefined;
|
||||||
|
|
||||||
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
|
||||||
|
$: 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'] },
|
||||||
|
];
|
||||||
|
|
||||||
|
$: storageType = $values[storageTypeField];
|
||||||
|
$: dbinfo = useDatabaseInfo({ conid: $values[connectionIdField], database: $values[databaseNameField] });
|
||||||
|
$: archiveFiles = useArchiveFiles({ folder: $values[archiveFolderField] });
|
||||||
|
$: format = findFileFormat($extensions, storageType);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
{#if direction == 'source'}
|
||||||
|
<div class="title">
|
||||||
|
<FontIcon icon="icon import" /> Source configuration
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if direction == 'target'}
|
||||||
|
<div class="title">
|
||||||
|
<FontIcon icon="icon export" /> Target configuration
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<FormSelectField
|
||||||
|
options={types.filter(x => x.directions.includes(direction))}
|
||||||
|
name={storageTypeField}
|
||||||
|
label="Storage type"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if storageType == 'database' || storageType == 'query'}
|
||||||
|
<FormConnectionSelect name={connectionIdField} label="Server" />
|
||||||
|
<FormDatabaseSelect conidName={connectionIdField} name={databaseNameField} label="Database" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
margin: 10px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
</style>
|
240
packages/web/src/impexp/createImpExpScript.ts
Normal file
240
packages/web/src/impexp/createImpExpScript.ts
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
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 '../plugins/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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
135
packages/web/src/modals/ImportExportModal.svelte
Normal file
135
packages/web/src/modals/ImportExportModal.svelte
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
|
||||||
|
import LargeButton from '../elements/LargeButton.svelte';
|
||||||
|
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
||||||
|
|
||||||
|
import FormProvider from '../forms/FormProvider.svelte';
|
||||||
|
import FormTextField from '../forms/FormTextField.svelte';
|
||||||
|
import LargeFormButton from '../forms/LargeFormButton.svelte';
|
||||||
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import ImportExportConfigurator from '../impexp/ImportExportConfigurator.svelte';
|
||||||
|
import RunnerOutputFiles from '../query/RunnerOutputFiles';
|
||||||
|
import SocketMessageView from '../query/SocketMessageView.svelte';
|
||||||
|
import WidgetColumnBar from '../widgets/WidgetColumnBar.svelte';
|
||||||
|
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
|
||||||
|
import ModalBase from './ModalBase.svelte';
|
||||||
|
import { closeCurrentModal } from './modalTools';
|
||||||
|
|
||||||
|
let busy = false;
|
||||||
|
let executeNumber = 0;
|
||||||
|
let runnerId = null;
|
||||||
|
let previewReader = null;
|
||||||
|
|
||||||
|
export let initialValues;
|
||||||
|
export let uploadedFile = undefined;
|
||||||
|
export let openedFile = undefined;
|
||||||
|
export let importToArchive = false;
|
||||||
|
|
||||||
|
const handleGenerateScript = async () => {
|
||||||
|
// const code = await createImpExpScript(extensions, values);
|
||||||
|
// openNewTab(
|
||||||
|
// {
|
||||||
|
// title: 'Shell #',
|
||||||
|
// icon: 'img shell',
|
||||||
|
// tabComponent: 'ShellTab',
|
||||||
|
// },
|
||||||
|
// { editor: code }
|
||||||
|
// );
|
||||||
|
// modalState.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExecute = async values => {
|
||||||
|
// if (busy) return;
|
||||||
|
// setBusy(true);
|
||||||
|
// const script = await createImpExpScript(extensions, values);
|
||||||
|
// setExecuteNumber(num => num + 1);
|
||||||
|
// let runid = runnerId;
|
||||||
|
// const resp = await axios.post('runners/start', { script });
|
||||||
|
// runid = resp.data.runid;
|
||||||
|
// setRunnerId(runid);
|
||||||
|
// if (values.targetStorageType == 'archive') {
|
||||||
|
// refreshArchiveFolderRef.current = values.targetArchiveFolder;
|
||||||
|
// } else {
|
||||||
|
// refreshArchiveFolderRef.current = null;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
// axios.post('runners/cancel', {
|
||||||
|
// runid: runnerId,
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormProvider>
|
||||||
|
<ModalBase {...$$restProps} fullScreen skipBody skipFooter>
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
Import/Export
|
||||||
|
{#if busy}
|
||||||
|
<FontIcon icon="icon loading" />
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
<HorizontalSplitter>
|
||||||
|
<svelte:fragment slot="1">
|
||||||
|
<ImportExportConfigurator />
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<svelte:fragment slot="2">
|
||||||
|
<WidgetColumnBar>
|
||||||
|
<WidgetColumnBarItem title="Output files" name="output" height="20%">
|
||||||
|
<RunnerOutputFiles {runnerId} {executeNumber} />
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
<WidgetColumnBarItem title="Messages" name="messages">
|
||||||
|
<SocketMessageView eventName={runnerId ? `runner-info-${runnerId}` : null} {executeNumber} />
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
{#if previewReader}
|
||||||
|
<WidgetColumnBarItem title="Preview" name="preview">
|
||||||
|
<!-- <PreviewDataGrid reader={previewReader} /> -->
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
{/if}
|
||||||
|
<WidgetColumnBarItem title="Advanced configuration" name="config" collapsed>
|
||||||
|
<FormTextField label="Schedule" name="schedule" />
|
||||||
|
<FormTextField label="Start variable index" name="startVariableIndex" />
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
</WidgetColumnBar>
|
||||||
|
</svelte:fragment>
|
||||||
|
</HorizontalSplitter>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<div class="flex m-2">
|
||||||
|
{#if busy}
|
||||||
|
<LargeButton icon="icon close" on:click={handleCancel}>Cancel</LargeButton>
|
||||||
|
{:else}
|
||||||
|
<LargeFormButton on:click={handleExecute} icon="icon run">Run</LargeFormButton>
|
||||||
|
{/if}
|
||||||
|
<LargeFormButton icon="img sql-file" on:click={handleGenerateScript}>Generate script</LargeFormButton>
|
||||||
|
|
||||||
|
<LargeButton on:click={closeCurrentModal} icon="icon close">Close</LargeButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalBase>
|
||||||
|
</FormProvider>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 60px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 100px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
height: 100px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0px;
|
||||||
|
border-top: 1px solid var(--theme-border);
|
||||||
|
background-color: var(--theme-bg-modalheader);
|
||||||
|
}
|
||||||
|
</style>
|
@ -8,6 +8,8 @@
|
|||||||
export let fullScreen = false;
|
export let fullScreen = false;
|
||||||
export let noPadding = false;
|
export let noPadding = false;
|
||||||
export let modalId;
|
export let modalId;
|
||||||
|
export let skipBody = false;
|
||||||
|
export let skipFooter = false;
|
||||||
|
|
||||||
function handleCloseModal() {
|
function handleCloseModal() {
|
||||||
closeModal(modalId);
|
closeModal(modalId);
|
||||||
@ -31,18 +33,26 @@
|
|||||||
<div id="myModal" class="bglayer">
|
<div id="myModal" class="bglayer">
|
||||||
<!-- Modal content -->
|
<!-- Modal content -->
|
||||||
<div class="window" class:fullScreen use:clickOutside on:clickOutside={handleCloseModal}>
|
<div class="window" class:fullScreen use:clickOutside on:clickOutside={handleCloseModal}>
|
||||||
<div class="header">
|
{#if $$slots.header}
|
||||||
<div><slot name="header" /></div>
|
<div class="header">
|
||||||
<div class="close" on:click={handleCloseModal}>
|
<div><slot name="header" /></div>
|
||||||
<FontIcon icon="icon close" />
|
<div class="close" on:click={handleCloseModal}>
|
||||||
|
<FontIcon icon="icon close" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
<div class="content" class:noPadding>
|
{#if !skipBody}
|
||||||
|
<div class="content" class:noPadding>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
{/if}
|
||||||
<div class="footer">
|
{#if !skipFooter}
|
||||||
<slot name="footer" />
|
<div class="footer">
|
||||||
</div>
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -63,16 +73,24 @@
|
|||||||
|
|
||||||
.window {
|
.window {
|
||||||
background-color: var(--theme-bg-0);
|
background-color: var(--theme-bg-0);
|
||||||
margin: auto;
|
|
||||||
margin-top: 15vh;
|
|
||||||
border: 1px solid var(--theme-border);
|
border: 1px solid var(--theme-border);
|
||||||
width: 50%;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.window:not(.fullScreen) {
|
.window:not(.fullScreen) {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 15vh;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window.fullScreen {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
|
Loading…
Reference in New Issue
Block a user