mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-24 17:23:05 -05:00
Update publish dialog UI (#11996)
* change svgs * change label to server and get rid of connection string * fix user for windows auth * move blue icons out of dark and light folders
This commit is contained in:
@@ -87,7 +87,8 @@ export const sqlCmdValueColumn = localize('sqlCmdValueColumn', "Value");
|
||||
export const loadSqlCmdVarsButtonTitle = localize('reloadValuesFromProjectButtonTitle', "Reload values from project");
|
||||
export const profile = localize('profile', "Profile");
|
||||
export const selectConnection = localize('selectConnection', "Select connection");
|
||||
export const connection = localize('connection', "Connection");
|
||||
export const server = localize('server', "Server");
|
||||
export const defaultUser = localize('default', "default");
|
||||
|
||||
// Add Database Reference dialog strings
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ export class IconPathHelper {
|
||||
|
||||
public static refresh: IconPath;
|
||||
public static folder_blue: IconPath;
|
||||
public static edit: IconPath;
|
||||
public static selectConnection: IconPath;
|
||||
public static connect: IconPath;
|
||||
|
||||
public static folder: IconPath;
|
||||
|
||||
@@ -37,19 +38,27 @@ export class IconPathHelper {
|
||||
IconPathHelper.referenceGroup = IconPathHelper.makeIcon('referenceGroup');
|
||||
IconPathHelper.referenceDatabase = IconPathHelper.makeIcon('reference-database');
|
||||
|
||||
IconPathHelper.refresh = IconPathHelper.makeIcon('refresh');
|
||||
IconPathHelper.folder_blue = IconPathHelper.makeIcon('folder_blue');
|
||||
IconPathHelper.edit = IconPathHelper.makeIcon('edit');
|
||||
IconPathHelper.refresh = IconPathHelper.makeIcon('refresh', true);
|
||||
IconPathHelper.folder_blue = IconPathHelper.makeIcon('folder_blue', true);
|
||||
IconPathHelper.selectConnection = IconPathHelper.makeIcon('selectConnection', true);
|
||||
IconPathHelper.connect = IconPathHelper.makeIcon('connect', true);
|
||||
|
||||
IconPathHelper.folder = IconPathHelper.makeIcon('folder');
|
||||
}
|
||||
|
||||
private static makeIcon(name: string) {
|
||||
private static makeIcon(name: string, sameIcon: boolean = false) {
|
||||
const folder = 'images';
|
||||
|
||||
return {
|
||||
dark: IconPathHelper.extensionContext.asAbsolutePath(`${folder}/dark/${name}.svg`),
|
||||
light: IconPathHelper.extensionContext.asAbsolutePath(`${folder}/light/${name}.svg`)
|
||||
};
|
||||
if (sameIcon) {
|
||||
return {
|
||||
dark: IconPathHelper.extensionContext.asAbsolutePath(`${folder}/${name}.svg`),
|
||||
light: IconPathHelper.extensionContext.asAbsolutePath(`${folder}/${name}.svg`)
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
dark: IconPathHelper.extensionContext.asAbsolutePath(`${folder}/dark/${name}.svg`),
|
||||
light: IconPathHelper.extensionContext.asAbsolutePath(`${folder}/light/${name}.svg`)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,8 @@ export class PublishDatabaseDialog {
|
||||
value: '',
|
||||
ariaLabel: constants.targetConnectionLabel,
|
||||
placeHolder: constants.selectConnection,
|
||||
width: cssStyles.publishDialogTextboxWidth
|
||||
width: cssStyles.publishDialogTextboxWidth,
|
||||
enabled: false
|
||||
}).component();
|
||||
|
||||
this.targetConnectionTextBox.onTextChanged(() => {
|
||||
@@ -350,13 +351,13 @@ export class PublishDatabaseDialog {
|
||||
this.targetConnectionTextBox = this.createTargetConnectionComponent(view);
|
||||
const selectConnectionButton: azdata.Component = this.createSelectConnectionButton(view);
|
||||
|
||||
const connectionLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||
value: constants.connection,
|
||||
const serverLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||
value: constants.server,
|
||||
requiredIndicator: true,
|
||||
width: cssStyles.publishDialogLabelWidth
|
||||
}).component();
|
||||
|
||||
const connectionRow = view.modelBuilder.flexContainer().withItems([connectionLabel, this.targetConnectionTextBox], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
||||
const connectionRow = view.modelBuilder.flexContainer().withItems([serverLabel, this.targetConnectionTextBox], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
||||
connectionRow.insertItem(selectConnectionButton, 2, { CSSStyles: { 'margin-right': '0px' } });
|
||||
|
||||
return connectionRow;
|
||||
@@ -410,7 +411,7 @@ export class PublishDatabaseDialog {
|
||||
headerCssStyles: cssStyles.tableHeader,
|
||||
rowCssStyles: cssStyles.tableRow
|
||||
}],
|
||||
width: '410px'
|
||||
width: '420px'
|
||||
}).component();
|
||||
|
||||
table.onDataChanged(() => {
|
||||
@@ -453,7 +454,7 @@ export class PublishDatabaseDialog {
|
||||
private createSelectConnectionButton(view: azdata.ModelView): azdata.Component {
|
||||
let selectConnectionButton: azdata.ButtonComponent = view.modelBuilder.button().withProperties({
|
||||
ariaLabel: constants.selectConnection,
|
||||
iconPath: IconPathHelper.edit,
|
||||
iconPath: IconPathHelper.selectConnection,
|
||||
height: '16px',
|
||||
width: '16px'
|
||||
}).component();
|
||||
@@ -462,29 +463,47 @@ export class PublishDatabaseDialog {
|
||||
let connection = await azdata.connection.openConnectionDialog();
|
||||
this.connectionId = connection.connectionId;
|
||||
|
||||
// show connection name if there is one, otherwise show connection string
|
||||
// show connection name if there is one, otherwise show connection in format that shows in OE
|
||||
let connectionTextboxValue: string;
|
||||
if (connection.options['connectionName']) {
|
||||
this.targetConnectionTextBox!.value = connection.options['connectionName'];
|
||||
connectionTextboxValue = connection.options['connectionName'];
|
||||
} else {
|
||||
this.targetConnectionTextBox!.value = await azdata.connection.getConnectionString(connection.connectionId, false);
|
||||
let user = connection.options['user'];
|
||||
if (!user) {
|
||||
user = constants.defaultUser;
|
||||
}
|
||||
|
||||
connectionTextboxValue = `${connection.options['server']} (${user})`;
|
||||
}
|
||||
|
||||
// populate database dropdown with the databases for this connection
|
||||
const databaseValues = (await azdata.connection.listDatabases(this.connectionId))
|
||||
// filter out system dbs
|
||||
.filter(db => constants.systemDbs.find(systemdb => db === systemdb) === undefined);
|
||||
|
||||
this.targetDatabaseDropDown!.values = databaseValues;
|
||||
this.updateConnectionComponents(connectionTextboxValue, this.connectionId);
|
||||
|
||||
// change the database inputbox value to the connection's database if there is one
|
||||
if (connection.options.database && connection.options.database !== constants.master) {
|
||||
this.targetDatabaseDropDown!.value = connection.options.database;
|
||||
}
|
||||
|
||||
// change icon to the one without a plus sign
|
||||
selectConnectionButton.iconPath = IconPathHelper.connect;
|
||||
});
|
||||
|
||||
return selectConnectionButton;
|
||||
}
|
||||
|
||||
private async updateConnectionComponents(connectionTextboxValue: string, connectionId: string) {
|
||||
this.targetConnectionTextBox!.value = connectionTextboxValue;
|
||||
this.targetConnectionTextBox!.placeHolder = connectionTextboxValue;
|
||||
|
||||
// populate database dropdown with the databases for this connection
|
||||
if (connectionId) {
|
||||
const databaseValues = (await azdata.connection.listDatabases(connectionId))
|
||||
// filter out system dbs
|
||||
.filter(db => constants.systemDbs.find(systemdb => db === systemdb) === undefined);
|
||||
|
||||
this.targetDatabaseDropDown!.values = databaseValues;
|
||||
}
|
||||
}
|
||||
|
||||
private createLoadProfileButton(view: azdata.ModelView): azdata.ButtonComponent {
|
||||
let loadProfileButton: azdata.ButtonComponent = view.modelBuilder.button().withProperties({
|
||||
ariaLabel: constants.loadProfilePlaceholderText,
|
||||
@@ -512,10 +531,12 @@ export class PublishDatabaseDialog {
|
||||
|
||||
if (this.readPublishProfile) {
|
||||
const result = await this.readPublishProfile(fileUris[0]);
|
||||
// clear out old database dropdown values. They'll get populated later if there was a connection specified in the profile
|
||||
(<azdata.DropDownComponent>this.targetDatabaseDropDown).values = [];
|
||||
(<azdata.DropDownComponent>this.targetDatabaseDropDown).value = result.databaseName;
|
||||
|
||||
this.connectionId = result.connectionId;
|
||||
(<azdata.InputBoxComponent>this.targetConnectionTextBox).value = result.connectionString;
|
||||
await this.updateConnectionComponents(result.connection, <string>this.connectionId);
|
||||
|
||||
for (let key in result.sqlCmdVariables) {
|
||||
(<Record<string, string>>this.sqlCmdVars)[key] = result.sqlCmdVariables[key];
|
||||
@@ -538,8 +559,9 @@ export class PublishDatabaseDialog {
|
||||
this.formBuilder?.removeFormItem(<azdata.FormComponentGroup>this.sqlCmdVariablesFormComponentGroup);
|
||||
}
|
||||
|
||||
// show file path in text box
|
||||
// show file path in text box and hover text
|
||||
this.loadProfileTextBox!.value = fileUris[0].fsPath;
|
||||
this.loadProfileTextBox!.placeHolder = fileUris[0].fsPath;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { SqlConnectionDataSource } from '../dataSources/sqlConnectionStringSourc
|
||||
export interface PublishProfile {
|
||||
databaseName: string;
|
||||
connectionId: string;
|
||||
connectionString: string;
|
||||
connection: string;
|
||||
sqlCmdVariables: Record<string, string>;
|
||||
options?: mssql.DeploymentOptions;
|
||||
}
|
||||
@@ -46,38 +46,46 @@ export async function load(profileUri: Uri, dacfxService: mssql.IDacFxService):
|
||||
return {
|
||||
databaseName: targetDbName,
|
||||
connectionId: connectionInfo.connectionId,
|
||||
connectionString: connectionInfo.connectionString,
|
||||
connection: connectionInfo.connection,
|
||||
sqlCmdVariables: sqlCmdVariables,
|
||||
options: optionsResult.deploymentOptions
|
||||
};
|
||||
}
|
||||
|
||||
async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string, connectionString: string }> {
|
||||
let targetConnectionString: string = '';
|
||||
async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string, connection: string }> {
|
||||
let targetConnection: string = '';
|
||||
let connId: string = '';
|
||||
|
||||
if (xmlDoc.documentElement.getElementsByTagName(constants.targetConnectionString).length > 0) {
|
||||
targetConnectionString = xmlDoc.documentElement.getElementsByTagName(constants.TargetConnectionString)[0].textContent;
|
||||
const targetConnectionString = xmlDoc.documentElement.getElementsByTagName(constants.TargetConnectionString)[0].textContent;
|
||||
const dataSource = new SqlConnectionDataSource('temp', targetConnectionString);
|
||||
let server: string = '';
|
||||
let username: string = '';
|
||||
const connectionProfile = dataSource.getConnectionProfile();
|
||||
|
||||
try {
|
||||
if (dataSource.integratedSecurity) {
|
||||
connId = (await azdata.connection.connect(connectionProfile, false, false)).connectionId;
|
||||
const connection = await azdata.connection.connect(connectionProfile, false, false);
|
||||
connId = connection.connectionId;
|
||||
server = dataSource.server;
|
||||
username = constants.defaultUser;
|
||||
}
|
||||
else {
|
||||
connId = (await azdata.connection.openConnectionDialog(undefined, connectionProfile)).connectionId;
|
||||
const connection = await azdata.connection.openConnectionDialog(undefined, connectionProfile);
|
||||
connId = connection.connectionId;
|
||||
server = connection.options['server'];
|
||||
username = connection.options['username'];
|
||||
}
|
||||
|
||||
targetConnection = `${server} (${username})`;
|
||||
} catch (err) {
|
||||
throw new Error(constants.unableToCreatePublishConnection(utils.getErrorMessage(err)));
|
||||
}
|
||||
}
|
||||
|
||||
// mask password in connection string
|
||||
targetConnectionString = await azdata.connection.getConnectionString(connId, false);
|
||||
|
||||
return {
|
||||
connectionId: connId,
|
||||
connectionString: targetConnectionString
|
||||
connection: targetConnection
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,16 +43,14 @@ describe('Publish profile tests', function (): void {
|
||||
testContext.dacFxService.setup(x => x.getOptionsFromProfile(TypeMoq.It.isAny())).returns(async () => {
|
||||
return Promise.resolve(mockDacFxOptionsResult);
|
||||
});
|
||||
const connectionString = 'Data Source=testserver;Integrated Security=true;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True';
|
||||
sinon.stub(azdata.connection, 'connect').resolves(connectionResult);
|
||||
sinon.stub(azdata.connection, 'getConnectionString').resolves(connectionString);
|
||||
|
||||
let result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object);
|
||||
should(result.databaseName).equal('targetDb');
|
||||
should(Object.keys(result.sqlCmdVariables).length).equal(1);
|
||||
should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase');
|
||||
should(result.connectionId).equal('connId');
|
||||
should(result.connectionString).equal('Data Source=testserver;Integrated Security=true;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True');
|
||||
should(result.connection).equal('testserver (default)');
|
||||
should(result.options).equal(mockDacFxOptionsResult.deploymentOptions);
|
||||
});
|
||||
|
||||
@@ -62,21 +60,22 @@ describe('Publish profile tests', function (): void {
|
||||
const connectionResult = {
|
||||
providerName: 'MSSQL',
|
||||
connectionId: 'connId',
|
||||
options: {}
|
||||
options: {
|
||||
'server': 'testserver',
|
||||
'username': 'testUser'
|
||||
}
|
||||
};
|
||||
testContext.dacFxService.setup(x => x.getOptionsFromProfile(TypeMoq.It.isAny())).returns(async () => {
|
||||
return Promise.resolve(mockDacFxOptionsResult);
|
||||
});
|
||||
const connectionString = 'Data Source=testserver;User Id=testUser;Password=******;Integrated Security=false;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True';
|
||||
sinon.stub(azdata.connection, 'openConnectionDialog').resolves(connectionResult);
|
||||
sinon.stub(azdata.connection, 'getConnectionString').resolves(connectionString);
|
||||
|
||||
let result = await load(vscode.Uri.file(profilePath), testContext.dacFxService.object);
|
||||
should(result.databaseName).equal('targetDb');
|
||||
should(Object.keys(result.sqlCmdVariables).length).equal(1);
|
||||
should(result.sqlCmdVariables['ProdDatabaseName']).equal('MyProdDatabase');
|
||||
should(result.connectionId).equal('connId');
|
||||
should(result.connectionString).equal('Data Source=testserver;User Id=testUser;Password=******;Integrated Security=false;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True');
|
||||
should(result.connection).equal('testserver (testUser)');
|
||||
should(result.options).equal(mockDacFxOptionsResult.deploymentOptions);
|
||||
});
|
||||
|
||||
@@ -85,9 +84,7 @@ describe('Publish profile tests', function (): void {
|
||||
let profilePath = await testUtils.createTestFile(baselines.publishProfileIntegratedSecurityBaseline, 'publishProfile.publish.xml');
|
||||
const projController = new ProjectsController(new SqlDatabaseProjectTreeViewProvider());
|
||||
|
||||
const connectionString = 'Data Source=testserver;Integrated Security=true;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Connect Timeout=60;Encrypt=False;TrustServerCertificate=True';
|
||||
sinon.stub(azdata.connection, 'connect').throws(new Error('Could not connect'));
|
||||
sinon.stub(azdata.connection, 'getConnectionString').resolves(connectionString);
|
||||
|
||||
await testUtils.shouldThrowSpecificError(async () => await projController.readPublishProfileCallback(vscode.Uri.file(profilePath)), constants.unableToCreatePublishConnection('Could not connect'));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user