From 9403b9b17434e12b5afd3928054e36f405a214c4 Mon Sep 17 00:00:00 2001 From: Daniel Duarte Date: Fri, 30 Aug 2019 18:05:15 -0300 Subject: [PATCH] Added conditional resolver. --- package-lock.json | 6 +- src/engine/flow.ts | 7 +- src/engine/task.ts | 5 +- src/resolver-library.ts | 6 ++ test/resolver-library.ts | 216 +++++++++++++++++++++++++++++++++++++++ tsconfig.json | 3 +- tsconfig.test.json | 3 +- 7 files changed, 237 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b4d1ae..4a1e513 100644 --- a/package-lock.json +++ b/package-lock.json @@ -129,9 +129,9 @@ "dev": true }, "@types/node": { - "version": "12.7.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz", - "integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==", + "version": "12.7.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.3.tgz", + "integrity": "sha512-3SiLAIBkDWDg6vFo0+5YJyHPWU9uwu40Qe+v+0MH8wRKYBimHvvAOyk3EzMrD/TrIlLYfXrqDqrg913PynrMJQ==", "dev": true }, "ansi-colors": { diff --git a/src/engine/flow.ts b/src/engine/flow.ts index ba47148..ca11ab0 100644 --- a/src/engine/flow.ts +++ b/src/engine/flow.ts @@ -145,7 +145,6 @@ export class Flow { } protected setExpectedResults(expectedResults: string[] = []) { - // Check expected results that cannot be fulfilled const missingExpected = expectedResults.filter(r => !this.taskProvisions.includes(r)); if (missingExpected.length > 0) { @@ -298,8 +297,10 @@ export class Flow { this.runStatus.runningTasks.splice(this.runStatus.runningTasks.indexOf(task.getCode()), 1); for (const resultName of taskProvisions) { - const result = taskResults[resultName]; - this.supplyResult(resultName, result); + if (taskResults.hasOwnProperty(resultName)) { + const result = taskResults[resultName]; + this.supplyResult(resultName, result); + } } const stopExecution = error && stopFlowExecutionOnError; diff --git a/src/engine/task.ts b/src/engine/task.ts index 0489ce8..7c44210 100644 --- a/src/engine/task.ts +++ b/src/engine/task.ts @@ -126,7 +126,10 @@ export class Task { const results: GenericValueMap = {}; for (const resolverResultName in this.spec.resolver.results) { - if (this.spec.resolver.results.hasOwnProperty(resolverResultName)) { + if ( + this.spec.resolver.results.hasOwnProperty(resolverResultName) && + resolverResults.hasOwnProperty(resolverResultName) + ) { const taskResultName = this.spec.resolver.results[resolverResultName]; // noinspection UnnecessaryLocalVariableJS const resolverResult = resolverResults[resolverResultName]; diff --git a/src/resolver-library.ts b/src/resolver-library.ts index 948f0e1..21467ee 100644 --- a/src/resolver-library.ts +++ b/src/resolver-library.ts @@ -68,4 +68,10 @@ export class RepeaterResolver { } } +export class ConditionalResolver { + public async exec(params: GenericValueMap): Promise { + return params.condition ? { onTrue: params.trueResult } : { onFalse: params.falseResult }; + } +} + // @todo add ThrowErrorResolver diff --git a/test/resolver-library.ts b/test/resolver-library.ts index 636d9be..918b64e 100644 --- a/test/resolver-library.ts +++ b/test/resolver-library.ts @@ -1,3 +1,4 @@ +import { expect } from 'chai'; import { debug as rawDebug } from 'debug'; import { GenericValueMap } from '../src'; import { FlowManager } from '../src/engine'; @@ -158,4 +159,219 @@ describe('the ResolverLibrary', () => { }, ); }); + + it('runs conditional resolver', async () => { + class TrueTask { + public async exec(params: GenericValueMap): Promise { + return { msg: `This is the TRUE branch: ${params.text}` }; + } + } + + class FalseTask { + public async exec(params: GenericValueMap): Promise { + return { msg: `This is the FALSE branch: ${params.text}` }; + } + } + + const runFlow = async (testCondition: boolean) => { + return await FlowManager.run( + { + tasks: { + if: { + requires: ['condition', 'true-text', 'false-text'], + provides: ['on-true', 'on-false'], + resolver: { + name: 'if', + params: { condition: 'condition', trueResult: 'true-text', falseResult: 'false-text' }, + results: { onTrue: 'on-true', onFalse: 'on-false' }, + }, + }, + trueTask: { + requires: ['on-true'], + provides: ['true-msg'], + resolver: { + name: 'true', + params: { text: 'on-true' }, + results: { msg: 'true-msg' }, + }, + }, + falseTask: { + requires: ['on-false'], + provides: ['false-msg'], + resolver: { + name: 'false', + params: { text: 'on-false' }, + results: { msg: 'false-msg' }, + }, + }, + }, + }, + { + condition: testCondition, + 'true-text': '(YES)', + 'false-text': '(NO)', + }, + ['on-true', 'on-false', 'true-msg', 'false-msg'], + { + if: ResolverLibrary.ConditionalResolver, + true: TrueTask, + false: FalseTask, + }, + ); + }; + + const resultTrue = await runFlow(true); + expect(resultTrue).to.be.eql({ + 'on-true': '(YES)', + 'true-msg': 'This is the TRUE branch: (YES)', + }); + + const resultFalse = await runFlow(false); + expect(resultFalse).to.be.eql({ + 'on-false': '(NO)', + 'false-msg': 'This is the FALSE branch: (NO)', + }); + }); + + it('runs conditional resolver with the same result name', async () => { + class TrueTask { + public async exec(params: GenericValueMap): Promise { + return { msg: `This is the TRUE branch: ${params.text}` }; + } + } + + class FalseTask { + public async exec(params: GenericValueMap): Promise { + return { msg: `This is the FALSE branch: ${params.text}` }; + } + } + + const runFlow = async (testCondition: boolean) => { + return await FlowManager.run( + { + tasks: { + if: { + requires: ['condition', 'true-text', 'false-text'], + provides: ['on-true', 'on-false'], + resolver: { + name: 'if', + params: { condition: 'condition', trueResult: 'true-text', falseResult: 'false-text' }, + results: { onTrue: 'on-true', onFalse: 'on-false' }, + }, + }, + trueTask: { + requires: ['on-true'], + provides: ['msg'], + resolver: { + name: 'true', + params: { text: 'on-true' }, + results: { msg: 'msg' }, + }, + }, + falseTask: { + requires: ['on-false'], + provides: ['msg'], + resolver: { + name: 'false', + params: { text: 'on-false' }, + results: { msg: 'msg' }, + }, + }, + }, + }, + { + condition: testCondition, + 'true-text': '(YES)', + 'false-text': '(NO)', + }, + ['msg'], + { + if: ResolverLibrary.ConditionalResolver, + true: TrueTask, + false: FalseTask, + }, + ); + }; + + const resultTrue = await runFlow(true); + expect(resultTrue).to.be.eql({ + msg: 'This is the TRUE branch: (YES)', + }); + + const resultFalse = await runFlow(false); + expect(resultFalse).to.be.eql({ + msg: 'This is the FALSE branch: (NO)', + }); + }); + + it('runs conditional resolver with missing conditional results', async () => { + class TrueTask { + public async exec(params: GenericValueMap): Promise { + return { msg: `This is the TRUE branch: ${params.text}` }; + } + } + + class FalseTask { + public async exec(params: GenericValueMap): Promise { + return { msg: `This is the FALSE branch: ${params.text}` }; + } + } + + const runFlow = async (testCondition: boolean) => { + return await FlowManager.run( + { + tasks: { + if: { + requires: ['condition'], + provides: ['on-true', 'on-false'], + resolver: { + name: 'if', + params: { condition: 'condition', trueResult: 'true-text', falseResult: 'false-text' }, + results: { onTrue: 'on-true', onFalse: 'on-false' }, + }, + }, + trueTask: { + requires: ['on-true'], + provides: ['msg'], + resolver: { + name: 'true', + params: { text: 'on-true' }, + results: { msg: 'msg' }, + }, + }, + falseTask: { + requires: ['on-false'], + provides: ['msg'], + resolver: { + name: 'false', + params: { text: 'on-false' }, + results: { msg: 'msg' }, + }, + }, + }, + }, + { + condition: testCondition, + }, + ['msg'], + { + if: ResolverLibrary.ConditionalResolver, + true: TrueTask, + false: FalseTask, + }, + ); + }; + + const resultTrue = await runFlow(true); + expect(resultTrue).to.be.eql({ + msg: 'This is the TRUE branch: undefined', + }); + + const resultFalse = await runFlow(false); + expect(resultFalse).to.be.eql({ + msg: 'This is the FALSE branch: undefined', + }); + }); }); + +// @todo add test to check error thrown on non resolver set in flow spec (check src/engine/flow.ts method startReadyTasks around line 190) diff --git a/tsconfig.json b/tsconfig.json index f665b58..650f56d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,8 @@ "pretty": true, "removeComments": true, "resolveJsonModule": true, - "noUnusedLocals": true + "noUnusedLocals": true, + "sourceMap": true }, "include": ["src"], "exclude": ["node_modules"] diff --git a/tsconfig.test.json b/tsconfig.test.json index 9045d6f..c6c4976 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -9,7 +9,8 @@ "pretty": true, "removeComments": true, "resolveJsonModule": true, - "noUnusedLocals": true + "noUnusedLocals": true, + "sourceMap": true }, "include": ["test"], "exclude": ["node_modules"]