[Port] Improved behavior for accepting EULA. (#12453) (#12749)

* Improved behavior for accepting EULA. (#12453)

* working version of overloading "select" button

* promptForEula to use showErrorMessage

* make parameter optional in promptForEula

* remove test code

* PR feedback

* eula to EULA

* minor fix

* Fix compile error

Co-authored-by: Arvind Ranasaria <ranasaria@outlook.com>
This commit is contained in:
Charles Gagnon
2020-10-05 18:52:16 -07:00
committed by GitHub
parent 4c6b606c82
commit 867faae14f
8 changed files with 81 additions and 34 deletions

View File

@@ -374,7 +374,8 @@ export interface ITool {
finishInitialization(): Promise<void>;
install(): Promise<void>;
isSameOrNewerThan(version: string): boolean;
validateEula(): boolean;
isEulaAccepted(): boolean;
promptForEula(): Promise<boolean>;
}
export const enum BdcDeploymentType {

View File

@@ -15,7 +15,7 @@ export const subscriptionDescription = localize('azure.account.subscriptionDescr
export const resourceGroup = localize('azure.account.resourceGroup', "Resource Group");
export const location = localize('azure.account.location', "Azure Location");
export const browse = localize('filePicker.browse', "Browse");
export const select = localize('filePicker.select', "Select");
export const select = localize('button.label', "Select");
export const kubeConfigFilePath = localize('kubeConfigClusterPicker.kubeConfigFilePath', "Kube config file path");
export const clusterContextNotFound = localize('kubeConfigClusterPicker.clusterContextNotFound', "No cluster context information found");
export const signIn = localize('azure.signin', "Sign in…");
@@ -35,4 +35,6 @@ export const optionsNotDefined = (fieldType: FieldType) => localize('optionsNotD
export const optionsNotObjectOrArray = localize('optionsNotObjectOrArray', "FieldInfo.options must be an object if it is not an array");
export const optionsTypeNotFound = localize('optionsTypeNotFound', "When FieldInfo.options is an object it must have 'optionsType' property");
export const optionsTypeRadioOrDropdown = localize('optionsTypeRadioOrDropdown', "When optionsType is not {0} then it must be {1}", OptionsType.Radio, OptionsType.Dropdown);
export const azdataEulaNotAccepted = localize('azdataEulaNotAccepted', "Deployment cannot continue. Azure Data CLI license terms have not been accepted. Execute the command: [Azure Data CLI: Accept EULA] to accept EULA to enable the features that requires Azure Data CLI.");
export const azdataEulaNotAccepted = localize('azdataEulaNotAccepted', "Deployment cannot continue. Azure Data CLI license terms have not yet been accepted. Please accept the EULA to enable the features that requires Azure Data CLI.");
export const azdataEulaDeclined = localize('azdataEulaDeclined', "Deployment cannot continue. Azure Data CLI license terms were declined.You can either Accept EULA to continue or Cancel this operation");
export const acceptEulaAndSelect = localize('deploymentDialog.RecheckEulaButton', "Accept EULA & Select");

View File

@@ -45,7 +45,7 @@ export class AzdataTool extends ToolBase {
return 'https://docs.microsoft.com/sql/big-data-cluster/deploy-install-azdata';
}
public validateEula(): boolean {
public isEulaAccepted(): boolean {
if (apiService.azdataApi.isEulaAccepted()) {
return true;
} else {
@@ -54,6 +54,14 @@ export class AzdataTool extends ToolBase {
}
}
public async promptForEula(): Promise<boolean> {
const eulaAccepted = await apiService.azdataApi.promptForEula();
if (!eulaAccepted) {
this.setStatusDescription(loc.azdataEulaDeclined);
}
return eulaAccepted;
}
/* unused */
protected get versionCommand(): Command {
return {

View File

@@ -58,7 +58,9 @@ export abstract class ToolBase implements ITool {
protected abstract readonly versionCommand: Command;
public validateEula(): boolean { return true; }
public isEulaAccepted(): boolean { return true; }
public promptForEula(): Promise<boolean> { return Promise.resolve(true); }
public get dependencyMessages(): string[] {
return (this.dependenciesByOsType.get(this.osDistribution) || []).map((msgType: dependencyType) => messageByDependencyType.get(msgType)!);

View File

@@ -9,6 +9,7 @@ import { AgreementInfo, DeploymentProvider, ITool, ResourceType, ToolStatus } fr
import { IResourceTypeService } from '../services/resourceTypeService';
import { IToolsService } from '../services/toolsService';
import { getErrorMessage } from '../utils';
import * as loc from './../localizedConstants';
import { DialogBase } from './dialogBase';
import { createFlexContainer } from './modelViewUtils';
@@ -26,9 +27,9 @@ export class ResourceTypePickerDialog extends DialogBase {
private _agreementContainer!: azdata.DivContainer;
private _agreementCheckboxChecked: boolean = false;
private _installToolButton: azdata.window.Button;
private _recheckEulaButton: azdata.window.Button;
private _installationInProgress: boolean = false;
private _tools: ITool[] = [];
private _eulaValidationSucceeded: boolean = false;
constructor(
private toolsService: IToolsService,
@@ -38,32 +39,30 @@ export class ResourceTypePickerDialog extends DialogBase {
super(localize('resourceTypePickerDialog.title', "Select the deployment options"), 'ResourceTypePickerDialog', true);
this._selectedResourceType = defaultResourceType;
this._installToolButton = azdata.window.createButton(localize('deploymentDialog.InstallToolsButton', "Install tools"));
this._recheckEulaButton = azdata.window.createButton(localize('deploymentDialog.RecheckEulaButton', "Validate EULA"));
this._recheckEulaButton.hidden = true;
this._toDispose.push(this._installToolButton.onClick(() => {
this.installTools().catch(error => console.log(error));
}));
this._toDispose.push(this._recheckEulaButton.onClick(() => {
this._dialogObject.message = { text: '' }; // clear any previous message.
this._dialogObject.okButton.enabled = this.validateToolsEula(); // re-enable the okButton if validation succeeds.
}));
this._dialogObject.customButtons = [this._installToolButton, this._recheckEulaButton];
this._dialogObject.customButtons = [this._installToolButton];
this._installToolButton.hidden = true;
this._dialogObject.okButton.label = localize('deploymentDialog.OKButtonText', "Select");
this._dialogObject.okButton.label = loc.select;
this._dialogObject.okButton.enabled = false; // this is enabled after all tools are discovered.
}
initialize() {
let tab = azdata.window.createTab('');
this._dialogObject.registerCloseValidator(() => {
this._dialogObject.registerCloseValidator(async () => {
const isValid = this._selectedResourceType && (this._selectedResourceType.agreement === undefined || this._agreementCheckboxChecked);
if (!isValid) {
this._dialogObject.message = {
text: localize('deploymentDialog.AcceptAgreements', "You must agree to the license agreements in order to proceed."),
level: azdata.window.MessageLevel.Error
};
return false;
}
return isValid;
if (!this._eulaValidationSucceeded && !(await this.acquireEulaAndProceed())) {
return false; // we return false so that the workflow does not proceed and user gets to either click acceptEulaAndSelect again or cancel
}
return true;
});
tab.registerContent((view: azdata.ModelView) => {
const tableWidth = 1126;
@@ -103,8 +102,8 @@ export class ResourceTypePickerDialog extends DialogBase {
iconPosition: 'left'
}).component();
this._toDispose.push(this._cardGroup.onSelectionChanged(({ cardId }) => {
this._recheckEulaButton.hidden = true;
this._dialogObject.okButton.enabled = true;
this._dialogObject.message = { text: '' };
this._dialogObject.okButton.label = loc.select;
const resourceType = resourceTypes.find(rt => { return rt.name === cardId; });
if (resourceType) {
this.selectResourceType(resourceType);
@@ -114,7 +113,7 @@ export class ResourceTypePickerDialog extends DialogBase {
this._agreementContainer = view.modelBuilder.divContainer().component();
const toolColumn: azdata.TableColumn = {
value: localize('deploymentDialog.toolNameColumnHeader', "Tool"),
width: 80
width: 105
};
const descriptionColumn: azdata.TableColumn = {
value: localize('deploymentDialog.toolDescriptionColumnHeader', "Description"),
@@ -130,7 +129,7 @@ export class ResourceTypePickerDialog extends DialogBase {
};
const minVersionColumn: azdata.TableColumn = {
value: localize('deploymentDialog.toolMinimumVersionColumnHeader', "Required Version"),
width: 95
width: 105
};
const installedPathColumn: azdata.TableColumn = {
value: localize('deploymentDialog.toolDiscoveredPathColumnHeader', "Discovered Path or Additional Information"),
@@ -288,7 +287,7 @@ export class ResourceTypePickerDialog extends DialogBase {
return [tool.displayName, tool.description, tool.displayStatus, tool.fullVersion || '', toolRequirement.version || '', tool.installationPathOrAdditionalInformation || ''];
});
this._installToolButton.hidden = erroredOrFailedTool || minVersionCheckFailed || (toolsToAutoInstall.length === 0);
this._dialogObject.okButton.enabled = !erroredOrFailedTool && messages.length === 0 && !minVersionCheckFailed && (toolsToAutoInstall.length === 0) && this.validateToolsEula();
this._dialogObject.okButton.enabled = !erroredOrFailedTool && messages.length === 0 && !minVersionCheckFailed && (toolsToAutoInstall.length === 0);
if (messages.length !== 0) {
if (messages.length > 1) {
messages = messages.map(message => `${message}`);
@@ -320,22 +319,41 @@ export class ResourceTypePickerDialog extends DialogBase {
text: infoText.join(EOL)
};
}
if (!this.areToolsEulaAccepted()) {
this._dialogObject.okButton.label = loc.acceptEulaAndSelect;
}
this._toolsLoadingComponent.loading = false;
}
private validateToolsEula(): boolean {
const validationSucceeded = this._tools.every(tool => {
const eulaValidated = tool.validateEula();
if (!eulaValidated) {
private areToolsEulaAccepted(): boolean {
// we run 'map' on each tool before doing 'every' so that we collect eula messages for all tools (instead of bailing out after 1st failure)
this._eulaValidationSucceeded = this._tools.map(tool => {
const eulaAccepted = tool.isEulaAccepted();
if (!eulaAccepted) {
this._dialogObject.message = {
level: azdata.window.MessageLevel.Error,
text: tool.statusDescription!
text: [tool.statusDescription!, this._dialogObject.message.text].join(EOL)
};
}
return eulaValidated;
});
this._recheckEulaButton.hidden = validationSucceeded;
return validationSucceeded;
return eulaAccepted;
}).every(isEulaAccepted => isEulaAccepted);
return this._eulaValidationSucceeded;
}
private async acquireEulaAndProceed(): Promise<boolean> {
this._dialogObject.message = { text: '' };
let eulaAccepted = true;
for (const tool of this._tools) {
eulaAccepted = tool.isEulaAccepted() || await tool.promptForEula();
if (!eulaAccepted) {
this._dialogObject.message = {
level: azdata.window.MessageLevel.Error,
text: [tool.statusDescription!, this._dialogObject.message.text].join(EOL)
};
break;
}
}
return eulaAccepted;
}
private get toolRequirements() {