Skip to content

Commit 3bb7615

Browse files
committed
Fix #233 - Add support to 'switch' task
Signed-off-by: Ricardo Zanini <[email protected]>
1 parent 5237b86 commit 3bb7615

File tree

6 files changed

+160
-2
lines changed

6 files changed

+160
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ The table below lists the current state of this implementation. This table is a
132132
| Task Raise ||
133133
| Task Run ||
134134
| Task Set ||
135-
| Task Switch | |
135+
| Task Switch | |
136136
| Task Try ||
137137
| Task Wait ||
138138
| Lifecycle Events | 🟡 |
@@ -157,7 +157,7 @@ The table below lists the current state of this implementation. This table is a
157157
| AsyncAPI Server ||
158158
| AsyncAPI Outbound Message ||
159159
| AsyncAPI Subscription ||
160-
| Workflow Definition Reference | |
160+
| Workflow Definition Reference | |
161161
| Subscription Iterator ||
162162

163163
We love contributions! Our aim is to have a complete implementation to serve as a reference or to become a project on its own to favor the CNCF Ecosystem.

impl/runner_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,51 @@ func TestForTaskRunner_Run(t *testing.T) {
407407
})
408408

409409
}
410+
411+
func TestSwitchTaskRunner_Run(t *testing.T) {
412+
t.Run("Color is red", func(t *testing.T) {
413+
workflowPath := "./testdata/switch_match.yaml"
414+
input := map[string]interface{}{
415+
"color": "red",
416+
}
417+
expectedOutput := map[string]interface{}{
418+
"colors": []interface{}{"red"},
419+
}
420+
runWorkflowTest(t, workflowPath, input, expectedOutput)
421+
})
422+
423+
t.Run("Color is green", func(t *testing.T) {
424+
workflowPath := "./testdata/switch_match.yaml"
425+
input := map[string]interface{}{
426+
"color": "green",
427+
}
428+
expectedOutput := map[string]interface{}{
429+
"colors": []interface{}{"green"},
430+
}
431+
runWorkflowTest(t, workflowPath, input, expectedOutput)
432+
})
433+
434+
t.Run("Color is blue", func(t *testing.T) {
435+
workflowPath := "./testdata/switch_match.yaml"
436+
input := map[string]interface{}{
437+
"color": "blue",
438+
}
439+
expectedOutput := map[string]interface{}{
440+
"colors": []interface{}{"blue"},
441+
}
442+
runWorkflowTest(t, workflowPath, input, expectedOutput)
443+
})
444+
}
445+
446+
func TestSwitchTaskRunner_DefaultCase(t *testing.T) {
447+
t.Run("Color is unknown, should match default", func(t *testing.T) {
448+
workflowPath := "./testdata/switch_with_default.yaml"
449+
input := map[string]interface{}{
450+
"color": "yellow",
451+
}
452+
expectedOutput := map[string]interface{}{
453+
"colors": []interface{}{"default"},
454+
}
455+
runWorkflowTest(t, workflowPath, input, expectedOutput)
456+
})
457+
}

impl/task_runner_do.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,24 @@ func (d *DoTaskRunner) runTasks(input interface{}, tasks *model.TaskList) (outpu
8686
}
8787

8888
d.TaskSupport.SetTaskStatus(currentTask.Key, ctx.PendingStatus)
89+
90+
// Check if this task is a SwitchTask and handle it
91+
if switchTask, ok := currentTask.Task.(*model.SwitchTask); ok {
92+
flowDirective, err := d.evaluateSwitchTask(input, currentTask.Key, switchTask)
93+
if err != nil {
94+
d.TaskSupport.SetTaskStatus(currentTask.Key, ctx.FaultedStatus)
95+
return output, err
96+
}
97+
d.TaskSupport.SetTaskStatus(currentTask.Key, ctx.CompletedStatus)
98+
99+
// Process FlowDirective: update idx/currentTask accordingly
100+
idx, currentTask = tasks.KeyAndIndex(flowDirective.Value)
101+
if currentTask == nil {
102+
return nil, fmt.Errorf("flow directive target '%s' not found", flowDirective.Value)
103+
}
104+
continue
105+
}
106+
89107
runner, err := NewTaskRunner(currentTask.Key, currentTask.Task, d.TaskSupport)
90108
if err != nil {
91109
return output, err
@@ -116,6 +134,32 @@ func (d *DoTaskRunner) shouldRunTask(input interface{}, task *model.TaskItem) (b
116134
return true, nil
117135
}
118136

137+
func (d *DoTaskRunner) evaluateSwitchTask(input interface{}, taskKey string, switchTask *model.SwitchTask) (*model.FlowDirective, error) {
138+
var defaultThen *model.FlowDirective
139+
for _, switchItem := range switchTask.Switch {
140+
for _, switchCase := range switchItem {
141+
if switchCase.When == nil {
142+
defaultThen = switchCase.Then
143+
continue
144+
}
145+
result, err := traverseAndEvaluateBool(model.NormalizeExpr(switchCase.When.String()), input, d.TaskSupport.GetContext())
146+
if err != nil {
147+
return nil, model.NewErrExpression(err, taskKey)
148+
}
149+
if result {
150+
if switchCase.Then == nil {
151+
return nil, model.NewErrExpression(fmt.Errorf("missing 'then' directive in matched switch case"), taskKey)
152+
}
153+
return switchCase.Then, nil
154+
}
155+
}
156+
}
157+
if defaultThen != nil {
158+
return defaultThen, nil
159+
}
160+
return nil, model.NewErrExpression(fmt.Errorf("no matching switch case"), taskKey)
161+
}
162+
119163
// runTask executes an individual task.
120164
func (d *DoTaskRunner) runTask(input interface{}, runner TaskRunner, task *model.TaskBase) (output interface{}, err error) {
121165
taskName := runner.GetTaskName()

impl/testdata/switch_match.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
document:
2+
dsl: '1.0.0'
3+
namespace: default
4+
name: switch-match
5+
version: '1.0.0'
6+
do:
7+
- switchColor:
8+
switch:
9+
- red:
10+
when: '.color == "red"'
11+
then: setRed
12+
- green:
13+
when: '.color == "green"'
14+
then: setGreen
15+
- blue:
16+
when: '.color == "blue"'
17+
then: setBlue
18+
- setRed:
19+
set:
20+
colors: '${ .colors + [ "red" ] }'
21+
then: end
22+
- setGreen:
23+
set:
24+
colors: '${ .colors + [ "green" ] }'
25+
then: end
26+
- setBlue:
27+
set:
28+
colors: '${ .colors + [ "blue" ] }'
29+
then: end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
document:
2+
dsl: '1.0.0'
3+
namespace: default
4+
name: switch-with-default
5+
version: '1.0.0'
6+
7+
do:
8+
- switchColor:
9+
switch:
10+
- red:
11+
when: '.color == "red"'
12+
then: setRed
13+
- green:
14+
when: '.color == "green"'
15+
then: setGreen
16+
- fallback:
17+
then: setDefault
18+
- setRed:
19+
set:
20+
colors: '${ .colors + [ "red" ] }'
21+
then: end
22+
- setGreen:
23+
set:
24+
colors: '${ .colors + [ "green" ] }'
25+
then: end
26+
- setDefault:
27+
set:
28+
colors: '${ .colors + [ "default" ] }'
29+
then: end

model/runtime_expression.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ func IsValidExpr(expression string) bool {
5959
return err == nil
6060
}
6161

62+
// NormalizeExpr adds ${} to the given string
63+
func NormalizeExpr(expr string) string {
64+
if strings.HasPrefix(expr, "${") {
65+
return expr
66+
}
67+
return fmt.Sprintf("${%s}", expr)
68+
}
69+
6270
// IsValid checks if the RuntimeExpression value is valid, handling both with and without `${}`.
6371
func (r *RuntimeExpression) IsValid() bool {
6472
return IsValidExpr(r.Value)

0 commit comments

Comments
 (0)