Make sql project publish SQLCMD table editable (#11812)

* make table editable

* add button to load values from sqlproj

* use form component group

* match mockups

* move UI constants to a separate file

* fix error
This commit is contained in:
Kim Santiago
2020-08-18 12:04:12 -07:00
committed by GitHub
parent b16c6f3faa
commit 17856855f6
7 changed files with 130 additions and 38 deletions

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.1328 0.296875C10.9974 0.53125 11.7891 0.898438 12.5078 1.39844C13.2266 1.89323 13.8438 2.48177 14.3594 3.16406C14.8802 3.84115 15.2839 4.59375 15.5703 5.42188C15.8568 6.24479 16 7.10417 16 8C16 8.73438 15.9036 9.44271 15.7109 10.125C15.5234 10.8073 15.2552 11.4453 14.9062 12.0391C14.5625 12.6328 14.1458 13.1745 13.6562 13.6641C13.1719 14.1484 12.6328 14.5651 12.0391 14.9141C11.4453 15.2578 10.8073 15.526 10.125 15.7188C9.44271 15.9062 8.73438 16 8 16C7.26562 16 6.55729 15.9062 5.875 15.7188C5.19271 15.526 4.55469 15.2578 3.96094 14.9141C3.36719 14.5651 2.82552 14.1484 2.33594 13.6641C1.85156 13.1745 1.4349 12.6328 1.08594 12.0391C0.742188 11.4453 0.473958 10.8099 0.28125 10.1328C0.09375 9.45052 0 8.73958 0 8C0 7.27083 0.0963542 6.5625 0.289062 5.875C0.481771 5.1875 0.755208 4.54167 1.10938 3.9375C1.46875 3.32812 1.90365 2.77604 2.41406 2.28125C2.92448 1.78125 3.5 1.35417 4.14062 1H2V0H6V4H5V1.67969C4.39062 1.97135 3.83854 2.33854 3.34375 2.78125C2.85417 3.21875 2.4349 3.71354 2.08594 4.26562C1.73698 4.8125 1.46875 5.40365 1.28125 6.03906C1.09375 6.67448 1 7.32812 1 8C1 8.64062 1.08333 9.26042 1.25 9.85938C1.41667 10.4531 1.65104 11.0104 1.95312 11.5312C2.26042 12.0469 2.6276 12.5182 3.05469 12.9453C3.48177 13.3724 3.95312 13.7396 4.46875 14.0469C4.98958 14.349 5.54688 14.5833 6.14062 14.75C6.73438 14.9167 7.35417 15 8 15C8.64062 15 9.25781 14.9167 9.85156 14.75C10.4505 14.5833 11.0078 14.349 11.5234 14.0469C12.0443 13.7396 12.5182 13.3724 12.9453 12.9453C13.3724 12.5182 13.737 12.0469 14.0391 11.5312C14.3464 11.0104 14.5833 10.4531 14.75 9.85938C14.9167 9.26562 15 8.64583 15 8C15 7.21875 14.8724 6.46615 14.6172 5.74219C14.3672 5.01823 14.0156 4.35938 13.5625 3.76562C13.1094 3.17188 12.5677 2.65885 11.9375 2.22656C11.3125 1.78906 10.6224 1.46615 9.86719 1.25781L10.1328 0.296875Z" fill="#0078D4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.1328 0.296875C10.9974 0.53125 11.7891 0.898438 12.5078 1.39844C13.2266 1.89323 13.8438 2.48177 14.3594 3.16406C14.8802 3.84115 15.2839 4.59375 15.5703 5.42188C15.8568 6.24479 16 7.10417 16 8C16 8.73438 15.9036 9.44271 15.7109 10.125C15.5234 10.8073 15.2552 11.4453 14.9062 12.0391C14.5625 12.6328 14.1458 13.1745 13.6562 13.6641C13.1719 14.1484 12.6328 14.5651 12.0391 14.9141C11.4453 15.2578 10.8073 15.526 10.125 15.7188C9.44271 15.9062 8.73438 16 8 16C7.26562 16 6.55729 15.9062 5.875 15.7188C5.19271 15.526 4.55469 15.2578 3.96094 14.9141C3.36719 14.5651 2.82552 14.1484 2.33594 13.6641C1.85156 13.1745 1.4349 12.6328 1.08594 12.0391C0.742188 11.4453 0.473958 10.8099 0.28125 10.1328C0.09375 9.45052 0 8.73958 0 8C0 7.27083 0.0963542 6.5625 0.289062 5.875C0.481771 5.1875 0.755208 4.54167 1.10938 3.9375C1.46875 3.32812 1.90365 2.77604 2.41406 2.28125C2.92448 1.78125 3.5 1.35417 4.14062 1H2V0H6V4H5V1.67969C4.39062 1.97135 3.83854 2.33854 3.34375 2.78125C2.85417 3.21875 2.4349 3.71354 2.08594 4.26562C1.73698 4.8125 1.46875 5.40365 1.28125 6.03906C1.09375 6.67448 1 7.32812 1 8C1 8.64062 1.08333 9.26042 1.25 9.85938C1.41667 10.4531 1.65104 11.0104 1.95312 11.5312C2.26042 12.0469 2.6276 12.5182 3.05469 12.9453C3.48177 13.3724 3.95312 13.7396 4.46875 14.0469C4.98958 14.349 5.54688 14.5833 6.14062 14.75C6.73438 14.9167 7.35417 15 8 15C8.64062 15 9.25781 14.9167 9.85156 14.75C10.4505 14.5833 11.0078 14.349 11.5234 14.0469C12.0443 13.7396 12.5182 13.3724 12.9453 12.9453C13.3724 12.5182 13.737 12.0469 14.0391 11.5312C14.3464 11.0104 14.5833 10.4531 14.75 9.85938C14.9167 9.26562 15 8.64583 15 8C15 7.21875 14.8724 6.46615 14.6172 5.74219C14.3672 5.01823 14.0156 4.35938 13.5625 3.76562C13.1094 3.17188 12.5677 2.65885 11.9375 2.22656C11.3125 1.78906 10.6224 1.46615 9.86719 1.25781L10.1328 0.296875Z" fill="#0078D4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -79,8 +79,9 @@ export const noDataSourcesText = localize('noDataSourcesText', "No data sources
export const loadProfileButtonText = localize('loadProfileButtonText', "Load Profile..."); export const loadProfileButtonText = localize('loadProfileButtonText', "Load Profile...");
export const profileReadError = localize('profileReadError', "Could not load the profile file."); export const profileReadError = localize('profileReadError', "Could not load the profile file.");
export const sqlCmdTableLabel = localize('sqlCmdTableLabel', "SQLCMD Variables"); export const sqlCmdTableLabel = localize('sqlCmdTableLabel', "SQLCMD Variables");
export const sqlCmdVariableColumn = localize('sqlCmdVariableColumn', "Variable"); export const sqlCmdVariableColumn = localize('sqlCmdVariableColumn', "Name");
export const sqlCmdValueColumn = localize('sqlCmdValueColumn', "Value"); export const sqlCmdValueColumn = localize('sqlCmdValueColumn', "Value");
export const loadSqlCmdVarsButtonTitle = localize('reloadValuesFromProjectButtonTitle', "Reload values from project");
// Error messages // Error messages

View File

@@ -20,6 +20,8 @@ export class IconPathHelper {
public static referenceGroup: IconPath; public static referenceGroup: IconPath;
public static referenceDatabase: IconPath; public static referenceDatabase: IconPath;
public static refresh: IconPath;
public static setExtensionContext(extensionContext: vscode.ExtensionContext) { public static setExtensionContext(extensionContext: vscode.ExtensionContext) {
IconPathHelper.extensionContext = extensionContext; IconPathHelper.extensionContext = extensionContext;
@@ -30,6 +32,8 @@ export class IconPathHelper {
IconPathHelper.referenceGroup = IconPathHelper.makeIcon('referenceGroup'); IconPathHelper.referenceGroup = IconPathHelper.makeIcon('referenceGroup');
IconPathHelper.referenceDatabase = IconPathHelper.makeIcon('reference-database'); IconPathHelper.referenceDatabase = IconPathHelper.makeIcon('reference-database');
IconPathHelper.refresh = IconPathHelper.makeIcon('refresh');
} }
private static makeIcon(name: string) { private static makeIcon(name: string) {

View File

@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// CSS Styles
export namespace cssStyles {
export const text = { 'user-select': 'text', 'cursor': 'text' };
export const tableHeader = { ...text, 'text-align': 'left', 'border': 'none', 'font-size': '12px', 'font-weight': 'normal', 'color': '#666666' };
export const tableRow = { ...text, 'border-top': 'solid 1px #ccc', 'border-bottom': 'solid 1px #ccc', 'border-left': 'none', 'border-right': 'none', 'font-size': '12px' };
export const titleFontSize = 13;
}

View File

@@ -12,8 +12,8 @@ import { Project } from '../models/project';
import { SqlConnectionDataSource } from '../models/dataSources/sqlConnectionStringSource'; import { SqlConnectionDataSource } from '../models/dataSources/sqlConnectionStringSource';
import { IPublishSettings, IGenerateScriptSettings } from '../models/IPublishSettings'; import { IPublishSettings, IGenerateScriptSettings } from '../models/IPublishSettings';
import { DeploymentOptions } from '../../../mssql/src/mssql'; import { DeploymentOptions } from '../../../mssql/src/mssql';
import { IconPathHelper } from '../common/iconHelper';
const titleFontSize = 12; import { cssStyles } from '../common/uiConstants';
interface DataSourceDropdownValue extends azdata.CategoryValue { interface DataSourceDropdownValue extends azdata.CategoryValue {
dataSource: SqlConnectionDataSource; dataSource: SqlConnectionDataSource;
@@ -31,13 +31,14 @@ export class PublishDatabaseDialog {
private connectionsRadioButton: azdata.RadioButtonComponent | undefined; private connectionsRadioButton: azdata.RadioButtonComponent | undefined;
private dataSourcesRadioButton: azdata.RadioButtonComponent | undefined; private dataSourcesRadioButton: azdata.RadioButtonComponent | undefined;
private loadProfileButton: azdata.ButtonComponent | undefined; private loadProfileButton: azdata.ButtonComponent | undefined;
private sqlCmdVariablesTable: azdata.TableComponent | undefined; private sqlCmdVariablesTable: azdata.DeclarativeTableComponent | undefined;
private sqlCmdVariablesFormComponent: azdata.FormComponent | undefined; private sqlCmdVariablesFormComponentGroup: azdata.FormComponentGroup | undefined;
private loadSqlCmdVarsButton: azdata.ButtonComponent | undefined;
private formBuilder: azdata.FormBuilder | undefined; private formBuilder: azdata.FormBuilder | undefined;
private connectionId: string | undefined; private connectionId: string | undefined;
private connectionIsDataSource: boolean | undefined; private connectionIsDataSource: boolean | undefined;
private profileSqlCmdVars: Record<string, string> | undefined; private sqlCmdVars: Record<string, string> | undefined;
private deploymentOptions: DeploymentOptions | undefined; private deploymentOptions: DeploymentOptions | undefined;
private toDispose: vscode.Disposable[] = []; private toDispose: vscode.Disposable[] = [];
@@ -97,23 +98,21 @@ export class PublishDatabaseDialog {
}); });
this.loadProfileButton = this.createLoadProfileButton(view); this.loadProfileButton = this.createLoadProfileButton(view);
this.sqlCmdVariablesTable = view.modelBuilder.table().withProperties({ this.sqlCmdVariablesTable = this.createSqlCmdTable(view);
title: constants.sqlCmdTableLabel, this.loadSqlCmdVarsButton = this.createLoadSqlCmdVarsButton(view);
data: this.convertSqlCmdVarsToTableFormat(this.project.sqlCmdVariables),
columns: [ this.sqlCmdVariablesFormComponentGroup = {
components: [
{ {
value: constants.sqlCmdVariableColumn title: '',
component: this.loadSqlCmdVarsButton
}, },
{ {
value: constants.sqlCmdValueColumn, title: '',
}], component: <azdata.DeclarativeTableComponent>this.sqlCmdVariablesTable
width: 400, }
height: 400 ],
}).component(); title: constants.sqlCmdTableLabel
this.sqlCmdVariablesFormComponent = {
title: constants.sqlCmdTableLabel,
component: <azdata.TableComponent>this.sqlCmdVariablesTable
}; };
this.formBuilder = <azdata.FormBuilder>view.modelBuilder.formContainer() this.formBuilder = <azdata.FormBuilder>view.modelBuilder.formContainer()
@@ -146,7 +145,7 @@ export class PublishDatabaseDialog {
// add SQLCMD variables table if the project has any // add SQLCMD variables table if the project has any
if (Object.keys(this.project.sqlCmdVariables).length > 0) { if (Object.keys(this.project.sqlCmdVariables).length > 0) {
this.formBuilder.addFormItem(this.sqlCmdVariablesFormComponent, { titleFontSize: titleFontSize }); this.formBuilder.addFormItem(this.sqlCmdVariablesFormComponentGroup, { titleFontSize: cssStyles.titleFontSize });
} }
let formModel = this.formBuilder.component(); let formModel = this.formBuilder.component();
@@ -220,14 +219,8 @@ export class PublishDatabaseDialog {
} }
private getSqlCmdVariablesForPublish(): Record<string, string> { private getSqlCmdVariablesForPublish(): Record<string, string> {
// get SQLCMD variables from project // get SQLCMD variables from table
let sqlCmdVariables = { ...this.project.sqlCmdVariables }; let sqlCmdVariables = { ...this.sqlCmdVars };
// update with SQLCMD variables loaded from profile if there are any
for (const key in this.profileSqlCmdVars) {
sqlCmdVariables[key] = this.profileSqlCmdVars[key];
}
return sqlCmdVariables; return sqlCmdVariables;
} }
@@ -346,6 +339,69 @@ export class PublishDatabaseDialog {
} }
} }
private createSqlCmdTable(view: azdata.ModelView): azdata.DeclarativeTableComponent {
this.sqlCmdVars = { ...this.project.sqlCmdVariables };
const table = view.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
ariaLabel: constants.sqlCmdTableLabel,
data: this.convertSqlCmdVarsToTableFormat(this.sqlCmdVars),
columns: [
{
displayName: constants.sqlCmdVariableColumn,
valueType: azdata.DeclarativeDataType.string,
width: '50%',
isReadOnly: true,
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
},
{
displayName: constants.sqlCmdValueColumn,
valueType: azdata.DeclarativeDataType.string,
width: '50%',
isReadOnly: false,
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
}],
width: '100%'
}).component();
table.onDataChanged(() => {
this.sqlCmdVars = {};
table.data.forEach((row) => {
(<Record<string, string>>this.sqlCmdVars)[row[0]] = row[1];
});
this.tryEnableGenerateScriptAndOkButtons();
});
return table;
}
private createLoadSqlCmdVarsButton(view: azdata.ModelView): azdata.ButtonComponent {
let loadSqlCmdVarsButton: azdata.ButtonComponent = view.modelBuilder.button().withProperties({
label: constants.loadSqlCmdVarsButtonTitle,
title: constants.loadSqlCmdVarsButtonTitle,
ariaLabel: constants.loadSqlCmdVarsButtonTitle,
width: '210px',
iconPath: IconPathHelper.refresh,
height: '18px',
CSSStyles: { 'font-size': '13px' }
}).component();
loadSqlCmdVarsButton.onDidClick(async () => {
this.sqlCmdVars = { ...this.project.sqlCmdVariables };
const data = this.convertSqlCmdVarsToTableFormat(this.getSqlCmdVariablesForPublish());
await (<azdata.DeclarativeTableComponent>this.sqlCmdVariablesTable).updateProperties({
data: data
});
this.tryEnableGenerateScriptAndOkButtons();
});
return loadSqlCmdVarsButton;
}
private createEditConnectionButton(view: azdata.ModelView): azdata.Component { private createEditConnectionButton(view: azdata.ModelView): azdata.Component {
let editConnectionButton: azdata.ButtonComponent = view.modelBuilder.button().withProperties({ let editConnectionButton: azdata.ButtonComponent = view.modelBuilder.button().withProperties({
label: constants.editConnectionButtonText, label: constants.editConnectionButtonText,
@@ -419,22 +475,25 @@ export class PublishDatabaseDialog {
this.connectionId = result.connectionId; this.connectionId = result.connectionId;
(<azdata.InputBoxComponent>this.targetConnectionTextBox).value = result.connectionString; (<azdata.InputBoxComponent>this.targetConnectionTextBox).value = result.connectionString;
this.deploymentOptions = result.options; for (let key in result.sqlCmdVariables) {
this.profileSqlCmdVars = result.sqlCmdVariables; (<Record<string, string>>this.sqlCmdVars)[key] = result.sqlCmdVariables[key];
const data = this.convertSqlCmdVarsToTableFormat(this.getSqlCmdVariablesForPublish()); }
await (<azdata.TableComponent>this.sqlCmdVariablesTable).updateProperties({ this.deploymentOptions = result.options;
const data = this.convertSqlCmdVarsToTableFormat(this.getSqlCmdVariablesForPublish());
await (<azdata.DeclarativeTableComponent>this.sqlCmdVariablesTable).updateProperties({
data: data data: data
}); });
if (Object.keys(result.sqlCmdVariables).length) { if (Object.keys(result.sqlCmdVariables).length) {
// add SQLCMD Variables table if it wasn't there before // add SQLCMD Variables table if it wasn't there before
if (Object.keys(this.project.sqlCmdVariables).length === 0) { if (Object.keys(this.project.sqlCmdVariables).length === 0) {
this.formBuilder?.addFormItem(<azdata.FormComponent>this.sqlCmdVariablesFormComponent, { titleFontSize: titleFontSize }); this.formBuilder?.addFormItem(<azdata.FormComponentGroup>this.sqlCmdVariablesFormComponentGroup, { titleFontSize: cssStyles.titleFontSize });
} }
} else if (Object.keys(this.project.sqlCmdVariables).length === 0) { } else if (Object.keys(this.project.sqlCmdVariables).length === 0) {
// remove the table if there are no SQLCMD variables in the project and loaded profile // remove the table if there are no SQLCMD variables in the project and loaded profile
this.formBuilder?.removeFormItem(<azdata.FormComponent>this.sqlCmdVariablesFormComponent); this.formBuilder?.removeFormItem(<azdata.FormComponentGroup>this.sqlCmdVariablesFormComponentGroup);
} }
} }
}); });
@@ -453,8 +512,9 @@ export class PublishDatabaseDialog {
// only enable Generate Script and Ok buttons if all fields are filled // only enable Generate Script and Ok buttons if all fields are filled
private tryEnableGenerateScriptAndOkButtons(): void { private tryEnableGenerateScriptAndOkButtons(): void {
if (this.targetConnectionTextBox!.value && this.targetDatabaseTextBox!.value if ((this.targetConnectionTextBox!.value && this.targetDatabaseTextBox!.value
|| this.connectionIsDataSource && this.targetDatabaseTextBox!.value) { || this.connectionIsDataSource && this.targetDatabaseTextBox!.value)
&& this.allSqlCmdVariablesFilled()) {
this.dialog.okButton.enabled = true; this.dialog.okButton.enabled = true;
this.dialog.customButtons[0].enabled = true; this.dialog.customButtons[0].enabled = true;
} else { } else {
@@ -462,4 +522,14 @@ export class PublishDatabaseDialog {
this.dialog.customButtons[0].enabled = false; this.dialog.customButtons[0].enabled = false;
} }
} }
private allSqlCmdVariablesFilled(): boolean {
for (let key in this.sqlCmdVars) {
if (this.sqlCmdVars[key] === '' || this.sqlCmdVars[key] === undefined) {
return false;
}
}
return true;
}
} }

View File

@@ -52,7 +52,6 @@ export async function load(profileUri: Uri, dacfxService: mssql.IDacFxService):
}; };
} }
async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string, connectionString: string }> { async function readConnectionString(xmlDoc: any): Promise<{ connectionId: string, connectionString: string }> {
let targetConnectionString: string = ''; let targetConnectionString: string = '';
let connId: string = ''; let connId: string = '';