From f6949d834b170b45655c16cd695bb33084d8f5fb Mon Sep 17 00:00:00 2001 From: Alex Ma Date: Fri, 16 Oct 2020 09:38:07 -0700 Subject: [PATCH] When Clause processing added to getokbutton and getprovider (#12886) * processwhenclause added * processWhenClause explanation added * changed comment to be more generic. * changed expected comparison * test and space fix added * fixed tests * resourceTypeService now uses forloop --- .../src/services/resourceTypeService.ts | 51 ++++++++++--------- .../src/test/resourceTypeService.test.ts | 45 +++++++++++++++- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/extensions/resource-deployment/src/services/resourceTypeService.ts b/extensions/resource-deployment/src/services/resourceTypeService.ts index f3e7c60b3e..22e211b248 100644 --- a/extensions/resource-deployment/src/services/resourceTypeService.ts +++ b/extensions/resource-deployment/src/services/resourceTypeService.ts @@ -225,28 +225,8 @@ export class ResourceTypeService implements IResourceTypeService { private getProvider(resourceType: ResourceType, selectedOptions: { option: string, value: string }[]): DeploymentProvider | undefined { for (let i = 0; i < resourceType.providers.length; i++) { const provider = resourceType.providers[i]; - if (provider.when === undefined || provider.when.toString().toLowerCase() === 'true') { + if (processWhenClause(provider.when, selectedOptions)) { return provider; - } else { - const expected = provider.when.replace(' ', '').split('&&').sort(); - let actual: string[] = []; - selectedOptions.forEach(option => { - actual.push(`${option.option}=${option.value}`); - }); - actual = actual.sort(); - - if (actual.length === expected.length) { - let matches = true; - for (let j = 0; j < actual.length; j++) { - if (actual[j] !== expected[j]) { - matches = false; - break; - } - } - if (matches) { - return provider; - } - } } } return undefined; @@ -256,10 +236,9 @@ export class ResourceTypeService implements IResourceTypeService { * Get the ok button text based on the selected options */ private getOkButtonText(resourceType: ResourceType, selectedOptions: { option: string, value: string }[]): string | undefined { - if (resourceType.okButtonText && selectedOptions.length === 1) { - const optionGiven = `${selectedOptions[0].option}=${selectedOptions[0].value}`; + if (resourceType.okButtonText) { for (const possibleOption of resourceType.okButtonText) { - if (possibleOption.when === optionGiven || possibleOption.when === undefined || possibleOption.when.toString().toLowerCase() === 'true') { + if (processWhenClause(possibleOption.when, selectedOptions)) { return possibleOption.value; } } @@ -267,6 +246,7 @@ export class ResourceTypeService implements IResourceTypeService { return loc.select; } + public startDeployment(provider: DeploymentProvider): void { const self = this; if (instanceOfWizardDeploymentProvider(provider)) { @@ -370,3 +350,26 @@ async function exists(path: string): Promise { return false; } } + +/** + * processWhenClause takes in a when clause (either the word 'true' or a series of clauses in the format: + * '=' joined by '&&'). + * If the when clause is true or undefined, return true as there is no clause to check. + * It evaluates each individual when clause by comparing the equivalent selected options (sorted in alphabetical order and formatted to match). + * If there is any selected option that doesn't match, return false. + * Return true if all clauses match. + */ +export function processWhenClause(when: string | undefined, selectedOptions: { option: string, value: string }[]): boolean { + if (when === undefined || when.toString().toLowerCase() === 'true') { + return true; + } else { + const expected = when.replace(/\s/g, '').split('&&').sort(); + const actual = selectedOptions.map(option => `${option.option}=${option.value}`); + for (let whenClause of expected) { + if (actual.indexOf(whenClause) === -1) { + return false; + } + } + return true; + } +} diff --git a/extensions/resource-deployment/src/test/resourceTypeService.test.ts b/extensions/resource-deployment/src/test/resourceTypeService.test.ts index 38af15a48b..cd0d840387 100644 --- a/extensions/resource-deployment/src/test/resourceTypeService.test.ts +++ b/extensions/resource-deployment/src/test/resourceTypeService.test.ts @@ -6,8 +6,9 @@ import 'mocha'; import * as TypeMoq from 'typemoq'; import assert = require('assert'); +import should = require('should'); import { EOL } from 'os'; -import { ResourceTypeService } from '../services/resourceTypeService'; +import { ResourceTypeService, processWhenClause } from '../services/resourceTypeService'; import { IPlatformService } from '../services/platformService'; import { ToolsService } from '../services/toolsService'; import { NotebookService } from '../services/notebookService'; @@ -45,4 +46,46 @@ suite('Resource Type Service Tests', function (): void { const validationErrors = resourceTypeService.validateResourceTypes(allResourceTypes); assert(validationErrors.length === 0, `Validation errors detected in the package.json: ${validationErrors.join(EOL)}.`); }); + + test('Selected options containing all when clauses should return true', () => { + const whenSelectedTrue: { when: string; selectedOptions: { option: string, value: string }[] }[] = [ + { + when: 'resourceType=sql-bdc && newType=sql-windows-setup', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }, { option: 'newType', value: 'sql-windows-setup' }] + }, + { + when: 'resourceType=sql-image', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }] + }, + ]; + + whenSelectedTrue.forEach(whenOption => { + should(processWhenClause(whenOption.when, whenOption.selectedOptions)).be.true(`when clause '${whenOption.when}' should return true for it's associated selectedOptions`); + }); + }); + + test('When clause that reads "true" (ignoring case) should always return true', () => { + should(processWhenClause(undefined, [])).be.true('undefined when clause should always return true'); + should(processWhenClause('TrUe', [])).be.true(`"true" when clause should always return true`); + }); + + test('No selected options returns false', () => { + should(processWhenClause('newType=empty', [])).be.false('No selected options should return false'); + }); + + test('Unfulfilled or partially fulfilled when clauses return false', () => { + const whenSelectedFalse: { when: string; selectedOptions: { option: string, value: string }[] }[] = [ + { + when: 'resourceType=sql-bdc && dneType=does-not-exist', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }, { option: 'newType', value: 'sql-windows-setup' }] + }, + { + when: 'dneType=does-not-exist', selectedOptions: [{ option: 'resourceType', value: 'sql-image' }, { option: 'resourceType', value: 'sql-bdc' }, { option: 'newType', value: 'sql-windows-setup' }] + } + ]; + whenSelectedFalse.forEach(whenOption => { + should(processWhenClause(whenOption.when, whenOption.selectedOptions)).be.false(`when clause '${whenOption.when}' should return false for it's associated selectedOptions`); + }); + }); + + test('An invalid when clause should always return false', () => { + should(processWhenClause('badWhenClause', [{ option: 'bad', value: 'WhenClause' }])).be.false(`invalid when clause should return false`); + }); });