mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-17 22:36:01 +00:00
Merge branch 'master' into redis
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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}`);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
21
packages/api/src/utility/ColumnMapTransformStream.js
Normal file
21
packages/api/src/utility/ColumnMapTransformStream.js
Normal 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;
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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)]));
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -175,10 +175,12 @@
|
||||
if (domFocusField) domFocusField.focus();
|
||||
}}
|
||||
on:setvisibility={e => {
|
||||
for (const name of selectedColumns) {
|
||||
const column = items.find(x => x.uniqueName == name);
|
||||
if (column) {
|
||||
display.setColumnVisibility(column.uniquePath, e.detail);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -25,12 +25,20 @@ 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) {
|
||||
this.put(`await dbgateApi.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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
91
packages/web/src/modals/ColumnMapModal.svelte
Normal file
91
packages/web/src/modals/ColumnMapModal.svelte
Normal 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>
|
||||
@@ -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}
|
||||
|
||||
@@ -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,14 +72,20 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<AceEditor
|
||||
value={$editorState.value || ''}
|
||||
menu={createMenu()}
|
||||
on:input={e => setEditorData(e.detail)}
|
||||
on:focus={() => {
|
||||
activator.activate();
|
||||
invalidateCommands();
|
||||
}}
|
||||
bind:this={domEditor}
|
||||
mode="json"
|
||||
/>
|
||||
<ToolStripContainer>
|
||||
<AceEditor
|
||||
value={$editorState.value || ''}
|
||||
menu={createMenu()}
|
||||
on:input={e => setEditorData(e.detail)}
|
||||
on:focus={() => {
|
||||
activator.activate();
|
||||
invalidateCommands();
|
||||
}}
|
||||
bind:this={domEditor}
|
||||
mode="json"
|
||||
/>
|
||||
|
||||
<svelte:fragment slot="toolstrip">
|
||||
<ToolStripSaveButton idPrefix="json" />
|
||||
</svelte:fragment>
|
||||
</ToolStripContainer>
|
||||
|
||||
@@ -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() });
|
||||
|
||||
@@ -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) },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -387,15 +387,17 @@
|
||||
draggingDbGroupTarget = null;
|
||||
}}
|
||||
>
|
||||
<FontIcon icon={getDbIcon(tabGroup.tabDbKey)} padRight />
|
||||
{tabGroup.tabDbName}
|
||||
<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 {
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user