@@ -240,7 +240,7 @@ def get_node_by_id(self, id: str=None) -> dict:
240240
241241
242242
243-
243+
244244 def objects_are_complete (self , object_list : list = []) -> bool :
245245 """
246246 Iterates through the object_list of a given node
@@ -382,6 +382,81 @@ def finalize_node(self, index: int=None) -> None:
382382
383383
384384
385+ def complete_flowrun (self , current_data : dict = None ) -> None :
386+ """
387+ Checks for run completion and updates final
388+ run status.
389+
390+ Expects: {
391+ 'current_data': dict
392+ }
393+
394+ Returns: `FlowRun` object
395+ """
396+
397+ # defaults
398+ status = 'passed'
399+ nodes = self .flowrun .nodes
400+ logs = self .flowrun .logs
401+
402+ # mark current current node as finalized
403+ # and update status
404+ if current_data :
405+ index = current_data ['index' ]
406+ current_status = nodes [int (index )]['data' ]['status' ]
407+
408+ nodes [int (index )]['data' ]['finalized' ] = True
409+ nodes [int (index )]['data' ]['status' ] = 'passed' if current_status == 'working' else current_status
410+
411+ # check all nodes statuses
412+ for node in FlowRun .objects .get (id = self .flowrun_id ).nodes :
413+
414+ # check for non-current 'working' nodes
415+ if node ['data' ]['status' ] == 'working' :
416+ if current_data :
417+ if node ['id' ] != current_data ['node' ]['id' ]:
418+ return self .flowrun
419+ else :
420+ return self .flowrun
421+
422+ # check for any 'failed' nodes
423+ if node ['data' ]['status' ] == 'failed' :
424+ status = 'failed'
425+
426+ # check for current node failure
427+ if current_data :
428+ current_status = nodes [int (current_data ['index' ])]['data' ]['status' ]
429+ status = current_status if current_status == 'failed' else status
430+
431+ # build log data
432+ logs .append ({
433+ 'timestamp' : self .build_timestamp (),
434+ 'step' : current_data ['node' ]['id' ] if current_data else self .get_last_node_id (),
435+ 'message' : (
436+ f'flowrun completed with status: { "✅ PASSED" if status == "passed" else "❌ FAILED" } '
437+ ),
438+ })
439+ # sort logs
440+ logs = sorted (logs , key = lambda l : int (l ['step' ]),)
441+
442+ # update flowrun
443+ self .flowrun .time_completed = self .build_timestamp ()
444+ self .flowrun .status = status
445+ self .flowrun .logs = logs
446+ self .flowrun .nodes = nodes
447+ self .flowrun .save ()
448+
449+ # run alert if requested
450+ alert_id = current_data ['node' ]['data' ].get ('alert_id' ) if current_data else None
451+ if alert_id :
452+ Alerter (alert_id = alert_id , object_id = str (self .flowrun_id )).run_alert ()
453+
454+ # return flowrun
455+ return self .flowrun
456+
457+
458+
459+
385460 def run_next (self ) -> None :
386461 """
387462 Checks for the next step and executes
@@ -421,12 +496,17 @@ def run_next(self) -> None:
421496 }
422497 objs = [alert_obj ,] if step_data ['node' ]['data' ]['task_type' ] in self .alert_types else []
423498
499+ # catch empty task_type
500+ if not step_data ['node' ]['data' ]['task_type' ]:
501+ self .complete_flowrun (current_data = step_data )
502+ return self .flowrun
503+
424504 # run first step
425505 print ('running first step' )
426506 self .execute_step (step_data = step_data , objects = objs )
427507 return self .flowrun
428508
429-
509+
430510 # catch updates without a current_node
431511 if current_data ['node' ] is None :
432512 return self .flowrun
@@ -443,11 +523,11 @@ def run_next(self) -> None:
443523 self .finalize_node (index = current_data ['index' ])
444524
445525 # set defaults
446- true_outcomes = []
447- false_outcomes = []
448- run_as_cumulative = False
449- false_child_ran = False
450- true_child_ran = False
526+ true_outcomes = []
527+ false_outcomes = []
528+ run_as_cumulative = False
529+ false_child_ran = False
530+ true_child_ran = False
451531
452532 # iterate through the objects and run conditions for each
453533 for obj_data in current_data ['node' ]['data' ].get ('objects' , []):
@@ -472,15 +552,15 @@ def run_next(self) -> None:
472552 # sorting
473553 if outcome == True :
474554 true_outcomes .append ({
475- 'parent' : str (parentID ),
476- 'id' : obj_data ['id' ],
477- 'status' : 'working'
555+ 'parent' : str (parentID ),
556+ 'id' : obj_data ['id' ],
557+ 'status' : 'working'
478558 })
479559 if outcome == False :
480560 false_outcomes .append ({
481- 'parent' : str (parentID ),
482- 'id' : obj_data ['id' ],
483- 'status' : 'working'
561+ 'parent' : str (parentID ),
562+ 'id' : obj_data ['id' ],
563+ 'status' : 'working'
484564 })
485565
486566 # get child edges
@@ -535,42 +615,17 @@ def run_next(self) -> None:
535615 if len (children ) == 1 :
536616 if children [0 ] is not None :
537617 next_step = children [0 ]
538- print ('running next step after "PASSED" non-conditional step' )
539- objs = []
540- if next_step ['node' ]['data' ]['task_type' ] in self .alert_types :
541- objs = current_data ['node' ]['data' ].get ('objects' , [])
542- self .execute_step (step_data = next_step , objects = objs )
543- return self .flowrun
544-
545- # check for other working nodes
546- for node in FlowRun .objects .get (id = self .flowrun_id ).nodes :
547- if node ['data' ]['status' ] == 'working' :
548- return self .flowrun
549-
550- # if no children and no node is 'working'
551- # then end flowrun as 'passed' and update logs
552- logs = self .flowrun .logs
553- logs .append ({
554- 'timestamp' :self .build_timestamp (),
555- 'message' : (
556- f'flowrun completed with status: ✅ PASSED'
557- ),
558- 'step' : self .get_last_node_id ()
559- })
560- # sort logs
561- logs = sorted (logs , key = lambda l : int (l ['step' ]),)
562-
563- # update flowrun
564- self .flowrun .time_completed = self .build_timestamp ()
565- self .flowrun .status = 'passed'
566- self .flowrun .logs = logs
567- self .flowrun .save ()
568-
569- # run alert if requested
570- alert_id = current_data ['node' ]['data' ].get ('alert_id' )
571- if alert_id :
572- Alerter (alert_id = alert_id , object_id = str (self .flowrun_id )).run_alert ()
573-
618+ if next_step ['node' ]['data' ]['task_type' ]:
619+ print ('running next step after "PASSED" non-conditional step' )
620+ objs = []
621+ if next_step ['node' ]['data' ]['task_type' ] in self .alert_types :
622+ objs = current_data ['node' ]['data' ].get ('objects' , [])
623+ self .execute_step (step_data = next_step , objects = objs )
624+ return self .flowrun
625+
626+ # if no children, end flowrun and update logs
627+ self .complete_flowrun (current_data = current_data )
628+
574629 # return flowrun
575630 return self .flowrun
576631
@@ -582,36 +637,11 @@ def run_next(self) -> None:
582637 # finialize node
583638 self .finalize_node (index = current_data ['index' ])
584639
585- # define failed log
586- failed_log = {
587- 'timestamp' :self .build_timestamp (),
588- 'message' : (
589- f'flowrun completed with status: ❌ FAILED'
590- ),
591- 'step' : self .get_last_node_id ()
592- }
593-
594640 # end flowrun if requested
595641 if self .flowrun .configs .get ('end_on_fail' , True ):
596642
597643 print ('--- ending run early due to failure ---' )
598-
599- # update logs
600- logs = self .flowrun .logs
601- logs .append (failed_log )
602- # sort logs
603- logs = sorted (logs , key = lambda l : (int (l ['step' ])),)
604-
605- # update & end flowrun
606- self .flowrun .time_completed = self .build_timestamp ()
607- self .flowrun .status = 'failed'
608- self .flowrun .logs = logs
609- self .flowrun .save ()
610-
611- # run alert if requested
612- alert_id = current_data ['node' ]['data' ].get ('alert_id' )
613- if alert_id :
614- Alerter (alert_id = alert_id , object_id = str (self .flowrun_id )).run_alert ()
644+ self .complete_flowrun (current_data = current_data )
615645
616646 # return flowrun
617647 return self .flowrun
@@ -625,35 +655,17 @@ def run_next(self) -> None:
625655 if len (children ) == 1 :
626656 if children [0 ] is not None :
627657 next_step = children [0 ]
628- print ('running next step after "FAILED" non-conditional step' )
629- objs = []
630- if next_step ['node' ]['data' ]['task_type' ] in self .alert_types :
631- objs = current_data ['node' ]['data' ].get ('objects' , [])
632- self .execute_step (step_data = next_step , objects = objs )
633- return self .flowrun
634-
635- # check for other working nodes
636- for node in FlowRun .objects .get (id = self .flowrun_id ).nodes :
637- if node ['data' ]['status' ] == 'working' :
638- return self .flowrun
639-
640- # if no children and no node is 'working'
641- # then end flowrun as 'failed' and update logs
642- logs = self .flowrun .logs
643- logs .append (failed_log )
644- # sort logs
645- logs = sorted (logs , key = lambda l : int (l ['step' ]),)
646-
647- # update & end flowrun
648- self .flowrun .time_completed = self .build_timestamp ()
649- self .flowrun .status = 'failed'
650- self .flowrun .logs = logs
651- self .flowrun .save ()
652-
653- # run alert if requested
654- alert_id = current_data ['node' ]['data' ].get ('alert_id' )
655- if alert_id :
656- Alerter (alert_id = alert_id , object_id = str (self .flowrun_id )).run_alert ()
658+ # check for data in next_step
659+ if next_step ['node' ]['data' ]['task_type' ]:
660+ print ('running next step after "FAILED" non-conditional step' )
661+ objs = []
662+ if next_step ['node' ]['data' ]['task_type' ] in self .alert_types :
663+ objs = current_data ['node' ]['data' ].get ('objects' , [])
664+ self .execute_step (step_data = next_step , objects = objs )
665+ return self .flowrun
666+
667+ # if no children, end flowrun as 'failed' and update logs
668+ self .complete_flowrun (current_data = current_data )
657669
658670 # return flowrun
659671 return self .flowrun
@@ -677,28 +689,34 @@ def execute_step(self, step_data: dict=None, objects: list=None) -> None:
677689 """
678690
679691 if step_data is None :
680- print ('no step_data provided - returning early' )
692+ print ('no step_data provided - attempting to end run...' )
693+ self .complete_flowrun (current_data = step_data )
694+ return
695+
696+ if not step_data ['node' ]['data' ]['task_type' ]:
697+ print ('no task_type provided - attempting to end run...' )
698+ self .complete_flowrun (current_data = step_data )
681699 return
682700
683701 # get step/node data & task_type
684- node_data = step_data ['node' ]['data' ]
685- task_type = node_data ['task_type' ]
686- node_index = step_data ['index' ]
702+ node_data = step_data ['node' ]['data' ]
703+ task_type = node_data ['task_type' ]
704+ node_index = step_data ['index' ]
687705 parent_data = None if node_index == 0 else self .get_node_by_id (node_data ['parentId' ])
688- message = (
706+ message = (
689707 f'starting job ID: { node_data ["id" ]} ' +
690708 f'| job type is [ { task_type .upper ()} ]'
691709 )
692710
693711 # update self.flowrun logs, nodes, & edges
694- self .flowrun = FlowRun .objects .get (id = self .flowrun_id )
695- nodes = self .flowrun .nodes
696- edges = self .flowrun .edges
697- logs = self .flowrun .logs
712+ self .flowrun = FlowRun .objects .get (id = self .flowrun_id )
713+ nodes = self .flowrun .nodes
714+ edges = self .flowrun .edges
715+ logs = self .flowrun .logs
698716
699717 # update current node
700- nodes [step_data ['index' ]]['data' ]['status' ] = 'working'
701- nodes [step_data ['index' ]]['data' ]['time_started' ] = self .build_timestamp ()
718+ nodes [step_data ['index' ]]['data' ]['status' ] = 'working'
719+ nodes [step_data ['index' ]]['data' ]['time_started' ] = self .build_timestamp ()
702720
703721 # update node objects only if task_type is not 'issue' or 'report'
704722 nodes [step_data ['index' ]]['data' ]['objects' ] = objects if (task_type != 'issue' and task_type != 'report' ) else []
@@ -711,27 +729,27 @@ def execute_step(self, step_data: dict=None, objects: list=None) -> None:
711729
712730 # update current logs
713731 logs .append ({
714- 'timestamp' : self .build_timestamp (),
715- 'message' : message ,
716- 'step' : node_data ['id' ]
732+ 'timestamp' : self .build_timestamp (),
733+ 'message' : message ,
734+ 'step' : node_data ['id' ]
717735 })
718736
719737 # sort logs
720738 logs = sorted (logs , key = lambda l : int (l ['step' ]),)
721739
722740 # save updates
723- self .flowrun .nodes = nodes
724- self .flowrun .edges = edges
725- self .flowrun .logs = logs
741+ self .flowrun .nodes = nodes
742+ self .flowrun .edges = edges
743+ self .flowrun .logs = logs
726744 self .flowrun .save ()
727745
728746 # build common data
729- scope = 'account'
730- configs = node_data ['configs' ]
731- flowrun_id = str (self .flowrun .id )
732- account_id = str (self .flowrun .account .id )
733- types = node_data .get ('type' )
734- resources = [{
747+ scope = 'account'
748+ configs = node_data ['configs' ]
749+ flowrun_id = str (self .flowrun .id )
750+ account_id = str (self .flowrun .account .id )
751+ types = node_data .get ('type' )
752+ resources = [{
735753 'str' : self .flowrun .site .site_url ,
736754 'id' : str (self .flowrun .site .id ),
737755 'type' : 'site'
0 commit comments