Skip to content

Commit afa5ef4

Browse files
committed
pkg/workflows/sdk: add WorkflowSpecFactory.BeginSerial/BeginAsync
1 parent fefe57c commit afa5ef4

File tree

5 files changed

+164
-5
lines changed

5 files changed

+164
-5
lines changed

pkg/workflows/sdk/builder.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,22 @@ type WorkflowSpecFactory struct {
1515
emptyNames bool
1616
badCapTypes []string
1717
fns map[string]func(runtime Runtime, request capabilities.CapabilityRequest) (capabilities.CapabilityResponse, error)
18+
serialMode bool
19+
prevRefs []string
1820
}
1921

2022
func (w *WorkflowSpecFactory) GetFn(name string) func(sdk Runtime, request capabilities.CapabilityRequest) (capabilities.CapabilityResponse, error) {
2123
return w.fns[name]
2224
}
2325

26+
func (w *WorkflowSpecFactory) BeginSerial() {
27+
w.serialMode = true
28+
}
29+
30+
func (w *WorkflowSpecFactory) BeginAsync() {
31+
w.serialMode = false
32+
}
33+
2434
type CapDefinition[O any] interface {
2535
Ref() any
2636
self() CapDefinition[O]
@@ -98,6 +108,15 @@ type NewWorkflowParams struct {
98108
Name string
99109
}
100110

111+
// NewSerialWorkflowSpecFactory returns a new WorkflowSpecFactory in Serial mode.
112+
// This is the same as calling NewWorkflowSpecFactory then WorkflowSpecFactory.BeginSerial.
113+
func NewSerialWorkflowSpecFactory(params NewWorkflowParams) *WorkflowSpecFactory {
114+
f := NewWorkflowSpecFactory(params)
115+
f.BeginSerial()
116+
return f
117+
}
118+
119+
// NewWorkflowSpecFactory returns a new NewWorkflowSpecFactory.
101120
func NewWorkflowSpecFactory(
102121
params NewWorkflowParams,
103122
) *WorkflowSpecFactory {
@@ -119,6 +138,16 @@ func NewWorkflowSpecFactory(
119138
// AddTo is meant to be called by generated code
120139
func (step *Step[O]) AddTo(w *WorkflowSpecFactory) CapDefinition[O] {
121140
stepDefinition := step.Definition
141+
142+
if w.serialMode {
143+
// ensure we depend on each previous step
144+
for _, prevRef := range w.prevRefs {
145+
if !stepDefinition.Inputs.HasRef(prevRef) {
146+
stepDefinition.Condition = fmt.Sprintf("$(%s.success)", prevRef)
147+
}
148+
}
149+
}
150+
122151
stepRef := stepDefinition.Ref
123152
if w.names[stepRef] && stepDefinition.CapabilityType != capabilities.CapabilityTypeTarget {
124153
w.duplicateNames[stepRef] = true
@@ -143,7 +172,14 @@ func (step *Step[O]) AddTo(w *WorkflowSpecFactory) CapDefinition[O] {
143172
w.badCapTypes = append(w.badCapTypes, stepDefinition.ID)
144173
}
145174

146-
return &capDefinitionImpl[O]{ref: fmt.Sprintf("$(%s.outputs)", step.Definition.Ref)}
175+
c := &capDefinitionImpl[O]{ref: fmt.Sprintf("$(%s.outputs)", step.Definition.Ref)}
176+
177+
if w.serialMode {
178+
w.prevRefs = []string{step.Definition.Ref}
179+
} else {
180+
w.prevRefs = append(w.prevRefs, step.Definition.Ref)
181+
}
182+
return c
147183
}
148184

149185
// AccessField is meant to be used by generated code
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
```mermaid
2+
flowchart
3+
4+
trigger[\"<b>trigger</b><br>trigger<br><i>(basic-test-trigger[at]1.0.0)</i>"/]
5+
6+
compute["<b>compute</b><br>action<br><i>(custom_compute[at]1.0.0)</i>"]
7+
get-bar -- Value --> compute
8+
get-baz -- Value --> compute
9+
get-foo -- Value --> compute
10+
11+
get-bar["<b>get-bar</b><br>action<br><i>(custom_compute[at]1.0.0)</i>"]
12+
get-foo -..-> get-bar
13+
trigger -- cool_output --> get-bar
14+
15+
get-baz["<b>get-baz</b><br>action<br><i>(custom_compute[at]1.0.0)</i>"]
16+
get-bar -..-> get-baz
17+
trigger -- cool_output --> get-baz
18+
19+
get-foo["<b>get-foo</b><br>action<br><i>(custom_compute[at]1.0.0)</i>"]
20+
trigger -- cool_output --> get-foo
21+
22+
consensus[["<b>consensus</b><br>consensus<br><i>(offchain_reporting[at]1.0.0)</i>"]]
23+
compute -- Value --> consensus
24+
25+
unnamed6[/"target<br><i>(id)</i>"\]
26+
consensus --> unnamed6
27+
28+
```

pkg/workflows/sdk/workflow_spec.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,11 @@ func (os outputs) addOutput(s string) {
128128
//
129129
// Within the workflow spec, they are called "Capability Properties".
130130
type StepDefinition struct {
131-
ID string
132-
Ref string
133-
Inputs StepInputs
134-
Config map[string]any
131+
ID string
132+
Ref string
133+
Condition string
134+
Inputs StepInputs
135+
Config map[string]any
135136

136137
CapabilityType capabilities.CapabilityType
137138
}
@@ -193,6 +194,10 @@ var tmpl = template.Must(template.New("").Funcs(map[string]any{
193194
{{ else -}}
194195
{{ $ref }}["{{$name}}"]
195196
{{ end -}}
197+
{{ $condRef := parseRef .Condition -}}
198+
{{ if $condRef -}}
199+
{{ $condRef }} -..-> {{ $step.Ref }}
200+
{{ end -}}
196201
{{ if .Inputs.OutputRef -}}
197202
{{ .Inputs.OutputRef }} --> {{ $step.Ref }}
198203
{{ else -}}

pkg/workflows/sdk/workflow_spec_test.go

+87
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,13 @@ func TestWorkflowSpecFormatChart(t *testing.T) {
6767
{"notstreamssepolia", notStreamSepoliaWorkflowSpec},
6868
{"serial", serialWorkflowSpec},
6969
{"parallel", parallelWorkflowSpec},
70+
{"parallel_serialized", parallelSerializedWorkflowSpec},
7071
{"builder_parallel", buildSimpleWorkflowSpec(
7172
sdk.NewWorkflowSpecFactory(sdk.NewWorkflowParams{Owner: "test", Name: "parallel"}),
7273
).MustSpec(t)},
74+
{"builder_serial", buildSimpleWorkflowSpec(
75+
sdk.NewSerialWorkflowSpecFactory(sdk.NewWorkflowParams{Owner: "test", Name: "serial"}),
76+
).MustSpec(t)},
7377
} {
7478
t.Run(tt.name, func(t *testing.T) {
7579
requireEqualChart(t, tt.name, tt.workflow)
@@ -386,3 +390,86 @@ var parallelWorkflowSpec = sdk.WorkflowSpec{
386390
},
387391
},
388392
}
393+
394+
var parallelSerializedWorkflowSpec = sdk.WorkflowSpec{
395+
Name: "parallel-serialized",
396+
Owner: "owner",
397+
Triggers: []sdk.StepDefinition{
398+
{
399+
400+
Ref: "trigger-chain-event",
401+
Inputs: sdk.StepInputs{},
402+
Config: map[string]any{"maxFrequencyMs": 5000},
403+
CapabilityType: capabilities.CapabilityTypeTrigger,
404+
},
405+
},
406+
Actions: []sdk.StepDefinition{
407+
{
408+
409+
Ref: "get-foo",
410+
Inputs: sdk.StepInputs{
411+
Mapping: map[string]any{"Arg0": "$(trigger-chain-event.outputs)"},
412+
},
413+
CapabilityType: capabilities.CapabilityTypeAction,
414+
},
415+
{
416+
417+
Ref: "compute-foo",
418+
Inputs: sdk.StepInputs{
419+
Mapping: map[string]any{"Arg0": "$(get-foo.outputs)"},
420+
},
421+
CapabilityType: capabilities.CapabilityTypeAction,
422+
},
423+
{
424+
425+
Ref: "get-bar",
426+
Condition: "$(compute-foo.success)",
427+
Inputs: sdk.StepInputs{
428+
Mapping: map[string]any{"Arg0": "$(trigger-chain-event.outputs)"},
429+
},
430+
CapabilityType: capabilities.CapabilityTypeAction,
431+
},
432+
{
433+
434+
Ref: "compute-bar",
435+
Inputs: sdk.StepInputs{
436+
Mapping: map[string]any{"Arg0": "$(get-bar.outputs)"},
437+
},
438+
CapabilityType: capabilities.CapabilityTypeAction,
439+
},
440+
{
441+
442+
Ref: "read-token-price",
443+
Condition: "$(compute-bar.success)",
444+
Inputs: sdk.StepInputs{
445+
Mapping: map[string]any{"Arg0": "$(trigger-chain-event.outputs)"},
446+
},
447+
CapabilityType: capabilities.CapabilityTypeAction,
448+
},
449+
},
450+
Consensus: []sdk.StepDefinition{
451+
{
452+
453+
Ref: "data-feeds-report",
454+
Inputs: sdk.StepInputs{
455+
Mapping: map[string]any{
456+
"observations": []string{
457+
"$(compute-foo.outputs.Value)",
458+
"$(compute-bar.outputs.Value)",
459+
},
460+
"token_price": "$(read-token-price.outputs.Value)",
461+
},
462+
},
463+
CapabilityType: capabilities.CapabilityTypeConsensus,
464+
},
465+
},
466+
Targets: []sdk.StepDefinition{
467+
{
468+
469+
Inputs: sdk.StepInputs{
470+
Mapping: map[string]any{"signed_report": "$(data-feeds-report.outputs)"},
471+
},
472+
CapabilityType: capabilities.CapabilityTypeTarget,
473+
},
474+
},
475+
}

pkg/workflows/testdata/fixtures/workflows/marshalling/workflow_2_spec.json

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
{
66
77
"Ref": "report_data",
8+
"Condition": "",
89
"Inputs": {
910
"OutputRef": "",
1011
"Mapping": null
@@ -18,6 +19,7 @@
1819
{
1920
"ID": "trigger_test:aaShouldBeFirst_true:chain_ethereum:[email protected]",
2021
"Ref": "",
22+
"Condition": "",
2123
"Inputs": {
2224
"OutputRef": "",
2325
"Mapping": {
@@ -34,6 +36,7 @@
3436
{
3537
3638
"Ref": "",
39+
"Condition": "",
3740
"Inputs": {
3841
"OutputRef": "",
3942
"Mapping": {

0 commit comments

Comments
 (0)