@@ -0,0 +1,28 @@ | |||
package constant | |||
const ( | |||
VarNameProcessType string = "_ProcessType_" | |||
VarNameOperationName string = "_operation_name_" | |||
OperationNameStart string = "start" | |||
VarNameAsyncCallback string = "_async_callback_" | |||
VarNameStateMachineInst string = "_current_statemachine_instance_" | |||
VarNameStateMachine string = "_current_statemachine_" | |||
VarNameStateMachineEngine string = "_current_statemachine_engine_" | |||
VarNameStateMachineConfig string = "_statemachine_config_" | |||
VarNameStateMachineContext string = "context" | |||
VarNameIsAsyncExecution string = "_is_async_execution_" | |||
VarNameStateInst string = "_current_state_instance_" | |||
SeqEntityStateMachineInst string = "STATE_MACHINE_INST" | |||
VarNameBusinesskey string = "_business_key_" | |||
VarNameParentId string = "_parent_id_" | |||
StateTypeServiceTask string = "ServiceTask" | |||
StateTypeChoice string = "Choice" | |||
StateTypeSubStateMachine string = "SubStateMachine" | |||
CompensateSubMachine string = "CompensateSubMachine" | |||
StateTypeSucceed string = "Succeed" | |||
StateTypeFail string = "Fail" | |||
StateTypeCompensationTrigger string = "CompensationTrigger" | |||
StateTypeScriptTask string = "ScriptTask" | |||
CompensateSubMachineStateNamePrefix string = "_compensate_sub_machine_state_" | |||
DefaultScriptType string = "groovy" | |||
) |
@@ -1,20 +0,0 @@ | |||
package constant | |||
const ( | |||
VarNameProcessType string = "_ProcessType_" | |||
VarNameOperationName string = "_operation_name_" | |||
OperationNameStart string = "start" | |||
VarNameAsyncCallback string = "_async_callback_" | |||
VarNameStateMachineInst string = "_current_statemachine_instance_" | |||
VarNameStateMachine string = "_current_statemachine_" | |||
VarNameStateMachineEngine string = "_current_statemachine_engine_" | |||
VarNameStateMachineConfig string = "_statemachine_config_" | |||
VarNameStateMachineContext string = "context" | |||
VarNameIsAsyncExecution string = "_is_async_execution_" | |||
VarNameStateInst string = "_current_state_instance_" | |||
SeqEntityStateMachineInst string = "STATE_MACHINE_INST" | |||
VarNameBusinesskey string = "_business_key_" | |||
VarNameParentId string = "_parent_id_" | |||
StateTypeServiceTask string = "ServiceTask" | |||
StateTypeChoice string = "Choice" | |||
) |
@@ -3,21 +3,23 @@ package parser | |||
import ( | |||
"fmt" | |||
"github.com/pkg/errors" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/engine" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang/state" | |||
) | |||
type ChoiceStateParser struct { | |||
BaseStateParser | |||
*BaseStateParser | |||
} | |||
func NewChoiceStateParser() *ChoiceStateParser { | |||
return &ChoiceStateParser{} | |||
return &ChoiceStateParser{ | |||
&BaseStateParser{}, | |||
} | |||
} | |||
func (c ChoiceStateParser) StateType() string { | |||
return engine.StateTypeChoice | |||
return constant.StateTypeChoice | |||
} | |||
func (c ChoiceStateParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
@@ -0,0 +1,31 @@ | |||
package parser | |||
import ( | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang/state" | |||
) | |||
type CompensationTriggerStateParser struct { | |||
*BaseStateParser | |||
} | |||
func NewCompensationTriggerStateParser() *CompensationTriggerStateParser { | |||
return &CompensationTriggerStateParser{ | |||
&BaseStateParser{}, | |||
} | |||
} | |||
func (c CompensationTriggerStateParser) StateType() string { | |||
return constant.StateTypeCompensationTrigger | |||
} | |||
func (c CompensationTriggerStateParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
compensateSubStateMachineStateImpl := state.NewCompensationTriggerStateImpl() | |||
err := c.ParseBaseAttributes(stateName, compensateSubStateMachineStateImpl, stateMap) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return compensateSubStateMachineStateImpl, nil | |||
} |
@@ -0,0 +1,66 @@ | |||
package parser | |||
import ( | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang/state" | |||
) | |||
type SucceedEndStateParser struct { | |||
*BaseStateParser | |||
} | |||
func NewSucceedEndStateParser() *SucceedEndStateParser { | |||
return &SucceedEndStateParser{ | |||
&BaseStateParser{}, | |||
} | |||
} | |||
func (s SucceedEndStateParser) StateType() string { | |||
return constant.StateTypeSucceed | |||
} | |||
func (s SucceedEndStateParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
succeedEndStateImpl := state.NewSucceedEndStateImpl() | |||
err := s.ParseBaseAttributes(stateName, succeedEndStateImpl, stateMap) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return succeedEndStateImpl, nil | |||
} | |||
type FailEndStateParser struct { | |||
*BaseStateParser | |||
} | |||
func NewFailEndStateParser() *FailEndStateParser { | |||
return &FailEndStateParser{ | |||
&BaseStateParser{}, | |||
} | |||
} | |||
func (f FailEndStateParser) StateType() string { | |||
return constant.StateTypeFail | |||
} | |||
func (f FailEndStateParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
failEndStateImpl := state.NewFailEndStateImpl() | |||
err := f.ParseBaseAttributes(stateName, failEndStateImpl, stateMap) | |||
if err != nil { | |||
return nil, err | |||
} | |||
errorCode, err := f.GetStringOrDefault(stateName, stateMap, "ErrorCode", "") | |||
if err != nil { | |||
return nil, err | |||
} | |||
failEndStateImpl.SetErrorCode(errorCode) | |||
message, err := f.GetStringOrDefault(stateName, stateMap, "Message", "") | |||
if err != nil { | |||
return nil, err | |||
} | |||
failEndStateImpl.SetMessage(message) | |||
return failEndStateImpl, nil | |||
} |
@@ -3,14 +3,19 @@ package parser | |||
import ( | |||
"encoding/json" | |||
"github.com/pkg/errors" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang/state" | |||
) | |||
type JSONStateMachineParser struct { | |||
*BaseStateParser | |||
} | |||
func NewJSONStateMachineParser() *JSONStateMachineParser { | |||
return &JSONStateMachineParser{} | |||
return &JSONStateMachineParser{ | |||
&BaseStateParser{}, | |||
} | |||
} | |||
func (stateMachineParser JSONStateMachineParser) GetType() string { | |||
@@ -77,15 +82,40 @@ func (stateMachineParser JSONStateMachineParser) Parse(content string) (statelan | |||
stateMachine.States()[stateName] = state | |||
} | |||
//TODO setCompensateState | |||
//for stateName, state := range stateMachine.GetStates() { | |||
// | |||
//} | |||
// | |||
for _, stateValue := range stateMachine.States() { | |||
if stateMachineParser.isTaskState(stateValue.Type()) { | |||
stateMachineParser.setForCompensation(stateValue, stateMachine) | |||
} | |||
} | |||
return stateMachine, nil | |||
} | |||
func (stateMachineParser JSONStateMachineParser) setForCompensation(stateValue statelang.State, stateMachine *statelang.StateMachineImpl) { | |||
switch stateValue.Type() { | |||
case stateValue.Type(): | |||
serviceTaskStateImpl, ok := stateValue.(*state.ServiceTaskStateImpl) | |||
if ok { | |||
if serviceTaskStateImpl.CompensateState() != "" { | |||
compState := stateMachine.States()[serviceTaskStateImpl.CompensateState()] | |||
if stateMachineParser.isTaskState(compState.Type()) { | |||
compStateImpl, ok := compState.(state.ServiceTaskStateImpl) | |||
if ok { | |||
compStateImpl.SetForCompensation(true) | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
func (stateMachineParser JSONStateMachineParser) isTaskState(stateType string) bool { | |||
if stateType == constant.StateTypeServiceTask { | |||
return true | |||
} | |||
return false | |||
} | |||
type StateMachineJsonObject struct { | |||
Name string `json:"Name"` | |||
Comment string `json:"Comment"` | |||
@@ -1,12 +1,44 @@ | |||
package parser | |||
import ( | |||
"os" | |||
"testing" | |||
) | |||
func TestParseChoice(t *testing.T) { | |||
var content = "{\n \"Name\":\"ChoiceTest\",\n \"Comment\":\"ChoiceTest\",\n \"StartState\":\"ChoiceState\",\n \"Version\":\"0.0.1\",\n \"States\":{\n \"ChoiceState\":{\n \"Type\":\"Choice\",\n \"Choices\":[\n {\n \"Expression\":\"[a] == 1\",\n \"Next\":\"SecondState\"\n },\n {\n \"Expression\":\"[a] == 2\",\n \"Next\":\"ThirdState\"\n }\n ],\n \"Default\":\"Fail\"\n }\n }\n}" | |||
_, err := NewJSONStateMachineParser().Parse(content) | |||
filePath := "../../../../../testdata/saga/statelang/simple_statelang_with_choice.json" | |||
fileContent, err := os.ReadFile(filePath) | |||
if err != nil { | |||
t.Error("parse fail: " + err.Error()) | |||
return | |||
} | |||
_, err = NewJSONStateMachineParser().Parse(string(fileContent)) | |||
if err != nil { | |||
t.Error("parse fail: " + err.Error()) | |||
} | |||
} | |||
func TestParseServiceTaskForSimpleStateMachine(t *testing.T) { | |||
filePath := "../../../../../testdata/saga/statelang/simple_statemachine.json" | |||
fileContent, err := os.ReadFile(filePath) | |||
if err != nil { | |||
t.Error("parse fail: " + err.Error()) | |||
return | |||
} | |||
_, err = NewJSONStateMachineParser().Parse(string(fileContent)) | |||
if err != nil { | |||
t.Error("parse fail: " + err.Error()) | |||
} | |||
} | |||
func TestParseServiceTaskForNewDesigner(t *testing.T) { | |||
filePath := "../../../../../testdata/saga/statelang/state_machine_new_designer.json" | |||
fileContent, err := os.ReadFile(filePath) | |||
if err != nil { | |||
t.Error("parse fail: " + err.Error()) | |||
return | |||
} | |||
_, err = NewJSONStateMachineParser().Parse(string(fileContent)) | |||
if err != nil { | |||
t.Error("parse fail: " + err.Error()) | |||
} | |||
@@ -3,6 +3,8 @@ package parser | |||
import ( | |||
"github.com/pkg/errors" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"strconv" | |||
"strings" | |||
"sync" | |||
) | |||
@@ -19,20 +21,23 @@ type StateParser interface { | |||
type BaseStateParser struct { | |||
} | |||
func NewBaseStateParser() *BaseStateParser { | |||
return &BaseStateParser{} | |||
} | |||
func (b BaseStateParser) ParseBaseAttributes(stateName string, state statelang.State, stateMap map[string]interface{}) error { | |||
state.SetName(stateName) | |||
comment, err := b.GetString(stateName, stateMap, "Comment") | |||
comment, err := b.GetStringOrDefault(stateName, stateMap, "Comment", "") | |||
if err != nil { | |||
return err | |||
} | |||
state.SetComment(comment) | |||
next, err := b.GetString(stateName, stateMap, "Next") | |||
next, err := b.GetStringOrDefault(stateName, stateMap, "Next", "") | |||
if err != nil { | |||
return err | |||
} | |||
state.SetNext(next) | |||
return nil | |||
} | |||
@@ -52,9 +57,21 @@ func (b BaseStateParser) GetString(stateName string, stateMap map[string]interfa | |||
return valueAsString, nil | |||
} | |||
func (b BaseStateParser) GetSlice(stateName string, stateMap map[string]interface{}, key string) ([]interface{}, error) { | |||
func (b BaseStateParser) GetStringOrDefault(stateName string, stateMap map[string]interface{}, key string, defaultValue string) (string, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
return defaultValue, nil | |||
} | |||
valueAsString, ok := value.(string) | |||
if !ok { | |||
return defaultValue, errors.New("State [" + stateName + "] " + key + " illegal, required string") | |||
} | |||
return valueAsString, nil | |||
} | |||
func (b BaseStateParser) GetSlice(stateName string, stateMap map[string]interface{}, key string) ([]interface{}, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
var result []interface{} | |||
return result, errors.New("State [" + stateName + "] " + key + " not exist") | |||
@@ -62,12 +79,103 @@ func (b BaseStateParser) GetSlice(stateName string, stateMap map[string]interfac | |||
valueAsSlice, ok := value.([]interface{}) | |||
if !ok { | |||
var result []interface{} | |||
return result, errors.New("State [" + stateName + "] " + key + " illegal, required slice") | |||
var slice []interface{} | |||
return slice, errors.New("State [" + stateName + "] " + key + " illegal, required []interface{}") | |||
} | |||
return valueAsSlice, nil | |||
} | |||
func (b BaseStateParser) GetSliceOrDefault(stateName string, stateMap map[string]interface{}, key string, defaultValue []interface{}) ([]interface{}, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
return defaultValue, nil | |||
} | |||
valueAsSlice, ok := value.([]interface{}) | |||
if !ok { | |||
return defaultValue, errors.New("State [" + stateName + "] " + key + " illegal, required []interface{}") | |||
} | |||
return valueAsSlice, nil | |||
} | |||
func (b BaseStateParser) GetMapOrDefault(stateMap map[string]interface{}, key string, defaultValue map[string]interface{}) (map[string]interface{}, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
return defaultValue, nil | |||
} | |||
valueAsMap, ok := value.(map[string]interface{}) | |||
if !ok { | |||
return defaultValue, nil | |||
} | |||
return valueAsMap, nil | |||
} | |||
func (b BaseStateParser) GetBool(stateName string, stateMap map[string]interface{}, key string) (bool, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
return false, errors.New("State [" + stateName + "] " + key + " not exist") | |||
} | |||
valueAsBool, ok := value.(bool) | |||
if !ok { | |||
return false, errors.New("State [" + stateName + "] " + key + " illegal, required bool") | |||
} | |||
return valueAsBool, nil | |||
} | |||
func (b BaseStateParser) GetBoolOrDefault(stateName string, stateMap map[string]interface{}, key string, defaultValue bool) (bool, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
return defaultValue, nil | |||
} | |||
valueAsBool, ok := value.(bool) | |||
if !ok { | |||
return false, errors.New("State [" + stateName + "] " + key + " illegal, required bool") | |||
} | |||
return valueAsBool, nil | |||
} | |||
func (b BaseStateParser) GetIntOrDefault(stateName string, stateMap map[string]interface{}, key string, defaultValue int) (int, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
return defaultValue, nil | |||
} | |||
// just use float64 to convert, json reader will read all number as float64 | |||
valueAsFloat64, ok := value.(float64) | |||
if !ok { | |||
return defaultValue, errors.New("State [" + stateName + "] " + key + " illegal, required int") | |||
} | |||
floatStr := strconv.FormatFloat(valueAsFloat64, 'f', -1, 64) | |||
if strings.Contains(floatStr, ".") { | |||
return defaultValue, errors.New("State [" + stateName + "] " + key + " illegal, required int") | |||
} | |||
return int(valueAsFloat64), nil | |||
} | |||
func (b BaseStateParser) GetFloat64OrDefault(stateName string, stateMap map[string]interface{}, key string, defaultValue float64) (float64, error) { | |||
value := stateMap[key] | |||
if value == nil { | |||
return defaultValue, nil | |||
} | |||
valueAsFloat64, ok := value.(float64) | |||
if !ok { | |||
return defaultValue, errors.New("State [" + stateName + "] " + key + " illegal, required float64") | |||
} | |||
return valueAsFloat64, nil | |||
} | |||
type StateParserFactory interface { | |||
RegistryStateParser(stateType string, stateParser StateParser) | |||
@@ -89,8 +197,21 @@ func NewDefaultStateParserFactory() *DefaultStateParserFactory { | |||
// InitDefaultStateParser init StateParser by default | |||
func (d *DefaultStateParserFactory) InitDefaultStateParser() { | |||
choiceStateParser := NewChoiceStateParser() | |||
serviceTaskStateParser := NewServiceTaskStateParser() | |||
subStateMachineParser := NewSubStateMachineParser() | |||
succeedEndStateParser := NewSucceedEndStateParser() | |||
compensationTriggerStateParser := NewCompensationTriggerStateParser() | |||
failEndStateParser := NewFailEndStateParser() | |||
scriptTaskStateParser := NewScriptTaskStateParser() | |||
d.RegistryStateParser(choiceStateParser.StateType(), choiceStateParser) | |||
d.RegistryStateParser(serviceTaskStateParser.StateType(), serviceTaskStateParser) | |||
d.RegistryStateParser(subStateMachineParser.StateType(), subStateMachineParser) | |||
d.RegistryStateParser(succeedEndStateParser.StateType(), succeedEndStateParser) | |||
d.RegistryStateParser(compensationTriggerStateParser.StateType(), compensationTriggerStateParser) | |||
d.RegistryStateParser(compensationTriggerStateParser.StateType(), compensationTriggerStateParser) | |||
d.RegistryStateParser(failEndStateParser.StateType(), failEndStateParser) | |||
d.RegistryStateParser(scriptTaskStateParser.StateType(), scriptTaskStateParser) | |||
} | |||
func (d *DefaultStateParserFactory) RegistryStateParser(stateType string, stateParser StateParser) { | |||
@@ -0,0 +1,84 @@ | |||
package parser | |||
import ( | |||
"fmt" | |||
"github.com/pkg/errors" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang/state" | |||
) | |||
type SubStateMachineParser struct { | |||
*AbstractTaskStateParser | |||
} | |||
func NewSubStateMachineParser() *SubStateMachineParser { | |||
return &SubStateMachineParser{ | |||
NewAbstractTaskStateParser(), | |||
} | |||
} | |||
func (s SubStateMachineParser) StateType() string { | |||
return constant.StateTypeSubStateMachine | |||
} | |||
func (s SubStateMachineParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
subStateMachineImpl := state.NewSubStateMachineImpl() | |||
err := s.ParseTaskAttributes(stateName, subStateMachineImpl.AbstractTaskState, stateMap) | |||
if err != nil { | |||
return nil, err | |||
} | |||
stateMachineName, err := s.BaseStateParser.GetString(stateName, stateMap, "StateMachineName") | |||
if err != nil { | |||
return nil, err | |||
} | |||
subStateMachineImpl.SetName(stateMachineName) | |||
if subStateMachineImpl.CompensateState() == "" { | |||
// build default SubStateMachine compensate state | |||
compensateSubStateMachineStateParser := NewCompensateSubStateMachineStateParser() | |||
compensateState, err := compensateSubStateMachineStateParser.Parse(stateName, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
compensateStateImpl, ok := compensateState.(state.TaskState) | |||
if !ok { | |||
return nil, errors.New(fmt.Sprintf("State [name:%s] has wrong compensateState type", stateName)) | |||
} | |||
subStateMachineImpl.SetCompensateStateImpl(compensateStateImpl) | |||
subStateMachineImpl.SetCompensateState(compensateStateImpl.Name()) | |||
} | |||
return subStateMachineImpl, nil | |||
} | |||
type CompensateSubStateMachineStateParser struct { | |||
*AbstractTaskStateParser | |||
} | |||
func NewCompensateSubStateMachineStateParser() *CompensateSubStateMachineStateParser { | |||
return &CompensateSubStateMachineStateParser{ | |||
NewAbstractTaskStateParser(), | |||
} | |||
} | |||
func (c CompensateSubStateMachineStateParser) StateType() string { | |||
return constant.CompensateSubMachine | |||
} | |||
func (c CompensateSubStateMachineStateParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
compensateSubStateMachineStateImpl := state.NewCompensateSubStateMachineStateImpl() | |||
compensateSubStateMachineStateImpl.SetForCompensation(true) | |||
if stateMap != nil { | |||
err := c.ParseTaskAttributes(stateName, compensateSubStateMachineStateImpl.ServiceTaskStateImpl.AbstractTaskState, stateMap) | |||
if err != nil { | |||
return nil, err | |||
} | |||
} | |||
if compensateSubStateMachineStateImpl.Name() == "" { | |||
compensateSubStateMachineStateImpl.SetName(constant.CompensateSubMachineStateNamePrefix + compensateSubStateMachineStateImpl.Hashcode()) | |||
} | |||
return compensateSubStateMachineStateImpl, nil | |||
} |
@@ -0,0 +1,330 @@ | |||
package parser | |||
import ( | |||
"fmt" | |||
"github.com/pkg/errors" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang/state" | |||
) | |||
type AbstractTaskStateParser struct { | |||
*BaseStateParser | |||
} | |||
func NewAbstractTaskStateParser() *AbstractTaskStateParser { | |||
return &AbstractTaskStateParser{ | |||
&BaseStateParser{}, | |||
} | |||
} | |||
func (a *AbstractTaskStateParser) ParseTaskAttributes(stateName string, state *state.AbstractTaskState, stateMap map[string]interface{}) error { | |||
err := a.ParseBaseAttributes(state.Name(), state.BaseState, stateMap) | |||
if err != nil { | |||
return err | |||
} | |||
compensateState, err := a.GetStringOrDefault(stateName, stateMap, "CompensateState", "") | |||
if err != nil { | |||
return err | |||
} | |||
state.SetCompensateState(compensateState) | |||
isForCompensation, err := a.GetBoolOrDefault(stateName, stateMap, "IsForCompensation", false) | |||
if err != nil { | |||
return err | |||
} | |||
state.SetForCompensation(isForCompensation) | |||
isForUpdate, err := a.GetBoolOrDefault(stateName, stateMap, "IsForUpdate", false) | |||
if err != nil { | |||
return err | |||
} | |||
state.SetForUpdate(isForUpdate) | |||
isPersist, err := a.GetBoolOrDefault(stateName, stateMap, "IsPersist", false) | |||
if err != nil { | |||
return err | |||
} | |||
state.SetPersist(isPersist) | |||
isRetryPersistModeUpdate, err := a.GetBoolOrDefault(stateName, stateMap, "IsRetryPersistModeUpdate", false) | |||
if err != nil { | |||
return err | |||
} | |||
state.SetRetryPersistModeUpdate(isRetryPersistModeUpdate) | |||
isCompensatePersistModeUpdate, err := a.GetBoolOrDefault(stateName, stateMap, "IsCompensatePersistModeUpdate", false) | |||
if err != nil { | |||
return err | |||
} | |||
state.SetCompensatePersistModeUpdate(isCompensatePersistModeUpdate) | |||
retryInterfaces, err := a.GetSliceOrDefault(stateName, stateMap, "Retry", nil) | |||
if err != nil { | |||
return err | |||
} | |||
if retryInterfaces != nil { | |||
retries, err := a.parseRetries(state.Name(), retryInterfaces) | |||
if err != nil { | |||
return err | |||
} | |||
state.SetRetry(retries) | |||
} | |||
catchInterfaces, err := a.GetSliceOrDefault(stateName, stateMap, "Catch", nil) | |||
if err != nil { | |||
return err | |||
} | |||
if catchInterfaces != nil { | |||
catches, err := a.parseCatches(state.Name(), catchInterfaces) | |||
if err != nil { | |||
return err | |||
} | |||
state.SetCatches(catches) | |||
} | |||
inputInterfaces, err := a.GetSliceOrDefault(stateName, stateMap, "Input", nil) | |||
if err != nil { | |||
return err | |||
} | |||
if inputInterfaces != nil { | |||
state.SetInput(inputInterfaces) | |||
} | |||
output, err := a.GetMapOrDefault(stateMap, "Output", nil) | |||
if err != nil { | |||
return err | |||
} | |||
if output != nil { | |||
state.SetOutput(output) | |||
} | |||
statusMap, ok := stateMap["Status"].(map[string]string) | |||
if ok { | |||
state.SetStatus(statusMap) | |||
} | |||
loopMap, ok := stateMap["Loop"].(map[string]interface{}) | |||
if ok { | |||
loop := a.parseLoop(stateName, loopMap) | |||
state.SetLoop(loop) | |||
} | |||
return nil | |||
} | |||
func (a *AbstractTaskStateParser) parseLoop(stateName string, loopMap map[string]interface{}) state.Loop { | |||
loopImpl := &state.LoopImpl{} | |||
parallel, err := a.GetIntOrDefault(stateName, loopMap, "Parallel", 1) | |||
if err != nil { | |||
return nil | |||
} | |||
loopImpl.SetParallel(parallel) | |||
collection, err := a.GetStringOrDefault(stateName, loopMap, "Collection", "") | |||
if err != nil { | |||
return nil | |||
} | |||
loopImpl.SetCollection(collection) | |||
elementVariableName, err := a.GetStringOrDefault(stateName, loopMap, "ElementVariableName", "loopElement") | |||
if err != nil { | |||
return nil | |||
} | |||
loopImpl.SetElementVariableName(elementVariableName) | |||
elementIndexName, err := a.GetStringOrDefault(stateName, loopMap, "ElementIndexName", "loopCounter") | |||
if err != nil { | |||
return nil | |||
} | |||
loopImpl.SetElementIndexName(elementIndexName) | |||
completionCondition, err := a.GetStringOrDefault(stateName, loopMap, "CompletionCondition", "[nrOfInstances] == [nrOfCompletedInstances]") | |||
if err != nil { | |||
return nil | |||
} | |||
loopImpl.SetElementIndexName(completionCondition) | |||
return loopImpl | |||
} | |||
func (a *AbstractTaskStateParser) parseRetries(stateName string, retryInterfaces []interface{}) ([]state.Retry, error) { | |||
retries := make([]state.Retry, 0) | |||
for _, retryInterface := range retryInterfaces { | |||
retryMap, ok := retryInterface.(map[string]interface{}) | |||
if !ok { | |||
return nil, errors.New("State [" + stateName + "] " + "Retry illegal, require map[string]interface{}") | |||
} | |||
retry := &state.RetryImpl{} | |||
errorTypes, err := a.GetSliceOrDefault(stateName, retryMap, "Exceptions", nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if errorTypes != nil { | |||
errorTypeNames := make([]string, 0) | |||
for _, errorType := range errorTypes { | |||
errorTypeNames = append(errorTypeNames, errorType.(string)) | |||
} | |||
retry.SetErrorTypeNames(errorTypeNames) | |||
} | |||
maxAttempts, err := a.GetIntOrDefault(stateName, retryMap, "MaxAttempts", 0) | |||
if err != nil { | |||
return nil, err | |||
} | |||
retry.SetMaxAttempt(maxAttempts) | |||
backoffInterval, err := a.GetFloat64OrDefault(stateName, retryMap, "BackoffInterval", 0) | |||
if err != nil { | |||
return nil, err | |||
} | |||
retry.SetBackoffRate(backoffInterval) | |||
intervalSeconds, err := a.GetFloat64OrDefault(stateName, retryMap, "IntervalSeconds", 0) | |||
if err != nil { | |||
return nil, err | |||
} | |||
retry.SetIntervalSecond(intervalSeconds) | |||
retries = append(retries, retry) | |||
} | |||
return retries, nil | |||
} | |||
func (a *AbstractTaskStateParser) parseCatches(stateName string, catchInterfaces []interface{}) ([]state.ErrorMatch, error) { | |||
errorMatches := make([]state.ErrorMatch, 0, len(catchInterfaces)) | |||
for _, catchInterface := range catchInterfaces { | |||
catchMap, ok := catchInterface.(map[string]interface{}) | |||
if !ok { | |||
return nil, errors.New("State [" + stateName + "] " + "Catch illegal, require map[string]interface{}") | |||
} | |||
errorMatch := &state.ErrorMatchImpl{} | |||
errorInterfaces, err := a.GetSliceOrDefault(stateName, catchMap, "Exceptions", nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if errorInterfaces != nil { | |||
errorNames := make([]string, 0) | |||
for _, errorType := range errorInterfaces { | |||
errorNames = append(errorNames, errorType.(string)) | |||
} | |||
errorMatch.SetErrors(errorNames) | |||
} | |||
next, err := a.GetStringOrDefault(stateName, catchMap, "Next", "") | |||
if err != nil { | |||
return nil, err | |||
} | |||
errorMatch.SetNext(next) | |||
errorMatches = append(errorMatches, errorMatch) | |||
} | |||
return errorMatches, nil | |||
} | |||
type ServiceTaskStateParser struct { | |||
*AbstractTaskStateParser | |||
} | |||
func NewServiceTaskStateParser() *ServiceTaskStateParser { | |||
return &ServiceTaskStateParser{ | |||
NewAbstractTaskStateParser(), | |||
} | |||
} | |||
func (s ServiceTaskStateParser) StateType() string { | |||
return constant.StateTypeServiceTask | |||
} | |||
func (s ServiceTaskStateParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
serviceTaskStateImpl := state.NewServiceTaskStateImpl() | |||
err := s.ParseTaskAttributes(stateName, serviceTaskStateImpl.AbstractTaskState, stateMap) | |||
if err != nil { | |||
return nil, err | |||
} | |||
serviceName, err := s.GetString(stateName, stateMap, "ServiceName") | |||
if err != nil { | |||
return nil, err | |||
} | |||
serviceTaskStateImpl.SetServiceName(serviceName) | |||
serviceMethod, err := s.GetString(stateName, stateMap, "ServiceMethod") | |||
if err != nil { | |||
return nil, err | |||
} | |||
serviceTaskStateImpl.SetServiceMethod(serviceMethod) | |||
serviceType, err := s.GetStringOrDefault(stateName, stateMap, "ServiceType", "") | |||
if err != nil { | |||
return nil, err | |||
} | |||
serviceTaskStateImpl.SetServiceType(serviceType) | |||
parameterTypeInterfaces, err := s.GetSliceOrDefault(stateName, stateMap, "ParameterTypes", nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if parameterTypeInterfaces != nil { | |||
var parameterTypes []string | |||
for i := range parameterTypeInterfaces { | |||
parameterType, ok := parameterTypeInterfaces[i].(string) | |||
if !ok { | |||
return nil, errors.New(fmt.Sprintf("State [%s] parameterType required string", stateName)) | |||
} | |||
parameterTypes = append(parameterTypes, parameterType) | |||
} | |||
serviceTaskStateImpl.SetParameterTypes(parameterTypes) | |||
} | |||
isAsync, err := s.GetBoolOrDefault(stateName, stateMap, "IsAsync", false) | |||
if err != nil { | |||
return nil, err | |||
} | |||
serviceTaskStateImpl.SetIsAsync(isAsync) | |||
return serviceTaskStateImpl, nil | |||
} | |||
type ScriptTaskStateParser struct { | |||
*AbstractTaskStateParser | |||
} | |||
func NewScriptTaskStateParser() *ScriptTaskStateParser { | |||
return &ScriptTaskStateParser{ | |||
NewAbstractTaskStateParser(), | |||
} | |||
} | |||
func (s ScriptTaskStateParser) StateType() string { | |||
return constant.StateTypeScriptTask | |||
} | |||
func (s ScriptTaskStateParser) Parse(stateName string, stateMap map[string]interface{}) (statelang.State, error) { | |||
scriptTaskStateImpl := state.NewScriptTaskStateImpl() | |||
err := s.ParseTaskAttributes(stateName, scriptTaskStateImpl.AbstractTaskState, stateMap) | |||
if err != nil { | |||
return nil, err | |||
} | |||
scriptType, err := s.GetStringOrDefault(stateName, stateMap, "ScriptType", "") | |||
if err != nil { | |||
return nil, err | |||
} | |||
if scriptType != "" { | |||
scriptTaskStateImpl.SetScriptType(scriptType) | |||
} | |||
scriptContent, err := s.GetStringOrDefault(stateName, stateMap, "ScriptContent", "") | |||
if err != nil { | |||
return nil, err | |||
} | |||
scriptTaskStateImpl.SetScriptContent(scriptContent) | |||
scriptTaskStateImpl.SetForCompensation(false) | |||
scriptTaskStateImpl.SetForUpdate(false) | |||
scriptTaskStateImpl.SetPersist(false) | |||
return scriptTaskStateImpl, nil | |||
} |
@@ -30,6 +30,10 @@ type BaseState struct { | |||
stateMachine StateMachine | |||
} | |||
func NewBaseState() *BaseState { | |||
return &BaseState{} | |||
} | |||
func (b *BaseState) Name() string { | |||
return b.name | |||
} | |||
@@ -21,14 +21,15 @@ type Choice interface { | |||
} | |||
type ChoiceStateImpl struct { | |||
statelang.BaseState | |||
*statelang.BaseState | |||
defaultChoice string `alias:"Default"` | |||
choices []Choice `alias:"Choices"` | |||
} | |||
func NewChoiceStateImpl() *ChoiceStateImpl { | |||
return &ChoiceStateImpl{ | |||
choices: make([]Choice, 0), | |||
BaseState: statelang.NewBaseState(), | |||
choices: make([]Choice, 0), | |||
} | |||
} | |||
@@ -0,0 +1,22 @@ | |||
package state | |||
import ( | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
) | |||
type CompensationTriggerState interface { | |||
statelang.State | |||
} | |||
type CompensationTriggerStateImpl struct { | |||
*statelang.BaseState | |||
} | |||
func NewCompensationTriggerStateImpl() *CompensationTriggerStateImpl { | |||
s := &CompensationTriggerStateImpl{ | |||
BaseState: statelang.NewBaseState(), | |||
} | |||
s.SetType(constant.StateTypeCompensationTrigger) | |||
return s | |||
} |
@@ -0,0 +1,64 @@ | |||
package state | |||
import ( | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
) | |||
type EndState interface { | |||
statelang.State | |||
} | |||
type SucceedEndState interface { | |||
EndState | |||
} | |||
type SucceedEndStateImpl struct { | |||
*statelang.BaseState | |||
} | |||
func NewSucceedEndStateImpl() *SucceedEndStateImpl { | |||
s := &SucceedEndStateImpl{ | |||
BaseState: statelang.NewBaseState(), | |||
} | |||
s.SetType(constant.StateTypeSucceed) | |||
return s | |||
} | |||
type FailEndState interface { | |||
EndState | |||
ErrorCode() string | |||
Message() string | |||
} | |||
type FailEndStateImpl struct { | |||
*statelang.BaseState | |||
errorCode string | |||
message string | |||
} | |||
func NewFailEndStateImpl() *FailEndStateImpl { | |||
s := &FailEndStateImpl{ | |||
BaseState: statelang.NewBaseState(), | |||
} | |||
s.SetType(constant.StateTypeFail) | |||
return s | |||
} | |||
func (f *FailEndStateImpl) ErrorCode() string { | |||
return f.errorCode | |||
} | |||
func (f *FailEndStateImpl) SetErrorCode(errorCode string) { | |||
f.errorCode = errorCode | |||
} | |||
func (f *FailEndStateImpl) Message() string { | |||
return f.message | |||
} | |||
func (f *FailEndStateImpl) SetMessage(message string) { | |||
f.message = message | |||
} |
@@ -0,0 +1,69 @@ | |||
package state | |||
import ( | |||
"github.com/google/uuid" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
) | |||
type SubStateMachine interface { | |||
TaskState | |||
StateMachineName() string | |||
CompensateStateImpl() TaskState | |||
} | |||
type SubStateMachineImpl struct { | |||
*ServiceTaskStateImpl | |||
stateMachineName string | |||
compensateState TaskState | |||
} | |||
func NewSubStateMachineImpl() *SubStateMachineImpl { | |||
return &SubStateMachineImpl{ | |||
ServiceTaskStateImpl: NewServiceTaskStateImpl(), | |||
} | |||
} | |||
func (s *SubStateMachineImpl) StateMachineName() string { | |||
return s.stateMachineName | |||
} | |||
func (s *SubStateMachineImpl) SetStateMachineName(stateMachineName string) { | |||
s.stateMachineName = stateMachineName | |||
} | |||
func (s *SubStateMachineImpl) CompensateStateImpl() TaskState { | |||
return s.compensateState | |||
} | |||
func (s *SubStateMachineImpl) SetCompensateStateImpl(compensateState TaskState) { | |||
s.compensateState = compensateState | |||
} | |||
type CompensateSubStateMachineState interface { | |||
ServiceTaskState | |||
} | |||
type CompensateSubStateMachineStateImpl struct { | |||
*ServiceTaskStateImpl | |||
hashcode string | |||
} | |||
func NewCompensateSubStateMachineStateImpl() *CompensateSubStateMachineStateImpl { | |||
uuid := uuid.New() | |||
c := &CompensateSubStateMachineStateImpl{ | |||
ServiceTaskStateImpl: NewServiceTaskStateImpl(), | |||
hashcode: uuid.String(), | |||
} | |||
c.SetType(constant.CompensateSubMachine) | |||
return c | |||
} | |||
func (c *CompensateSubStateMachineStateImpl) Hashcode() string { | |||
return c.hashcode | |||
} | |||
func (c *CompensateSubStateMachineStateImpl) SetHashcode(hashcode string) { | |||
c.hashcode = hashcode | |||
} |
@@ -1,7 +1,9 @@ | |||
package state | |||
import ( | |||
"github.com/seata/seata-go/pkg/saga/statemachine/constant" | |||
"github.com/seata/seata-go/pkg/saga/statemachine/statelang" | |||
"reflect" | |||
) | |||
type TaskState interface { | |||
@@ -9,9 +11,39 @@ type TaskState interface { | |||
CompensateState() string | |||
Status() map[string]string | |||
ForCompensation() bool | |||
ForUpdate() bool | |||
Retry() []Retry | |||
Catches() []ErrorMatch | |||
Status() map[string]string | |||
Loop() Loop | |||
} | |||
type Loop interface { | |||
Parallel() int | |||
Collection() string | |||
ElementVariableName() string | |||
ElementIndexName() string | |||
CompletionCondition() string | |||
} | |||
type ErrorMatch interface { | |||
Errors() []string | |||
ErrorTypes() []reflect.Type | |||
SetErrorTypes(errorTypes []reflect.Type) | |||
Next() string | |||
} | |||
type Retry interface { | |||
@@ -26,5 +58,376 @@ type Retry interface { | |||
type ServiceTaskState interface { | |||
TaskState | |||
//TODO add serviceTask | |||
ServiceType() string | |||
ServiceName() string | |||
ServiceMethod() string | |||
ParameterTypes() []string | |||
Persist() bool | |||
RetryPersistModeUpdate() bool | |||
CompensatePersistModeUpdate() bool | |||
} | |||
type AbstractTaskState struct { | |||
*statelang.BaseState | |||
loop Loop | |||
catches []ErrorMatch | |||
input []interface{} | |||
output map[string]interface{} | |||
compensatePersistModeUpdate bool | |||
retryPersistModeUpdate bool | |||
forCompensation bool | |||
forUpdate bool | |||
persist bool | |||
compensateState string | |||
status map[string]string | |||
retry []Retry | |||
} | |||
func NewAbstractTaskState() *AbstractTaskState { | |||
return &AbstractTaskState{ | |||
BaseState: &statelang.BaseState{}, | |||
} | |||
} | |||
func (a *AbstractTaskState) Input() []interface{} { | |||
return a.input | |||
} | |||
func (a *AbstractTaskState) SetInput(input []interface{}) { | |||
a.input = input | |||
} | |||
func (a *AbstractTaskState) Output() map[string]interface{} { | |||
return a.output | |||
} | |||
func (a *AbstractTaskState) SetOutput(output map[string]interface{}) { | |||
a.output = output | |||
} | |||
func (a *AbstractTaskState) CompensatePersistModeUpdate() bool { | |||
return a.compensatePersistModeUpdate | |||
} | |||
func (a *AbstractTaskState) SetCompensatePersistModeUpdate(isCompensatePersistModeUpdate bool) { | |||
a.compensatePersistModeUpdate = isCompensatePersistModeUpdate | |||
} | |||
func (a *AbstractTaskState) RetryPersistModeUpdate() bool { | |||
return a.retryPersistModeUpdate | |||
} | |||
func (a *AbstractTaskState) SetRetryPersistModeUpdate(retryPersistModeUpdate bool) { | |||
a.retryPersistModeUpdate = retryPersistModeUpdate | |||
} | |||
func (a *AbstractTaskState) Persist() bool { | |||
return a.persist | |||
} | |||
func (a *AbstractTaskState) SetPersist(persist bool) { | |||
a.persist = persist | |||
} | |||
func (a *AbstractTaskState) SetLoop(loop Loop) { | |||
a.loop = loop | |||
} | |||
func (a *AbstractTaskState) SetCatches(catches []ErrorMatch) { | |||
a.catches = catches | |||
} | |||
func (a *AbstractTaskState) SetForCompensation(forCompensation bool) { | |||
a.forCompensation = forCompensation | |||
} | |||
func (a *AbstractTaskState) SetForUpdate(forUpdate bool) { | |||
a.forUpdate = forUpdate | |||
} | |||
func (a *AbstractTaskState) SetCompensateState(compensateState string) { | |||
a.compensateState = compensateState | |||
} | |||
func (a *AbstractTaskState) SetStatus(status map[string]string) { | |||
a.status = status | |||
} | |||
func (a *AbstractTaskState) SetRetry(retry []Retry) { | |||
a.retry = retry | |||
} | |||
func (a *AbstractTaskState) ForCompensation() bool { | |||
return a.forCompensation | |||
} | |||
func (a *AbstractTaskState) ForUpdate() bool { | |||
return a.forUpdate | |||
} | |||
func (a *AbstractTaskState) Catches() []ErrorMatch { | |||
return a.catches | |||
} | |||
func (a *AbstractTaskState) Loop() Loop { | |||
return a.loop | |||
} | |||
func (a *AbstractTaskState) CompensateState() string { | |||
return a.compensateState | |||
} | |||
func (a *AbstractTaskState) Status() map[string]string { | |||
return a.status | |||
} | |||
func (a *AbstractTaskState) Retry() []Retry { | |||
return a.retry | |||
} | |||
type ServiceTaskStateImpl struct { | |||
*AbstractTaskState | |||
serviceType string | |||
serviceName string | |||
serviceMethod string | |||
parameterTypes []string | |||
persist bool | |||
retryPersistModeUpdate bool | |||
compensatePersistModeUpdate bool | |||
isAsync bool | |||
} | |||
func NewServiceTaskStateImpl() *ServiceTaskStateImpl { | |||
return &ServiceTaskStateImpl{ | |||
AbstractTaskState: NewAbstractTaskState(), | |||
} | |||
} | |||
func (s *ServiceTaskStateImpl) IsAsync() bool { | |||
return s.isAsync | |||
} | |||
func (s *ServiceTaskStateImpl) SetIsAsync(isAsync bool) { | |||
s.isAsync = isAsync | |||
} | |||
func (s *ServiceTaskStateImpl) SetServiceType(serviceType string) { | |||
s.serviceType = serviceType | |||
} | |||
func (s *ServiceTaskStateImpl) SetServiceName(serviceName string) { | |||
s.serviceName = serviceName | |||
} | |||
func (s *ServiceTaskStateImpl) SetServiceMethod(serviceMethod string) { | |||
s.serviceMethod = serviceMethod | |||
} | |||
func (s *ServiceTaskStateImpl) SetParameterTypes(parameterTypes []string) { | |||
s.parameterTypes = parameterTypes | |||
} | |||
func (s *ServiceTaskStateImpl) SetPersist(persist bool) { | |||
s.persist = persist | |||
} | |||
func (s *ServiceTaskStateImpl) SetRetryPersistModeUpdate(retryPersistModeUpdate bool) { | |||
s.retryPersistModeUpdate = retryPersistModeUpdate | |||
} | |||
func (s *ServiceTaskStateImpl) SetCompensatePersistModeUpdate(compensatePersistModeUpdate bool) { | |||
s.compensatePersistModeUpdate = compensatePersistModeUpdate | |||
} | |||
func (s *ServiceTaskStateImpl) Loop() Loop { | |||
return s.loop | |||
} | |||
func (s *ServiceTaskStateImpl) ServiceType() string { | |||
return s.serviceType | |||
} | |||
func (s *ServiceTaskStateImpl) ServiceName() string { | |||
return s.serviceName | |||
} | |||
func (s *ServiceTaskStateImpl) ServiceMethod() string { | |||
return s.serviceMethod | |||
} | |||
func (s *ServiceTaskStateImpl) ParameterTypes() []string { | |||
return s.parameterTypes | |||
} | |||
func (s *ServiceTaskStateImpl) Persist() bool { | |||
return s.persist | |||
} | |||
func (s *ServiceTaskStateImpl) RetryPersistModeUpdate() bool { | |||
return s.retryPersistModeUpdate | |||
} | |||
func (s *ServiceTaskStateImpl) CompensatePersistModeUpdate() bool { | |||
return s.compensatePersistModeUpdate | |||
} | |||
type LoopImpl struct { | |||
parallel int | |||
collection string | |||
elementVariableName string | |||
elementIndexName string | |||
completionCondition string | |||
} | |||
func (l *LoopImpl) SetParallel(parallel int) { | |||
l.parallel = parallel | |||
} | |||
func (l *LoopImpl) SetCollection(collection string) { | |||
l.collection = collection | |||
} | |||
func (l *LoopImpl) SetElementVariableName(elementVariableName string) { | |||
l.elementVariableName = elementVariableName | |||
} | |||
func (l *LoopImpl) SetElementIndexName(elementIndexName string) { | |||
l.elementIndexName = elementIndexName | |||
} | |||
func (l *LoopImpl) SetCompletionCondition(completionCondition string) { | |||
l.completionCondition = completionCondition | |||
} | |||
func (l *LoopImpl) Parallel() int { | |||
return l.parallel | |||
} | |||
func (l *LoopImpl) Collection() string { | |||
return l.collection | |||
} | |||
func (l *LoopImpl) ElementVariableName() string { | |||
return l.elementVariableName | |||
} | |||
func (l *LoopImpl) ElementIndexName() string { | |||
return l.elementIndexName | |||
} | |||
func (l *LoopImpl) CompletionCondition() string { | |||
return l.completionCondition | |||
} | |||
type RetryImpl struct { | |||
errorTypeNames []string | |||
intervalSecond float64 | |||
maxAttempt int | |||
backoffRate float64 | |||
} | |||
func (r *RetryImpl) SetErrorTypeNames(errorTypeNames []string) { | |||
r.errorTypeNames = errorTypeNames | |||
} | |||
func (r *RetryImpl) SetIntervalSecond(intervalSecond float64) { | |||
r.intervalSecond = intervalSecond | |||
} | |||
func (r *RetryImpl) SetMaxAttempt(maxAttempt int) { | |||
r.maxAttempt = maxAttempt | |||
} | |||
func (r *RetryImpl) SetBackoffRate(backoffRate float64) { | |||
r.backoffRate = backoffRate | |||
} | |||
func (r *RetryImpl) ErrorTypeNames() []string { | |||
return r.errorTypeNames | |||
} | |||
func (r *RetryImpl) IntervalSecond() float64 { | |||
return r.intervalSecond | |||
} | |||
func (r *RetryImpl) MaxAttempt() int { | |||
return r.maxAttempt | |||
} | |||
func (r *RetryImpl) BackoffRate() float64 { | |||
return r.backoffRate | |||
} | |||
type ErrorMatchImpl struct { | |||
errors []string | |||
errorTypes []reflect.Type | |||
next string | |||
} | |||
func (e *ErrorMatchImpl) SetErrors(errors []string) { | |||
e.errors = errors | |||
} | |||
func (e *ErrorMatchImpl) SetNext(next string) { | |||
e.next = next | |||
} | |||
func (e *ErrorMatchImpl) Errors() []string { | |||
return e.errors | |||
} | |||
func (e *ErrorMatchImpl) ErrorTypes() []reflect.Type { | |||
return e.errorTypes | |||
} | |||
func (e *ErrorMatchImpl) SetErrorTypes(errorTypes []reflect.Type) { | |||
e.errorTypes = errorTypes | |||
} | |||
func (e *ErrorMatchImpl) Next() string { | |||
return e.next | |||
} | |||
type ScriptTaskState interface { | |||
TaskState | |||
ScriptType() string | |||
ScriptContent() string | |||
} | |||
type ScriptTaskStateImpl struct { | |||
*AbstractTaskState | |||
scriptType string | |||
scriptContent string | |||
} | |||
func NewScriptTaskStateImpl() *ScriptTaskStateImpl { | |||
return &ScriptTaskStateImpl{ | |||
AbstractTaskState: NewAbstractTaskState(), | |||
scriptType: constant.DefaultScriptType, | |||
} | |||
} | |||
func (s *ScriptTaskStateImpl) SetScriptType(scriptType string) { | |||
s.scriptType = scriptType | |||
} | |||
func (s *ScriptTaskStateImpl) SetScriptContent(scriptContent string) { | |||
s.scriptContent = scriptContent | |||
} | |||
func (s *ScriptTaskStateImpl) ScriptType() string { | |||
return s.scriptType | |||
} | |||
func (s *ScriptTaskStateImpl) ScriptContent() string { | |||
return s.scriptContent | |||
} |
@@ -0,0 +1,38 @@ | |||
{ | |||
"Name": "simpleChoiceTestStateMachine", | |||
"Comment": "带条件分支的测试状态机定义", | |||
"StartState": "FirstState", | |||
"Version": "0.0.1", | |||
"States": { | |||
"FirstState": { | |||
"Type": "ServiceTask", | |||
"ServiceName": "demoService", | |||
"ServiceMethod": "foo", | |||
"Next": "ChoiceState" | |||
}, | |||
"ChoiceState":{ | |||
"Type": "Choice", | |||
"Choices":[ | |||
{ | |||
"Expression":"[a] == 1", | |||
"Next":"SecondState" | |||
}, | |||
{ | |||
"Expression":"[a] == 2", | |||
"Next":"ThirdState" | |||
} | |||
], | |||
"Default":"SecondState" | |||
}, | |||
"SecondState": { | |||
"Type": "ServiceTask", | |||
"ServiceName": "demoService", | |||
"ServiceMethod": "bar" | |||
}, | |||
"ThirdState": { | |||
"Type": "ServiceTask", | |||
"ServiceName": "demoService", | |||
"ServiceMethod": "foo" | |||
} | |||
} | |||
} |
@@ -0,0 +1,138 @@ | |||
{ | |||
"Name": "simpleTestStateMachine", | |||
"Comment": "测试状态机定义", | |||
"StartState": "FirstState", | |||
"Version": "0.0.1", | |||
"States": { | |||
"FirstState": { | |||
"Type": "ServiceTask", | |||
"ServiceName": "is.seata.saga.DemoService", | |||
"ServiceMethod": "foo", | |||
"IsPersist": false, | |||
"Next": "ScriptState" | |||
}, | |||
"ScriptState": { | |||
"Type": "ScriptTask", | |||
"ScriptType": "groovy", | |||
"ScriptContent": "return 'hello ' + inputA", | |||
"Input": [ | |||
{ | |||
"inputA": "$.data1" | |||
} | |||
], | |||
"Output": { | |||
"scriptStateResult": "$.#root" | |||
}, | |||
"Next": "ChoiceState" | |||
}, | |||
"ChoiceState": { | |||
"Type": "Choice", | |||
"Choices": [ | |||
{ | |||
"Expression": "foo == 1", | |||
"Next": "FirstMatchState" | |||
}, | |||
{ | |||
"Expression": "foo == 2", | |||
"Next": "SecondMatchState" | |||
} | |||
], | |||
"Default": "FailState" | |||
}, | |||
"FirstMatchState": { | |||
"Type": "ServiceTask", | |||
"ServiceName": "is.seata.saga.DemoService", | |||
"ServiceMethod": "bar", | |||
"CompensateState": "CompensateFirst", | |||
"Status": { | |||
"return.code == 'S'": "SU", | |||
"return.code == 'F'": "FA", | |||
"$exception{java.lang.Throwable}": "UN" | |||
}, | |||
"Input": [ | |||
{ | |||
"inputA1": "$.data1", | |||
"inputA2": { | |||
"a": "$.data2.a" | |||
} | |||
}, | |||
{ | |||
"inputB": "$.header" | |||
} | |||
], | |||
"Output": { | |||
"firstMatchStateResult": "$.#root" | |||
}, | |||
"Retry": [ | |||
{ | |||
"Exceptions": ["java.lang.Exception"], | |||
"IntervalSeconds": 2, | |||
"MaxAttempts": 3, | |||
"BackoffRate": 1.5 | |||
} | |||
], | |||
"Catch": [ | |||
{ | |||
"Exceptions": [ | |||
"java.lang.Exception" | |||
], | |||
"Next": "CompensationTrigger" | |||
} | |||
], | |||
"Next": "SuccessState" | |||
}, | |||
"CompensateFirst": { | |||
"Type": "ServiceTask", | |||
"ServiceName": "is.seata.saga.DemoService", | |||
"ServiceMethod": "compensateBar", | |||
"IsForCompensation": true, | |||
"IsForUpdate": true, | |||
"Input": [ | |||
{ | |||
"input": "$.data" | |||
} | |||
], | |||
"Output": { | |||
"firstMatchStateResult": "$.#root" | |||
}, | |||
"Status": { | |||
"return.code == 'S'": "SU", | |||
"return.code == 'F'": "FA", | |||
"$exception{java.lang.Throwable}": "UN" | |||
} | |||
}, | |||
"CompensationTrigger": { | |||
"Type": "CompensationTrigger", | |||
"Next": "CompensateEndState" | |||
}, | |||
"CompensateEndState": { | |||
"Type": "Fail", | |||
"ErrorCode": "StateCompensated", | |||
"Message": "State Compensated!" | |||
}, | |||
"SecondMatchState": { | |||
"Type": "SubStateMachine", | |||
"StateMachineName": "simpleTestSubStateMachine", | |||
"Input": [ | |||
{ | |||
"input": "$.data" | |||
}, | |||
{ | |||
"header": "$.header" | |||
} | |||
], | |||
"Output": { | |||
"firstMatchStateResult": "$.#root" | |||
}, | |||
"Next": "SuccessState" | |||
}, | |||
"FailState": { | |||
"Type": "Fail", | |||
"ErrorCode": "DefaultStateError", | |||
"Message": "No Matches!" | |||
}, | |||
"SuccessState": { | |||
"Type": "Succeed" | |||
} | |||
} | |||
} |
@@ -0,0 +1,424 @@ | |||
{ | |||
"Name": "StateMachineNewDesigner", | |||
"Comment": "This state machine is modeled by designer tools.", | |||
"Version": "0.0.1", | |||
"style": { | |||
"bounds": { | |||
"x": 200, | |||
"y": 200, | |||
"width": 36, | |||
"height": 36 | |||
} | |||
}, | |||
"States": { | |||
"ServiceTask-a9h2o51": { | |||
"style": { | |||
"bounds": { | |||
"x": 300, | |||
"y": 178, | |||
"width": 100, | |||
"height": 80 | |||
} | |||
}, | |||
"Name": "ServiceTask-a9h2o51", | |||
"IsForCompensation": false, | |||
"Input": [ | |||
{} | |||
], | |||
"Output": {}, | |||
"Status": {}, | |||
"Retry": [], | |||
"ServiceName": "", | |||
"ServiceMethod": "", | |||
"Type": "ServiceTask", | |||
"Next": "Choice-4ajl8nt", | |||
"edge": { | |||
"Choice-4ajl8nt": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 400, | |||
"y": 218 | |||
}, | |||
"x": 400, | |||
"y": 218 | |||
}, | |||
{ | |||
"x": 435, | |||
"y": 218 | |||
}, | |||
{ | |||
"original": { | |||
"x": 455, | |||
"y": 218 | |||
}, | |||
"x": 455, | |||
"y": 218 | |||
} | |||
], | |||
"source": "ServiceTask-a9h2o51", | |||
"target": "Choice-4ajl8nt" | |||
}, | |||
"Type": "Transition" | |||
} | |||
}, | |||
"CompensateState": "CompensateFirstState" | |||
}, | |||
"Choice-4ajl8nt": { | |||
"style": { | |||
"bounds": { | |||
"x": 455, | |||
"y": 193, | |||
"width": 50, | |||
"height": 50 | |||
} | |||
}, | |||
"Name": "Choice-4ajl8nt", | |||
"Type": "Choice", | |||
"Choices": [ | |||
{ | |||
"Expression": "", | |||
"Next": "SubStateMachine-cauj9uy" | |||
}, | |||
{ | |||
"Expression": "", | |||
"Next": "ServiceTask-vdij28l" | |||
} | |||
], | |||
"Default": "SubStateMachine-cauj9uy", | |||
"edge": { | |||
"SubStateMachine-cauj9uy": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 505, | |||
"y": 218 | |||
}, | |||
"x": 505, | |||
"y": 218 | |||
}, | |||
{ | |||
"x": 530, | |||
"y": 218 | |||
}, | |||
{ | |||
"original": { | |||
"x": 550, | |||
"y": 218 | |||
}, | |||
"x": 550, | |||
"y": 218 | |||
} | |||
], | |||
"source": "Choice-4ajl8nt", | |||
"target": "SubStateMachine-cauj9uy" | |||
}, | |||
"Type": "ChoiceEntry" | |||
}, | |||
"ServiceTask-vdij28l": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 480, | |||
"y": 243 | |||
}, | |||
"x": 480, | |||
"y": 243 | |||
}, | |||
{ | |||
"x": 600, | |||
"y": 290 | |||
}, | |||
{ | |||
"original": { | |||
"x": 600, | |||
"y": 310 | |||
}, | |||
"x": 600, | |||
"y": 310 | |||
} | |||
], | |||
"source": "Choice-4ajl8nt", | |||
"target": "ServiceTask-vdij28l" | |||
}, | |||
"Type": "ChoiceEntry" | |||
} | |||
} | |||
}, | |||
"CompensateFirstState": { | |||
"style": { | |||
"bounds": { | |||
"x": 300, | |||
"y": 310, | |||
"width": 100, | |||
"height": 80 | |||
} | |||
}, | |||
"Name": "CompensateFirstState", | |||
"IsForCompensation": true, | |||
"Input": [ | |||
{} | |||
], | |||
"Output": {}, | |||
"Status": {}, | |||
"Retry": [], | |||
"ServiceName": "", | |||
"ServiceMethod": "", | |||
"Type": "ServiceTask" | |||
}, | |||
"SubStateMachine-cauj9uy": { | |||
"style": { | |||
"bounds": { | |||
"x": 550, | |||
"y": 178, | |||
"width": 100, | |||
"height": 80 | |||
} | |||
}, | |||
"Name": "SubStateMachine-cauj9uy", | |||
"IsForCompensation": false, | |||
"Input": [ | |||
{} | |||
], | |||
"Output": {}, | |||
"Status": {}, | |||
"Retry": [], | |||
"StateMachineName": "", | |||
"Type": "SubStateMachine", | |||
"Next": "Succeed-5x3z98u", | |||
"edge": { | |||
"Succeed-5x3z98u": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 650, | |||
"y": 218 | |||
}, | |||
"x": 650, | |||
"y": 218 | |||
}, | |||
{ | |||
"x": 702, | |||
"y": 218 | |||
}, | |||
{ | |||
"original": { | |||
"x": 722, | |||
"y": 218 | |||
}, | |||
"x": 722, | |||
"y": 218 | |||
} | |||
], | |||
"source": "SubStateMachine-cauj9uy", | |||
"target": "Succeed-5x3z98u" | |||
}, | |||
"Type": "Transition" | |||
} | |||
} | |||
}, | |||
"ServiceTask-vdij28l": { | |||
"style": { | |||
"bounds": { | |||
"x": 550, | |||
"y": 310, | |||
"width": 100, | |||
"height": 80 | |||
} | |||
}, | |||
"Name": "ServiceTask-vdij28l", | |||
"IsForCompensation": false, | |||
"Input": [ | |||
{} | |||
], | |||
"Output": {}, | |||
"Status": {}, | |||
"Retry": [], | |||
"ServiceName": "", | |||
"ServiceMethod": "", | |||
"Catch": [ | |||
{ | |||
"Exceptions": [], | |||
"Next": "CompensationTrigger-uldp2ou" | |||
} | |||
], | |||
"Type": "ServiceTask", | |||
"catch": { | |||
"style": { | |||
"bounds": { | |||
"x": 632, | |||
"y": 372, | |||
"width": 36, | |||
"height": 36 | |||
} | |||
}, | |||
"edge": { | |||
"CompensationTrigger-uldp2ou": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 668, | |||
"y": 390 | |||
}, | |||
"x": 668, | |||
"y": 390 | |||
}, | |||
{ | |||
"x": 702, | |||
"y": 390 | |||
}, | |||
{ | |||
"original": { | |||
"x": 722, | |||
"y": 390 | |||
}, | |||
"x": 722, | |||
"y": 390 | |||
} | |||
], | |||
"source": "ServiceTask-vdij28l", | |||
"target": "CompensationTrigger-uldp2ou" | |||
}, | |||
"Type": "ExceptionMatch" | |||
} | |||
} | |||
}, | |||
"Next": "Succeed-5x3z98u", | |||
"edge": { | |||
"Succeed-5x3z98u": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 600, | |||
"y": 310 | |||
}, | |||
"x": 600, | |||
"y": 310 | |||
}, | |||
{ | |||
"x": 740, | |||
"y": 256 | |||
}, | |||
{ | |||
"original": { | |||
"x": 740, | |||
"y": 236 | |||
}, | |||
"x": 740, | |||
"y": 236 | |||
} | |||
], | |||
"source": "ServiceTask-vdij28l", | |||
"target": "Succeed-5x3z98u" | |||
}, | |||
"Type": "Transition" | |||
} | |||
} | |||
}, | |||
"Succeed-5x3z98u": { | |||
"style": { | |||
"bounds": { | |||
"x": 722, | |||
"y": 200, | |||
"width": 36, | |||
"height": 36 | |||
} | |||
}, | |||
"Name": "Succeed-5x3z98u", | |||
"Type": "Succeed" | |||
}, | |||
"CompensationTrigger-uldp2ou": { | |||
"style": { | |||
"bounds": { | |||
"x": 722, | |||
"y": 372, | |||
"width": 36, | |||
"height": 36 | |||
} | |||
}, | |||
"Name": "CompensationTrigger-uldp2ou", | |||
"Type": "CompensationTrigger", | |||
"Next": "Fail-9roxcv5", | |||
"edge": { | |||
"Fail-9roxcv5": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 758, | |||
"y": 390 | |||
}, | |||
"x": 758, | |||
"y": 390 | |||
}, | |||
{ | |||
"x": 792, | |||
"y": 390 | |||
}, | |||
{ | |||
"original": { | |||
"x": 812, | |||
"y": 390 | |||
}, | |||
"x": 812, | |||
"y": 390 | |||
} | |||
], | |||
"source": "CompensationTrigger-uldp2ou", | |||
"target": "Fail-9roxcv5" | |||
}, | |||
"Type": "Transition" | |||
} | |||
} | |||
}, | |||
"Fail-9roxcv5": { | |||
"style": { | |||
"bounds": { | |||
"x": 812, | |||
"y": 372, | |||
"width": 36, | |||
"height": 36 | |||
} | |||
}, | |||
"Name": "Fail-9roxcv5", | |||
"ErrorCode": "", | |||
"Message": "", | |||
"Type": "Fail" | |||
} | |||
}, | |||
"StartState": "ServiceTask-a9h2o51", | |||
"edge": { | |||
"style": { | |||
"waypoints": [ | |||
{ | |||
"original": { | |||
"x": 236, | |||
"y": 218 | |||
}, | |||
"x": 236, | |||
"y": 218 | |||
}, | |||
{ | |||
"x": 280, | |||
"y": 218 | |||
}, | |||
{ | |||
"original": { | |||
"x": 300, | |||
"y": 218 | |||
}, | |||
"x": 300, | |||
"y": 218 | |||
} | |||
], | |||
"target": "ServiceTask-a9h2o51" | |||
}, | |||
"Type": "Transition" | |||
} | |||
} |