def test_input_in_substate() -> None: @inputstep("Input Name", assignee=Assignee.SYSTEM) def input_action(state: State) -> FormGenerator: class SubForm(FormPage): a: int class TestForm(FormPage): sub: SubForm user_input = yield TestForm return user_input.dict() wf = workflow("Workflow with user interaction")( lambda: begin >> input_action >> purestep("process inputs")(Success)) log: List[Tuple[str, Process]] = [] pid = uuid4() p = ProcessStat(pid=pid, workflow=wf, state=Suspend({"sub": { "a": 1, "b": 2 }}), log=wf.steps[1:], current_user="******") result = runwf(p, logstep=store(log)) assert_success(result) assert_state(result, {"sub": {"a": 1, "b": 2}})
def test_resume_suspended_workflow(): wf = workflow("Workflow with user interaction")( lambda: begin >> step1 >> user_action >> step2) log = [] p = ProcessStat( pid=1, workflow=wf, state=Suspend({ "steps": [1], "name": "Jane Doe" }), log=wf.steps[1:], current_user="******", ) result = runwf(p, logstep=store(log)) assert_success(result) assert result == Success({"steps": [1, 2], "name": "Jane Doe"}) assert [ ("Input Name", Success({ "steps": [1], "name": "Jane Doe" })), ("Step 2", Success({ "steps": [1, 2], "name": "Jane Doe" })), ] == log
def start_process( workflow_key: str, user_inputs: Optional[List[State]] = None, user: str = SYSTEM_USER, broadcast_func: Optional[BroadcastFunc] = None, ) -> Tuple[UUID, Future]: """Start a process for workflow. Args: workflow_key: name of workflow user_inputs: List of form inputs from frontend user: User who starts this process broadcast_func: Optional function to broadcast process data Returns: process id """ # ATTENTION!! When modifying this function make sure you make similar changes to `run_workflow` in the test code if user_inputs is None: user_inputs = [{}] pid = uuid4() workflow = get_workflow(workflow_key) if not workflow: raise_status(HTTPStatus.NOT_FOUND, "Workflow does not exist") initial_state = { "process_id": pid, "reporter": user, "workflow_name": workflow_key, "workflow_target": workflow.target, } try: state = post_process(workflow.initial_input_form, initial_state, user_inputs) except FormValidationError: logger.exception("Validation errors", user_inputs=user_inputs) raise pstat = ProcessStat(pid, workflow=workflow, state=Success({ **state, **initial_state }), log=workflow.steps, current_user=user) _db_create_process(pstat) _safe_logstep_withfunc = partial(_safe_logstep, broadcast_func=broadcast_func) return _run_process_async(pstat.pid, lambda: runwf(pstat, _safe_logstep_withfunc))
def test_recover(): log = [] p = ProcessStat( pid=1, workflow=sample_workflow, state=Success({"steps": [4]}), log=sample_workflow.steps[1:], current_user="******", ) result = runwf(p, store(log)) assert_success(result) assert_state(result, {"steps": [4, 2, 3]})
def load_process(process: ProcessTable) -> ProcessStat: workflow = get_workflow(process.workflow) if not workflow: workflow = removed_workflow log = _restore_log(process.steps) pstate, remaining = _recoverwf(workflow, log) return ProcessStat(pid=process.pid, workflow=workflow, state=pstate, log=remaining, current_user=SYSTEM_USER)
def test_abort_workflow(): wf = workflow("Workflow with user interaction")( lambda: begin >> step1 >> user_action) log = [] state = {"steps": [1]} pstat = ProcessStat(pid=1, workflow=wf, state=Success(state), log=wf.steps[1:], current_user="******") result = abort_wf(pstat, store(log)) assert Abort({"steps": [1]}) == result assert log == [("User Aborted", Abort(state))]
def test_resume_waiting_workflow(): hack = {"error": True} @retrystep("Waiting step") def soft_fail(): if hack["error"]: raise ValueError("error") else: return {"some_key": True} wf = workflow("Workflow with soft fail")( lambda: begin >> step1 >> soft_fail >> step2) log = [] state = Waiting({"steps": [1]}) hack["error"] = False p = ProcessStat(pid=1, workflow=wf, state=state, log=wf.steps[1:], current_user="******") result = runwf(p, logstep=store(log)) assert_success(result) assert [ ("Waiting step", Success({ "steps": [1], "some_key": True })), ("Step 2", Success({ "steps": [1, 2], "some_key": True })), ] == log
def create_new_process_stat(workflow, initial_state): return ProcessStat(pid=str(uuid4()), workflow=workflow, state=Success(initial_state), log=workflow.steps, current_user=SYSTEM_USER)