Skip to content

Commit 7b28917

Browse files
authored
fix(122): split switch state to separate file (#127)
Signed-off-by: lsytj0413 <511121939@qq.com> Signed-off-by: lsytj0413 <511121939@qq.com>
1 parent 09942a6 commit 7b28917

11 files changed

+746
-341
lines changed

model/states.go

Lines changed: 1 addition & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414

1515
package model
1616

17-
import (
18-
"encoding/json"
19-
)
20-
2117
const (
2218
// StateTypeDelay ...
2319
StateTypeDelay = "delay"
@@ -50,10 +46,7 @@ func getActionsModelMapping(stateType string, s map[string]interface{}) (State,
5046
case StateTypeParallel:
5147
return &ParallelState{}, true
5248
case StateTypeSwitch:
53-
if _, ok := s["dataConditions"]; ok {
54-
return &DataBasedSwitchState{}, true
55-
}
56-
return &EventBasedSwitchState{}, true
49+
return &SwitchState{}, true
5750
case StateTypeInject:
5851
return &InjectState{}, true
5952
case StateTypeForEach:
@@ -135,200 +128,3 @@ func (s *BaseState) GetStateDataFilter() *StateDataFilter { return s.StateDataFi
135128

136129
// GetMetadata ...
137130
func (s *BaseState) GetMetadata() *Metadata { return s.Metadata }
138-
139-
// BaseSwitchState ...
140-
type BaseSwitchState struct {
141-
BaseState
142-
// Default transition of the workflow if there is no matching data conditions. Can include a transition or end definition
143-
DefaultCondition DefaultCondition `json:"defaultCondition,omitempty"`
144-
}
145-
146-
// EventBasedSwitchState Permits transitions to other states based on events
147-
type EventBasedSwitchState struct {
148-
BaseSwitchState
149-
// Defines conditions evaluated against events
150-
EventConditions []EventCondition `json:"eventConditions" validate:"required,min=1,dive"`
151-
// State specific timeouts
152-
Timeouts *EventBasedSwitchStateTimeout `json:"timeouts,omitempty"`
153-
}
154-
155-
// UnmarshalJSON implementation for json Unmarshal function for the EventBasedSwitch type
156-
func (j *EventBasedSwitchState) UnmarshalJSON(data []byte) error {
157-
if err := json.Unmarshal(data, &j.BaseSwitchState); err != nil {
158-
return err
159-
}
160-
eventBasedSwitch := make(map[string]json.RawMessage)
161-
if err := json.Unmarshal(data, &eventBasedSwitch); err != nil {
162-
return err
163-
}
164-
165-
eventBaseTimeoutsRawMessage, ok := eventBasedSwitch["timeouts"]
166-
if ok {
167-
if err := json.Unmarshal(eventBaseTimeoutsRawMessage, &j.Timeouts); err != nil {
168-
return err
169-
}
170-
}
171-
172-
var rawConditions []json.RawMessage
173-
if err := json.Unmarshal(eventBasedSwitch["eventConditions"], &rawConditions); err != nil {
174-
return err
175-
}
176-
177-
j.EventConditions = make([]EventCondition, len(rawConditions))
178-
var mapConditions map[string]interface{}
179-
for i, rawCondition := range rawConditions {
180-
if err := json.Unmarshal(rawCondition, &mapConditions); err != nil {
181-
return err
182-
}
183-
var condition EventCondition
184-
if _, ok := mapConditions["end"]; ok {
185-
condition = &EndEventCondition{}
186-
} else {
187-
condition = &TransitionEventCondition{}
188-
}
189-
if err := json.Unmarshal(rawCondition, condition); err != nil {
190-
return err
191-
}
192-
j.EventConditions[i] = condition
193-
}
194-
195-
return nil
196-
}
197-
198-
// EventBasedSwitchStateTimeout ...
199-
type EventBasedSwitchStateTimeout struct {
200-
StateExecTimeout *StateExecTimeout `json:"stateExecTimeout,omitempty"`
201-
EventTimeout string `json:"eventTimeout,omitempty"`
202-
}
203-
204-
// EventCondition ...
205-
type EventCondition interface {
206-
GetName() string
207-
GetEventRef() string
208-
GetEventDataFilter() EventDataFilter
209-
GetMetadata() Metadata
210-
}
211-
212-
// BaseEventCondition ...
213-
type BaseEventCondition struct {
214-
// Event condition name
215-
Name string `json:"name,omitempty"`
216-
// References a unique event name in the defined workflow events
217-
EventRef string `json:"eventRef" validate:"required"`
218-
// Event data filter definition
219-
EventDataFilter EventDataFilter `json:"eventDataFilter,omitempty"`
220-
Metadata Metadata `json:"metadata,omitempty"`
221-
}
222-
223-
// GetEventRef ...
224-
func (e *BaseEventCondition) GetEventRef() string { return e.EventRef }
225-
226-
// GetEventDataFilter ...
227-
func (e *BaseEventCondition) GetEventDataFilter() EventDataFilter { return e.EventDataFilter }
228-
229-
// GetMetadata ...
230-
func (e *BaseEventCondition) GetMetadata() Metadata { return e.Metadata }
231-
232-
// GetName ...
233-
func (e *BaseEventCondition) GetName() string { return e.Name }
234-
235-
// TransitionEventCondition Switch state data event condition
236-
type TransitionEventCondition struct {
237-
BaseEventCondition
238-
// Next transition of the workflow if there is valid matches
239-
Transition Transition `json:"transition" validate:"required"`
240-
}
241-
242-
// EndEventCondition Switch state data event condition
243-
type EndEventCondition struct {
244-
BaseEventCondition
245-
// Explicit transition to end
246-
End End `json:"end" validate:"required"`
247-
}
248-
249-
// DataBasedSwitchState Permits transitions to other states based on data conditions
250-
type DataBasedSwitchState struct {
251-
BaseSwitchState
252-
DataConditions []DataCondition `json:"dataConditions" validate:"required,min=1,dive"`
253-
Timeouts *DataBasedSwitchStateTimeout `json:"timeouts,omitempty"`
254-
}
255-
256-
// UnmarshalJSON implementation for json Unmarshal function for the DataBasedSwitch type
257-
func (j *DataBasedSwitchState) UnmarshalJSON(data []byte) error {
258-
if err := json.Unmarshal(data, &j.BaseSwitchState); err != nil {
259-
return err
260-
}
261-
dataBasedSwitch := make(map[string]json.RawMessage)
262-
if err := json.Unmarshal(data, &dataBasedSwitch); err != nil {
263-
return err
264-
}
265-
if err := json.Unmarshal(data, &j.Timeouts); err != nil {
266-
return err
267-
}
268-
var rawConditions []json.RawMessage
269-
if err := json.Unmarshal(dataBasedSwitch["dataConditions"], &rawConditions); err != nil {
270-
return err
271-
}
272-
j.DataConditions = make([]DataCondition, len(rawConditions))
273-
var mapConditions map[string]interface{}
274-
for i, rawCondition := range rawConditions {
275-
if err := json.Unmarshal(rawCondition, &mapConditions); err != nil {
276-
return err
277-
}
278-
var condition DataCondition
279-
if _, ok := mapConditions["end"]; ok {
280-
condition = &EndDataCondition{}
281-
} else {
282-
condition = &TransitionDataCondition{}
283-
}
284-
if err := json.Unmarshal(rawCondition, condition); err != nil {
285-
return err
286-
}
287-
j.DataConditions[i] = condition
288-
}
289-
return nil
290-
}
291-
292-
// DataBasedSwitchStateTimeout ...
293-
type DataBasedSwitchStateTimeout struct {
294-
StateExecTimeout *StateExecTimeout `json:"stateExecTimeout,omitempty"`
295-
}
296-
297-
// DataCondition ...
298-
type DataCondition interface {
299-
GetName() string
300-
GetCondition() string
301-
GetMetadata() Metadata
302-
}
303-
304-
// BaseDataCondition ...
305-
type BaseDataCondition struct {
306-
// Data condition name
307-
Name string `json:"name,omitempty"`
308-
// Workflow expression evaluated against state data. Must evaluate to true or false
309-
Condition string `json:"condition" validate:"required"`
310-
Metadata Metadata `json:"metadata,omitempty"`
311-
}
312-
313-
// GetName ...
314-
func (b *BaseDataCondition) GetName() string { return b.Name }
315-
316-
// GetCondition ...
317-
func (b *BaseDataCondition) GetCondition() string { return b.Condition }
318-
319-
// GetMetadata ...
320-
func (b *BaseDataCondition) GetMetadata() Metadata { return b.Metadata }
321-
322-
// TransitionDataCondition ...
323-
type TransitionDataCondition struct {
324-
BaseDataCondition
325-
// Workflow transition if condition is evaluated to true
326-
Transition Transition `json:"transition" validate:"required"`
327-
}
328-
329-
// EndDataCondition ...
330-
type EndDataCondition struct {
331-
BaseDataCondition
332-
// Workflow end definition
333-
End End `json:"end" validate:"required"`
334-
}

model/switch_state.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
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+
"context"
19+
"reflect"
20+
21+
val "github.com/serverlessworkflow/sdk-go/v2/validator"
22+
23+
"github.com/go-playground/validator/v10"
24+
)
25+
26+
func init() {
27+
val.GetValidator().RegisterStructValidationCtx(SwitchStateStructLevelValidation, SwitchState{})
28+
val.GetValidator().RegisterStructValidationCtx(DefaultConditionStructLevelValidation, DefaultCondition{})
29+
val.GetValidator().RegisterStructValidationCtx(EventConditionStructLevelValidation, EventCondition{})
30+
val.GetValidator().RegisterStructValidationCtx(DataConditionStructLevelValidation, DataCondition{})
31+
}
32+
33+
// SwitchState is workflow's gateways: direct transitions onf a workflow based on certain conditions.
34+
type SwitchState struct {
35+
// TODO: don't use BaseState for this, there are a few fields that SwitchState don't need.
36+
BaseState
37+
38+
// Default transition of the workflow if there is no matching data conditions. Can include a transition or end definition
39+
// Required
40+
DefaultCondition DefaultCondition `json:"defaultCondition"`
41+
// Defines conditions evaluated against events
42+
EventConditions []EventCondition `json:"eventConditions" validate:"omitempty,min=1,dive"`
43+
// Defines conditions evaluated against data
44+
DataConditions []DataCondition `json:"dataConditions" validate:"omitempty,min=1,dive"`
45+
// SwitchState specific timeouts
46+
Timeouts *SwitchStateTimeout `json:"timeouts,omitempty"`
47+
}
48+
49+
// SwitchStateStructLevelValidation custom validator for SwitchState
50+
func SwitchStateStructLevelValidation(ctx context.Context, structLevel validator.StructLevel) {
51+
switchState := structLevel.Current().Interface().(SwitchState)
52+
switch {
53+
case len(switchState.DataConditions) == 0 && len(switchState.EventConditions) == 0:
54+
structLevel.ReportError(reflect.ValueOf(switchState), "DataConditions", "dataConditions", "required", "must have one of dataCnoditions, eventConditions")
55+
case len(switchState.DataConditions) > 0 && len(switchState.EventConditions) > 0:
56+
structLevel.ReportError(reflect.ValueOf(switchState), "DataConditions", "dataConditions", "exclusive", "must have one of dataCnoditions, eventConditions")
57+
}
58+
}
59+
60+
// DefaultCondition Can be either a transition or end definition
61+
type DefaultCondition struct {
62+
Transition *Transition `json:"transition,omitempty"`
63+
End *End `json:"end,omitempty"`
64+
}
65+
66+
// DefaultConditionStructLevelValidation custom validator for DefaultCondition
67+
func DefaultConditionStructLevelValidation(ctx context.Context, structLevel validator.StructLevel) {
68+
defaultCondition := structLevel.Current().Interface().(DefaultCondition)
69+
switch {
70+
case defaultCondition.End == nil && defaultCondition.Transition == nil:
71+
structLevel.ReportError(reflect.ValueOf(defaultCondition), "Transition", "transition", "required", "must have one of transition, end")
72+
case defaultCondition.Transition != nil && defaultCondition.End != nil:
73+
structLevel.ReportError(reflect.ValueOf(defaultCondition), "Transition", "transition", "exclusive", "must have one of transition, end")
74+
}
75+
}
76+
77+
// SwitchStateTimeout defines the specific timeout settings for switch state
78+
type SwitchStateTimeout struct {
79+
StateExecTimeout *StateExecTimeout `json:"stateExecTimeout,omitempty"`
80+
81+
// EventTimeout specify the expire value to transitions to defaultCondition
82+
// when event-based conditions do not arrive.
83+
// NOTE: this is only available for EventConditions
84+
EventTimeout string `json:"eventTimeout,omitempty" validate:"omitempty,iso8601duration"`
85+
}
86+
87+
// EventCondition specify events which the switch state must wait for.
88+
type EventCondition struct {
89+
// Event condition name
90+
Name string `json:"name,omitempty"`
91+
// References a unique event name in the defined workflow events
92+
EventRef string `json:"eventRef" validate:"required"`
93+
// Event data filter definition
94+
EventDataFilter EventDataFilter `json:"eventDataFilter,omitempty"`
95+
Metadata Metadata `json:"metadata,omitempty"`
96+
97+
// Explicit transition to end
98+
End *End `json:"end" validate:"omitempty"`
99+
// Workflow transition if condition is evaluated to true
100+
Transition *Transition `json:"transition" validate:"omitempty"`
101+
}
102+
103+
// EventConditionStructLevelValidation custom validator for EventCondition
104+
func EventConditionStructLevelValidation(ctx context.Context, structLevel validator.StructLevel) {
105+
eventCondition := structLevel.Current().Interface().(EventCondition)
106+
switch {
107+
case eventCondition.End == nil && eventCondition.Transition == nil:
108+
structLevel.ReportError(reflect.ValueOf(eventCondition), "Transition", "transition", "required", "must have one of transition, end")
109+
case eventCondition.Transition != nil && eventCondition.End != nil:
110+
structLevel.ReportError(reflect.ValueOf(eventCondition), "Transition", "transition", "exclusive", "must have one of transition, end")
111+
}
112+
}
113+
114+
// DataCondition specify a data-based condition statement which causes a transition to another workflow state
115+
// if evaluated to true.
116+
type DataCondition struct {
117+
// Data condition name
118+
Name string `json:"name,omitempty"`
119+
// Workflow expression evaluated against state data. Must evaluate to true or false
120+
Condition string `json:"condition" validate:"required"`
121+
Metadata Metadata `json:"metadata,omitempty"`
122+
123+
// Explicit transition to end
124+
End *End `json:"end" validate:"omitempty"`
125+
// Workflow transition if condition is evaluated to true
126+
Transition *Transition `json:"transition" validate:"omitempty"`
127+
}
128+
129+
// DataConditionStructLevelValidation custom validator for DataCondition
130+
func DataConditionStructLevelValidation(ctx context.Context, structLevel validator.StructLevel) {
131+
dataCondition := structLevel.Current().Interface().(DataCondition)
132+
switch {
133+
case dataCondition.End == nil && dataCondition.Transition == nil:
134+
structLevel.ReportError(reflect.ValueOf(dataCondition), "Transition", "transition", "required", "must have one of transition, end")
135+
case dataCondition.Transition != nil && dataCondition.End != nil:
136+
structLevel.ReportError(reflect.ValueOf(dataCondition), "Transition", "transition", "exclusive", "must have one of transition, end")
137+
}
138+
}

0 commit comments

Comments
 (0)