mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 09:59:47 -05:00
640 lines
21 KiB
TypeScript
640 lines
21 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as vscode from 'vscode';
|
|
import * as mssql from 'mssql';
|
|
import type * as azdata from 'azdata';
|
|
import * as constants from '../common/constants';
|
|
import * as newProjectTool from '../tools/newProjectTool';
|
|
import type * as mssqlVscode from 'vscode-mssql';
|
|
|
|
import { Deferred } from '../common/promise';
|
|
import { Project } from '../models/project';
|
|
import { cssStyles } from '../common/uiConstants';
|
|
import { IconPathHelper } from '../common/iconHelper';
|
|
import { UpdateProjectDataModel, UpdateProjectAction } from '../models/api/updateProject';
|
|
import { exists, getAzdataApi, getDataWorkspaceExtensionApi } from '../common/utils';
|
|
import * as path from 'path';
|
|
import { mapExtractTargetEnum } from './utils';
|
|
|
|
export class UpdateProjectFromDatabaseDialog {
|
|
public dialog: azdata.window.Dialog;
|
|
public serverDropdown: azdata.DropDownComponent | undefined;
|
|
public databaseDropdown: azdata.DropDownComponent | undefined;
|
|
public projectFileDropdown: azdata.DropDownComponent | undefined;
|
|
public compareActionRadioButton: azdata.RadioButtonComponent | undefined;
|
|
private updateProjectFromDatabaseTab: azdata.window.DialogTab;
|
|
private connectionButton: azdata.ButtonComponent | undefined;
|
|
private folderStructureDropDown: azdata.DropDownComponent | undefined;
|
|
private updateActionRadioButton: azdata.RadioButtonComponent | undefined;
|
|
private formBuilder: azdata.FormBuilder | undefined;
|
|
private connectionId: string | undefined;
|
|
private profile: azdata.IConnectionProfile | undefined;
|
|
public action: UpdateProjectAction | undefined;
|
|
private toDispose: vscode.Disposable[] = [];
|
|
private initDialogPromise: Deferred = new Deferred();
|
|
public populatedInputsPromise: Deferred = new Deferred();
|
|
|
|
public updateProjectFromDatabaseCallback: ((model: UpdateProjectDataModel) => any) | undefined;
|
|
|
|
constructor(connection: azdata.IConnectionProfile | mssqlVscode.IConnectionInfo | undefined, private project: Project | undefined, private workspaceProjects: vscode.Uri[]) {
|
|
if (connection && 'connectionName' in connection) {
|
|
this.profile = connection;
|
|
}
|
|
|
|
// need to set profile when database is updated as well as here
|
|
// see what schemaCompare is doing!
|
|
|
|
this.dialog = getAzdataApi()!.window.createModelViewDialog(constants.updateProjectFromDatabaseDialogName, 'updateProjectFromDatabaseDialog');
|
|
this.updateProjectFromDatabaseTab = getAzdataApi()!.window.createTab(constants.updateProjectFromDatabaseDialogName);
|
|
this.dialog.registerCloseValidator(async () => {
|
|
return this.validate();
|
|
});
|
|
|
|
this.toDispose.push(this.dialog.onClosed(_ => this.initDialogPromise.resolve()));
|
|
}
|
|
|
|
public async openDialog(): Promise<void> {
|
|
let connection = await getAzdataApi()!.connection.getCurrentConnection();
|
|
if (connection) {
|
|
this.connectionId = connection.connectionId;
|
|
}
|
|
|
|
this.initializeDialog();
|
|
|
|
this.dialog.okButton.label = constants.updateText;
|
|
this.dialog.okButton.enabled = false;
|
|
this.toDispose.push(this.dialog.okButton.onClick(async () => await this.handleUpdateButtonClick()));
|
|
|
|
this.dialog.cancelButton.label = constants.cancelButtonText;
|
|
|
|
getAzdataApi()!.window.openDialog(this.dialog);
|
|
await this.initDialogPromise;
|
|
|
|
this.tryEnableUpdateButton();
|
|
}
|
|
|
|
private dispose(): void {
|
|
this.toDispose.forEach(disposable => disposable.dispose());
|
|
}
|
|
|
|
private initializeDialog(): void {
|
|
this.initializeUpdateProjectFromDatabaseTab();
|
|
this.dialog.content = [this.updateProjectFromDatabaseTab];
|
|
}
|
|
|
|
private initializeUpdateProjectFromDatabaseTab(): void {
|
|
this.updateProjectFromDatabaseTab.registerContent(async view => {
|
|
|
|
const connectionRow = this.createServerRow(view);
|
|
const databaseRow = this.createDatabaseRow(view);
|
|
await this.populateServerDropdown();
|
|
|
|
const sourceDatabaseFormSection = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
|
sourceDatabaseFormSection.addItems([connectionRow, databaseRow]);
|
|
|
|
const projectLocationRow = this.createProjectLocationRow(view);
|
|
const folderStructureRow = this.createFolderStructureRow(view);
|
|
const targetProjectFormSection = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
|
targetProjectFormSection.addItems([projectLocationRow, folderStructureRow]);
|
|
|
|
const actionRow = await this.createActionRow(view);
|
|
const actionFormSection = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
|
actionFormSection.addItems([actionRow]);
|
|
|
|
this.formBuilder = <azdata.FormBuilder>view.modelBuilder.formContainer()
|
|
.withFormItems([
|
|
{
|
|
title: constants.sourceDatabase,
|
|
components: [
|
|
{
|
|
component: sourceDatabaseFormSection,
|
|
}
|
|
]
|
|
},
|
|
{
|
|
title: constants.targetProject,
|
|
components: [
|
|
{
|
|
component: targetProjectFormSection,
|
|
}
|
|
]
|
|
},
|
|
{
|
|
title: constants.updateAction,
|
|
components: [
|
|
{
|
|
component: actionFormSection,
|
|
}
|
|
]
|
|
}
|
|
], {
|
|
horizontal: false,
|
|
titleFontSize: cssStyles.titleFontSize
|
|
})
|
|
.withLayout({
|
|
width: '100%',
|
|
padding: '10px 10px 0 20px'
|
|
});
|
|
|
|
let formModel = this.formBuilder.component();
|
|
await view.initializeModel(formModel);
|
|
await this.connectionButton?.focus();
|
|
this.initDialogPromise.resolve();
|
|
});
|
|
}
|
|
|
|
private createServerRow(view: azdata.ModelView): azdata.FlexContainer {
|
|
this.createServerComponent(view);
|
|
|
|
const serverLabel = view.modelBuilder.text().withProps({
|
|
value: constants.server,
|
|
requiredIndicator: true,
|
|
width: cssStyles.updateProjectFromDatabaseLabelWidth
|
|
}).component();
|
|
|
|
const connectionRow = view.modelBuilder.flexContainer().withItems([serverLabel, this.serverDropdown!], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px', 'margin-bottom': '-5px', 'margin-top': '-10px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
|
connectionRow.addItem(this.connectionButton!, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '-5px', 'margin-top': '-10px' } });
|
|
|
|
return connectionRow;
|
|
}
|
|
|
|
private createDatabaseRow(view: azdata.ModelView): azdata.FlexContainer {
|
|
this.createDatabaseComponent(view);
|
|
|
|
const databaseLabel = view.modelBuilder.text().withProps({
|
|
value: constants.databaseNameLabel,
|
|
requiredIndicator: true,
|
|
width: cssStyles.updateProjectFromDatabaseLabelWidth
|
|
}).component();
|
|
|
|
const databaseRow = view.modelBuilder.flexContainer().withItems([databaseLabel, <azdata.DropDownComponent>this.databaseDropdown!], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px', 'margin-bottom': '-10px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
|
|
|
return databaseRow;
|
|
}
|
|
|
|
private createServerComponent(view: azdata.ModelView) {
|
|
this.serverDropdown = view.modelBuilder.dropDown().withProps({
|
|
editable: true,
|
|
fireOnTextChange: true,
|
|
width: cssStyles.updateProjectFromDatabaseTextboxWidth,
|
|
ariaLabel: constants.server,
|
|
required: true
|
|
}).component();
|
|
|
|
this.createConnectionButton(view);
|
|
|
|
this.serverDropdown.onValueChanged(() => {
|
|
this.tryEnableUpdateButton();
|
|
});
|
|
}
|
|
|
|
private createDatabaseComponent(view: azdata.ModelView) {
|
|
this.databaseDropdown = view.modelBuilder.dropDown().withProps({
|
|
editable: true,
|
|
fireOnTextChange: true,
|
|
width: cssStyles.updateProjectFromDatabaseTextboxWidth,
|
|
ariaLabel: constants.databaseNameLabel,
|
|
required: true
|
|
}).component();
|
|
|
|
this.databaseDropdown.onValueChanged(() => {
|
|
this.tryEnableUpdateButton();
|
|
});
|
|
}
|
|
|
|
private async populateServerDropdown() {
|
|
this.serverDropdown!.loading = true;
|
|
const values = await this.getServerValues();
|
|
|
|
if (values && values.length > 0) {
|
|
await this.serverDropdown!.updateProperties({
|
|
values: values,
|
|
value: values[0]
|
|
});
|
|
}
|
|
|
|
this.serverDropdown!.loading = false;
|
|
|
|
if (this.serverDropdown!.value) {
|
|
await this.populateDatabaseDropdown();
|
|
}
|
|
|
|
this.tryEnableUpdateButton();
|
|
this.populatedInputsPromise.resolve();
|
|
}
|
|
|
|
protected async populateDatabaseDropdown() {
|
|
const connectionProfile = (this.serverDropdown!.value as ConnectionDropdownValue).connection;
|
|
|
|
this.databaseDropdown!.loading = true;
|
|
|
|
await this.databaseDropdown!.updateProperties({
|
|
values: [],
|
|
value: undefined
|
|
});
|
|
|
|
let values = [];
|
|
try {
|
|
values = await this.getDatabaseValues(connectionProfile.connectionId);
|
|
|
|
// move system dbs to the bottom of the list so it's easier to find user dbs
|
|
const systemDbs = values.filter(db => constants.systemDbs.includes(db));
|
|
values = values.filter(db => !constants.systemDbs.includes(db)).concat(systemDbs);
|
|
} catch (e) {
|
|
// if the user doesn't have access to master, just set the database of the connection profile
|
|
values = [connectionProfile.databaseName];
|
|
console.warn(e);
|
|
}
|
|
|
|
if (values && values.length > 0) {
|
|
await this.databaseDropdown!.updateProperties({
|
|
values: values,
|
|
value: values[0],
|
|
});
|
|
|
|
// change the database dropdown value to the connection's database if there is one
|
|
if (connectionProfile.options.database && connectionProfile.options.database !== constants.master) {
|
|
this.databaseDropdown!.value = connectionProfile.options.database;
|
|
}
|
|
}
|
|
|
|
this.databaseDropdown!.loading = false;
|
|
}
|
|
|
|
private async getServerValues() {
|
|
let cons = await getAzdataApi()!.connection.getConnections(/* activeConnectionsOnly */ true);
|
|
|
|
// This user has no active connections
|
|
if (!cons || cons.length === 0) {
|
|
return undefined;
|
|
}
|
|
|
|
// Update connection icon to "connected" state
|
|
this.connectionButton!.iconPath = IconPathHelper.connect;
|
|
|
|
// reverse list so that most recent connections are first
|
|
cons.reverse();
|
|
|
|
let count = -1;
|
|
let idx = -1;
|
|
let values = cons.map(c => {
|
|
count++;
|
|
|
|
let usr = c.options.user;
|
|
|
|
if (!usr) {
|
|
usr = constants.defaultUser;
|
|
}
|
|
|
|
let srv = c.options.server;
|
|
|
|
let finalName = `${srv} (${usr})`;
|
|
|
|
if (c.options.connectionName) {
|
|
finalName = c.options.connectionName;
|
|
}
|
|
|
|
if (c.connectionId === this.connectionId) {
|
|
idx = count;
|
|
}
|
|
|
|
return {
|
|
connection: c,
|
|
displayName: finalName,
|
|
name: srv,
|
|
};
|
|
});
|
|
|
|
// move server of current connection to the top of the list so it is the default
|
|
if (idx >= 1) {
|
|
let tmp = values[0];
|
|
values[0] = values[idx];
|
|
values[idx] = tmp;
|
|
}
|
|
|
|
values = values.reduce((uniqueValues: { connection: azdata.connection.ConnectionProfile, displayName: string, name: string }[], conn) => {
|
|
let exists = uniqueValues.find(x => x.displayName === conn.displayName);
|
|
if (!exists) {
|
|
uniqueValues.push(conn);
|
|
}
|
|
return uniqueValues;
|
|
}, []);
|
|
|
|
return values;
|
|
}
|
|
|
|
protected async getDatabaseValues(connectionId: string) {
|
|
let idx = -1;
|
|
let count = -1;
|
|
|
|
let values = (await getAzdataApi()!.connection.listDatabases(connectionId)).sort((a, b) => a.localeCompare(b)).map(db => {
|
|
count++;
|
|
|
|
// put currently selected db at the top of the dropdown if there is one
|
|
if (this.profile && this.profile.databaseName && this.profile.databaseName === db) {
|
|
idx = count;
|
|
}
|
|
|
|
return db;
|
|
});
|
|
|
|
if (idx >= 0) {
|
|
let tmp = values[0];
|
|
values[0] = values[idx];
|
|
values[idx] = tmp;
|
|
}
|
|
return values;
|
|
}
|
|
|
|
private createConnectionButton(view: azdata.ModelView) {
|
|
this.connectionButton = view.modelBuilder.button().withProps({
|
|
ariaLabel: constants.selectConnection,
|
|
title: constants.selectConnection,
|
|
iconPath: IconPathHelper.selectConnection,
|
|
height: '20px',
|
|
width: '20px'
|
|
}).component();
|
|
|
|
this.connectionButton.onDidClick(async () => {
|
|
await this.connectionButtonClick();
|
|
this.connectionButton!.iconPath = IconPathHelper.connect;
|
|
});
|
|
}
|
|
|
|
private async connectionButtonClick() {
|
|
let connection = await getAzdataApi()!.connection.openConnectionDialog();
|
|
if (connection) {
|
|
this.connectionId = connection.connectionId;
|
|
await this.populateServerDropdown();
|
|
}
|
|
}
|
|
|
|
private createProjectLocationRow(view: azdata.ModelView): azdata.FlexContainer {
|
|
const browseFolderButton: azdata.Component = this.createBrowseFileButton(view);
|
|
|
|
let values: string[] = [];
|
|
this.workspaceProjects.forEach(projectUri => {
|
|
values.push(projectUri.fsPath);
|
|
});
|
|
|
|
const value = this.project ? this.project.projectFilePath : (values[0] ?? '');
|
|
|
|
this.projectFileDropdown = view.modelBuilder.dropDown().withProps({
|
|
editable: true,
|
|
fireOnTextChange: true,
|
|
value: value,
|
|
values: values,
|
|
width: cssStyles.updateProjectFromDatabaseTextboxWidth,
|
|
ariaLabel: constants.location,
|
|
required: true
|
|
}).component();
|
|
|
|
this.projectFileDropdown.onValueChanged(async () => {
|
|
await this.projectFileDropdown!.updateProperty('title', this.projectFileDropdown!.value);
|
|
this.tryEnableUpdateButton();
|
|
});
|
|
|
|
const projectLocationLabel = view.modelBuilder.text().withProps({
|
|
value: constants.projectLocationLabel,
|
|
requiredIndicator: true,
|
|
width: cssStyles.updateProjectFromDatabaseLabelWidth
|
|
}).component();
|
|
|
|
const projectLocationRow = view.modelBuilder.flexContainer().withItems([projectLocationLabel,], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px', 'margin-bottom': '-5px', 'margin-top': '-7px' } }).component();
|
|
projectLocationRow.addItem(this.projectFileDropdown, { CSSStyles: { 'margin-right': '10px' } });
|
|
projectLocationRow.addItem(browseFolderButton, { CSSStyles: { 'margin-top': '2px' } });
|
|
|
|
return projectLocationRow;
|
|
}
|
|
|
|
private createBrowseFileButton(view: azdata.ModelView): azdata.ButtonComponent {
|
|
const browseFolderButton = view.modelBuilder.button().withProps({
|
|
ariaLabel: constants.browseButtonText,
|
|
title: constants.browseButtonText,
|
|
iconPath: IconPathHelper.folder_blue,
|
|
height: '18px',
|
|
width: '18px'
|
|
}).component();
|
|
|
|
browseFolderButton.onDidClick(async () => {
|
|
let fileUris = await vscode.window.showOpenDialog({
|
|
canSelectFiles: true,
|
|
canSelectFolders: false,
|
|
canSelectMany: false,
|
|
openLabel: constants.selectString,
|
|
defaultUri: newProjectTool.defaultProjectSaveLocation(),
|
|
filters: {
|
|
'Files': ['sqlproj']
|
|
}
|
|
});
|
|
|
|
if (!fileUris || fileUris.length === 0) {
|
|
return;
|
|
}
|
|
|
|
this.projectFileDropdown!.value = fileUris[0].fsPath;
|
|
await this.projectFileDropdown!.updateProperty('title', fileUris[0].fsPath);
|
|
});
|
|
|
|
return browseFolderButton;
|
|
}
|
|
|
|
private createFolderStructureRow(view: azdata.ModelView): azdata.FlexContainer {
|
|
this.folderStructureDropDown = view.modelBuilder.dropDown().withProps({
|
|
values: [constants.file, constants.flat, constants.objectType, constants.schema, constants.schemaObjectType],
|
|
value: constants.schemaObjectType,
|
|
ariaLabel: constants.folderStructureLabel,
|
|
required: true,
|
|
width: cssStyles.updateProjectFromDatabaseTextboxWidth
|
|
}).component();
|
|
|
|
this.folderStructureDropDown.onValueChanged(() => {
|
|
this.tryEnableUpdateButton();
|
|
});
|
|
|
|
const folderStructureLabel = view.modelBuilder.text().withProps({
|
|
value: constants.folderStructureLabel,
|
|
requiredIndicator: true,
|
|
width: cssStyles.createProjectFromDatabaseLabelWidth
|
|
}).component();
|
|
|
|
const folderStructureRow = view.modelBuilder.flexContainer().withItems([folderStructureLabel, <azdata.DropDownComponent>this.folderStructureDropDown], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px', 'margin-bottom': '-10px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
|
|
|
return folderStructureRow;
|
|
}
|
|
|
|
private async createActionRow(view: azdata.ModelView): Promise<azdata.FlexContainer> {
|
|
this.compareActionRadioButton = view.modelBuilder.radioButton().withProps({
|
|
name: 'action',
|
|
label: constants.compareActionRadioButtonLabel,
|
|
checked: true
|
|
}).component();
|
|
|
|
this.updateActionRadioButton = view.modelBuilder.radioButton().withProps({
|
|
name: 'action',
|
|
label: constants.updateActionRadioButtonLabel
|
|
}).component();
|
|
|
|
await this.compareActionRadioButton.updateProperties({ checked: true });
|
|
this.action = UpdateProjectAction.Compare;
|
|
|
|
this.compareActionRadioButton.onDidChangeCheckedState((checked) => {
|
|
if (checked) {
|
|
this.action = UpdateProjectAction.Compare;
|
|
this.tryEnableUpdateButton();
|
|
}
|
|
});
|
|
|
|
this.updateActionRadioButton.onDidChangeCheckedState((checked) => {
|
|
if (checked) {
|
|
this.action = UpdateProjectAction.Update;
|
|
this.tryEnableUpdateButton();
|
|
}
|
|
});
|
|
|
|
let radioButtons = view.modelBuilder.flexContainer()
|
|
.withLayout({ flexFlow: 'column' })
|
|
.withItems([this.compareActionRadioButton, this.updateActionRadioButton])
|
|
.withProps({ ariaRole: 'radiogroup', ariaLabel: constants.actionLabel })
|
|
.component();
|
|
|
|
const actionLabel = view.modelBuilder.text().withProps({
|
|
value: constants.actionLabel,
|
|
width: cssStyles.updateProjectFromDatabaseLabelWidth
|
|
}).component();
|
|
|
|
const actionRow = view.modelBuilder.flexContainer().withItems([actionLabel], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px', 'margin-top': '-17px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
|
actionRow.addItem(radioButtons);
|
|
return actionRow;
|
|
}
|
|
|
|
// only enable Update button if all fields are filled
|
|
public tryEnableUpdateButton(): void {
|
|
if (this.serverDropdown?.value
|
|
&& this.databaseDropdown?.value
|
|
&& this.projectFileDropdown?.value
|
|
&& this.folderStructureDropDown?.value
|
|
&& this.action !== undefined) {
|
|
this.dialog.okButton.enabled = true;
|
|
} else {
|
|
this.dialog.okButton.enabled = false;
|
|
}
|
|
}
|
|
|
|
public async handleUpdateButtonClick(): Promise<void> {
|
|
const serverDropdownValue = this.serverDropdown!.value! as azdata.CategoryValue as ConnectionDropdownValue;
|
|
const ownerUri = await getAzdataApi()!.connection.getUriForConnection(serverDropdownValue.connection.connectionId);
|
|
|
|
let connection = (await getAzdataApi()!.connection.getConnections(true)).filter(con => con.connectionId === serverDropdownValue.connection.connectionId)[0];
|
|
connection.databaseName = this.databaseDropdown!.value! as string;
|
|
|
|
const credentials = await getAzdataApi()!.connection.getCredentials(connection.connectionId);
|
|
if (credentials.hasOwnProperty('password')) {
|
|
connection.password = connection.options.password = credentials.password;
|
|
}
|
|
|
|
const projectFilePath = this.projectFileDropdown!.value! as string;
|
|
this.project = await Project.openProject(projectFilePath);
|
|
|
|
const connectionDetails: azdata.IConnectionProfile = {
|
|
id: connection.connectionId,
|
|
userName: connection.userName,
|
|
password: connection.password,
|
|
serverName: connection.serverName,
|
|
databaseName: connection.databaseName,
|
|
connectionName: connection.connectionName,
|
|
providerName: connection.providerId,
|
|
groupId: connection.groupId,
|
|
groupFullName: connection.groupFullName,
|
|
authenticationType: connection.authenticationType,
|
|
savePassword: connection.savePassword,
|
|
saveProfile: connection.saveProfile,
|
|
options: connection.options,
|
|
};
|
|
|
|
const sourceEndpointInfo: mssql.SchemaCompareEndpointInfo = {
|
|
endpointType: mssql.SchemaCompareEndpointType.Database,
|
|
databaseName: this.databaseDropdown!.value! as string,
|
|
serverDisplayName: serverDropdownValue.displayName,
|
|
serverName: serverDropdownValue.name!,
|
|
connectionDetails: connectionDetails,
|
|
ownerUri: ownerUri,
|
|
projectFilePath: '',
|
|
extractTarget: mssql.ExtractTarget.schemaObjectType,
|
|
targetScripts: [],
|
|
dataSchemaProvider: '',
|
|
packageFilePath: '',
|
|
connectionName: serverDropdownValue.connection.options.connectionName
|
|
};
|
|
|
|
const targetEndpointInfo: mssql.SchemaCompareEndpointInfo = {
|
|
endpointType: mssql.SchemaCompareEndpointType.Project,
|
|
projectFilePath: projectFilePath,
|
|
extractTarget: mapExtractTargetEnum(<string>this.folderStructureDropDown!.value),
|
|
targetScripts: [],
|
|
dataSchemaProvider: this.project.getProjectTargetVersion(),
|
|
connectionDetails: connectionDetails,
|
|
databaseName: '',
|
|
serverDisplayName: '',
|
|
serverName: '',
|
|
ownerUri: '',
|
|
packageFilePath: '',
|
|
};
|
|
|
|
const model: UpdateProjectDataModel = {
|
|
sourceEndpointInfo: sourceEndpointInfo,
|
|
targetEndpointInfo: targetEndpointInfo,
|
|
action: this.action!
|
|
};
|
|
|
|
void this.updateProjectFromDatabaseCallback!(model);
|
|
this.dispose();
|
|
}
|
|
|
|
async validate(): Promise<boolean> {
|
|
try {
|
|
if (await getDataWorkspaceExtensionApi().validateWorkspace() === false) {
|
|
return false;
|
|
}
|
|
// the selected location should be an existing directory
|
|
const parentDirectoryExists = await exists(path.dirname(this.projectFileDropdown!.value! as string));
|
|
if (!parentDirectoryExists) {
|
|
this.showErrorMessage(constants.ProjectParentDirectoryNotExistError(this.projectFileDropdown!.value! as string));
|
|
return false;
|
|
}
|
|
|
|
// the selected location must contain a .sqlproj file
|
|
const fileExists = await exists(this.projectFileDropdown!.value! as string);
|
|
if (!fileExists) {
|
|
this.showErrorMessage(constants.noSqlProjFile);
|
|
return false;
|
|
}
|
|
|
|
// schema compare extension must be downloaded
|
|
if (!vscode.extensions.getExtension(constants.schemaCompareExtensionId)) {
|
|
this.showErrorMessage(constants.noSchemaCompareExtension);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (err) {
|
|
this.showErrorMessage(err?.message ? err.message : err);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected showErrorMessage(message: string): void {
|
|
this.dialog.message = {
|
|
text: message,
|
|
level: getAzdataApi()!.window.MessageLevel.Error
|
|
};
|
|
}
|
|
}
|
|
|
|
export interface ConnectionDropdownValue extends azdata.CategoryValue {
|
|
connection: azdata.connection.ConnectionProfile;
|
|
}
|