From a68335d7bd08df2a2b2aa8f9119f80a08fce2c2a Mon Sep 17 00:00:00 2001 From: Pedro Escaleira Date: Fri, 11 Jul 2025 12:52:36 +0100 Subject: [PATCH 1/3] removed unused imports Signed-off-by: Pedro Escaleira --- serverlessworkflow/sdk/state_machine_generator.py | 1 - serverlessworkflow/sdk/state_machine_helper.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/serverlessworkflow/sdk/state_machine_generator.py b/serverlessworkflow/sdk/state_machine_generator.py index a1e118b..c210ebd 100644 --- a/serverlessworkflow/sdk/state_machine_generator.py +++ b/serverlessworkflow/sdk/state_machine_generator.py @@ -23,7 +23,6 @@ from serverlessworkflow.sdk.transition_data_condition import TransitionDataCondition from serverlessworkflow.sdk.end_data_condition import EndDataCondition -from transitions.extensions import HierarchicalMachine, GraphMachine from transitions.extensions.nesting import NestedState import warnings diff --git a/serverlessworkflow/sdk/state_machine_helper.py b/serverlessworkflow/sdk/state_machine_helper.py index cefeb69..9436d93 100644 --- a/serverlessworkflow/sdk/state_machine_helper.py +++ b/serverlessworkflow/sdk/state_machine_helper.py @@ -1,13 +1,10 @@ from typing import List from serverlessworkflow.sdk.workflow import Workflow from serverlessworkflow.sdk.state_machine_generator import StateMachineGenerator -from transitions.extensions.diagrams import HierarchicalGraphMachine, GraphMachine from serverlessworkflow.sdk.state_machine_extensions import ( CustomGraphMachine, CustomHierarchicalGraphMachine, ) -from transitions.extensions.nesting import NestedState -from transitions.extensions.diagrams_base import BaseGraph class StateMachineHelper: From 6a80846bc4f65f2906dd1746325c1195d41461a2 Mon Sep 17 00:00:00 2001 From: Pedro Escaleira Date: Sat, 12 Jul 2025 00:55:53 +0100 Subject: [PATCH 2/3] event state now has all the information regarding its events and states Signed-off-by: Pedro Escaleira --- serverlessworkflow/sdk/on_events.py | 2 +- .../sdk/state_machine_generator.py | 82 ++++++++++++++++++- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/serverlessworkflow/sdk/on_events.py b/serverlessworkflow/sdk/on_events.py index d052464..22d5030 100644 --- a/serverlessworkflow/sdk/on_events.py +++ b/serverlessworkflow/sdk/on_events.py @@ -19,7 +19,7 @@ def __init__(self, **kwargs): _default_values = {'actionMode': 'sequential'} - SwfBase.__init__(self, locals(), kwargs, SwfBase.default_hydration, _default_values) + SwfBase.__init__(self, locals(), kwargs, OnEvents.f_hydration, _default_values) @staticmethod def f_hydration(p_key, p_value): diff --git a/serverlessworkflow/sdk/state_machine_generator.py b/serverlessworkflow/sdk/state_machine_generator.py index c210ebd..dacdc4f 100644 --- a/serverlessworkflow/sdk/state_machine_generator.py +++ b/serverlessworkflow/sdk/state_machine_generator.py @@ -259,7 +259,67 @@ def sleep_state_details(self): def event_state_details(self): if isinstance(self.current_state, EventState): - self.state_to_machine_state(["event_state", "state"]) + state = self.state_to_machine_state(["event_state", "state"]) + if self.get_actions: + if on_events := self.current_state.onEvents: + state.initial = [] if len(on_events) > 1 else on_events[0] + for i, oe in enumerate(on_events): + state.add_substate( + oe_state := self.state_machine.state_cls( + oe_name := f"onEvent {i}" + ) + ) + + # define initial state + if i == 0 and len(on_events) > 1: + state.initial = [oe_state.name] + elif i == 0 and len(on_events) == 1: + state.initial = oe_state.name + else: + state.initial.append(oe_state.name) + + event_names = [] + for ie, event in enumerate(oe.eventRefs): + oe_state.add_substate( + ns := self.state_machine.state_cls(event) + ) + ns.tags = ["event"] + self.get_action_event(state=ns, e_name=event) + event_names.append(event) + + # define initial state + if ie == 0 and len(oe.eventRefs) > 1: + oe_state.initial = [event] + elif ie == 0 and len(oe.eventRefs) == 1: + oe_state.initial = event + else: + oe_state.initial.append(event) + + if self.current_state.exclusive: + oe_state.add_substate( + ns := self.state_machine.state_cls( + action_name := f"action {ie}" + ) + ) + self.state_machine.add_transition( + trigger="", + source=f"{self.current_state.name}.{oe_name}.{event}", + dest=f"{self.current_state.name}.{oe_name}.{action_name}", + ) + self.generate_actions_info( + machine_state=ns, + state_name=f"{self.current_state.name}.{oe_name}.{action_name}", + actions=oe.actions, + action_mode=oe.actionMode, + ) + if not self.current_state.exclusive and oe.actions: + self.generate_actions_info( + machine_state=oe_state, + state_name=f"{self.current_state.name}.{oe_name}", + actions=oe.actions, + action_mode=oe.actionMode, + initial_states=event_names, + ) def foreach_state_details(self): if isinstance(self.current_state, ForEachState): @@ -352,6 +412,7 @@ def generate_actions_info( state_name: str, actions: List[Dict[str, Action]], action_mode: str = "sequential", + initial_states: List[str] = [], ): if self.get_actions: parallel_states = [] @@ -445,12 +506,27 @@ def generate_actions_info( source=f"{state_name}.{name}", dest=f"{state_name}.{next_name}", ) - if i == 0: + if i == 0 and not initial_states: machine_state.initial = name + elif i == 0 and initial_states: + for init_s in initial_states: + self.state_machine.add_transition( + trigger="", + source=f"{state_name}.{init_s}", + dest=f"{state_name}.{name}", + ) elif action_mode == "parallel": parallel_states.append(name) - if action_mode == "parallel": + if action_mode == "parallel" and not initial_states: machine_state.initial = parallel_states + elif action_mode == "parallel" and initial_states: + for init_s in initial_states: + for ps in parallel_states: + self.state_machine.add_transition( + trigger="", + source=f"{state_name}.{init_s}", + dest=f"{state_name}.{ps}", + ) def get_action_function(self, state: NestedState, f_name: str): if self.workflow.functions: From 1f556e518931692bd7f9e0a1684c2b8b0de94e2f Mon Sep 17 00:00:00 2001 From: Pedro Escaleira Date: Tue, 29 Jul 2025 00:27:41 +0100 Subject: [PATCH 3/3] generating metadata for action events Signed-off-by: Pedro Escaleira --- serverlessworkflow/sdk/state_machine_generator.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/serverlessworkflow/sdk/state_machine_generator.py b/serverlessworkflow/sdk/state_machine_generator.py index dacdc4f..f2ce36b 100644 --- a/serverlessworkflow/sdk/state_machine_generator.py +++ b/serverlessworkflow/sdk/state_machine_generator.py @@ -447,7 +447,11 @@ def generate_actions_info( ns := self.state_machine.state_cls(name) ) ns.tags = ["event"] - self.get_action_event(state=ns, e_name=name) + self.get_action_event( + state=ns, + e_name=action.eventRef.triggerEventRef, + er_name=action.eventRef.resultEventRef, + ) if name: if action_mode == "sequential": if i < len(actions) - 1: @@ -499,7 +503,9 @@ def generate_actions_info( ) ns.tags = ["event"] self.get_action_event( - state=ns, e_name=next_name + state=ns, + e_name=action.eventRef.triggerEventRef, + er_name=action.eventRef.resultEventRef, ) self.state_machine.add_transition( trigger="", @@ -536,13 +542,14 @@ def get_action_function(self, state: NestedState, f_name: str): state.metadata = {"function": current_function} break - def get_action_event(self, state: NestedState, e_name: str): + def get_action_event(self, state: NestedState, e_name: str, er_name: str = ""): if self.workflow.events: for event in self.workflow.events: current_event = event.serialize().__dict__ if current_event["name"] == e_name: state.metadata = {"event": current_event} - break + if current_event["name"] == er_name: + state.metadata = {"result_event": current_event} def subflow_state_name(self, action: Action, subflow: Workflow): return (