mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Schema Compare open scmp file (#6118)
* can compare scmp with databases * show error if can't connect to db * excludes now work * fixes after rebase and other small fixes * Addressing comments * fixes after rebasing * fix excludes not always being remembered correctly * fix switched check * addressing comments
This commit is contained in:
@@ -10,7 +10,7 @@ import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { SchemaCompareOptionsDialog } from './dialogs/schemaCompareOptionsDialog';
|
||||
import { Telemetry } from './telemetry';
|
||||
import { getTelemetryErrorType, getEndpointName } from './utils';
|
||||
import { getTelemetryErrorType, getEndpointName, verifyConnectionAndGetOwnerUri } from './utils';
|
||||
import { SchemaCompareDialog } from './dialogs/schemaCompareDialog';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
const localize = nls.loadMessageBundle();
|
||||
@@ -26,6 +26,11 @@ const applyNoChangesMessage = localize('schemaCompare.applyNoChanges', 'No chang
|
||||
// TODO : In future icon should be decided based on language id (scmp) and not resource name
|
||||
const schemaCompareResourceName = 'Schema Compare';
|
||||
|
||||
enum ResetButtonState {
|
||||
noSourceTarget,
|
||||
beforeCompareStart,
|
||||
comparing,
|
||||
}
|
||||
|
||||
export class SchemaCompareResult {
|
||||
private differencesTable: azdata.TableComponent;
|
||||
@@ -44,6 +49,7 @@ export class SchemaCompareResult {
|
||||
private optionsButton: azdata.ButtonComponent;
|
||||
private generateScriptButton: azdata.ButtonComponent;
|
||||
private applyButton: azdata.ButtonComponent;
|
||||
private openScmpButton: azdata.ButtonComponent;
|
||||
private selectSourceButton: azdata.ButtonComponent;
|
||||
private selectTargetButton: azdata.ButtonComponent;
|
||||
private saveScmpButton: azdata.ButtonComponent;
|
||||
@@ -60,6 +66,8 @@ export class SchemaCompareResult {
|
||||
private sourceTargetSwitched = false;
|
||||
private sourceName: string;
|
||||
private targetName: string;
|
||||
private scmpSourceExcludes: azdata.SchemaCompareObjectId[];
|
||||
private scmpTargetExcludes: azdata.SchemaCompareObjectId[];
|
||||
|
||||
public sourceEndpointInfo: azdata.SchemaCompareEndpointInfo;
|
||||
public targetEndpointInfo: azdata.SchemaCompareEndpointInfo;
|
||||
@@ -84,7 +92,8 @@ export class SchemaCompareResult {
|
||||
serverName: profile.serverName,
|
||||
databaseName: profile.databaseName,
|
||||
ownerUri: ownerUri,
|
||||
packageFilePath: ''
|
||||
packageFilePath: '',
|
||||
connectionDetails: undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -121,9 +130,10 @@ export class SchemaCompareResult {
|
||||
this.createGenerateScriptButton(view);
|
||||
this.createApplyButton(view);
|
||||
this.createOptionsButton(view);
|
||||
this.createSourceAndTargetButtons(view);
|
||||
this.createOpenScmpButton(view);
|
||||
this.createSaveScmpButton(view);
|
||||
this.resetButtons(false); // disable buttons because source and target aren't both selected yet
|
||||
this.createSourceAndTargetButtons(view);
|
||||
this.resetButtons(ResetButtonState.noSourceTarget);
|
||||
|
||||
let toolBar = view.modelBuilder.toolbarContainer();
|
||||
toolBar.addToolbarItems([{
|
||||
@@ -140,6 +150,8 @@ export class SchemaCompareResult {
|
||||
}, {
|
||||
component: this.switchButton,
|
||||
toolbarSeparatorAfter: true
|
||||
}, {
|
||||
component: this.openScmpButton
|
||||
}, {
|
||||
component: this.saveScmpButton
|
||||
}]);
|
||||
@@ -158,7 +170,8 @@ export class SchemaCompareResult {
|
||||
value: localize('schemaCompare.switchLabel', '➔')
|
||||
}).component();
|
||||
|
||||
this.sourceName = this.sourceEndpointInfo ? `${this.sourceEndpointInfo.serverName}.${this.sourceEndpointInfo.databaseName}` : ' ';
|
||||
this.sourceName = getEndpointName(this.sourceEndpointInfo);
|
||||
this.targetName = ' ';
|
||||
this.sourceNameComponent = view.modelBuilder.table().withProperties({
|
||||
columns: [
|
||||
{
|
||||
@@ -172,9 +185,9 @@ export class SchemaCompareResult {
|
||||
this.targetNameComponent = view.modelBuilder.table().withProperties({
|
||||
columns: [
|
||||
{
|
||||
value: ' ',
|
||||
value: this.targetName,
|
||||
headerCssClass: 'no-borders',
|
||||
toolTip: ''
|
||||
toolTip: this.targetName
|
||||
},
|
||||
]
|
||||
}).component();
|
||||
@@ -239,7 +252,7 @@ export class SchemaCompareResult {
|
||||
]);
|
||||
|
||||
// reset buttons to before comparison state
|
||||
this.resetButtons(true);
|
||||
this.resetButtons(ResetButtonState.beforeCompareStart);
|
||||
}
|
||||
|
||||
// only for test
|
||||
@@ -330,6 +343,7 @@ export class SchemaCompareResult {
|
||||
this.switchButton.enabled = true;
|
||||
this.compareButton.enabled = true;
|
||||
this.optionsButton.enabled = true;
|
||||
this.openScmpButton.enabled = true;
|
||||
this.cancelCompareButton.enabled = false;
|
||||
|
||||
if (this.comparisonResult.differences.length > 0) {
|
||||
@@ -393,6 +407,8 @@ export class SchemaCompareResult {
|
||||
if (key) {
|
||||
if (!this.sourceTargetSwitched) {
|
||||
this.originalSourceExcludes.delete(key);
|
||||
this.removeExcludeEntry(this.scmpSourceExcludes, key);
|
||||
|
||||
if (!rowState.checked) {
|
||||
this.originalSourceExcludes.set(key, diff);
|
||||
if (this.originalSourceExcludes.size === this.comparisonResult.differences.length) {
|
||||
@@ -405,6 +421,8 @@ export class SchemaCompareResult {
|
||||
}
|
||||
else {
|
||||
this.originalTargetExcludes.delete(key);
|
||||
this.removeExcludeEntry(this.scmpTargetExcludes, key);
|
||||
|
||||
if (!rowState.checked) {
|
||||
this.originalTargetExcludes.set(key, diff);
|
||||
if (this.originalTargetExcludes.size === this.comparisonResult.differences.length) {
|
||||
@@ -422,12 +440,14 @@ export class SchemaCompareResult {
|
||||
private shouldDiffBeIncluded(diff: azdata.DiffEntry): boolean {
|
||||
let key = (diff.sourceValue && diff.sourceValue.length > 0) ? this.createName(diff.sourceValue) : this.createName(diff.targetValue);
|
||||
if (key) {
|
||||
if (this.sourceTargetSwitched === true && this.originalTargetExcludes.has(key)) {
|
||||
this.originalTargetExcludes[key] = diff;
|
||||
if (this.sourceTargetSwitched === true
|
||||
&& (this.originalTargetExcludes.has(key) || this.hasExcludeEntry(this.scmpTargetExcludes, key))) {
|
||||
this.originalTargetExcludes.set(key, diff);
|
||||
return false;
|
||||
}
|
||||
if (this.sourceTargetSwitched === false && this.originalSourceExcludes.has(key)) {
|
||||
this.originalSourceExcludes[key] = diff;
|
||||
if (this.sourceTargetSwitched === false
|
||||
&& (this.originalSourceExcludes.has(key) || this.hasExcludeEntry(this.scmpSourceExcludes, key))) {
|
||||
this.originalSourceExcludes.set(key, diff);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -435,6 +455,23 @@ export class SchemaCompareResult {
|
||||
return true;
|
||||
}
|
||||
|
||||
private hasExcludeEntry(collection: azdata.SchemaCompareObjectId[], entryName: string): boolean {
|
||||
let found = false;
|
||||
if (collection) {
|
||||
const index = collection.findIndex(e => this.createName(e.nameParts) === entryName);
|
||||
found = index !== -1;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private removeExcludeEntry(collection: azdata.SchemaCompareObjectId[], entryName: string) {
|
||||
if (collection) {
|
||||
console.error('removing ' + entryName);
|
||||
const index = collection.findIndex(e => this.createName(e.nameParts) === entryName);
|
||||
collection.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private getAllDifferences(differences: azdata.DiffEntry[]): string[][] {
|
||||
let data = [];
|
||||
if (differences) {
|
||||
@@ -502,7 +539,7 @@ export class SchemaCompareResult {
|
||||
if (this.tablelistenersToDispose) {
|
||||
this.tablelistenersToDispose.forEach(x => x.dispose());
|
||||
}
|
||||
this.resetButtons(false);
|
||||
this.resetButtons(ResetButtonState.comparing);
|
||||
this.execute();
|
||||
}
|
||||
|
||||
@@ -547,7 +584,7 @@ export class SchemaCompareResult {
|
||||
this.flexModel.removeItem(this.loader);
|
||||
this.flexModel.removeItem(this.waitText);
|
||||
this.flexModel.addItem(this.startText, { CSSStyles: { 'margin': 'auto' } });
|
||||
this.resetButtons(true);
|
||||
this.resetButtons(ResetButtonState.beforeCompareStart);
|
||||
|
||||
// cancel compare
|
||||
if (this.operationId) {
|
||||
@@ -611,16 +648,12 @@ export class SchemaCompareResult {
|
||||
}).component();
|
||||
|
||||
this.optionsButton.onDidClick(async (click) => {
|
||||
Telemetry.sendTelemetryEvent('SchemaCompareOptionsOpened', {
|
||||
'operationId': this.comparisonResult.operationId
|
||||
});
|
||||
//restore options from last time
|
||||
if (this.schemaCompareOptionDialog && this.schemaCompareOptionDialog.deploymentOptions) {
|
||||
this.deploymentOptions = this.schemaCompareOptionDialog.deploymentOptions;
|
||||
}
|
||||
Telemetry.sendTelemetryEvent('SchemaCompareOptionsOpened');
|
||||
|
||||
// create fresh every time
|
||||
this.schemaCompareOptionDialog = new SchemaCompareOptionsDialog(this.deploymentOptions, this);
|
||||
await this.schemaCompareOptionDialog.openDialog();
|
||||
this.deploymentOptions = this.schemaCompareOptionDialog.deploymentOptions;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -673,20 +706,35 @@ export class SchemaCompareResult {
|
||||
});
|
||||
}
|
||||
|
||||
private resetButtons(beforeCompareStart: boolean): void {
|
||||
if (beforeCompareStart) {
|
||||
this.compareButton.enabled = true;
|
||||
this.optionsButton.enabled = true;
|
||||
this.switchButton.enabled = true;
|
||||
this.cancelCompareButton.enabled = false;
|
||||
this.saveScmpButton.enabled = true;
|
||||
}
|
||||
else {
|
||||
this.compareButton.enabled = false;
|
||||
this.optionsButton.enabled = false;
|
||||
this.switchButton.enabled = false;
|
||||
this.cancelCompareButton.enabled = true;
|
||||
private resetButtons(resetButtonState: ResetButtonState): void {
|
||||
switch (resetButtonState) {
|
||||
case (ResetButtonState.noSourceTarget): {
|
||||
this.compareButton.enabled = false;
|
||||
this.optionsButton.enabled = false;
|
||||
this.switchButton.enabled = this.sourceEndpointInfo ? true : false; // allows switching if the source is set
|
||||
this.openScmpButton.enabled = true;
|
||||
this.cancelCompareButton.enabled = false;
|
||||
break;
|
||||
}
|
||||
case (ResetButtonState.beforeCompareStart): {
|
||||
this.compareButton.enabled = true;
|
||||
this.optionsButton.enabled = true;
|
||||
this.switchButton.enabled = true;
|
||||
this.openScmpButton.enabled = true;
|
||||
this.saveScmpButton.enabled = true;
|
||||
this.cancelCompareButton.enabled = false;
|
||||
break;
|
||||
}
|
||||
case (ResetButtonState.comparing): {
|
||||
this.compareButton.enabled = false;
|
||||
this.optionsButton.enabled = false;
|
||||
this.switchButton.enabled = false;
|
||||
this.openScmpButton.enabled = false;
|
||||
this.cancelCompareButton.enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.generateScriptButton.enabled = false;
|
||||
this.applyButton.enabled = false;
|
||||
this.generateScriptButton.title = generateScriptEnabledMessage;
|
||||
@@ -700,6 +748,14 @@ export class SchemaCompareResult {
|
||||
this.applyButton.title = reCompareToRefeshMessage;
|
||||
}
|
||||
|
||||
// reset state afer loading an scmp
|
||||
private resetForNewCompare(): void {
|
||||
this.resetButtons(ResetButtonState.beforeCompareStart);
|
||||
this.flexModel.removeItem(this.splitView);
|
||||
this.flexModel.removeItem(this.noDifferencesLabel);
|
||||
this.flexModel.addItem(this.startText, { CSSStyles: { 'margin': 'auto' } });
|
||||
}
|
||||
|
||||
private createSwitchButton(view: azdata.ModelView): void {
|
||||
this.switchButton = view.modelBuilder.button().withProperties({
|
||||
label: localize('schemaCompare.switchDirectionButton', 'Switch direction'),
|
||||
@@ -739,7 +795,11 @@ export class SchemaCompareResult {
|
||||
|
||||
// remember that source target have been toggled
|
||||
this.sourceTargetSwitched = this.sourceTargetSwitched ? false : true;
|
||||
this.startCompare();
|
||||
|
||||
// only compare if both source and target are set
|
||||
if (this.sourceEndpointInfo && this.targetEndpointInfo) {
|
||||
this.startCompare();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -767,6 +827,85 @@ export class SchemaCompareResult {
|
||||
});
|
||||
}
|
||||
|
||||
private createOpenScmpButton(view: azdata.ModelView) {
|
||||
this.openScmpButton = view.modelBuilder.button().withProperties({
|
||||
label: localize('schemaCompare.openScmpButton', 'Open .scmp file'),
|
||||
iconPath: {
|
||||
light: path.join(__dirname, 'media', 'open-scmp.svg'),
|
||||
dark: path.join(__dirname, 'media', 'open-scmp-inverse.svg')
|
||||
},
|
||||
title: localize('schemaCompare.openScmpButtonTitle', 'Load source, target, and options saved in an .scmp file')
|
||||
}).component();
|
||||
|
||||
this.openScmpButton.onDidClick(async (click) => {
|
||||
Telemetry.sendTelemetryEvent('SchemaCompareOpenScmpStarted');
|
||||
const rootPath = vscode.workspace.rootPath ? vscode.workspace.rootPath : os.homedir();
|
||||
let fileUris = await vscode.window.showOpenDialog(
|
||||
{
|
||||
canSelectFiles: true,
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false,
|
||||
defaultUri: vscode.Uri.file(rootPath),
|
||||
openLabel: localize('schemaCompare.openFile', 'Open'),
|
||||
filters: {
|
||||
'scmp Files': ['scmp'],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!fileUris || fileUris.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileUri = fileUris[0];
|
||||
const service = await SchemaCompareResult.getService('MSSQL');
|
||||
let startTime = Date.now();
|
||||
const result = await service.schemaCompareOpenScmp(fileUri.fsPath);
|
||||
if (!result || !result.success) {
|
||||
Telemetry.sendTelemetryEvent('SchemaCompareOpenScmpFailed', {
|
||||
'errorType': getTelemetryErrorType(result.errorMessage)
|
||||
});
|
||||
vscode.window.showErrorMessage(
|
||||
localize('schemaCompare.openScmpErrorMessage', "Open scmp failed: '{0}'", (result && result.errorMessage) ? result.errorMessage : 'Unknown'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.sourceEndpointInfo && result.sourceEndpointInfo.endpointType === azdata.SchemaCompareEndpointType.Database) {
|
||||
// only set endpoint info if able to connect to the database
|
||||
const ownerUri = await verifyConnectionAndGetOwnerUri(result.sourceEndpointInfo);
|
||||
if (ownerUri) {
|
||||
this.sourceEndpointInfo = result.sourceEndpointInfo;
|
||||
this.sourceEndpointInfo.ownerUri = ownerUri;
|
||||
}
|
||||
} else {
|
||||
this.sourceEndpointInfo = result.sourceEndpointInfo;
|
||||
}
|
||||
|
||||
if (result.targetEndpointInfo && result.targetEndpointInfo.endpointType === azdata.SchemaCompareEndpointType.Database) {
|
||||
const ownerUri = await verifyConnectionAndGetOwnerUri(result.targetEndpointInfo);
|
||||
if (ownerUri) {
|
||||
this.targetEndpointInfo = result.targetEndpointInfo;
|
||||
this.targetEndpointInfo.ownerUri = ownerUri;
|
||||
}
|
||||
} else {
|
||||
this.targetEndpointInfo = result.targetEndpointInfo;
|
||||
}
|
||||
|
||||
this.updateSourceAndTarget();
|
||||
this.deploymentOptions = result.deploymentOptions;
|
||||
this.scmpSourceExcludes = result.excludedSourceElements;
|
||||
this.scmpTargetExcludes = result.excludedTargetElements;
|
||||
this.sourceTargetSwitched = result.originalTargetName !== this.targetEndpointInfo.databaseName;
|
||||
|
||||
// clear out any old results
|
||||
this.resetForNewCompare();
|
||||
|
||||
Telemetry.sendTelemetryEvent('SchemaCompareOpenScmpEnded', {
|
||||
'elapsedTime:': (Date.now() - startTime).toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private createSaveScmpButton(view: azdata.ModelView): void {
|
||||
this.saveScmpButton = view.modelBuilder.button().withProperties({
|
||||
label: localize('schemaCompare.saveScmpButton', 'Save .scmp file'),
|
||||
@@ -812,7 +951,7 @@ export class SchemaCompareResult {
|
||||
}
|
||||
|
||||
Telemetry.sendTelemetryEvent('SchemaCompareSaveScmpEnded', {
|
||||
'totalSaveTime:': (Date.now() - startTime).toString(),
|
||||
'elapsedTime:': (Date.now() - startTime).toString(),
|
||||
'operationId': this.comparisonResult.operationId
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user