11from typing import Any , Dict , List , Optional , Union
2+ from serverlessworkflow .sdk .action import Action
23from serverlessworkflow .sdk .callback_state import CallbackState
34from serverlessworkflow .sdk .function_ref import FunctionRef
45from serverlessworkflow .sdk .sleep_state import SleepState
1011 ParallelState ,
1112 OperationState ,
1213 ForEachState ,
14+ Workflow ,
1315)
1416from serverlessworkflow .sdk .transition_data_condition import TransitionDataCondition
1517from serverlessworkflow .sdk .end_data_condition import EndDataCondition
@@ -25,13 +27,15 @@ def __init__(
2527 self ,
2628 state : State ,
2729 state_machine : Union [HierarchicalMachine , GraphMachine ],
30+ subflows : List [Workflow ] = [],
2831 is_first_state = False ,
2932 get_actions = False ,
3033 ):
3134 self .state = state
3235 self .is_first_state = is_first_state
3336 self .state_machine = state_machine
3437 self .get_actions = get_actions
38+ self .subflows = subflows
3539
3640 if self .get_actions and not isinstance (self .state_machine , HierarchicalMachine ):
3741 raise AttributeError (
@@ -314,6 +318,11 @@ def data_based_switch_state_details(self) -> str:
314318
315319 def operation_state_details (self ) -> Optional [str ]:
316320 descriptions = []
321+ if self .state .name not in self .state_machine .states .keys ():
322+ self .state_machine .add_states (self .state .name )
323+ if self .is_first_state :
324+ self .state_machine ._initial = self .state .name
325+
317326 if isinstance (self .state , OperationState ):
318327 action_mode = self .state .actionMode
319328 if action_mode :
@@ -324,26 +333,11 @@ def operation_state_details(self) -> Optional[str]:
324333 action_mode ,
325334 )
326335 )
327- actions = self .state .actions
328- if actions :
329- descriptions .append (
330- self .state_description (
331- self .state_key_diagram (self .state .name ),
332- "Num. of actions" ,
333- str (len (actions )),
334- )
336+ descriptions .extend (
337+ self .generate_actions_info (
338+ actions = self .state .actions , action_mode = self .state .actionMode
335339 )
336- if self .get_actions :
337- descriptions .append (
338- f"state { self .state_key_diagram (self .state .name )} {{\n "
339- f"{ self .generate_composite_state (self .state_machine .get_state (self .state .name ), self .state .name , actions , action_mode )} \n "
340- f"}}\n "
341- )
342-
343- if self .state .name not in self .state_machine .states .keys ():
344- self .state_machine .add_states (self .state .name )
345- if self .is_first_state :
346- self .state_machine ._initial = self .state .name
340+ )
347341
348342 return "\n " .join (descriptions ) if descriptions else None
349343
@@ -368,15 +362,12 @@ def foreach_state_details(self) -> Optional[str]:
368362 input_collection ,
369363 )
370364 )
371- actions = self .state .actions
372- if actions :
373- descriptions .append (
374- self .state_description (
375- self .state_key_diagram (self .state .name ),
376- "Num. of actions" ,
377- str (len (actions )),
378- )
365+ descriptions .extend (
366+ self .generate_actions_info (
367+ actions = self .state .actions , action_mode = self .state .mode
379368 )
369+ )
370+
380371 return "\n " .join (descriptions ) if descriptions else None
381372
382373 def callback_state_details (self ) -> Optional [str ]:
@@ -398,12 +389,7 @@ def callback_state_details(self) -> Optional[str]:
398389 )
399390 )
400391
401- if self .get_actions :
402- descriptions .append (
403- f"state { self .state_key_diagram (self .state .name )} {{\n "
404- f"{ self .generate_composite_state (self .state_machine .get_state (self .state .name ), self .state .name , [action ], 'sequential' )} \n "
405- f"}}\n "
406- )
392+ self .generate_actions_info (actions = [action ], singular_action = True )
407393 event_ref = self .state .eventRef
408394 if event_ref :
409395 descriptions .append (
@@ -438,7 +424,7 @@ def generate_composite_state(
438424 machine_state : NestedState ,
439425 state_name : str ,
440426 actions : List [Dict [str , Any ]],
441- action_mode : str ,
427+ action_mode : str = "sequential" ,
442428 ) -> str :
443429 transitions = ""
444430 parallel_states = []
@@ -502,6 +488,91 @@ def generate_composite_state(
502488
503489 return transitions
504490
491+ def generate_actions_info (
492+ self ,
493+ actions : List [Action ],
494+ action_mode : str = "sequential" ,
495+ singular_action = False ,
496+ ):
497+ descriptions = []
498+ if actions :
499+ if not singular_action :
500+ descriptions .append (
501+ self .state_description (
502+ self .state_key_diagram (self .state .name ),
503+ "Num. of actions" ,
504+ str (len (actions )),
505+ )
506+ )
507+ if self .get_actions :
508+ descriptions .append (
509+ f"state { self .state_key_diagram (self .state .name )} {{\n "
510+ f"{ self .generate_composite_state (self .state_machine .get_state (self .state .name ), self .state .name , actions , action_mode )} \n "
511+ f"}}\n "
512+ )
513+ for action in actions :
514+ if action .subFlowRef :
515+ if isinstance (action .subFlowRef , str ):
516+ workflow_id = action .subFlowRef
517+ workflow_version = None
518+ else :
519+ workflow_id = action .subFlowRef .workflowId
520+ workflow_version = action .subFlowRef .version
521+ for sf in self .subflows :
522+ if (
523+ sf .id == workflow_id
524+ ): # and (workflow_version and sf.version == workflow_version or not workflow_version):
525+ new_machine = HierarchicalMachine (
526+ model = None , initial = None , auto_transitions = False
527+ )
528+
529+ # Generate the state machine for the subflow
530+ for index , state in enumerate (sf .states ):
531+ StateMachineGenerator (
532+ state = state ,
533+ state_machine = new_machine ,
534+ is_first_state = index == 0 ,
535+ get_actions = self .get_actions ,
536+ subflows = self .subflows
537+ ).source_code ()
538+
539+ # Convert the new_machine into a NestedState
540+ nested_state = NestedState (
541+ action .name if action .name else f"{ sf .id } /{ sf .version .replace (NestedState .separator , '-' )} "
542+ )
543+ self .state_machine_to_nested_state (state_machine = new_machine , nested_state = nested_state )
544+ # else:
545+ # raise Warning("No correct subflow provided")
546+
547+ return descriptions
548+
549+ def add_all_sub_states (cls , original_state : Union [NestedState , HierarchicalMachine ], new_state : NestedState ):
550+ if len (original_state .states ) == 0 :
551+ return
552+ for substate in original_state .states .values ():
553+ new_state .add_substate (ns := NestedState (substate .name ))
554+ cls .add_all_sub_states (substate , ns )
555+
556+ def state_machine_to_nested_state (
557+ self , state_machine : HierarchicalMachine , nested_state : NestedState
558+ ) -> NestedState :
559+ self .state_machine .get_state (
560+ self .state .name
561+ ).add_substate (nested_state )
562+
563+ self .add_all_sub_states (state_machine , nested_state )
564+
565+ for trigger , event in state_machine .events .items ():
566+ for transition_l in event .transitions .values ():
567+ for transition in transition_l :
568+ source = transition .source
569+ dest = transition .dest
570+ self .state_machine .add_transition (
571+ trigger = trigger ,
572+ source = f"{ self .state .name } .{ nested_state .name } .{ source } " ,
573+ dest = f"{ self .state .name } .{ nested_state .name } .{ dest } " ,
574+ )
575+
505576 def get_function_name (
506577 self , fn_ref : Union [Dict [str , Any ], str , None ]
507578 ) -> Optional [str ]:
0 commit comments