@@ -51,7 +51,7 @@ func (wr *workflowRunnerImpl) Run(input interface{}) (output interface{}, err er
51
51
}()
52
52
53
53
// Process input
54
- if input , err = wr .processInput (input ); err != nil {
54
+ if input , err = wr .processWorkflowInput (input ); err != nil {
55
55
return nil , err
56
56
}
57
57
@@ -64,16 +64,24 @@ func (wr *workflowRunnerImpl) Run(input interface{}) (output interface{}, err er
64
64
output = wr .RunnerCtx .GetOutput ()
65
65
66
66
// Process output
67
- if output , err = wr .processOutput (output ); err != nil {
67
+ if output , err = wr .processWorkflowOutput (output ); err != nil {
68
68
return nil , err
69
69
}
70
70
71
71
wr .RunnerCtx .SetStatus (CompletedStatus )
72
72
return output , nil
73
73
}
74
74
75
- // processInput validates and transforms input if needed.
76
- func (wr * workflowRunnerImpl ) processInput (input interface {}) (interface {}, error ) {
75
+ // wrapWorkflowError ensures workflow errors have a proper instance reference.
76
+ func (wr * workflowRunnerImpl ) wrapWorkflowError (err error , taskName string ) error {
77
+ if knownErr := model .AsError (err ); knownErr != nil {
78
+ return knownErr .WithInstanceRef (wr .Workflow , taskName )
79
+ }
80
+ return model .NewErrRuntime (err , taskName )
81
+ }
82
+
83
+ // processWorkflowInput validates and transforms input if needed.
84
+ func (wr * workflowRunnerImpl ) processWorkflowInput (input interface {}) (interface {}, error ) {
77
85
if wr .Workflow .Input != nil {
78
86
var err error
79
87
if err = validateSchema (input , wr .Workflow .Input .Schema , "/" ); err != nil {
@@ -99,39 +107,45 @@ func (wr *workflowRunnerImpl) executeTasks(tasks *model.TaskList) error {
99
107
return nil
100
108
}
101
109
102
- // TODO: implement control flow: continue, end, then
103
- for _ , taskItem := range * tasks {
110
+ idx := 0
111
+ currentTask := (* tasks )[idx ]
112
+
113
+ for currentTask != nil {
104
114
wr .RunnerCtx .SetInput (wr .RunnerCtx .GetOutput ())
105
- if shouldRun , err := wr .shouldRunTask (taskItem ); err != nil {
115
+ if shouldRun , err := wr .shouldRunTask (currentTask ); err != nil {
106
116
return err
107
117
} else if ! shouldRun {
108
118
wr .RunnerCtx .SetOutput (wr .RunnerCtx .GetInput ())
119
+ idx , currentTask = tasks .Next (idx )
109
120
continue
110
121
}
111
122
112
- wr .RunnerCtx .SetTaskStatus (taskItem .Key , PendingStatus )
113
- runner , err := NewTaskRunner (taskItem .Key , taskItem .Task )
123
+ wr .RunnerCtx .SetTaskStatus (currentTask .Key , PendingStatus )
124
+ runner , err := NewTaskRunner (currentTask .Key , currentTask .Task , wr )
114
125
if err != nil {
115
126
return err
116
127
}
117
128
118
- wr .RunnerCtx .SetTaskStatus (taskItem .Key , RunningStatus )
129
+ wr .RunnerCtx .SetTaskStatus (currentTask .Key , RunningStatus )
119
130
var output interface {}
120
- if output , err = wr .runTask (runner , taskItem .Task .GetBase ()); err != nil {
121
- wr .RunnerCtx .SetTaskStatus (taskItem .Key , FaultedStatus )
131
+ if output , err = wr .runTask (runner , currentTask .Task .GetBase ()); err != nil {
132
+ wr .RunnerCtx .SetTaskStatus (currentTask .Key , FaultedStatus )
122
133
return err
123
134
}
135
+ // TODO: make sure that `output` is a map[string]interface{}, so compatible to JSON traversal.
124
136
125
- wr .RunnerCtx .SetTaskStatus (taskItem .Key , CompletedStatus )
137
+ wr .RunnerCtx .SetTaskStatus (currentTask .Key , CompletedStatus )
126
138
wr .RunnerCtx .SetOutput (output )
139
+
140
+ idx , currentTask = tasks .Next (idx )
127
141
}
128
142
129
143
return nil
130
144
}
131
145
132
146
func (wr * workflowRunnerImpl ) shouldRunTask (task * model.TaskItem ) (bool , error ) {
133
147
if task .GetBase ().If != nil {
134
- output , err := expr .EvaluateJQExpression (task .GetBase ().If .String (), wr .RunnerCtx .GetInput ())
148
+ output , err := expr .TraverseAndEvaluate (task .GetBase ().If .String (), wr .RunnerCtx .GetInput ())
135
149
if err != nil {
136
150
return false , model .NewErrExpression (err , task .Key )
137
151
}
@@ -142,8 +156,8 @@ func (wr *workflowRunnerImpl) shouldRunTask(task *model.TaskItem) (bool, error)
142
156
return true , nil
143
157
}
144
158
145
- // processOutput applies output transformations.
146
- func (wr * workflowRunnerImpl ) processOutput (output interface {}) (interface {}, error ) {
159
+ // processWorkflowOutput applies output transformations.
160
+ func (wr * workflowRunnerImpl ) processWorkflowOutput (output interface {}) (interface {}, error ) {
147
161
if wr .Workflow .Output != nil {
148
162
var err error
149
163
if output , err = traverseAndEvaluate (wr .Workflow .Output .As , wr .RunnerCtx .GetOutput (), "/" ); err != nil {
@@ -161,16 +175,40 @@ func (wr *workflowRunnerImpl) processOutput(output interface{}) (interface{}, er
161
175
162
176
// ----------------- Task funcs ------------------- //
163
177
178
+ // TODO: refactor to receive a resolver handler instead of the workflow runner
179
+
164
180
// NewTaskRunner creates a TaskRunner instance based on the task type.
165
- func NewTaskRunner (taskName string , task model.Task ) (TaskRunner , error ) {
181
+ func NewTaskRunner (taskName string , task model.Task , wr * workflowRunnerImpl ) (TaskRunner , error ) {
166
182
switch t := task .(type ) {
167
183
case * model.SetTask :
168
184
return NewSetTaskRunner (taskName , t )
185
+ case * model.RaiseTask :
186
+ if err := wr .resolveErrorDefinition (t ); err != nil {
187
+ return nil , err
188
+ }
189
+ return NewRaiseTaskRunner (taskName , t )
169
190
default :
170
191
return nil , fmt .Errorf ("unsupported task type '%T' for task '%s'" , t , taskName )
171
192
}
172
193
}
173
194
195
+ // TODO: can e refactored to a definition resolver callable from the context
196
+ func (wr * workflowRunnerImpl ) resolveErrorDefinition (t * model.RaiseTask ) error {
197
+ if t .Raise .Error .Ref != nil {
198
+ notFoundErr := model .NewErrValidation (fmt .Errorf ("%v error definition not found in 'uses'" , t .Raise .Error .Ref ), "" )
199
+ if wr .Workflow .Use != nil && wr .Workflow .Use .Errors != nil {
200
+ definition , ok := wr .Workflow .Use .Errors [* t .Raise .Error .Ref ]
201
+ if ! ok {
202
+ return notFoundErr
203
+ }
204
+ t .Raise .Error .Definition = definition
205
+ return nil
206
+ }
207
+ return notFoundErr
208
+ }
209
+ return nil
210
+ }
211
+
174
212
// runTask executes an individual task.
175
213
func (wr * workflowRunnerImpl ) runTask (runner TaskRunner , task * model.TaskBase ) (output interface {}, err error ) {
176
214
taskInput := wr .RunnerCtx .GetInput ()
@@ -234,14 +272,6 @@ func (wr *workflowRunnerImpl) validateAndEvaluateTaskOutput(task *model.TaskBase
234
272
return output , nil
235
273
}
236
274
237
- // wrapWorkflowError ensures workflow errors have a proper instance reference.
238
- func (wr * workflowRunnerImpl ) wrapWorkflowError (err error , taskName string ) error {
239
- if knownErr := model .AsError (err ); knownErr != nil {
240
- return knownErr .WithInstanceRef (wr .Workflow , taskName )
241
- }
242
- return model .NewErrRuntime (err , taskName )
243
- }
244
-
245
275
func validateSchema (data interface {}, schema * model.Schema , taskName string ) error {
246
276
if schema != nil {
247
277
if err := ValidateJSONSchema (data , schema ); err != nil {
0 commit comments