mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-27 20:46:00 +00:00
db diff: save/sync
This commit is contained in:
@@ -6,15 +6,18 @@ export function computeDiffRowsCore(sourceList, targetList, testEqual) {
|
|||||||
for (const obj of sourceList) {
|
for (const obj of sourceList) {
|
||||||
const paired = targetList.find(x => x.pairingId == obj.pairingId);
|
const paired = targetList.find(x => x.pairingId == obj.pairingId);
|
||||||
if (paired) {
|
if (paired) {
|
||||||
|
const isEqual = testEqual(obj, paired);
|
||||||
res.push({
|
res.push({
|
||||||
source: obj,
|
source: obj,
|
||||||
target: paired,
|
target: paired,
|
||||||
state: testEqual(obj, paired) ? 'equal' : 'changed',
|
state: isEqual ? 'equal' : 'changed',
|
||||||
|
__isChanged: !isEqual,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.push({
|
res.push({
|
||||||
source: obj,
|
source: obj,
|
||||||
state: 'removed',
|
state: 'added',
|
||||||
|
__isAdded: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +26,8 @@ export function computeDiffRowsCore(sourceList, targetList, testEqual) {
|
|||||||
if (!paired) {
|
if (!paired) {
|
||||||
res.push({
|
res.push({
|
||||||
target: obj,
|
target: obj,
|
||||||
state: 'added',
|
state: 'removed',
|
||||||
|
__isDeleted: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,6 +50,9 @@ export function computeDbDiffRows(
|
|||||||
sourcePureName: row?.source?.pureName,
|
sourcePureName: row?.source?.pureName,
|
||||||
targetSchemaName: row?.target?.schemaName,
|
targetSchemaName: row?.target?.schemaName,
|
||||||
targetPureName: row?.target?.pureName,
|
targetPureName: row?.target?.pureName,
|
||||||
|
identifier: `${row?.source?.schemaName || row?.target?.schemaName}.${
|
||||||
|
row?.source?.pureName || row?.target?.pureName
|
||||||
|
}`,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
background: white;
|
||||||
|
color: #000000;
|
||||||
}
|
}
|
||||||
/* :global(.d2h-file-diff) {
|
/* :global(.d2h-file-diff) {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
<svelte:fragment slot="0" let:row>
|
<svelte:fragment slot="0" let:row>
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
checked={row.isOutput}
|
checked={row.isOutput}
|
||||||
onChange={e => {
|
on:change={e => {
|
||||||
if (e.target.checked) changeColumn({ ...row, isOutput: true });
|
if (e.target.checked) changeColumn({ ...row, isOutput: true });
|
||||||
else changeColumn({ ...row, isOutput: false });
|
else changeColumn({ ...row, isOutput: false });
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
getProps?: any;
|
getProps?: any;
|
||||||
formatter?: any;
|
formatter?: any;
|
||||||
slot?: number;
|
slot?: number;
|
||||||
|
headerSlot?: number;
|
||||||
isHighlighted?: Function;
|
isHighlighted?: Function;
|
||||||
width?: string;
|
width?: string;
|
||||||
}
|
}
|
||||||
@@ -98,7 +99,22 @@
|
|||||||
>
|
>
|
||||||
<tr>
|
<tr>
|
||||||
{#each columnList as col}
|
{#each columnList as col}
|
||||||
<td style={col.width ? `width: ${col.width}` : undefined}>{col.header || ''}</td>
|
<td style={col.width ? `width: ${col.width}` : undefined}>
|
||||||
|
{#if col.headerSlot != null}
|
||||||
|
{#if col.headerSlot == -1}<slot name="-1" />
|
||||||
|
{:else if col.headerSlot == 0}<slot name="0" />
|
||||||
|
{:else if col.headerSlot == 1}<slot name="1" />
|
||||||
|
{:else if col.headerSlot == 2}<slot name="2" />
|
||||||
|
{:else if col.headerSlot == 3}<slot name="3" />
|
||||||
|
{:else if col.headerSlot == 4}<slot name="4" />
|
||||||
|
{:else if col.headerSlot == 5}<slot name="5" />
|
||||||
|
{:else if col.headerSlot == 6}<slot name="6" />
|
||||||
|
{:else if col.headerSlot == 7}<slot name="7" />
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
{col.header || ''}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -107,6 +123,9 @@
|
|||||||
<tr
|
<tr
|
||||||
class:selected={selectable && selectedIndex == index}
|
class:selected={selectable && selectedIndex == index}
|
||||||
class:clickable
|
class:clickable
|
||||||
|
class:isAdded={row.__isAdded}
|
||||||
|
class:isDeleted={row.__isDeleted}
|
||||||
|
class:isChanged={row.__isChanged}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (selectable) {
|
if (selectable) {
|
||||||
selectedIndex = index;
|
selectedIndex = index;
|
||||||
@@ -215,4 +234,15 @@
|
|||||||
td.isHighlighted {
|
td.isHighlighted {
|
||||||
background-color: var(--theme-bg-1);
|
background-color: var(--theme-bg-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr.isAdded {
|
||||||
|
background: var(--theme-bg-green);
|
||||||
|
}
|
||||||
|
tr.isChanged {
|
||||||
|
background: var(--theme-bg-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.isDeleted {
|
||||||
|
background: var(--theme-bg-red);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
'icon markdown': 'mdi mdi-application',
|
'icon markdown': 'mdi mdi-application',
|
||||||
'icon preview': 'mdi mdi-file-find',
|
'icon preview': 'mdi mdi-file-find',
|
||||||
'icon eye': 'mdi mdi-eye',
|
'icon eye': 'mdi mdi-eye',
|
||||||
|
'icon check-all': 'mdi mdi-check-all',
|
||||||
|
|
||||||
'icon run': 'mdi mdi-play',
|
'icon run': 'mdi mdi-play',
|
||||||
'icon chevron-down': 'mdi mdi-chevron-down',
|
'icon chevron-down': 'mdi mdi-chevron-down',
|
||||||
@@ -75,6 +76,8 @@
|
|||||||
'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',
|
'icon report': 'mdi mdi-file-chart',
|
||||||
|
'icon swap': 'mdi mdi-swap-horizontal-bold',
|
||||||
|
'icon save-sync': 'mdi mdi-content-save-move',
|
||||||
|
|
||||||
'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',
|
||||||
|
|||||||
@@ -14,6 +14,31 @@
|
|||||||
onClick: () => getCurrentEditor().showReport(),
|
onClick: () => getCurrentEditor().showReport(),
|
||||||
testEnabled: () => getCurrentEditor() != null,
|
testEnabled: () => getCurrentEditor() != null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'compareModels.swap',
|
||||||
|
category: 'Compare models',
|
||||||
|
toolbarName: 'Swap',
|
||||||
|
name: 'Swap source & target',
|
||||||
|
icon: 'icon swap',
|
||||||
|
toolbar: true,
|
||||||
|
isRelatedToTab: true,
|
||||||
|
onClick: () => getCurrentEditor().swap(),
|
||||||
|
testEnabled: () => getCurrentEditor() != null,
|
||||||
|
});
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'compareModels.saveSync',
|
||||||
|
category: 'Compare models',
|
||||||
|
toolbarName: 'Save/Sync',
|
||||||
|
name: 'Save/Sync',
|
||||||
|
icon: 'icon save-sync',
|
||||||
|
group: 'save',
|
||||||
|
toolbar: true,
|
||||||
|
isRelatedToTab: true,
|
||||||
|
onClick: () => getCurrentEditor().saveSync(),
|
||||||
|
testEnabled: () => getCurrentEditor() != null,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -27,10 +52,11 @@
|
|||||||
getCreateObjectScript,
|
getCreateObjectScript,
|
||||||
} from 'dbgate-tools';
|
} from 'dbgate-tools';
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _, { startsWith } from 'lodash';
|
||||||
import { derived, writable } from 'svelte/store';
|
import { derived, writable } from 'svelte/store';
|
||||||
import registerCommand from '../commands/registerCommand';
|
import registerCommand from '../commands/registerCommand';
|
||||||
import DiffView from '../elements/DiffView.svelte';
|
import DiffView from '../elements/DiffView.svelte';
|
||||||
|
import InlineButton from '../elements/InlineButton.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';
|
||||||
import TableControl from '../elements/TableControl.svelte';
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
@@ -40,6 +66,9 @@
|
|||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
import FormConnectionSelect from '../impexp/FormConnectionSelect.svelte';
|
import FormConnectionSelect from '../impexp/FormConnectionSelect.svelte';
|
||||||
import FormDatabaseSelect from '../impexp/FormDatabaseSelect.svelte';
|
import FormDatabaseSelect from '../impexp/FormDatabaseSelect.svelte';
|
||||||
|
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
|
||||||
|
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
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';
|
||||||
@@ -47,6 +76,7 @@
|
|||||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||||
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||||
import resolveApi from '../utility/resolveApi';
|
import resolveApi from '../utility/resolveApi';
|
||||||
|
import { showSnackbarSuccess } from '../utility/snackbar';
|
||||||
|
|
||||||
export let tabid;
|
export let tabid;
|
||||||
|
|
||||||
@@ -100,20 +130,69 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.open(`${resolveApi()}/uploads/get?file=${resp.data}`, '_blank');
|
window.open(`${resolveApi()}/uploads/get?file=${resp.data}`, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
// window.open(
|
export function swap() {
|
||||||
// `${resolveApi()}/database-connections/get-diff-html?sourceConid=` +
|
$values = {
|
||||||
// `${$values?.sourceConid}&sourceDatabase=${$values?.sourceDatabase}&` +
|
...$values,
|
||||||
// `targetConid=${$values?.targetConid}&targetDatabase=${$values?.targetDatabase}`,
|
sourceConid: $values?.targetConid,
|
||||||
// '_blank'
|
sourceDatabase: $values?.targetDatabase,
|
||||||
// );
|
targetConid: $values?.sourceConid,
|
||||||
|
targetDatabase: $values?.sourceDatabase,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCheckAll() {
|
||||||
|
const isAnyChecked = diffRows.some(row => $values[`isChecked_${row.identifier}`]);
|
||||||
|
if (isAnyChecked) {
|
||||||
|
$values = _.omitBy($values, (v, k) => k.startsWith('isChecked_'));
|
||||||
|
} else {
|
||||||
|
$values = {
|
||||||
|
...$values,
|
||||||
|
..._.fromPairs(diffRows.filter(row => row.state != 'equal').map(row => [`isChecked_${row.identifier}`, true])),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConfirmSql(sql) {
|
||||||
|
const conid = $values?.targetConid;
|
||||||
|
const database = $values?.targetDatabase;
|
||||||
|
|
||||||
|
const resp = await axiosInstance.request({
|
||||||
|
url: 'database-connections/run-script',
|
||||||
|
method: 'post',
|
||||||
|
params: { conid, database },
|
||||||
|
data: { sql },
|
||||||
|
});
|
||||||
|
const { errorMessage } = resp.data || {};
|
||||||
|
if (errorMessage) {
|
||||||
|
showModal(ErrorMessageModal, { title: 'Error when saving', message: errorMessage });
|
||||||
|
} else {
|
||||||
|
await axiosInstance.post('database-connections/sync-model', { conid, database });
|
||||||
|
showSnackbarSuccess('Saved to database');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSyncSql() {
|
||||||
|
return diffRows
|
||||||
|
.filter(row => $values[`isChecked_${row.identifier}`])
|
||||||
|
.map(row => getAlterTableScript(row?.source, row.target, dbDiffOptions, targetDb, driver).sql)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveSync() {
|
||||||
|
const sql = getSyncSql();
|
||||||
|
showModal(ConfirmSqlModal, {
|
||||||
|
sql,
|
||||||
|
onConfirm: () => {
|
||||||
|
handleConfirmSql(sql);
|
||||||
|
},
|
||||||
|
engine: driver.engine,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { editorState, editorValue, setEditorData } = useEditorData({
|
const { editorState, editorValue, setEditorData } = useEditorData({
|
||||||
tabid,
|
tabid,
|
||||||
// onInitialData: value => {
|
|
||||||
// dispatchModel({ type: 'reset', value });
|
|
||||||
// },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const values = {
|
const values = {
|
||||||
@@ -175,6 +254,7 @@
|
|||||||
selectable
|
selectable
|
||||||
disableFocusOutline
|
disableFocusOutline
|
||||||
columns={[
|
columns={[
|
||||||
|
{ fieldName: 'isChecked', header: '', width: '50px', slot: 1, headerSlot: 2 },
|
||||||
{ fieldName: 'type', header: 'Type', width: '100px' },
|
{ fieldName: 'type', header: 'Type', width: '100px' },
|
||||||
{ fieldName: 'sourceSchemaName', header: 'Schema' },
|
{ fieldName: 'sourceSchemaName', header: 'Schema' },
|
||||||
{ fieldName: 'sourcePureName', header: 'Name' },
|
{ fieldName: 'sourcePureName', header: 'Name' },
|
||||||
@@ -182,7 +262,24 @@
|
|||||||
{ fieldName: 'targetSchemaName', header: 'Schema' },
|
{ fieldName: 'targetSchemaName', header: 'Schema' },
|
||||||
{ fieldName: 'targetPureName', header: 'Name' },
|
{ fieldName: 'targetPureName', header: 'Name' },
|
||||||
]}
|
]}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
slot="1"
|
||||||
|
let:row
|
||||||
|
disabled={row.state == 'equal'}
|
||||||
|
checked={!!$values[`isChecked_${row['identifier']}`]}
|
||||||
|
on:change={e => {
|
||||||
|
// @ts-ignore
|
||||||
|
$values = { ...$values, [`isChecked_${row.identifier}`]: e.target.checked };
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
<svelte:fragment slot="2">
|
||||||
|
<InlineButton on:click={handleCheckAll}>
|
||||||
|
<FontIcon icon="icon check-all" />
|
||||||
|
</InlineButton>
|
||||||
|
</svelte:fragment>
|
||||||
|
</ScrollableTableControl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -194,7 +291,7 @@
|
|||||||
slot: 1,
|
slot: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'SQL script',
|
label: 'Synchronize script',
|
||||||
slot: 2,
|
slot: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user