mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
db diff report
This commit is contained in:
parent
336edfc93f
commit
17286e0c3e
@ -29,6 +29,8 @@
|
|||||||
"dbgate-query-splitter": "^4.1.1",
|
"dbgate-query-splitter": "^4.1.1",
|
||||||
"dbgate-sqltree": "^4.1.1",
|
"dbgate-sqltree": "^4.1.1",
|
||||||
"dbgate-tools": "^4.1.1",
|
"dbgate-tools": "^4.1.1",
|
||||||
|
"diff": "^5.0.0",
|
||||||
|
"diff2html": "^3.4.13",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-basic-auth": "^1.2.0",
|
"express-basic-auth": "^1.2.0",
|
||||||
|
@ -3,16 +3,27 @@ const connections = require('./connections');
|
|||||||
const archive = require('./archive');
|
const archive = require('./archive');
|
||||||
const socket = require('../utility/socket');
|
const socket = require('../utility/socket');
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const { DatabaseAnalyser, getAlterDatabaseScript, generateDbPairingId, matchPairedObjects } = require('dbgate-tools');
|
const {
|
||||||
|
DatabaseAnalyser,
|
||||||
|
computeDbDiffRows,
|
||||||
|
getCreateObjectScript,
|
||||||
|
getAlterDatabaseScript,
|
||||||
|
generateDbPairingId,
|
||||||
|
matchPairedObjects,
|
||||||
|
extendDatabaseInfo,
|
||||||
|
} = require('dbgate-tools');
|
||||||
|
const { html, parse } = require('diff2html');
|
||||||
const { handleProcessCommunication } = require('../utility/processComm');
|
const { handleProcessCommunication } = require('../utility/processComm');
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const exportDbModel = require('../utility/exportDbModel');
|
const exportDbModel = require('../utility/exportDbModel');
|
||||||
const { archivedir, resolveArchiveFolder } = require('../utility/directories');
|
const { archivedir, resolveArchiveFolder, uploadsdir } = require('../utility/directories');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const importDbModel = require('../utility/importDbModel');
|
const importDbModel = require('../utility/importDbModel');
|
||||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||||
const generateDeploySql = require('../shell/generateDeploySql');
|
const generateDeploySql = require('../shell/generateDeploySql');
|
||||||
|
const { createTwoFilesPatch } = require('diff');
|
||||||
|
const diff2htmlPage = require('../utility/diff2htmlPage');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
|
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
|
||||||
@ -285,4 +296,55 @@ module.exports = {
|
|||||||
// const res = await this.sendRequest(opened, { msgtype: 'queryData', sql });
|
// const res = await this.sendRequest(opened, { msgtype: 'queryData', sql });
|
||||||
// return res;
|
// return res;
|
||||||
// },
|
// },
|
||||||
|
|
||||||
|
async getUnifiedDiff({ sourceConid, sourceDatabase, targetConid, targetDatabase }) {
|
||||||
|
const dbDiffOptions = {
|
||||||
|
// schemaMode: 'ignore',
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourceDb = generateDbPairingId(
|
||||||
|
extendDatabaseInfo(await this.structure({ conid: sourceConid, database: sourceDatabase }))
|
||||||
|
);
|
||||||
|
const targetDb = generateDbPairingId(
|
||||||
|
extendDatabaseInfo(await this.structure({ conid: targetConid, database: targetDatabase }))
|
||||||
|
);
|
||||||
|
// const sourceConnection = await connections.get({conid:sourceConid})
|
||||||
|
const connection = await connections.get({ conid: targetConid });
|
||||||
|
const driver = requireEngineDriver(connection);
|
||||||
|
const targetDbPaired = matchPairedObjects(sourceDb, targetDb, dbDiffOptions);
|
||||||
|
const diffRows = computeDbDiffRows(sourceDb, targetDbPaired, dbDiffOptions, driver);
|
||||||
|
|
||||||
|
// console.log('sourceDb', sourceDb);
|
||||||
|
// console.log('targetDb', targetDb);
|
||||||
|
// console.log('sourceConid, sourceDatabase', sourceConid, sourceDatabase);
|
||||||
|
|
||||||
|
let res = '';
|
||||||
|
for (const row of diffRows) {
|
||||||
|
// console.log('PAIR', row.source && row.source.pureName, row.target && row.target.pureName);
|
||||||
|
const unifiedDiff = createTwoFilesPatch(
|
||||||
|
(row.source && row.source.pureName) || '',
|
||||||
|
(row.target && row.target.pureName) || '',
|
||||||
|
getCreateObjectScript(row.source, driver),
|
||||||
|
getCreateObjectScript(row.target, driver),
|
||||||
|
'',
|
||||||
|
''
|
||||||
|
);
|
||||||
|
res += unifiedDiff;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
|
generateDbDiffReport_meta: 'post',
|
||||||
|
async generateDbDiffReport({ sourceConid, sourceDatabase, targetConid, targetDatabase }) {
|
||||||
|
const unifiedDiff = await this.getUnifiedDiff({ sourceConid, sourceDatabase, targetConid, targetDatabase });
|
||||||
|
|
||||||
|
const diffJson = parse(unifiedDiff);
|
||||||
|
// $: diffHtml = html(diffJson, { outputFormat: 'side-by-side', drawFileList: false });
|
||||||
|
const diffHtml = html(diffJson, {});
|
||||||
|
|
||||||
|
const fileName = `${uuidv1()}.html`;
|
||||||
|
await fs.writeFile(path.join(uploadsdir(), fileName), diff2htmlPage(diffHtml));
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -25,4 +25,12 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get_meta: {
|
||||||
|
method: 'get',
|
||||||
|
raw: true,
|
||||||
|
},
|
||||||
|
get(req, res) {
|
||||||
|
res.sendFile(path.join(uploadsdir(), req.query.file));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
8
packages/api/src/utility/diff2htmlPage.js
Normal file
8
packages/api/src/utility/diff2htmlPage.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const diff2htmlCss =
|
||||||
|
'.d2h-d-none{display:none}.d2h-wrapper{text-align:left}.d2h-file-header{background-color:#f7f7f7;border-bottom:1px solid #d8d8d8;font-family:Source Sans Pro,Helvetica Neue,Helvetica,Arial,sans-serif;height:35px;padding:5px 10px}.d2h-file-header,.d2h-file-stats{display:-webkit-box;display:-ms-flexbox;display:flex}.d2h-file-stats{font-size:14px;margin-left:auto}.d2h-lines-added{border:1px solid #b4e2b4;border-radius:5px 0 0 5px;color:#399839;padding:2px;text-align:right;vertical-align:middle}.d2h-lines-deleted{border:1px solid #e9aeae;border-radius:0 5px 5px 0;color:#c33;margin-left:1px;padding:2px;text-align:left;vertical-align:middle}.d2h-file-name-wrapper{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;font-size:15px;width:100%}.d2h-file-name{overflow-x:hidden;text-overflow:ellipsis;white-space:nowrap}.d2h-file-wrapper{border:1px solid #ddd;border-radius:3px;margin-bottom:1em}.d2h-file-collapse{-webkit-box-pack:end;-ms-flex-pack:end;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid #ddd;border-radius:3px;cursor:pointer;display:none;font-size:12px;justify-content:flex-end;padding:4px 8px}.d2h-file-collapse.d2h-selected{background-color:#c8e1ff}.d2h-file-collapse-input{margin:0 4px 0 0}.d2h-diff-table{border-collapse:collapse;font-family:Menlo,Consolas,monospace;font-size:13px;width:100%}.d2h-files-diff{width:100%}.d2h-file-diff{overflow-y:hidden}.d2h-file-side-diff{display:inline-block;margin-bottom:-8px;margin-right:-4px;overflow-x:scroll;overflow-y:hidden;width:50%}.d2h-code-line{padding:0 8em}.d2h-code-line,.d2h-code-side-line{display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap;width:100%}.d2h-code-side-line{padding:0 4.5em}.d2h-code-line-ctn{word-wrap:normal;background:none;display:inline-block;padding:0;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;vertical-align:middle;white-space:pre;width:100%}.d2h-code-line del,.d2h-code-side-line del{background-color:#ffb6ba}.d2h-code-line del,.d2h-code-line ins,.d2h-code-side-line del,.d2h-code-side-line ins{border-radius:.2em;display:inline-block;margin-top:-1px;text-decoration:none;vertical-align:middle}.d2h-code-line ins,.d2h-code-side-line ins{background-color:#97f295;text-align:left}.d2h-code-line-prefix{word-wrap:normal;background:none;display:inline;padding:0;white-space:pre}.line-num1{float:left}.line-num1,.line-num2{-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;padding:0 .5em;text-overflow:ellipsis;width:3.5em}.line-num2{float:right}.d2h-code-linenumber{background-color:#fff;border:solid #eee;border-width:0 1px;-webkit-box-sizing:border-box;box-sizing:border-box;color:rgba(0,0,0,.3);cursor:pointer;display:inline-block;position:absolute;text-align:right;width:7.5em}.d2h-code-linenumber:after{content:"\200b"}.d2h-code-side-linenumber{background-color:#fff;border:solid #eee;border-width:0 1px;-webkit-box-sizing:border-box;box-sizing:border-box;color:rgba(0,0,0,.3);cursor:pointer;display:inline-block;overflow:hidden;padding:0 .5em;position:absolute;text-align:right;text-overflow:ellipsis;width:4em}.d2h-code-side-linenumber:after{content:"\200b"}.d2h-code-side-emptyplaceholder,.d2h-emptyplaceholder{background-color:#f1f1f1;border-color:#e1e1e1}.d2h-code-line-prefix,.d2h-code-linenumber,.d2h-code-side-linenumber,.d2h-emptyplaceholder{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.d2h-code-linenumber,.d2h-code-side-linenumber{direction:rtl}.d2h-del{background-color:#fee8e9;border-color:#e9aeae}.d2h-ins{background-color:#dfd;border-color:#b4e2b4}.d2h-info{background-color:#f8fafd;border-color:#d5e4f2;color:rgba(0,0,0,.3)}.d2h-file-diff .d2h-del.d2h-change{background-color:#fdf2d0}.d2h-file-diff .d2h-ins.d2h-change{background-color:#ded}.d2h-file-list-wrapper{margin-bottom:10px}.d2h-file-list-wrapper a{color:#3572b0;text-decoration:none}.d2h-file-list-wrapper a:visited{color:#3572b0}.d2h-file-list-header{text-align:left}.d2h-file-list-title{font-weight:700}.d2h-file-list-line{display:-webkit-box;display:-ms-flexbox;display:flex;text-align:left}.d2h-file-list{display:block;list-style:none;margin:0;padding:0}.d2h-file-list>li{border-bottom:1px solid #ddd;margin:0;padding:5px 10px}.d2h-file-list>li:last-child{border-bottom:none}.d2h-file-switch{cursor:pointer;display:none;font-size:10px}.d2h-icon{fill:currentColor;margin-right:10px;vertical-align:middle}.d2h-deleted{color:#c33}.d2h-added{color:#399839}.d2h-changed{color:#d0b44c}.d2h-moved{color:#3572b0}.d2h-tag{background-color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;font-size:10px;margin-left:5px;padding:0 2px}.d2h-deleted-tag{border:1px solid #c33}.d2h-added-tag{border:1px solid #399839}.d2h-changed-tag{border:1px solid #d0b44c}.d2h-moved-tag{border:1px solid #3572b0}';
|
||||||
|
|
||||||
|
function diff2htmlPage(content) {
|
||||||
|
return `<html><head><style>${diff2htmlCss}</style><body>${content}</body></html>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = diff2htmlPage;
|
@ -1,4 +1,4 @@
|
|||||||
import { DbDiffOptions, testEqualColumns, testEqualTables } from 'dbgate-tools';
|
import { DbDiffOptions, testEqualColumns, testEqualTables } from './diffTools';
|
||||||
import { DatabaseInfo, EngineDriver, TableInfo } from 'dbgate-types';
|
import { DatabaseInfo, EngineDriver, TableInfo } from 'dbgate-types';
|
||||||
|
|
||||||
export function computeDiffRowsCore(sourceList, targetList, testEqual) {
|
export function computeDiffRowsCore(sourceList, targetList, testEqual) {
|
||||||
@ -69,7 +69,7 @@ export function computeTableDiffColumns(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCreateTableScript(table: TableInfo, driver: EngineDriver) {
|
export function getCreateObjectScript(table: TableInfo, driver: EngineDriver) {
|
||||||
if (!table || !driver) return '';
|
if (!table || !driver) return '';
|
||||||
const dmp = driver.createDumper();
|
const dmp = driver.createDumper();
|
||||||
dmp.createTable(table);
|
dmp.createTable(table);
|
@ -15,3 +15,4 @@ export * from './diffTools';
|
|||||||
export * from './schemaEditorTools';
|
export * from './schemaEditorTools';
|
||||||
export * from './yamlModelConv';
|
export * from './yamlModelConv';
|
||||||
export * from './stringTools';
|
export * from './stringTools';
|
||||||
|
export * from './computeDiffRows';
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
{#if isNative}
|
{#if isNative}
|
||||||
<select
|
<select
|
||||||
value={value || defaultValue}
|
value={options.find(x => x.value == value) ? value : defaultValue}
|
||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
dispatch('change', e.target['value']);
|
dispatch('change', e.target['value']);
|
||||||
|
@ -74,6 +74,7 @@
|
|||||||
'icon menu': 'mdi mdi-menu',
|
'icon menu': 'mdi mdi-menu',
|
||||||
'icon add-column': 'mdi mdi-table-column-plus-after',
|
'icon add-column': 'mdi mdi-table-column-plus-after',
|
||||||
'icon add-key': 'mdi mdi-key-plus',
|
'icon add-key': 'mdi mdi-key-plus',
|
||||||
|
'icon report': 'mdi mdi-file-chart',
|
||||||
|
|
||||||
'img ok': 'mdi mdi-check-circle color-icon-green',
|
'img ok': 'mdi mdi-check-circle color-icon-green',
|
||||||
'img ok-inv': 'mdi mdi-check-circle color-icon-inv-green',
|
'img ok-inv': 'mdi mdi-check-circle color-icon-inv-green',
|
||||||
|
@ -1,12 +1,35 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export const matchingProps = [];
|
export const matchingProps = [];
|
||||||
|
|
||||||
|
const getCurrentEditor = () => getActiveComponent('CompareModelTab');
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'compareModels.reportDiff',
|
||||||
|
category: 'Compare models',
|
||||||
|
toolbarName: 'Report',
|
||||||
|
name: 'Report diff',
|
||||||
|
icon: 'icon report',
|
||||||
|
toolbar: true,
|
||||||
|
isRelatedToTab: true,
|
||||||
|
onClick: () => getCurrentEditor().showReport(),
|
||||||
|
testEnabled: () => getCurrentEditor() != null,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { findEngineDriver, generateDbPairingId, getAlterTableScript, matchPairedObjects } from 'dbgate-tools';
|
import {
|
||||||
|
findEngineDriver,
|
||||||
|
generateDbPairingId,
|
||||||
|
getAlterTableScript,
|
||||||
|
matchPairedObjects,
|
||||||
|
computeDbDiffRows,
|
||||||
|
computeTableDiffColumns,
|
||||||
|
getCreateObjectScript,
|
||||||
|
} from 'dbgate-tools';
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { derived, writable } from 'svelte/store';
|
import { derived, writable } from 'svelte/store';
|
||||||
|
import registerCommand from '../commands/registerCommand';
|
||||||
import DiffView from '../elements/DiffView.svelte';
|
import DiffView from '../elements/DiffView.svelte';
|
||||||
import ScrollableTableControl from '../elements/ScrollableTableControl.svelte';
|
import ScrollableTableControl from '../elements/ScrollableTableControl.svelte';
|
||||||
import TabControl from '../elements/TabControl.svelte';
|
import TabControl from '../elements/TabControl.svelte';
|
||||||
@ -20,13 +43,17 @@
|
|||||||
import SqlEditor from '../query/SqlEditor.svelte';
|
import SqlEditor from '../query/SqlEditor.svelte';
|
||||||
import useEditorData from '../query/useEditorData';
|
import useEditorData from '../query/useEditorData';
|
||||||
import { extensions } from '../stores';
|
import { extensions } from '../stores';
|
||||||
import { computeDbDiffRows, computeTableDiffColumns, getCreateTableScript } from '../utility/computeDiffRows';
|
import axiosInstance from '../utility/axiosInstance';
|
||||||
|
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||||
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||||
|
import resolveApi from '../utility/resolveApi';
|
||||||
|
|
||||||
export let tabid;
|
export let tabid;
|
||||||
|
|
||||||
let pairIndex = 0;
|
let pairIndex = 0;
|
||||||
|
|
||||||
|
export const activator = createActivator('CompareModelTab', true);
|
||||||
|
|
||||||
// let values = writable({
|
// let values = writable({
|
||||||
// sourceConid: null,
|
// sourceConid: null,
|
||||||
// sourceDatabase: null,
|
// sourceDatabase: null,
|
||||||
@ -64,6 +91,24 @@
|
|||||||
driver
|
driver
|
||||||
).sql;
|
).sql;
|
||||||
|
|
||||||
|
export async function showReport() {
|
||||||
|
const resp = await axiosInstance.post('database-connections/generate-db-diff-report', {
|
||||||
|
sourceConid: $values?.sourceConid,
|
||||||
|
sourceDatabase: $values?.sourceDatabase,
|
||||||
|
targetConid: $values?.targetConid,
|
||||||
|
targetDatabase: $values?.targetDatabase,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.open(`${resolveApi()}/uploads/get?file=${resp.data}`, '_blank');
|
||||||
|
|
||||||
|
// window.open(
|
||||||
|
// `${resolveApi()}/database-connections/get-diff-html?sourceConid=` +
|
||||||
|
// `${$values?.sourceConid}&sourceDatabase=${$values?.sourceDatabase}&` +
|
||||||
|
// `targetConid=${$values?.targetConid}&targetDatabase=${$values?.targetDatabase}`,
|
||||||
|
// '_blank'
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
const { editorState, editorValue, setEditorData } = useEditorData({
|
const { editorState, editorValue, setEditorData } = useEditorData({
|
||||||
tabid,
|
tabid,
|
||||||
// onInitialData: value => {
|
// onInitialData: value => {
|
||||||
@ -162,8 +207,8 @@
|
|||||||
<DiffView
|
<DiffView
|
||||||
leftTitle={diffRows[pairIndex]?.source?.pureName}
|
leftTitle={diffRows[pairIndex]?.source?.pureName}
|
||||||
rightTitle={diffRows[pairIndex]?.source?.pureName}
|
rightTitle={diffRows[pairIndex]?.source?.pureName}
|
||||||
leftText={getCreateTableScript(diffRows[pairIndex]?.source, driver)}
|
leftText={getCreateObjectScript(diffRows[pairIndex]?.source, driver)}
|
||||||
rightText={getCreateTableScript(diffRows[pairIndex]?.target, driver)}
|
rightText={getCreateObjectScript(diffRows[pairIndex]?.target, driver)}
|
||||||
/>
|
/>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user