From 62e8b1c7d6aa4cbafb121055c5edc2a28b5b9964 Mon Sep 17 00:00:00 2001 From: boney9 Date: Fri, 29 Oct 2021 10:03:13 -0700 Subject: [PATCH] Fixes #2528 - The UI was not supporting SWITCH task definitions or the execution. With this change it will support both SWITCH and DECISION. Tested UI rendering manually screenshots attached to the issue. (#2547) --- .../evaluators/ValueParamEvaluator.java | 4 +-- docs/docs/configuration/systask.md | 15 ++++++++-- ui/src/components/diagram/WorkflowDAG.js | 28 +++++++++++-------- ui/src/components/diagram/WorkflowGraph.jsx | 1 + 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java index 09559973d2..6516070e73 100644 --- a/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java +++ b/core/src/main/java/com/netflix/conductor/core/execution/evaluators/ValueParamEvaluator.java @@ -29,13 +29,13 @@ public class ValueParamEvaluator implements Evaluator { @Override public Object evaluate(String expression, Object input) { - LOGGER.debug("ValueParam evaluator -- Evaluating: {}", expression); + LOGGER.debug("ValueParam evaluator -- evaluating: {}", expression); if (input instanceof Map) { Object result = ((Map) input).get(expression); LOGGER.debug("ValueParam evaluator -- result: {}", result); return result; } else { - String errorMsg = String.format("Input must of a JSON object: %s", input.getClass()); + String errorMsg = String.format("Input has to be a JSON object: %s", input.getClass()); LOGGER.error(errorMsg); throw new TerminateWorkflowException(errorMsg); } diff --git a/docs/docs/configuration/systask.md b/docs/docs/configuration/systask.md index a32ea84b4b..b7fb3b4513 100644 --- a/docs/docs/configuration/systask.md +++ b/docs/docs/configuration/systask.md @@ -3,9 +3,8 @@ A switch task is similar to ```case...switch``` statement in a programming langu The `switch` expression, however, is simply an input parameter (`value-param` evaluator) or a complex javascript expression (`javascript` evaluator). Only two evaluators are supported by default in conductor. -**For Developers** Any type of custom evaluator can be implemented without having to touch the way `switch` task works. - -The task takes 2 parameters and even with growing number of evaluators, it always takes 2 parameters: +**For Conductor Developers**: Custom evaluators can be implemented without having to change the way `SWITCH` task works. +To implement and use the custom evaluators we can use the params `evaluatorType` and `expression`. **Parameters:** @@ -76,6 +75,16 @@ The task takes 2 parameters and even with growing number of evaluators, it alway } ``` +### Decision (Deprecated) + +`DECISION` task type has been **deprecated** and replaced with the `SWITCH` task type. Switch task type is identical to how Decision tasks works except for the following differences: + +`DECISION` task type used to take two parameters +1. `caseExpression` : If present, this takes precedence and will be evaluated as a Javascript expression +2. `caseValueParam` : If `caseExpression` param is null or empty, case value param will be used to determine the decision branch + +`SWITCH` works with the `evaluatorType` and `expression` params as a replacement to the above. For details refer to the `SWITCH` task documentation + ## Event Event task provides ability to publish an event (message) to either Conductor or an external eventing system like SQS. Event tasks are useful for creating event based dependencies for workflows and tasks. diff --git a/ui/src/components/diagram/WorkflowDAG.js b/ui/src/components/diagram/WorkflowDAG.js index 3ad9627269..7acfdaf314 100644 --- a/ui/src/components/diagram/WorkflowDAG.js +++ b/ui/src/components/diagram/WorkflowDAG.js @@ -91,13 +91,15 @@ export default class WorkflowDAG { } } - isTakenDecisionBranch(caseValue, decisionTaskRef) { + switchBranchTaken(caseValue, decisionTaskRef, type) { if (!this.taskResults.has(decisionTaskRef)) return false; - const decisionTaskResult = this.getLastTaskResult(decisionTaskRef); - const cases = Object.keys(decisionTaskResult.workflowTask.decisionCases); + const switchTaskResult = this.getLastTaskResult(decisionTaskRef); + const cases = Object.keys(switchTaskResult.workflowTask.decisionCases); - const actualValue = _.get(decisionTaskResult, "outputData.caseOutput[0]"); + // Required until DECISION is fully removed + let resultField = type === "SWITCH" ? "evaluationResult" : "caseOutput"; + const actualValue = _.get(switchTaskResult, `outputData.${resultField}[0]`); if (actualValue === undefined) return false; if (caseValue) { @@ -138,19 +140,20 @@ export default class WorkflowDAG { ); const edgeParams = {}; - // Special case - When the antecedent of an executed node is a DECISION, the edge may not necessarily be highlighted. + // Special case - When the antecedent of an executed node is a SWITCH, the edge may not necessarily be highlighted. // E.g. the default edge not taken. - - if (antecedent.type === "DECISION") { + // SWITCH is the newer version of DECISION and DECISION is deprecated + if (antecedent.type === "SWITCH" || antecedent.type === "DECISION") { edgeParams.caseValue = getCaseValue( taskConfig.taskReferenceName, antecedent ); // Highlight edge as executed only after thorough test - const branchTaken = this.isTakenDecisionBranch( + const branchTaken = this.switchBranchTaken( edgeParams.caseValue, - antecedent.taskReferenceName + antecedent.taskReferenceName, + antecedent.type ); if (branchTaken) { edgeParams.executed = true; @@ -183,7 +186,7 @@ export default class WorkflowDAG { } // Nodes are connected to previous - processDecisionTask(decisionTask, antecedents) { + processSwitchTask(decisionTask, antecedents) { console.assert(Array.isArray(antecedents)); const retval = []; @@ -268,8 +271,9 @@ export default class WorkflowDAG { return this.processForkJoinDynamic(task, antecedents); } - case "DECISION": { - return this.processDecisionTask(task, antecedents); + case "DECISION": // DECISION is deprecated and will be removed in a future release + case "SWITCH": { + return this.processSwitchTask(task, antecedents); } case "TERMINATE": { diff --git a/ui/src/components/diagram/WorkflowGraph.jsx b/ui/src/components/diagram/WorkflowGraph.jsx index 935a673f97..940919211a 100644 --- a/ui/src/components/diagram/WorkflowGraph.jsx +++ b/ui/src/components/diagram/WorkflowGraph.jsx @@ -391,6 +391,7 @@ class WorkflowGraph extends React.PureComponent { this.barNodes.push(v.ref); break; case "DECISION": + case "SWITCH": retval.label = v.ref; retval.shape = "diamond"; retval.height = 40;