Merge branch 'master' into redis

This commit is contained in:
Jan Prochazka
2022-03-13 17:33:35 +01:00
35 changed files with 394 additions and 52 deletions

View File

@@ -1,6 +1,6 @@
{
"private": true,
"version": "4.7.3-beta.1",
"version": "4.7.3-beta.4",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -11,7 +11,7 @@
"start:api": "yarn workspace dbgate-api start",
"start:app": "cd app && yarn start",
"start:api:portal": "yarn workspace dbgate-api start:portal",
"start:api:covid": "yarn workspace dbgate-api start:covid",
"start:api:singledb": "yarn workspace dbgate-api start:singledb",
"start:web": "yarn workspace dbgate-web dev",
"start:sqltree": "yarn workspace dbgate-sqltree start",
"start:tools": "yarn workspace dbgate-tools start",

View File

@@ -53,8 +53,8 @@
},
"scripts": {
"start": "env-cmd node src/index.js",
"start:portal": "env-cmd -f .env-portal node src/index.js",
"start:singledb": "env-cmd -f .env-singledb node src/index.js",
"start:portal": "env-cmd -f env/portal/.env node src/index.js",
"start:singledb": "env-cmd -f env/singledb/.env node src/index.js",
"start:filedb": "env-cmd node src/index.js /home/jena/test/chinook/Chinook.db",
"start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test",
"ts": "tsc",

View File

@@ -265,4 +265,16 @@ module.exports = {
return true;
},
createConfigFile_meta: true,
async createConfigFile({ appFolder, fileName, content }) {
const file = path.join(appdir(), appFolder, fileName);
if (!(await fs.exists(file))) {
await fs.writeFile(file, JSON.stringify(content, undefined, 2));
socket.emitChanged(`app-files-changed-${appFolder}`);
socket.emitChanged('used-apps-changed');
return true;
}
return false;
},
};

View File

@@ -98,6 +98,7 @@ module.exports = {
const app = folder.substring('app:'.length);
await fs.writeFile(path.join(appdir(), app, file), serialize(format, data));
socket.emitChanged(`app-files-changed-${app}`);
socket.emitChanged('used-apps-changed');
apps.emitChangedDbApp(folder);
return true;
} else {

View File

@@ -89,6 +89,12 @@ module.exports = {
encoding: 'utf-8',
})
.then(x => JSON.parse(x));
if (!manifest.keywords) {
continue;
}
if (!manifest.keywords.includes('dbgateplugin')) {
continue;
}
const readmeFile = path.join(isPackaged ? packagedPluginsDir() : pluginsdir(), packageName, 'README.md');
// @ts-ignore
if (await fs.exists(readmeFile)) {

View File

@@ -95,7 +95,12 @@ function start() {
if (platformInfo.isNpmDist) {
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
getPort({ port: 5000 }).then(port => {
getPort({
port: parseInt(
// @ts-ignore
process.env.PORT || 3000
),
}).then(port => {
server.listen(port, () => {
console.log(`DbGate API listening on port ${port}`);
});

View File

@@ -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);
// }
});
}

View File

@@ -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;

View File

@@ -3,7 +3,7 @@ const fs = require('fs-extra');
async function saveFreeTableData(file, data) {
const { structure, rows } = data;
const fileStream = fs.createWriteStream(file);
await fileStream.write(JSON.stringify(structure) + '\n');
await fileStream.write(JSON.stringify({ __isStreamHeader: true, ...structure }) + '\n');
for (const row of rows) {
await fileStream.write(JSON.stringify(row) + '\n');
}

View File

@@ -166,11 +166,12 @@ export abstract class GridDisplay {
hideAllColumns() {
this.setConfig(cfg => ({
...cfg,
hiddenColumns: this.columns.map(x => x.uniqueName),
hiddenColumns: this.columns.filter(x => x.uniquePath.length == 1).map(x => x.uniqueName),
}));
}
get hiddenColumnIndexes() {
// console.log('GridDisplay.hiddenColumn', this.config.hiddenColumns);
return (this.config.hiddenColumns || []).map(x => _.findIndex(this.allColumns, y => y.uniqueName == x));
}
@@ -569,6 +570,20 @@ export abstract class GridDisplay {
return sql;
}
getExportColumnMap() {
const changesDefined = this.config.hiddenColumns?.length > 0 || this.config.addedColumns?.length > 0;
if (this.isDynamicStructure && !changesDefined) {
return null;
}
return this.getColumns(null)
.filter(col => col.isChecked)
.map(col => ({
dst: col.headerText,
src: col.uniqueName,
ignore: !changesDefined,
}));
}
resizeColumn(uniqueName: string, computedSize: number, diff: number) {
this.setConfig(cfg => {
const columnWidths = {

View File

@@ -11,7 +11,7 @@ npm install -g dbgate
After installing, you can run dbgate with command:
```sh
dbgate
dbgate-serve
```
Then open http://localhost:5000 in your browser

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env node
const path = require('path');
require('dotenv').config();
global.dbgateApiModulePath = path.dirname(path.dirname(require.resolve('dbgate-api')));
global.dbgateApiPackagedPluginsPath = path.dirname(global.dbgateApiModulePath);

View File

@@ -10,7 +10,7 @@
"author": "Jan Prochazka",
"license": "MIT",
"bin": {
"dbgate": "./bin/dbgate.js"
"dbgate-serve": "./bin/dbgate-serve.js"
},
"keywords": [
"sql",
@@ -19,12 +19,14 @@
],
"dependencies": {
"dbgate-api": "^4.1.1",
"dbgate-web": "^4.1.1",
"dbgate-plugin-csv": "^4.1.1",
"dbgate-plugin-excel": "^4.1.1",
"dbgate-plugin-mongo": "^4.1.1",
"dbgate-plugin-mysql": "^4.1.1",
"dbgate-plugin-mssql": "^4.1.1",
"dbgate-plugin-postgres": "^4.1.1"
"dbgate-plugin-mysql": "^4.1.1",
"dbgate-plugin-postgres": "^4.1.1",
"dbgate-plugin-xml": "^4.1.1",
"dbgate-web": "^4.1.1",
"dotenv": "^16.0.0"
}
}

View File

@@ -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)]));
}

View File

@@ -174,6 +174,7 @@
initialValues.sourceDatabaseName = database;
initialValues.sourceSql = getExportQuery();
initialValues.sourceList = [pureName];
initialValues[`columns_${pureName}`] = display.getExportColumnMap();
showModal(ImportExportModal, { initialValues });
}
@@ -208,7 +209,8 @@
sql: getExportQuery(),
},
},
fmt
fmt,
display.getExportColumnMap()
);
};

View File

@@ -175,12 +175,14 @@
if (domFocusField) domFocusField.focus();
}}
on:setvisibility={e => {
if (selectedColumns.includes(column.uniqueName)) {
for (const name of selectedColumns) {
const column = items.find(x => x.uniqueName == name);
if (column) {
display.setColumnVisibility(column.uniquePath, e.detail);
}
}
}
}}
/>
{/each}

View File

@@ -800,6 +800,7 @@
// $: console.log('containerHeight', containerHeight);
// $: console.log('COLUMNS', columns);
// $: console.log('columnSizes.realCount', columnSizes.realCount);
// $: console.log('realColumnUniqueNames', realColumnUniqueNames);
// $: console.log('columnSizes.realCount', columnSizes.realCount);

View File

@@ -55,6 +55,7 @@
import RowsArrayGrider from './RowsArrayGrider';
export let jslid;
export let display;
export const activator = createActivator('JslDataGridCore', false);
@@ -93,10 +94,12 @@
initialValues.sourceStorageType = 'archive';
initialValues.sourceArchiveFolder = archiveMatch[1];
initialValues.sourceList = [archiveMatch[2]];
initialValues[`columns_${archiveMatch[2]}`] = display.getExportColumnMap();
} else {
initialValues.sourceStorageType = 'jsldata';
initialValues.sourceJslId = jslid;
initialValues.sourceList = ['query-data'];
initialValues[`columns_query-data`] = display.getExportColumnMap();
}
showModal(ImportExportModal, { initialValues });
}
@@ -113,7 +116,8 @@
fileName: archiveMatch[2],
},
},
fmt
fmt,
display.getExportColumnMap()
);
} else {
exportQuickExportFile(
@@ -124,7 +128,8 @@
jslid,
},
},
fmt
fmt,
display.getExportColumnMap()
);
}
};

View File

@@ -99,9 +99,12 @@ export class SeriesSizes {
}
this.modelIndexes = _.range(0, this.count);
// console.log('SeriesSize:build:this.modelIndexes-before', this.modelIndexes);
// console.log('SeriesSize:build:this.hiddenAndFrozenModelIndexes', this.hiddenAndFrozenModelIndexes);
if (this.hiddenAndFrozenModelIndexes) {
this.modelIndexes = this.modelIndexes.filter(col => !this.hiddenAndFrozenModelIndexes.includes(col));
}
// console.log('SeriesSize:build:this.modelIndexes-result', this.modelIndexes);
}
public getScrollIndexOnPosition(position: number): number {

View File

@@ -135,6 +135,7 @@
initialValues.sourceDatabaseName = database;
initialValues.sourceSql = display.getExportQuery();
initialValues.sourceList = display.baseTableOrSimilar ? [display.baseTableOrSimilar.pureName] : [];
initialValues[`columns_${pureName}`] = display.getExportColumnMap();
showModal(ImportExportModal, { initialValues });
}
@@ -193,7 +194,8 @@
sql: display.getExportQuery(),
},
},
fmt
fmt,
display.getExportColumnMap()
);
};
registerQuickExportHandler(quickExportHandler);

View File

@@ -17,6 +17,7 @@ export function countColumnSizes(grider: Grider, columns, containerWidth, displa
//return this.context.measureText(txt).width;
// console.log('countColumnSizes', loadedRows.length, containerWidth);
// console.log('countColumnSizes:columns', columns);
columnSizes.maxSize = (containerWidth * 2) / 3;
columnSizes.count = columns.length;
@@ -114,10 +115,12 @@ export function countVisibleRealColumns(columnSizes, firstVisibleColumnScrollInd
) {
visibleRealColumnIndexes.push(colIndex + columnSizes.frozenCount);
}
// console.log('countVisibleRealColumns:visibleRealColumnIndexes', visibleRealColumnIndexes);
// real columns
for (let colIndex of visibleRealColumnIndexes) {
let modelColumnIndex = columnSizes.realToModel(colIndex);
// console.log('countVisibleRealColumns:modelColumnIndex', modelColumnIndex);
modelIndexes[colIndex] = modelColumnIndex;
let col = columns[modelColumnIndex];
@@ -129,6 +132,7 @@ export function countVisibleRealColumns(columnSizes, firstVisibleColumnScrollInd
width,
});
}
// console.log('countVisibleRealColumns:realColumns', realColumns);
return realColumns;
}

View File

@@ -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}
</tr>
{/each}
{#if emptyMessage && rows.length == 0}
<tr>
<td colspan={columnList.length}>{emptyMessage}</td>
</tr>
{/if}
</tbody>
</table>

View File

@@ -16,6 +16,7 @@
import { createGridCache, FreeTableGridDisplay } from 'dbgate-datalib';
import { writable } from 'svelte/store';
import uuidv1 from 'uuid/v1';
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
import registerCommand from '../commands/registerCommand';
import DataGridCore from '../datagrid/DataGridCore.svelte';
@@ -24,6 +25,8 @@
import { apiCall } from '../utility/api';
import { registerMenu } from '../utility/contextMenu';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import createQuickExportMenu from '../utility/createQuickExportMenu';
import { exportQuickExportFile } from '../utility/exportFileTools';
import FreeTableGrider from './FreeTableGrider';
import MacroPreviewGrider from './MacroPreviewGrider';
@@ -51,10 +54,31 @@
initialValues.sourceStorageType = 'jsldata';
initialValues.sourceJslId = jslid;
initialValues.sourceList = ['editor-data'];
initialValues[`columns_editor-data`] = display.getExportColumnMap();
showModal(ImportExportModal, { initialValues: initialValues });
}
registerMenu({ command: 'freeTableGrid.export', tag: 'export' });
const quickExportHandler = fmt => async () => {
const jslid = uuidv1();
await apiCall('jsldata/save-free-table', { jslid, data: modelState.value });
exportQuickExportFile(
'editor-data',
{
functionName: 'jslDataReader',
props: {
jslid,
},
},
fmt,
display.getExportColumnMap()
);
};
registerQuickExportHandler(quickExportHandler);
registerMenu(() => ({
...createQuickExportMenu(quickExportHandler, { command: 'freeTableGrid.export' }),
tag: 'export',
}));
</script>
<DataGridCore {...$$props} {grider} {display} frameSelection={!!macroPreview} bind:selectedCellsPublished />

View File

@@ -42,11 +42,14 @@
<script lang="ts">
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
import Link from '../elements/Link.svelte';
import TableControl from '../elements/TableControl.svelte';
import CheckboxField from '../forms/CheckboxField.svelte';
import { getFormContext } from '../forms/FormProviderCore.svelte';
import TextField from '../forms/TextField.svelte';
import FontIcon from '../icons/FontIcon.svelte';
import ColumnMapModal from '../modals/ColumnMapModal.svelte';
import { showModal } from '../modals/modalTools';
import { findFileFormat } from '../plugins/fileformats';
import { extensions } from '../stores';
import getAsArray from '../utility/getAsArray';
@@ -189,6 +192,11 @@
header: 'Preview',
slot: 0,
},
{
fieldName: 'columns',
header: 'Columns',
slot: 2,
},
]}
>
<svelte:fragment slot="0" let:row>
@@ -214,6 +222,18 @@
)}
/>
</svelte:fragment>
<svelte:fragment slot="2" let:row>
{@const columnCount = ($values[`columns_${row}`] || []).filter(x => !x.skip).length}
<Link
onClick={() => {
showModal(ColumnMapModal, {
value: $values[`columns_${row}`],
onConfirm: value => setFieldValue(`columns_${row}`, value),
});
}}
>{columnCount > 0 ? `(${columnCount} columns)` : '(copy from source)'}
</Link>
</svelte:fragment>
</TableControl>
</div>
</div>

View File

@@ -25,13 +25,21 @@ export default class ScriptWriter {
this.packageNames.push(...extractShellApiPlugins(functionName, props));
}
assignValue(variableName, jsonValue) {
this.put(`const ${variableName} = ${JSON.stringify(jsonValue)};`);
}
requirePackage(packageName) {
this.packageNames.push(packageName);
}
copyStream(sourceVar, targetVar) {
copyStream(sourceVar, targetVar, colmapVar = null) {
if (colmapVar) {
this.put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar}, {columns: ${colmapVar}});`);
} else {
this.put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
}
}
comment(s) {
this.put(`// ${s}`);

View File

@@ -160,6 +160,21 @@ function getTargetExpr(extensions, sourceName, values, targetConnection, targetD
throw new Error(`Unknown target storage type: ${targetStorageType}`);
}
export function normalizeExportColumnMap(colmap) {
if (!colmap) {
return null;
}
if (!colmap.find(x => !x.ignore)) {
// all values are ignored, ignore column map
return null;
}
colmap = colmap.filter(x => !x.skip);
if (colmap.length > 0) {
return colmap.map(x => _.omit(x, ['ignore']));
}
return null;
}
export default async function createImpExpScript(extensions, values, addEditorInfo = true) {
const script = new ScriptWriter(values.startVariableIndex || 0);
@@ -186,7 +201,15 @@ export default async function createImpExpScript(extensions, values, addEditorIn
// @ts-ignore
script.assign(targetVar, ...getTargetExpr(extensions, sourceName, values, targetConnection, targetDriver));
script.copyStream(sourceVar, targetVar);
const colmap = normalizeExportColumnMap(values[`columns_${sourceName}`] );
let colmapVar = null;
if (colmap) {
colmapVar = script.allocVariable();
script.assignValue(colmapVar, colmap);
}
script.copyStream(sourceVar, targetVar, colmapVar);
script.put();
}
if (addEditorInfo) {

View File

@@ -0,0 +1,91 @@
<script lang="ts">
import FormStyledButton from '../buttons/FormStyledButton.svelte';
import Link from '../elements/Link.svelte';
import TableControl from '../elements/TableControl.svelte';
import CheckboxField from '../forms/CheckboxField.svelte';
import FormProvider from '../forms/FormProvider.svelte';
import FormSubmit from '../forms/FormSubmit.svelte';
import TextField from '../forms/TextField.svelte';
import ModalBase from './ModalBase.svelte';
import { closeCurrentModal } from './modalTools';
export let header = 'Configure columns';
export let onConfirm;
export let value = [];
</script>
<FormProvider>
<ModalBase {...$$restProps}>
<div slot="header">{header}</div>
<div class="m-3">
When no columns are defined in this mapping, source row is copied to target without any modifications
</div>
<TableControl
columns={[
{ fieldName: 'use', header: 'Use', slot: 4 },
{ fieldName: 'src', header: 'Source column', slot: 1 },
{ fieldName: 'dst', header: 'Target column', slot: 2 },
{ fieldName: 'actions', header: '', slot: 3 },
]}
rows={value || []}
emptyMessage="No transform defined"
>
<svelte:fragment slot="4" let:row let:index>
<CheckboxField
checked={!row['skip']}
on:change={e =>
(value = (value || []).map((x, i) => (i == index ? { ...x, skip: !e.target.checked, ignore: false } : x)))}
/>
</svelte:fragment>
<svelte:fragment slot="1" let:row let:index>
<TextField
value={row['src']}
on:change={e =>
(value = (value || []).map((x, i) => (i == index ? { ...x, src: e.target.value, ignore: false } : x)))}
/>
</svelte:fragment>
<svelte:fragment slot="2" let:row let:index>
<TextField
value={row['dst']}
on:change={e =>
(value = (value || []).map((x, i) => (i == index ? { ...x, dst: e.target.value, ignore: false } : x)))}
/>
</svelte:fragment>
<svelte:fragment slot="3" let:index>
<Link
onClick={() => {
value = value.filter((x, i) => i != index);
}}>Remove</Link
>
</svelte:fragment>
</TableControl>
<svelte:fragment slot="footer">
<FormSubmit
value="OK"
on:click={() => {
closeCurrentModal();
onConfirm(!value || value.length == 0 ? null : value);
}}
/>
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
<FormStyledButton
type="button"
value="Add column"
on:click={() => {
value = [...(value || []), {}];
}}
/>
<FormStyledButton
type="button"
value="Reset"
on:click={() => {
value = [];
}}
/>
</svelte:fragment>
</ModalBase>
</FormProvider>

View File

@@ -34,7 +34,7 @@
import { writable } from 'svelte/store';
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
import ToolStripExportButton from '../buttons/ToolStripExportButton.svelte';
import ToolStripExportButton, { createQuickExportHandlerRef } from '../buttons/ToolStripExportButton.svelte';
import registerCommand from '../commands/registerCommand';
import DataGrid from '../datagrid/DataGrid.svelte';
import ErrorInfo from '../elements/ErrorInfo.svelte';
@@ -137,6 +137,8 @@ import ToolStripExportButton from '../buttons/ToolStripExportButton.svelte';
// display is overridden in FreeTableGridCore, this is because of column manager
$: display = new FreeTableGridDisplay($modelState.value, $config, config.update, null, null);
const quickExportHandlerRef = createQuickExportHandlerRef();
</script>
{#if isLoading}
@@ -161,7 +163,7 @@ import ToolStripExportButton from '../buttons/ToolStripExportButton.svelte';
/>
<svelte:fragment slot="toolstrip">
<ToolStripCommandButton command="freeTable.save" />
<ToolStripExportButton command="freeTableGrid.export" />
<ToolStripExportButton command="freeTableGrid.export" {quickExportHandlerRef} />
</svelte:fragment>
</ToolStripContainer>
{/if}

View File

@@ -22,6 +22,8 @@
import useEditorData from '../query/useEditorData';
import invalidateCommands from '../commands/invalidateCommands';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
import ToolStripSaveButton from '../buttons/ToolStripSaveButton.svelte';
export let tabid;
@@ -70,7 +72,8 @@
}
</script>
<AceEditor
<ToolStripContainer>
<AceEditor
value={$editorState.value || ''}
menu={createMenu()}
on:input={e => setEditorData(e.detail)}
@@ -80,4 +83,9 @@
}}
bind:this={domEditor}
mode="json"
/>
/>
<svelte:fragment slot="toolstrip">
<ToolStripSaveButton idPrefix="json" />
</svelte:fragment>
</ToolStripContainer>

View File

@@ -3,8 +3,9 @@ import getElectron from './getElectron';
import { showSnackbar, showSnackbarInfo, showSnackbarError, closeSnackbar } from '../utility/snackbar';
import resolveApi from './resolveApi';
import { apiCall, apiOff, apiOn } from './api';
import { normalizeExportColumnMap } from '../impexp/createImpExpScript';
export async function exportQuickExportFile(dataName, reader, format) {
export async function exportQuickExportFile(dataName, reader, format, columnMap = null) {
const electron = getElectron();
let filePath;
@@ -31,7 +32,14 @@ export async function exportQuickExportFile(dataName, reader, format) {
const writer = format.createWriter(filePath, dataName);
script.assign(targetVar, writer.functionName, writer.props);
script.copyStream(sourceVar, targetVar);
const colmap = normalizeExportColumnMap(columnMap);
let colmapVar = null;
if (colmap) {
colmapVar = script.allocVariable();
script.assignValue(colmapVar, colmap);
}
script.copyStream(sourceVar, targetVar, colmapVar);
script.put();
const resp = await apiCall('runners/start', { script: script.getScript() });

View File

@@ -37,6 +37,7 @@
import { useAppFiles, useArchiveFolders } from '../utility/metadataLoaders';
import openNewTab from '../utility/openNewTab';
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
import { showSnackbarError } from '../utility/snackbar';
let filter = '';
@@ -66,12 +67,27 @@
});
}
async function handleNewConfigFile(fileName, content) {
if (!(await apiCall('apps/create-config-file', { fileName, content, appFolder: $currentApplication }))) {
showSnackbarError('File not created, probably already exists');
}
}
function createAddMenu() {
return [
{
text: 'New SQL command',
onClick: () => handleNewSqlFile('command.sql', 'Create new SQL command', COMMAND_TEMPLATE),
},
{
text: 'New virtual references file',
onClick: () => handleNewConfigFile('virtual-references.config.json', []),
},
{
text: 'New disctionary descriptions file',
onClick: () => handleNewConfigFile('dictionary-descriptions.config.json', []),
},
// { text: 'New query view', onClick: () => handleNewSqlFile('query.sql', 'Create new SQL query', QUERY_TEMPLATE) },
];
}

View File

@@ -387,15 +387,17 @@
draggingDbGroupTarget = null;
}}
>
<FontIcon icon={getDbIcon(tabGroup.tabDbKey)} padRight />
<div class="db-name-inner">
<FontIcon icon={getDbIcon(tabGroup.tabDbKey)} />
{tabGroup.tabDbName}
</div>
<span
<div
class="close-button-right tabCloseButton"
on:click={e => closeMultipleTabs(tab => tabGroup.tabs.find(x => x.tabid == tab.tabid))}
>
<FontIcon icon="icon close" />
</span>
</div>
</div>
<div class="db-group">
{#each tabGroup.tabs as tab}
@@ -505,7 +507,10 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.db-name-inner {
justify-content: center;
flex-grow: 1;
}
/* .db-name:hover {
background-color: var(--theme-bg-3);
@@ -541,7 +546,6 @@
margin-left: 5px;
margin-right: 5px;
color: var(--theme-font-3);
float: right;
}
.close-button:hover {

View File

@@ -3199,6 +3199,11 @@ dbgate-plugin-tools@^1.0.4, dbgate-plugin-tools@^1.0.7:
pacote "^11.1.13"
rimraf "^3.0.2"
dbgate-plugin-xml@^4.1.1:
version "4.7.2"
resolved "https://registry.yarnpkg.com/dbgate-plugin-xml/-/dbgate-plugin-xml-4.7.2.tgz#4a1817a7fc3b1ac5191e45dc7b1016b2f97c3158"
integrity sha512-LA52/Wg+blaRp/H+v4ypT+l6kbi4sYsTD/JdPaTbfwErMXhGZew9wtDolvbDjKZZ/zfRig0TDXj9fhLILw2cGw==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -3488,6 +3493,11 @@ dot-prop@^4.1.0:
dependencies:
is-obj "^1.0.0"
dotenv@^16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411"
integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==
duplexer2@~0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db"