Exclude Object Types Coming from DacFx and tests all working as expected (#20015)

* Include Objects Coming from DacFx and tests all working as expected

* Exclude Object types functionality is working as expected and Unit tests

* more refactor updates

* Updated comments and prop name

* Addressing the coments and code updates accordingly

* Updating according to the comments

* STS vbump

* These changes should be deleted with SC changes, not here

* format fixed
This commit is contained in:
Sai Avishkar Sreerama
2022-08-08 12:04:24 -05:00
committed by GitHub
parent 66115d8f80
commit 2b5d2f0a0b
12 changed files with 193 additions and 26 deletions

View File

@@ -149,11 +149,12 @@ export const nameMustNotBeEmpty = localize('nameMustNotBeEmpty', "Name must not
export const AdvancedOptionsButton = localize('advancedOptionsButton', 'Advanced...');
export const AdvancedPublishOptions = localize('advancedPublishOptions', 'Advanced Publish Options');
export const PublishOptions = localize('publishOptions', 'Publish Options');
export const ExcludeObjectTypeTab = localize('excludeObjectTypes', 'Exclude Object Types');
export const ResetButton: string = localize('reset', "Reset");
export const OptionDescription: string = localize('optionDescription', "Option Description");
export const OptionName: string = localize('optionName', "Option Name");
export const OptionInclude: string = localize('Include', "Include");
export function OptionNotFoundWarningMessage(label: string) { return localize('OptionNotFoundWarningMessage', "label: {0} does not exist in the options value name lookup", label); }
export const OptionInclude: string = localize('include', "Include");
export function OptionNotFoundWarningMessage(label: string) { return localize('optionNotFoundWarningMessage', "label: {0} does not exist in the options value name lookup", label); }
// Deploy
export const SqlServerName = 'SQL server';

View File

@@ -325,13 +325,6 @@ export async function defaultAzureAccountServiceFactory(): Promise<vscodeMssql.I
export async function getDefaultPublishDeploymentOptions(project: ISqlProject): Promise<mssql.DeploymentOptions | vscodeMssql.DeploymentOptions> {
const schemaCompareService = await getSchemaCompareService();
const result = await schemaCompareService.schemaCompareGetDefaultOptions();
// re-include database-scoped credentials
if (getAzdataApi()) {
result.defaultDeploymentOptions.excludeObjectTypes.value = (result.defaultDeploymentOptions as mssql.DeploymentOptions).excludeObjectTypes.value?.filter(x => x !== mssql.SchemaObjectType.DatabaseScopedCredentials);
} else {
result.defaultDeploymentOptions.excludeObjectTypes.value = (result.defaultDeploymentOptions as vscodeMssql.DeploymentOptions).excludeObjectTypes.value?.filter(x => x !== vscodeMssql.SchemaObjectType.DatabaseScopedCredentials);
}
// this option needs to be true for same database references validation to work
if (project.databaseReferences.length > 0) {
result.defaultDeploymentOptions.booleanOptionsDictionary.includeCompositeObjects.value = true;

View File

@@ -145,7 +145,6 @@ export class PublishDatabaseDialog {
this.connectionRow = this.createConnectionRow(view);
this.databaseRow = this.createDatabaseRow(view);
const displayOptionsButton = this.createOptionsButton(view);
displayOptionsButton.enabled = false;
const horizontalFormSection = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
horizontalFormSection.addItems([profileRow, this.databaseRow]);
@@ -172,12 +171,10 @@ export class PublishDatabaseDialog {
title: constants.selectConnectionRadioButtonsTitle,
component: selectConnectionRadioButtons
},*/
/* TODO : Disabling deployment options for the July release
{
component: displayOptionsButton,
title: ''
}
*/
]
}
], {
@@ -938,7 +935,12 @@ export class PublishDatabaseDialog {
* Gets the default deployment options from the dacfx service
*/
public async getDefaultDeploymentOptions(): Promise<DeploymentOptions> {
return await utils.getDefaultPublishDeploymentOptions(this.project) as DeploymentOptions;
const defaultDeploymentOptions = await utils.getDefaultPublishDeploymentOptions(this.project) as DeploymentOptions;
if (defaultDeploymentOptions && defaultDeploymentOptions.excludeObjectTypes !== undefined) {
// For publish dialog no default exclude options should exists
defaultDeploymentOptions.excludeObjectTypes.value = [];
}
return defaultDeploymentOptions;
}
/*

View File

@@ -24,6 +24,9 @@ export class PublishOptionsDialog {
private optionsFlexBuilder: azdataType.FlexContainer | undefined;
private optionsChanged: boolean = false;
private isResetOptionsClicked: boolean = false;
private excludeObjectTypesOptionsTab: azdataType.window.DialogTab | undefined;
private excludeObjectTypesOptionsTable: azdataType.TableComponent | undefined;
private excludeObjectTypesOptionsFlexBuilder: azdataType.FlexContainer | undefined;
constructor(defaultOptions: mssql.DeploymentOptions, private publish: PublishDatabaseDialog) {
this.optionsModel = new DeployOptionsModel(defaultOptions);
@@ -31,8 +34,10 @@ export class PublishOptionsDialog {
protected initializeDialog(): void {
this.optionsTab = utils.getAzdataApi()!.window.createTab(constants.PublishOptions);
this.intializeDeploymentOptionsDialogTab();
this.dialog.content = [this.optionsTab];
this.excludeObjectTypesOptionsTab = utils.getAzdataApi()!.window.createTab(constants.ExcludeObjectTypeTab);
this.initializeDeploymentOptionsDialogTab();
this.initializeExcludeObjectTypesOptionsDialogTab();
this.dialog.content = [this.optionsTab, this.excludeObjectTypesOptionsTab];
}
public openDialog(): void {
@@ -55,7 +60,7 @@ export class PublishOptionsDialog {
utils.getAzdataApi()!.window.openDialog(this.dialog);
}
private intializeDeploymentOptionsDialogTab(): void {
private initializeDeploymentOptionsDialogTab(): void {
this.optionsTab?.registerContent(async view => {
this.descriptionHeading = view.modelBuilder.table().withProps({
data: [],
@@ -113,6 +118,34 @@ export class PublishOptionsDialog {
});
}
private initializeExcludeObjectTypesOptionsDialogTab(): void {
this.excludeObjectTypesOptionsTab?.registerContent(async view => {
this.excludeObjectTypesOptionsTable = view.modelBuilder.table().component();
await this.updateExcludeObjectsTable();
// Update exclude type options value on checkbox onchange
this.disposableListeners.push(this.excludeObjectTypesOptionsTable!.onCellAction!((rowState) => {
const checkboxState = <azdataType.ICheckboxCellActionEventArgs>rowState;
if (checkboxState && checkboxState.row !== undefined) {
// data[row][1] contains the exclude type option display name
const displayName = this.excludeObjectTypesOptionsTable?.data[checkboxState.row][1];
this.optionsModel.setExcludeObjectTypesOptionValue(displayName, checkboxState.checked);
this.optionsChanged = true;
// customButton[0] is the reset button, enabling it when option checkbox is changed
this.dialog.customButtons[0].enabled = true;
}
}));
this.excludeObjectTypesOptionsFlexBuilder = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'column'
}).component();
this.excludeObjectTypesOptionsFlexBuilder.addItem(this.excludeObjectTypesOptionsTable, { CSSStyles: { 'overflow': 'scroll', 'height': '80vh', 'padding-top': '2px' } });
await view.initializeModel(this.excludeObjectTypesOptionsFlexBuilder);
});
}
/*
* Update the default options to the options table area
*/
@@ -141,12 +174,41 @@ export class PublishOptionsDialog {
});
}
/*
* Update the default options to the exclude objects table area
*/
private async updateExcludeObjectsTable(): Promise<void> {
const data = this.optionsModel.getExcludeObjectTypesOptionsData();
await this.excludeObjectTypesOptionsTable?.updateProperties({
data: data,
columns: [
<azdataType.CheckboxColumn>
{
value: constants.OptionInclude,
type: utils.getAzdataApi()!.ColumnType.checkBox,
action: utils.getAzdataApi()!.ActionOnCellCheckboxCheck.customAction,
headerCssClass: 'display-none',
cssClass: 'no-borders align-with-header',
width: 50
},
{
value: constants.OptionName,
headerCssClass: 'display-none',
cssClass: 'no-borders align-with-header',
width: 50
}
],
ariaRowCount: data.length
});
}
/*
* Ok button click, will update the deployment options with selections
*/
protected execute(): void {
// Update the model deploymentoptions with the updated table component values
// Update the model deploymentoptions with the updated options/excludeObjects table component values
this.optionsModel.setDeploymentOptions();
this.optionsModel.setExcludeObjectTypesToDeploymentOptions();
// Set the publish deploymentoptions with the updated table component values
this.publish.setDeploymentOptions(this.optionsModel.deploymentOptions);
this.disposeListeners();
@@ -173,14 +235,19 @@ export class PublishOptionsDialog {
const result = await this.publish.getDefaultDeploymentOptions();
this.optionsModel.deploymentOptions = result;
// reset optionsvalueNameLookup with default deployment options
// reset optionsvalueNameLookup and excludeObjectTypesLookup with default deployment options
this.optionsModel.setOptionsToValueNameLookup();
this.optionsModel.setExcludeObjectTypesLookup();
await this.updateOptionsTable();
this.optionsFlexBuilder?.removeItem(this.optionsTable!);
this.optionsFlexBuilder?.insertItem(this.optionsTable!, 0, { CSSStyles: { 'overflow': 'scroll', 'height': '65vh', 'padding-top': '2px' } });
TelemetryReporter.sendActionEvent(TelemetryViews.PublishOptionsDialog, TelemetryActions.resetOptions);
await this.updateExcludeObjectsTable();
this.excludeObjectTypesOptionsFlexBuilder?.removeItem(this.excludeObjectTypesOptionsTable!);
this.excludeObjectTypesOptionsFlexBuilder?.addItem(this.excludeObjectTypesOptionsTable!, { CSSStyles: { 'overflow': 'scroll', 'height': '80vh', 'padding-top': '2px' } });
// setting optionsChanged to false when reset click, if optionsChanged is true during execute, that means there is an option changed after reset
this.isResetOptionsClicked = true;
this.optionsChanged = false;

View File

@@ -10,9 +10,11 @@ import * as constants from '../../common/constants';
export class DeployOptionsModel {
// key is the option display name and values are checkboxValue and optionName
private optionsValueNameLookup: { [key: string]: mssql.IOptionWithValue } = {};
private excludeObjectTypesLookup: { [key: string]: mssql.IOptionWithValue } = {};
constructor(public deploymentOptions: mssql.DeploymentOptions) {
this.setOptionsToValueNameLookup();
this.setExcludeObjectTypesLookup();
}
/*
@@ -69,4 +71,62 @@ export class DeployOptionsModel {
}
return optionName !== undefined ? this.deploymentOptions.booleanOptionsDictionary[optionName.optionName].description : '';
}
/*
* Sets exclude object types option's checkbox values and property name to the excludeObjectTypesLookup map
*/
public setExcludeObjectTypesLookup(): void {
Object.entries(this.deploymentOptions.objectTypesDictionary).forEach(option => {
const optionValue: mssql.IOptionWithValue = {
optionName: option[0],
checked: this.getExcludeObjectTypeOptionCheckStatus(option[0])
};
this.excludeObjectTypesLookup[option[1]] = optionValue;
});
}
/*
* Initialize options data from objectTypesDictionary for table component
* Returns data as [booleanValue, optionName]
*/
public getExcludeObjectTypesOptionsData(): any[][] {
let data: any[][] = [];
Object.entries(this.deploymentOptions.objectTypesDictionary).forEach(option => {
// option[1] is the display name and option[0] is the optionName
data.push([this.getExcludeObjectTypeOptionCheckStatus(option[0]), option[1]]);
});
return data.sort((a, b) => a[1].localeCompare(b[1]));
}
/*
* Gets the selected/default value of the object type option
* return true for the deploymentOptions.excludeObjectTypes option, if it is in ObjectTypesDictionary
*/
public getExcludeObjectTypeOptionCheckStatus(optionName: string): boolean {
return (this.deploymentOptions.excludeObjectTypes.value?.find(x => x.toLowerCase() === optionName.toLowerCase())) !== undefined ? true : false;
}
/*
* Sets the checkbox value to the excludeObjectTypesLookup map
*/
public setExcludeObjectTypesOptionValue(displayName: string, checked: boolean): void {
this.excludeObjectTypesLookup[displayName].checked = checked;
}
/*
* Sets the selected option checkbox value to the deployment options
*/
public setExcludeObjectTypesToDeploymentOptions(): void {
let finalExcludedObjectTypes: string[] = [];
Object.entries(this.excludeObjectTypesLookup).forEach(option => {
// option[1] holds checkedbox value and optionName
if (option[1].checked) {
finalExcludedObjectTypes.push(option[1].optionName);
}
});
this.deploymentOptions.excludeObjectTypes.value = finalExcludedObjectTypes;
}
}

View File

@@ -19,7 +19,7 @@ describe('Publish Dialog Deploy Options Model', () => {
const model = new DeployOptionsModel(testUtils.getDeploymentOptions());
Object.entries(model.deploymentOptions.booleanOptionsDictionary).forEach(option => {
// option[1] contains the value, description and displayName
should(model.getOptionDescription(option[1].displayName)).not.equal(undefined);
should(model.getOptionDescription(option[1].displayName)).not.equal(undefined, 'publish option description should not be undefined');
});
});
@@ -27,4 +27,31 @@ describe('Publish Dialog Deploy Options Model', () => {
const model = new DeployOptionsModel(testUtils.getDeploymentOptions());
should(model.getOptionDescription('')).equal('');
});
it('Should have no default exclude object types', function (): void {
const model = new DeployOptionsModel(testUtils.getDeploymentOptions());
should(model.deploymentOptions.excludeObjectTypes.value.length).be.equal(0, 'There should be no object types excluded from excludeObjectTypes');
// should return true for all object type options as there are no default excludeObjectTypes in the deployment options
Object.keys(model.deploymentOptions.objectTypesDictionary).forEach(option => {
should(model.getExcludeObjectTypeOptionCheckStatus(option)).equal(false, 'excludeObjectTypes property should be empty by default and return false');
});
});
it('Should have atleast one default exclude object types', function (): void {
const model = new DeployOptionsModel(testUtils.getDeploymentOptions());
model.deploymentOptions.excludeObjectTypes.value = ['SampleProperty1'];
should(model.deploymentOptions.excludeObjectTypes.value.length).be.equal(1, 'There should be one excluded object');
// should return true for all exclude object types options and false for the exising defauit option
Object.keys(model.deploymentOptions.objectTypesDictionary).forEach(option => {
if (option === 'SampleProperty1') {
should(model.getExcludeObjectTypeOptionCheckStatus(option)).equal(true, 'should return true for the excludeObjectTypes SampleProperty1 ');
} else {
should(model.getExcludeObjectTypeOptionCheckStatus(option)).equal(false, 'should return false for all excludeObjectTypes property as it is empty');
}
});
});
});

View File

@@ -31,6 +31,10 @@ export function getDeploymentOptions(): mssql.DeploymentOptions {
booleanOptionsDictionary: {
'SampleProperty1': { value: false, description: sampleDesc, displayName: sampleName },
'SampleProperty2': { value: false, description: sampleDesc, displayName: sampleName }
},
objectTypesDictionary: {
'SampleProperty1': sampleName,
'SampleProperty2': sampleName
}
};
return defaultOptions;