From 1944813c4a4b3159869f7d4da386d38f01459f94 Mon Sep 17 00:00:00 2001 From: Aasim Khan Date: Thu, 4 Feb 2021 23:45:29 -0800 Subject: [PATCH] Adding help text for resourceTypes (#14166) * Adding help text to arc resource deployment. * Fixing null help text and agreement logic * Made some types optional Fixed a language specific link --- extensions/arc/package.json | 10 ++++++ extensions/arc/package.nls.json | 4 ++- extensions/resource-deployment/package.json | 12 +++++++ .../resource-deployment/package.nls.json | 6 ++-- .../resource-deployment/src/interfaces.ts | 14 ++++++-- .../src/services/resourceTypeService.ts | 17 +++++++++- .../src/ui/toolsAndEulaSettingsPage.ts | 32 +++++++++++++++---- 7 files changed, 81 insertions(+), 14 deletions(-) diff --git a/extensions/arc/package.json b/extensions/arc/package.json index c9a8b03d43..8b57a79844 100644 --- a/extensions/arc/package.json +++ b/extensions/arc/package.json @@ -1100,6 +1100,16 @@ } ], "when": "mi-type=arc-mi" + }, + "helpText": { + "template": "%arc.agreement.sql.help.text%", + "links": [ + { + "text": "%arc.agreement.sql.help.text.learn.more%", + "url": "https://go.microsoft.com/fwlink/?linkid=2141849" + } + ], + "when": "mi-type=arc-mi" } } ] diff --git a/extensions/arc/package.nls.json b/extensions/arc/package.nls.json index 6347571710..f39facdb66 100644 --- a/extensions/arc/package.nls.json +++ b/extensions/arc/package.nls.json @@ -153,5 +153,7 @@ "requested.cores.less.than.or.equal.to.cores.limit": "Requested cores must be less than or equal to cores limit", "cores.limit.greater.than.or.equal.to.requested.cores": "Cores limit must be greater than or equal to requested cores", "requested.memory.less.than.or.equal.to.memory.limit": "Requested memory must be less than or equal to memory limit", - "memory.limit.greater.than.or.equal.to.requested.memory": "Memory limit must be greater than or equal to requested memory" + "memory.limit.greater.than.or.equal.to.requested.memory": "Memory limit must be greater than or equal to requested memory", + "arc.agreement.sql.help.text": "Azure Arc enabled Managed Instance provides SQL Server access and feature compatibility that can be deployed on the infrastructure of your choice. While this service is in preview, it has some feature limitations compared to SQL Managed Instance on Azure. {0}", + "arc.agreement.sql.help.text.learn.more": "Learn more" } diff --git a/extensions/resource-deployment/package.json b/extensions/resource-deployment/package.json index f05501a354..e192cb7e66 100644 --- a/extensions/resource-deployment/package.json +++ b/extensions/resource-deployment/package.json @@ -587,6 +587,18 @@ ], "when": "mi-type=azure-sql-mi" } + ], + "helpTexts": [ + { + "template": "%azure-sql-mi-help-text%", + "links": [ + { + "text": "%azure-sql-mi-help-text-learn-more%", + "url": "https://docs.microsoft.com/azure/azure-sql/managed-instance/sql-managed-instance-paas-overview" + } + ], + "when": "mi-type=azure-sql-mi" + } ] } ] diff --git a/extensions/resource-deployment/package.nls.json b/extensions/resource-deployment/package.nls.json index 2ea7498698..3d0cb5b494 100644 --- a/extensions/resource-deployment/package.nls.json +++ b/extensions/resource-deployment/package.nls.json @@ -82,9 +82,11 @@ "azure-sqldb-agreement-sqldb-eula": "Azure SQL DB License Terms", "azure-sqldb-agreement-azdata-eula": "azdata License Terms", "azure-sql-mi-display-name": "Azure SQL managed instance", - "azure-sql-mi-display-description": "Create a SQL Managed Instance. Best for most migrations to the cloud.", + "azure-sql-mi-display-description": "Create a SQL Managed Instance in either Azure or a customer-managed environment", "azure-sql-mi-okButton-text": "Open in Portal", "azure-sql-mi-resource-type-option-label": "Resource Type", "azure-sql-mi-agreement": "I accept {0} and {1}.", - "azure-sql-mi-agreement-eula": "Azure SQL MI License Terms" + "azure-sql-mi-agreement-eula": "Azure SQL MI License Terms", + "azure-sql-mi-help-text": "Azure SQL Managed Instance provides full SQL Server access and feature compatibility for migrating SQL Servers to Azure, or developing new applications. {0}.", + "azure-sql-mi-help-text-learn-more" : "Learn More" } diff --git a/extensions/resource-deployment/src/interfaces.ts b/extensions/resource-deployment/src/interfaces.ts index 2b86c2114d..71d63c5b12 100644 --- a/extensions/resource-deployment/src/interfaces.ts +++ b/extensions/resource-deployment/src/interfaces.ts @@ -21,10 +21,11 @@ export interface ResourceType { agreements?: AgreementInfo[]; displayIndex?: number; okButtonText?: OkButtonTextValue[]; + helpTexts: HelpText[]; getOkButtonText(selectedOptions: { option: string, value: string }[]): string | undefined; getProvider(selectedOptions: { option: string, value: string }[]): DeploymentProvider | undefined; getAgreementInfo(selectedOptions: { option: string, value: string }[]): AgreementInfo | undefined; - getHelpText(selectedOption: { option: string, value: string }[]): string | undefined; + getHelpText(selectedOption: { option: string, value: string }[]): HelpText | undefined; tags?: string[]; } @@ -41,12 +42,19 @@ export interface ResourceSubType { provider: DeploymentProvider; okButtonText?: OkButtonTextValue; agreement?: AgreementInfo; + helpText?: HelpText; +} + +export interface HelpText { + template: string; + links?: azdata.LinkArea[]; + when?: string; } export interface AgreementInfo { template: string; - links: azdata.LinkArea[]; - when: string; + links?: azdata.LinkArea[]; + when?: string; } export interface ResourceTypeOption { diff --git a/extensions/resource-deployment/src/services/resourceTypeService.ts b/extensions/resource-deployment/src/services/resourceTypeService.ts index 8a91975c17..f2f473dac4 100644 --- a/extensions/resource-deployment/src/services/resourceTypeService.ts +++ b/extensions/resource-deployment/src/services/resourceTypeService.ts @@ -9,7 +9,7 @@ import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import { DeploymentProvider, instanceOfAzureSQLVMDeploymentProvider, instanceOfAzureSQLDBDeploymentProvider, instanceOfCommandDeploymentProvider, instanceOfDialogDeploymentProvider, instanceOfDownloadDeploymentProvider, instanceOfNotebookBasedDialogInfo, instanceOfNotebookDeploymentProvider, instanceOfNotebookWizardDeploymentProvider, instanceOfWebPageDeploymentProvider, instanceOfWizardDeploymentProvider, NotebookInfo, NotebookPathInfo, ResourceType, ResourceTypeOption, ResourceSubType, AgreementInfo } from '../interfaces'; +import { DeploymentProvider, instanceOfAzureSQLVMDeploymentProvider, instanceOfAzureSQLDBDeploymentProvider, instanceOfCommandDeploymentProvider, instanceOfDialogDeploymentProvider, instanceOfDownloadDeploymentProvider, instanceOfNotebookBasedDialogInfo, instanceOfNotebookDeploymentProvider, instanceOfNotebookWizardDeploymentProvider, instanceOfWebPageDeploymentProvider, instanceOfWizardDeploymentProvider, NotebookInfo, NotebookPathInfo, ResourceType, ResourceTypeOption, ResourceSubType, AgreementInfo, HelpText } from '../interfaces'; import { AzdataService } from './azdataService'; import { KubeService } from './kubeService'; import { INotebookService } from './notebookService'; @@ -56,6 +56,7 @@ export class ResourceTypeService implements IResourceTypeService { resourceType.getProvider = (selectedOptions) => { return this.getProvider(resourceType, selectedOptions); }; resourceType.getOkButtonText = (selectedOptions) => { return this.getOkButtonText(resourceType, selectedOptions); }; resourceType.getAgreementInfo = (selectedOptions) => { return this.getAgreementInfo(resourceType, selectedOptions); }; + resourceType.getHelpText = (selectedOptions) => { return this.getHelpText(resourceType, selectedOptions); }; this.getResourceSubTypes(resourceType); this._resourceTypes.push(resourceType); }); @@ -153,6 +154,9 @@ export class ResourceTypeService implements IResourceTypeService { if (resourceSubType.agreement) { resourceType.agreements?.push(resourceSubType.agreement!); } + if (resourceSubType.helpText) { + resourceType.helpTexts.push(resourceSubType.helpText); + } } }); }); @@ -307,6 +311,17 @@ export class ResourceTypeService implements IResourceTypeService { return undefined; } + private getHelpText(resourceType: ResourceType, selectedOptions: { option: string, value: string }[]): HelpText | undefined { + if (resourceType.helpTexts) { + for (const possibleOption of resourceType.helpTexts) { + if (processWhenClause(possibleOption.when, selectedOptions)) { + return possibleOption; + } + } + } + return undefined; + } + public startDeployment(resourceType: ResourceType, optionValuesFilter?: OptionValuesFilter): void { const wizard = new ResourceTypeWizard(resourceType, new KubeService(), new AzdataService(this.platformService), this.notebookService, this.toolsService, this.platformService, this, optionValuesFilter); wizard.open(); diff --git a/extensions/resource-deployment/src/ui/toolsAndEulaSettingsPage.ts b/extensions/resource-deployment/src/ui/toolsAndEulaSettingsPage.ts index 5622c3e31d..244e714e05 100644 --- a/extensions/resource-deployment/src/ui/toolsAndEulaSettingsPage.ts +++ b/extensions/resource-deployment/src/ui/toolsAndEulaSettingsPage.ts @@ -6,7 +6,7 @@ import * as azdata from 'azdata'; import { EOL } from 'os'; import * as nls from 'vscode-nls'; -import { AgreementInfo, DeploymentProvider, ITool, ResourceType, ResourceTypeOptionValue, ToolRequirementInfo, ToolStatus } from '../interfaces'; +import { AgreementInfo, DeploymentProvider, HelpText, ITool, ResourceType, ResourceTypeOptionValue, ToolRequirementInfo, ToolStatus } from '../interfaces'; import { createFlexContainer } from './modelViewUtils'; import * as loc from '../localizedConstants'; import { IToolsService } from '../services/toolsService'; @@ -26,6 +26,7 @@ export class ToolsAndEulaPage extends ResourceTypePage { private _optionDropDownMap: Map = new Map(); private _toolsLoadingComponent!: azdata.LoadingComponent; private _agreementContainer!: azdata.DivContainer; + private _helpTextContainer!: azdata.DivContainer; private _agreementCheckBox!: azdata.CheckBoxComponent; private _installToolButton!: azdata.ButtonComponent; private _installationInProgress: boolean = false; @@ -94,6 +95,7 @@ export class ToolsAndEulaPage extends ResourceTypePage { const tableWidth = 1060; this._optionsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); this._agreementContainer = view.modelBuilder.divContainer().component(); + this._helpTextContainer = view.modelBuilder.divContainer().component(); const toolColumn: azdata.TableColumn = { value: loc.toolText, width: 105 @@ -149,6 +151,8 @@ export class ToolsAndEulaPage extends ResourceTypePage { this.form = view.modelBuilder.formContainer().withFormItems( [ { + component: this._helpTextContainer, + }, { component: this._optionsContainer, }, { component: this._agreementContainer, @@ -234,9 +238,16 @@ export class ToolsAndEulaPage extends ResourceTypePage { }); } - if (this._resourceType.agreements) { - this._agreementContainer.addItem(this.createAgreementCheckbox()); + const agreementInfo = this._resourceType.getAgreementInfo(this.getSelectedOptions()); + if (agreementInfo) { + this._agreementContainer.addItem(this.createAgreementCheckbox(agreementInfo)); } + + const helpText = this._resourceType.getHelpText(this.getSelectedOptions()); + if (helpText) { + this._helpTextContainer.addItem(this.createHelpText(helpText)); + } + this.updateOkButtonText(); this.updateToolsDisplayTable(); }); @@ -245,8 +256,7 @@ export class ToolsAndEulaPage extends ResourceTypePage { } - private createAgreementCheckbox(): azdata.FlexContainer { - const agreementInfo = this._resourceType.getAgreementInfo(this.getSelectedOptions())!; + private createAgreementCheckbox(agreementInfo: AgreementInfo): azdata.FlexContainer { this._agreementCheckBox = this.view.modelBuilder.checkBox().withProperties({ ariaLabel: this.getAgreementDisplayText(agreementInfo), required: true @@ -259,12 +269,20 @@ export class ToolsAndEulaPage extends ResourceTypePage { return createFlexContainer(this.view, [this._agreementCheckBox, text]); } + private createHelpText(helpText: HelpText): azdata.FlexContainer { + const helpTextComponent = this.view.modelBuilder.text().withProps({ + value: helpText.template, + links: helpText.links, + }).component(); + return createFlexContainer(this.view, [helpTextComponent]); + } + private getAgreementDisplayText(agreementInfo: AgreementInfo): string { // the agreement template will have {index} as placeholder for hyperlinks // this method will get the display text after replacing the placeholders let text = agreementInfo.template; - for (let i: number = 0; i < agreementInfo.links.length; i++) { - text = text.replace(`{${i}}`, agreementInfo.links[i].text); + for (let i: number = 0; i < agreementInfo.links!.length; i++) { + text = text.replace(`{${i}}`, agreementInfo.links![i].text); } return text; }