Skip to content

Commit 1ae306c

Browse files
authored
feat(eventState): add default value for OnEvents.ActionMode (#96)
Signed-off-by: lsytj0413 <511121939@qq.com> Signed-off-by: lsytj0413 <511121939@qq.com>
1 parent 28e2e7d commit 1ae306c

File tree

4 files changed

+249
-72
lines changed

4 files changed

+249
-72
lines changed

model/event_state.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2022 The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package model
16+
17+
import (
18+
"encoding/json"
19+
)
20+
21+
// EventState used to wait for events from event sources, then consumes them and invoke one or more actions to run in sequence or parallel
22+
type EventState struct {
23+
// TODO: EventState doesn't have usedForCompensation field.
24+
BaseState
25+
26+
// If true consuming one of the defined events causes its associated actions to be performed. If false all of the defined events must be consumed in order for actions to be performed
27+
// Defaults to true
28+
Exclusive bool `json:"exclusive,omitempty"`
29+
// Define the events to be consumed and optional actions to be performed
30+
OnEvents []OnEvents `json:"onEvents" validate:"required,min=1,dive"`
31+
// State specific timeouts
32+
Timeout *EventStateTimeout `json:"timeouts,omitempty"`
33+
}
34+
35+
type eventStateForUnmarshal EventState
36+
37+
// UnmarshalJSON unmarshal EventState object from json bytes
38+
func (e *EventState) UnmarshalJSON(data []byte) error {
39+
v := eventStateForUnmarshal{
40+
Exclusive: true,
41+
}
42+
err := json.Unmarshal(data, &v)
43+
if err != nil {
44+
return err
45+
}
46+
47+
*e = EventState(v)
48+
return nil
49+
}
50+
51+
// OnEvents define which actions are be be performed for the one or more events.
52+
type OnEvents struct {
53+
// References one or more unique event names in the defined workflow events
54+
EventRefs []string `json:"eventRefs" validate:"required,min=1"`
55+
// Specifies how actions are to be performed (in sequence or parallel)
56+
// Defaults to sequential
57+
ActionMode ActionMode `json:"actionMode,omitempty" validate:"required,oneof=sequential parallel"`
58+
// Actions to be performed if expression matches
59+
Actions []Action `json:"actions,omitempty" validate:"omitempty,dive"`
60+
// Event data filter
61+
EventDataFilter EventDataFilter `json:"eventDataFilter,omitempty"`
62+
}
63+
64+
type onEventsForUnmarshal OnEvents
65+
66+
// UnmarshalJSON unmarshal OnEvents object from json bytes
67+
func (o *OnEvents) UnmarshalJSON(data []byte) error {
68+
v := onEventsForUnmarshal{
69+
ActionMode: ActionModeSequential,
70+
}
71+
72+
err := json.Unmarshal(data, &v)
73+
if err != nil {
74+
return err
75+
}
76+
77+
*o = OnEvents(v)
78+
return nil
79+
}
80+
81+
// EventStateTimeout defines timeout settings for event state
82+
type EventStateTimeout struct {
83+
StateExecTimeout StateExecTimeout `json:"stateExecTimeout,omitempty"`
84+
ActionExecTimeout string `json:"actionExecTimeout,omitempty" validate:"omitempty,iso8601duration"`
85+
EventTimeout string `json:"eventTimeout,omitempty" validate:"omitempty,iso8601duration"`
86+
}

model/event_state_test.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2022 The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package model
16+
17+
import (
18+
"encoding/json"
19+
"testing"
20+
21+
"github.com/stretchr/testify/assert"
22+
)
23+
24+
func TestEventStateUnmarshalJSON(t *testing.T) {
25+
type testCase struct {
26+
desp string
27+
data string
28+
expect EventState
29+
err string
30+
}
31+
testCases := []testCase{
32+
{
33+
desp: "all fields set",
34+
data: `{"name": "1", "Type": "event", "exclusive": false, "onEvents": [{"eventRefs": ["E1", "E2"], "actionMode": "parallel"}], "timeouts": {"actionExecTimeout": "PT5M", "eventTimeout": "PT5M", "stateExecTimeout": "PT5M"}}`,
35+
expect: EventState{
36+
BaseState: BaseState{
37+
Name: "1",
38+
Type: StateTypeEvent,
39+
},
40+
Exclusive: false,
41+
OnEvents: []OnEvents{
42+
{
43+
EventRefs: []string{"E1", "E2"},
44+
ActionMode: "parallel",
45+
},
46+
},
47+
Timeout: &EventStateTimeout{
48+
EventTimeout: "PT5M",
49+
ActionExecTimeout: "PT5M",
50+
StateExecTimeout: StateExecTimeout{
51+
Total: "PT5M",
52+
},
53+
},
54+
},
55+
err: ``,
56+
},
57+
{
58+
desp: "default exclusive",
59+
data: `{"name": "1", "Type": "event", "onEvents": [{"eventRefs": ["E1", "E2"], "actionMode": "parallel"}], "timeouts": {"actionExecTimeout": "PT5M", "eventTimeout": "PT5M", "stateExecTimeout": "PT5M"}}`,
60+
expect: EventState{
61+
BaseState: BaseState{
62+
Name: "1",
63+
Type: StateTypeEvent,
64+
},
65+
Exclusive: true,
66+
OnEvents: []OnEvents{
67+
{
68+
EventRefs: []string{"E1", "E2"},
69+
ActionMode: "parallel",
70+
},
71+
},
72+
Timeout: &EventStateTimeout{
73+
EventTimeout: "PT5M",
74+
ActionExecTimeout: "PT5M",
75+
StateExecTimeout: StateExecTimeout{
76+
Total: "PT5M",
77+
},
78+
},
79+
},
80+
err: ``,
81+
},
82+
}
83+
for _, tc := range testCases {
84+
t.Run(tc.desp, func(t *testing.T) {
85+
v := EventState{}
86+
err := json.Unmarshal([]byte(tc.data), &v)
87+
88+
if tc.err != "" {
89+
assert.Error(t, err)
90+
assert.Regexp(t, tc.err, err)
91+
return
92+
}
93+
94+
assert.NoError(t, err)
95+
assert.Equal(t, tc.expect, v)
96+
})
97+
}
98+
}
99+
100+
func TestOnEventsUnmarshalJSON(t *testing.T) {
101+
type testCase struct {
102+
desp string
103+
data string
104+
expect OnEvents
105+
err string
106+
}
107+
testCases := []testCase{
108+
{
109+
desp: "all fields set",
110+
data: `{"eventRefs": ["E1", "E2"], "actionMode": "parallel"}`,
111+
expect: OnEvents{
112+
EventRefs: []string{"E1", "E2"},
113+
ActionMode: ActionModeParallel,
114+
},
115+
err: ``,
116+
},
117+
{
118+
desp: "default action mode",
119+
data: `{"eventRefs": ["E1", "E2"]}`,
120+
expect: OnEvents{
121+
EventRefs: []string{"E1", "E2"},
122+
ActionMode: ActionModeSequential,
123+
},
124+
err: ``,
125+
},
126+
{
127+
desp: "invalid object format",
128+
data: `"eventRefs": ["E1", "E2"], "actionMode": "parallel"}`,
129+
expect: OnEvents{},
130+
err: `invalid character ':' after top-level value`,
131+
},
132+
}
133+
for _, tc := range testCases {
134+
t.Run(tc.desp, func(t *testing.T) {
135+
v := OnEvents{}
136+
err := json.Unmarshal([]byte(tc.data), &v)
137+
138+
if tc.err != "" {
139+
assert.Error(t, err)
140+
assert.Regexp(t, tc.err, err)
141+
return
142+
}
143+
144+
assert.NoError(t, err)
145+
assert.Equal(t, tc.expect, v)
146+
})
147+
}
148+
}

model/states.go

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -126,58 +126,6 @@ func (s *BaseState) GetStateDataFilter() *StateDataFilter { return s.StateDataFi
126126
// GetMetadata ...
127127
func (s *BaseState) GetMetadata() *Metadata { return s.Metadata }
128128

129-
// EventState This state is used to wait for events from event sources, then consumes them and invoke one or more actions to run in sequence or parallel
130-
type EventState struct {
131-
BaseState
132-
// If true consuming one of the defined events causes its associated actions to be performed. If false all of the defined events must be consumed in order for actions to be performed
133-
Exclusive bool `json:"exclusive,omitempty"`
134-
// Define the events to be consumed and optional actions to be performed
135-
OnEvents []OnEvents `json:"onEvents" validate:"required,min=1,dive"`
136-
// State specific timeouts
137-
Timeout *EventStateTimeout `json:"timeouts,omitempty"`
138-
}
139-
140-
// UnmarshalJSON ...
141-
func (e *EventState) UnmarshalJSON(data []byte) error {
142-
if err := json.Unmarshal(data, &e.BaseState); err != nil {
143-
return err
144-
}
145-
146-
eventStateMap := make(map[string]interface{})
147-
if err := json.Unmarshal(data, &eventStateMap); err != nil {
148-
return err
149-
}
150-
151-
e.Exclusive = true
152-
153-
if eventStateMap["exclusive"] != nil {
154-
exclusiveVal, ok := eventStateMap["exclusive"].(bool)
155-
if ok {
156-
e.Exclusive = exclusiveVal
157-
}
158-
}
159-
160-
eventStateRaw := make(map[string]json.RawMessage)
161-
if err := json.Unmarshal(data, &eventStateRaw); err != nil {
162-
return err
163-
}
164-
if err := json.Unmarshal(eventStateRaw["onEvents"], &e.OnEvents); err != nil {
165-
return err
166-
}
167-
if err := unmarshalKey("timeouts", eventStateRaw, &e.Timeout); err != nil {
168-
return err
169-
}
170-
171-
return nil
172-
}
173-
174-
// EventStateTimeout ...
175-
type EventStateTimeout struct {
176-
StateExecTimeout StateExecTimeout `json:"stateExecTimeout,omitempty"`
177-
ActionExecTimeout string `json:"actionExecTimeout,omitempty"`
178-
EventTimeout string `json:"eventTimeout,omitempty"`
179-
}
180-
181129
// OperationState Defines actions be performed. Does not wait for incoming events
182130
type OperationState struct {
183131
BaseState
@@ -244,6 +192,7 @@ type ForEachState struct {
244192
// State specific timeout
245193
Timeouts *ForEachStateTimeout `json:"timeouts,omitempty"`
246194
// Mode Specifies how iterations are to be performed (sequentially or in parallel)
195+
// Defaults to parallel
247196
Mode ForEachModeType `json:"mode,omitempty"`
248197
}
249198

model/workflow.go

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ package model
1717
import (
1818
"encoding/json"
1919
"fmt"
20+
"reflect"
21+
2022
"github.com/go-playground/validator/v10"
2123
val "github.com/serverlessworkflow/sdk-go/v2/validator"
22-
"reflect"
2324
)
2425

2526
// InvokeKind defines how the target is invoked.
@@ -33,13 +34,21 @@ const (
3334
InvokeKindAsync InvokeKind = "async"
3435
)
3536

37+
// ActionMode specifies how actions are to be performed.
38+
type ActionMode string
39+
3640
const (
37-
// DefaultExpressionLang ...
38-
DefaultExpressionLang = "jq"
39-
// ActionModeSequential ...
41+
// ActionModeSequential specifies actions should be performed in sequence
4042
ActionModeSequential ActionMode = "sequential"
41-
// ActionModeParallel ...
43+
44+
// ActionModeParallel specifies actions should be performed in parallel
4245
ActionModeParallel ActionMode = "parallel"
46+
)
47+
48+
const (
49+
// DefaultExpressionLang ...
50+
DefaultExpressionLang = "jq"
51+
4352
// UnlimitedTimeout description for unlimited timeouts
4453
UnlimitedTimeout = "unlimited"
4554
)
@@ -75,9 +84,6 @@ func continueAsStructLevelValidation(structLevel validator.StructLevel) {
7584
}
7685
}
7786

78-
// ActionMode ...
79-
type ActionMode string
80-
8187
// BaseWorkflow describes the partial Workflow definition that does not rely on generic interfaces
8288
// to make it easy for custom unmarshalers implementations to unmarshal the common data structure.
8389
type BaseWorkflow struct {
@@ -462,18 +468,6 @@ type OnError struct {
462468
End *End `json:"end,omitempty"`
463469
}
464470

465-
// OnEvents ...
466-
type OnEvents struct {
467-
// References one or more unique event names in the defined workflow events
468-
EventRefs []string `json:"eventRefs" validate:"required,min=1"`
469-
// Specifies how actions are to be performed (in sequence of parallel)
470-
ActionMode ActionMode `json:"actionMode,omitempty"`
471-
// Actions to be performed if expression matches
472-
Actions []Action `json:"actions,omitempty" validate:"omitempty,dive"`
473-
// Event data filter
474-
EventDataFilter EventDataFilter `json:"eventDataFilter,omitempty"`
475-
}
476-
477471
// End definition
478472
type End struct {
479473
// If true, completes all execution flows in the given workflow instance

0 commit comments

Comments
 (0)