Merge pull request #200 from sartography/feature/fix_process_instance_rewind
Feature/fix process instance rewind
This commit is contained in:
commit
457487ff63
|
@ -1344,7 +1344,14 @@ class ProcessInstanceProcessor:
|
|||
# db.session.commit()
|
||||
#
|
||||
# for task_to_update in tasks_to_update:
|
||||
# # print(f"task_to_update: {task_to_update}")
|
||||
# print(f"task_to_update.state: {task_to_update.state}")
|
||||
# TaskService.reset_task_model(task_to_update, state="FUTURE", commit=commit)
|
||||
# # TaskService.reset_task_model(task_to_update, state=task_to_update.state, commit=commit)
|
||||
# # if task_to_update.task_definition.bpmn_identifier != 'top_level_process_script_after_gate':
|
||||
# # TaskService.reset_task_model(task_to_update, state='FUTURE', commit=commit)
|
||||
# # else:
|
||||
# # TaskService.reset_task_model(task_to_update, state=task_to_update.state, commit=commit)
|
||||
#
|
||||
# parent_task_model = TaskModel.query.filter_by(guid=to_task_model.properties_json["parent"]).first()
|
||||
# if parent_task_model is None:
|
||||
|
@ -1362,6 +1369,13 @@ class ProcessInstanceProcessor:
|
|||
# for task_model in task_models_of_parent_bpmn_processes:
|
||||
# TaskService.reset_task_model(task_model, state="WAITING", commit=commit)
|
||||
#
|
||||
# bpmn_process = to_task_model.bpmn_process
|
||||
# properties_json = copy.copy(bpmn_process.properties_json)
|
||||
# properties_json["last_task"] = parent_task_model.guid
|
||||
# bpmn_process.properties_json = properties_json
|
||||
# db.session.add(bpmn_process)
|
||||
# db.session.commit()
|
||||
#
|
||||
# if commit:
|
||||
# processor = ProcessInstanceProcessor(process_instance)
|
||||
# processor.save()
|
||||
|
@ -1802,6 +1816,13 @@ class ProcessInstanceProcessor:
|
|||
user_id=user.id,
|
||||
)
|
||||
|
||||
task_service = TaskService(
|
||||
process_instance=self.process_instance_model,
|
||||
serializer=self._serializer,
|
||||
bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings,
|
||||
)
|
||||
task_service.process_parents_and_children_and_save_to_database(spiff_task)
|
||||
|
||||
# this is the thing that actually commits the db transaction (on behalf of the other updates above as well)
|
||||
self.save()
|
||||
|
||||
|
@ -1873,6 +1894,9 @@ class ProcessInstanceProcessor:
|
|||
all_tasks = self.bpmn_process_instance.get_tasks(TaskState.ANY_MASK)
|
||||
return [t for t in all_tasks if t.state in [TaskState.WAITING, TaskState.READY]]
|
||||
|
||||
def get_task_by_guid(self, task_guid: str) -> Optional[SpiffTask]:
|
||||
return self.bpmn_process_instance.get_task_from_id(UUID(task_guid))
|
||||
|
||||
@classmethod
|
||||
def get_task_by_bpmn_identifier(
|
||||
cls, bpmn_task_identifier: str, bpmn_process_instance: BpmnWorkflow
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import copy
|
||||
import json
|
||||
import time
|
||||
from hashlib import sha256
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
@ -19,6 +21,8 @@ from spiffworkflow_backend.models.bpmn_process import BpmnProcessNotFoundError
|
|||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel
|
||||
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType
|
||||
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
||||
|
||||
|
||||
|
@ -30,6 +34,145 @@ class JsonDataDict(TypedDict):
|
|||
class TaskService:
|
||||
PYTHON_ENVIRONMENT_STATE_KEY = "spiff__python_env_state"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
process_instance: ProcessInstanceModel,
|
||||
serializer: BpmnWorkflowSerializer,
|
||||
bpmn_definition_to_task_definitions_mappings: dict,
|
||||
) -> None:
|
||||
self.process_instance = process_instance
|
||||
self.bpmn_definition_to_task_definitions_mappings = bpmn_definition_to_task_definitions_mappings
|
||||
self.serializer = serializer
|
||||
|
||||
self.bpmn_processes: dict[str, BpmnProcessModel] = {}
|
||||
self.task_models: dict[str, TaskModel] = {}
|
||||
self.json_data_dicts: dict[str, JsonDataDict] = {}
|
||||
self.process_instance_events: dict[str, ProcessInstanceEventModel] = {}
|
||||
|
||||
def save_objects_to_database(self) -> None:
|
||||
db.session.bulk_save_objects(self.bpmn_processes.values())
|
||||
db.session.bulk_save_objects(self.task_models.values())
|
||||
db.session.bulk_save_objects(self.process_instance_events.values())
|
||||
self.__class__.insert_or_update_json_data_records(self.json_data_dicts)
|
||||
|
||||
def process_parents_and_children_and_save_to_database(
|
||||
self,
|
||||
spiff_task: SpiffTask,
|
||||
) -> None:
|
||||
self.process_spiff_task_children(spiff_task)
|
||||
self.process_spiff_task_parent_subprocess_tasks(spiff_task)
|
||||
self.save_objects_to_database()
|
||||
|
||||
def process_spiff_task_children(
|
||||
self,
|
||||
spiff_task: SpiffTask,
|
||||
) -> None:
|
||||
for child_spiff_task in spiff_task.children:
|
||||
# if child_spiff_task._has_state(TaskState.PREDICTED_MASK):
|
||||
# self.__class__.remove_spiff_task_from_parent(child_spiff_task, self.task_models)
|
||||
# continue
|
||||
self.update_task_model_with_spiff_task(
|
||||
spiff_task=child_spiff_task,
|
||||
)
|
||||
self.process_spiff_task_children(
|
||||
spiff_task=child_spiff_task,
|
||||
)
|
||||
|
||||
def process_spiff_task_parent_subprocess_tasks(
|
||||
self,
|
||||
spiff_task: SpiffTask,
|
||||
) -> None:
|
||||
"""Find the parent subprocess of a given spiff_task and update its data.
|
||||
|
||||
This will also process that subprocess task's children and will recurse upwards
|
||||
to process its parent subprocesses as well.
|
||||
"""
|
||||
(parent_subprocess_guid, _parent_subprocess) = self.__class__.task_subprocess(spiff_task)
|
||||
if parent_subprocess_guid is not None:
|
||||
spiff_task_of_parent_subprocess = spiff_task.workflow._get_outermost_workflow().get_task_from_id(
|
||||
UUID(parent_subprocess_guid)
|
||||
)
|
||||
|
||||
if spiff_task_of_parent_subprocess is not None:
|
||||
self.update_task_model_with_spiff_task(
|
||||
spiff_task=spiff_task_of_parent_subprocess,
|
||||
)
|
||||
self.process_spiff_task_children(
|
||||
spiff_task=spiff_task_of_parent_subprocess,
|
||||
)
|
||||
self.process_spiff_task_parent_subprocess_tasks(
|
||||
spiff_task=spiff_task_of_parent_subprocess,
|
||||
)
|
||||
|
||||
def update_task_model_with_spiff_task(
|
||||
self,
|
||||
spiff_task: SpiffTask,
|
||||
task_failed: bool = False,
|
||||
) -> TaskModel:
|
||||
(
|
||||
new_bpmn_process,
|
||||
task_model,
|
||||
new_task_models,
|
||||
new_json_data_dicts,
|
||||
) = self.__class__.find_or_create_task_model_from_spiff_task(
|
||||
spiff_task,
|
||||
self.process_instance,
|
||||
self.serializer,
|
||||
bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings,
|
||||
)
|
||||
bpmn_process = new_bpmn_process or task_model.bpmn_process
|
||||
bpmn_process_json_data = self.__class__.update_task_data_on_bpmn_process(
|
||||
bpmn_process, spiff_task.workflow.data
|
||||
)
|
||||
self.task_models.update(new_task_models)
|
||||
self.json_data_dicts.update(new_json_data_dicts)
|
||||
json_data_dict_list = self.__class__.update_task_model(task_model, spiff_task, self.serializer)
|
||||
self.task_models[task_model.guid] = task_model
|
||||
if bpmn_process_json_data is not None:
|
||||
json_data_dict_list.append(bpmn_process_json_data)
|
||||
self._update_json_data_dicts_using_list(json_data_dict_list, self.json_data_dicts)
|
||||
|
||||
if task_model.state == "COMPLETED" or task_failed:
|
||||
event_type = ProcessInstanceEventType.task_completed.value
|
||||
if task_failed:
|
||||
event_type = ProcessInstanceEventType.task_failed.value
|
||||
|
||||
# FIXME: some failed tasks will currently not have either timestamp since we only hook into spiff when tasks complete
|
||||
# which script tasks execute when READY.
|
||||
timestamp = task_model.end_in_seconds or task_model.start_in_seconds or time.time()
|
||||
process_instance_event = ProcessInstanceEventModel(
|
||||
task_guid=task_model.guid,
|
||||
process_instance_id=self.process_instance.id,
|
||||
event_type=event_type,
|
||||
timestamp=timestamp,
|
||||
)
|
||||
self.process_instance_events[task_model.guid] = process_instance_event
|
||||
|
||||
self.update_bpmn_process(spiff_task.workflow, bpmn_process)
|
||||
return task_model
|
||||
|
||||
def update_bpmn_process(
|
||||
self,
|
||||
spiff_workflow: BpmnWorkflow,
|
||||
bpmn_process: BpmnProcessModel,
|
||||
) -> None:
|
||||
new_properties_json = copy.copy(bpmn_process.properties_json)
|
||||
new_properties_json["last_task"] = str(spiff_workflow.last_task) if spiff_workflow.last_task else None
|
||||
new_properties_json["success"] = spiff_workflow.success
|
||||
bpmn_process.properties_json = new_properties_json
|
||||
|
||||
bpmn_process_json_data = self.__class__.update_task_data_on_bpmn_process(bpmn_process, spiff_workflow.data)
|
||||
if bpmn_process_json_data is not None:
|
||||
self.json_data_dicts[bpmn_process_json_data["hash"]] = bpmn_process_json_data
|
||||
|
||||
self.bpmn_processes[bpmn_process.guid or "top_level"] = bpmn_process
|
||||
|
||||
if spiff_workflow.outer_workflow != spiff_workflow:
|
||||
direct_parent_bpmn_process = BpmnProcessModel.query.filter_by(
|
||||
id=bpmn_process.direct_parent_process_id
|
||||
).first()
|
||||
self.update_bpmn_process(spiff_workflow.outer_workflow, direct_parent_bpmn_process)
|
||||
|
||||
@classmethod
|
||||
def insert_or_update_json_data_records(
|
||||
cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]
|
||||
|
@ -58,6 +201,8 @@ class TaskService:
|
|||
It also returns the relating json_data object so they can be imported later.
|
||||
"""
|
||||
new_properties_json = serializer.task_to_dict(spiff_task)
|
||||
if new_properties_json["task_spec"] == "Start":
|
||||
new_properties_json["parent"] = None
|
||||
spiff_task_data = new_properties_json.pop("data")
|
||||
python_env_data_dict = cls._get_python_env_data_dict_from_spiff_task(spiff_task, serializer)
|
||||
task_model.properties_json = new_properties_json
|
||||
|
@ -110,9 +255,9 @@ class TaskService:
|
|||
for sp_id, sp in top_level_workflow.subprocesses.items():
|
||||
if sp == my_wf:
|
||||
my_sp = sp
|
||||
my_sp_id = sp_id
|
||||
my_sp_id = str(sp_id)
|
||||
break
|
||||
return (str(my_sp_id), my_sp)
|
||||
return (my_sp_id, my_sp)
|
||||
|
||||
@classmethod
|
||||
def task_bpmn_process(
|
||||
|
@ -180,7 +325,7 @@ class TaskService:
|
|||
if "subprocess_specs" in bpmn_process_dict:
|
||||
bpmn_process_dict.pop("subprocess_specs")
|
||||
|
||||
new_task_models = {}
|
||||
new_task_models: dict[str, TaskModel] = {}
|
||||
new_json_data_dicts: dict[str, JsonDataDict] = {}
|
||||
|
||||
bpmn_process = None
|
||||
|
@ -250,12 +395,13 @@ class TaskService:
|
|||
# bpmn process defintion so let's avoid using it.
|
||||
if task_properties["task_spec"] == "Root":
|
||||
continue
|
||||
if task_properties["task_spec"] == "Start":
|
||||
task_properties["parent"] = None
|
||||
|
||||
task_data_dict = task_properties.pop("data")
|
||||
state_int = task_properties["state"]
|
||||
# we are going to avoid saving likely and maybe tasks to the db.
|
||||
# that means we need to remove them from their parents' lists of children as well.
|
||||
spiff_task = spiff_workflow.get_task_from_id(UUID(task_id))
|
||||
# if spiff_task._has_state(TaskState.PREDICTED_MASK):
|
||||
# cls.remove_spiff_task_from_parent(spiff_task, new_task_models)
|
||||
# continue
|
||||
|
||||
task_model = TaskModel.query.filter_by(guid=task_id).first()
|
||||
if task_model is None:
|
||||
|
@ -265,25 +411,28 @@ class TaskService:
|
|||
spiff_task,
|
||||
bpmn_definition_to_task_definitions_mappings,
|
||||
)
|
||||
task_model.state = TaskStateNames[state_int]
|
||||
task_model.properties_json = task_properties
|
||||
new_task_models[task_model.guid] = task_model
|
||||
|
||||
json_data_dict = TaskService.update_task_data_on_task_model(
|
||||
task_model, task_data_dict, "json_data_hash"
|
||||
)
|
||||
json_data_dict, python_env_dict = cls.update_task_model(task_model, spiff_task, serializer)
|
||||
|
||||
new_task_models[task_model.guid] = task_model
|
||||
if json_data_dict is not None:
|
||||
new_json_data_dicts[json_data_dict["hash"]] = json_data_dict
|
||||
|
||||
python_env_data_dict = cls._get_python_env_data_dict_from_spiff_task(spiff_task, serializer)
|
||||
python_env_dict = TaskService.update_task_data_on_task_model(
|
||||
task_model, python_env_data_dict, "python_env_data_hash"
|
||||
)
|
||||
if python_env_dict is not None:
|
||||
new_json_data_dicts[python_env_dict["hash"]] = python_env_dict
|
||||
|
||||
return (bpmn_process, new_task_models, new_json_data_dicts)
|
||||
|
||||
@classmethod
|
||||
def remove_spiff_task_from_parent(cls, spiff_task: SpiffTask, task_models: dict[str, TaskModel]) -> None:
|
||||
"""Removes the given spiff task from its parent and then updates the task_models dict with the changes."""
|
||||
spiff_task_parent_guid = str(spiff_task.parent.id)
|
||||
spiff_task_guid = str(spiff_task.id)
|
||||
if spiff_task_parent_guid in task_models:
|
||||
parent_task_model = task_models[spiff_task_parent_guid]
|
||||
new_parent_properties_json = copy.copy(parent_task_model.properties_json)
|
||||
new_parent_properties_json["children"].remove(spiff_task_guid)
|
||||
parent_task_model.properties_json = new_parent_properties_json
|
||||
task_models[spiff_task_parent_guid] = parent_task_model
|
||||
|
||||
@classmethod
|
||||
def update_task_data_on_bpmn_process(
|
||||
cls, bpmn_process: BpmnProcessModel, bpmn_process_data_dict: dict
|
||||
|
@ -360,7 +509,7 @@ class TaskService:
|
|||
else:
|
||||
task_model.python_env_data_hash = python_env_data_hash
|
||||
|
||||
new_properties_json = task_model.properties_json
|
||||
new_properties_json = copy.copy(task_model.properties_json)
|
||||
task_model.state = state
|
||||
task_model.start_in_seconds = None
|
||||
task_model.end_in_seconds = None
|
||||
|
@ -405,3 +554,11 @@ class TaskService:
|
|||
# this helps to convert items like datetime objects to be json serializable
|
||||
converted_data: dict = serializer.data_converter.convert(user_defined_state)
|
||||
return converted_data
|
||||
|
||||
@classmethod
|
||||
def _update_json_data_dicts_using_list(
|
||||
cls, json_data_dict_list: list[Optional[JsonDataDict]], json_data_dicts: dict[str, JsonDataDict]
|
||||
) -> None:
|
||||
for json_data_dict in json_data_dict_list:
|
||||
if json_data_dict is not None:
|
||||
json_data_dicts[json_data_dict["hash"]] = json_data_dict
|
||||
|
|
|
@ -15,14 +15,12 @@ from spiffworkflow_backend.models.message_instance_correlation import (
|
|||
MessageInstanceCorrelationRuleModel,
|
||||
)
|
||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventModel
|
||||
from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType
|
||||
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
||||
from spiffworkflow_backend.models.task import TaskModel
|
||||
from spiffworkflow_backend.models.task_definition import TaskDefinitionModel # noqa: F401
|
||||
from spiffworkflow_backend.services.assertion_service import safe_assertion
|
||||
from spiffworkflow_backend.services.process_instance_lock_service import (
|
||||
ProcessInstanceLockService,
|
||||
)
|
||||
from spiffworkflow_backend.services.task_service import JsonDataDict
|
||||
from spiffworkflow_backend.services.task_service import TaskService
|
||||
|
||||
|
||||
|
@ -63,23 +61,29 @@ class TaskModelSavingDelegate(EngineStepDelegate):
|
|||
self.current_task_model: Optional[TaskModel] = None
|
||||
self.current_task_start_in_seconds: Optional[float] = None
|
||||
|
||||
self.task_models: dict[str, TaskModel] = {}
|
||||
self.json_data_dicts: dict[str, JsonDataDict] = {}
|
||||
self.process_instance_events: dict[str, ProcessInstanceEventModel] = {}
|
||||
self.last_completed_spiff_task: Optional[SpiffTask] = None
|
||||
|
||||
self.task_service = TaskService(
|
||||
process_instance=self.process_instance,
|
||||
serializer=self.serializer,
|
||||
bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings,
|
||||
)
|
||||
|
||||
def will_complete_task(self, spiff_task: SpiffTask) -> None:
|
||||
if self._should_update_task_model():
|
||||
self.current_task_start_in_seconds = time.time()
|
||||
spiff_task.task_spec._predict(spiff_task, mask=TaskState.NOT_FINISHED_MASK)
|
||||
if self.secondary_engine_step_delegate:
|
||||
self.secondary_engine_step_delegate.will_complete_task(spiff_task)
|
||||
|
||||
def did_complete_task(self, spiff_task: SpiffTask) -> None:
|
||||
if self._should_update_task_model():
|
||||
task_model = self._update_task_model_with_spiff_task(spiff_task)
|
||||
task_model = self.task_service.update_task_model_with_spiff_task(spiff_task)
|
||||
if self.current_task_start_in_seconds is None:
|
||||
raise Exception("Could not find cached current_task_start_in_seconds. This should never have happend")
|
||||
task_model.start_in_seconds = self.current_task_start_in_seconds
|
||||
task_model.end_in_seconds = time.time()
|
||||
self.last_completed_spiff_task = spiff_task
|
||||
if self.secondary_engine_step_delegate:
|
||||
self.secondary_engine_step_delegate.did_complete_task(spiff_task)
|
||||
|
||||
|
@ -87,12 +91,9 @@ class TaskModelSavingDelegate(EngineStepDelegate):
|
|||
script_engine = bpmn_process_instance.script_engine
|
||||
if hasattr(script_engine, "failing_spiff_task") and script_engine.failing_spiff_task is not None:
|
||||
failing_spiff_task = script_engine.failing_spiff_task
|
||||
self._update_task_model_with_spiff_task(failing_spiff_task, task_failed=True)
|
||||
self.task_service.update_task_model_with_spiff_task(failing_spiff_task, task_failed=True)
|
||||
|
||||
db.session.bulk_save_objects(self.task_models.values())
|
||||
db.session.bulk_save_objects(self.process_instance_events.values())
|
||||
|
||||
TaskService.insert_or_update_json_data_records(self.json_data_dicts)
|
||||
self.task_service.save_objects_to_database()
|
||||
|
||||
if self.secondary_engine_step_delegate:
|
||||
self.secondary_engine_step_delegate.save(bpmn_process_instance, commit=False)
|
||||
|
@ -103,10 +104,13 @@ class TaskModelSavingDelegate(EngineStepDelegate):
|
|||
# TODO: also include children of the last task processed. This may help with task resets
|
||||
# if we have to set their states to FUTURE.
|
||||
# excludes FUTURE and COMPLETED. the others were required to get PP1 to go to completion.
|
||||
for waiting_spiff_task in bpmn_process_instance.get_tasks(
|
||||
TaskState.WAITING | TaskState.CANCELLED | TaskState.READY | TaskState.MAYBE | TaskState.LIKELY
|
||||
):
|
||||
self._update_task_model_with_spiff_task(waiting_spiff_task)
|
||||
# for waiting_spiff_task in bpmn_process_instance.get_tasks(
|
||||
# TaskState.WAITING | TaskState.CANCELLED | TaskState.READY | TaskState.MAYBE | TaskState.LIKELY
|
||||
# ):
|
||||
# self._update_task_model_with_spiff_task(waiting_spiff_task)
|
||||
if self.last_completed_spiff_task is not None:
|
||||
self.task_service.process_spiff_task_parent_subprocess_tasks(self.last_completed_spiff_task)
|
||||
self.task_service.process_spiff_task_children(self.last_completed_spiff_task)
|
||||
|
||||
def _should_update_task_model(self) -> bool:
|
||||
"""We need to figure out if we have previously save task info on this process intance.
|
||||
|
@ -116,52 +120,6 @@ class TaskModelSavingDelegate(EngineStepDelegate):
|
|||
# return self.process_instance.bpmn_process_id is not None
|
||||
return True
|
||||
|
||||
def _update_json_data_dicts_using_list(self, json_data_dict_list: list[Optional[JsonDataDict]]) -> None:
|
||||
for json_data_dict in json_data_dict_list:
|
||||
if json_data_dict is not None:
|
||||
self.json_data_dicts[json_data_dict["hash"]] = json_data_dict
|
||||
|
||||
def _update_task_model_with_spiff_task(self, spiff_task: SpiffTask, task_failed: bool = False) -> TaskModel:
|
||||
(
|
||||
bpmn_process,
|
||||
task_model,
|
||||
new_task_models,
|
||||
new_json_data_dicts,
|
||||
) = TaskService.find_or_create_task_model_from_spiff_task(
|
||||
spiff_task,
|
||||
self.process_instance,
|
||||
self.serializer,
|
||||
bpmn_definition_to_task_definitions_mappings=self.bpmn_definition_to_task_definitions_mappings,
|
||||
)
|
||||
bpmn_process_json_data = TaskService.update_task_data_on_bpmn_process(
|
||||
bpmn_process or task_model.bpmn_process, spiff_task.workflow.data
|
||||
)
|
||||
self.task_models.update(new_task_models)
|
||||
self.json_data_dicts.update(new_json_data_dicts)
|
||||
json_data_dict_list = TaskService.update_task_model(task_model, spiff_task, self.serializer)
|
||||
self.task_models[task_model.guid] = task_model
|
||||
if bpmn_process_json_data is not None:
|
||||
json_data_dict_list.append(bpmn_process_json_data)
|
||||
self._update_json_data_dicts_using_list(json_data_dict_list)
|
||||
|
||||
if task_model.state == "COMPLETED" or task_failed:
|
||||
event_type = ProcessInstanceEventType.task_completed.value
|
||||
if task_failed:
|
||||
event_type = ProcessInstanceEventType.task_failed.value
|
||||
|
||||
# FIXME: some failed tasks will currently not have either timestamp since we only hook into spiff when tasks complete
|
||||
# which script tasks execute when READY.
|
||||
timestamp = task_model.end_in_seconds or task_model.start_in_seconds or time.time()
|
||||
process_instance_event = ProcessInstanceEventModel(
|
||||
task_guid=task_model.guid,
|
||||
process_instance_id=self.process_instance.id,
|
||||
event_type=event_type,
|
||||
timestamp=timestamp,
|
||||
)
|
||||
self.process_instance_events[task_model.guid] = process_instance_event
|
||||
|
||||
return task_model
|
||||
|
||||
|
||||
class ExecutionStrategy:
|
||||
"""Interface of sorts for a concrete execution strategy."""
|
||||
|
@ -169,13 +127,12 @@ class ExecutionStrategy:
|
|||
def __init__(self, delegate: EngineStepDelegate):
|
||||
"""__init__."""
|
||||
self.delegate = delegate
|
||||
self.bpmn_process_instance = None
|
||||
|
||||
def do_engine_steps(self, bpmn_process_instance: BpmnWorkflow, exit_at: None = None) -> None:
|
||||
pass
|
||||
|
||||
def save(self) -> None:
|
||||
self.delegate.save(self.bpmn_process_instance)
|
||||
def save(self, bpmn_process_instance: BpmnWorkflow) -> None:
|
||||
self.delegate.save(bpmn_process_instance)
|
||||
|
||||
|
||||
class GreedyExecutionStrategy(ExecutionStrategy):
|
||||
|
@ -281,7 +238,7 @@ class WorkflowExecutionService:
|
|||
raise ApiError.from_workflow_exception("task_error", str(swe), swe) from swe
|
||||
|
||||
finally:
|
||||
self.execution_strategy.save()
|
||||
self.execution_strategy.save(self.bpmn_process_instance)
|
||||
db.session.commit()
|
||||
|
||||
if save:
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
|
||||
<bpmn:process id="test_loopback_to_subprocess" isExecutable="true">
|
||||
<bpmn:startEvent id="Event_17ujsfj">
|
||||
<bpmn:outgoing>Flow_1dk6oyl</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:exclusiveGateway id="gateway" name="Gateway" default="Flow_11uu31d">
|
||||
<bpmn:incoming>Flow_0s9lss3</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_02xy1ag</bpmn:outgoing>
|
||||
<bpmn:outgoing>Flow_11uu31d</bpmn:outgoing>
|
||||
</bpmn:exclusiveGateway>
|
||||
<bpmn:sequenceFlow id="Flow_0s9lss3" sourceRef="script_task" targetRef="gateway" />
|
||||
<bpmn:scriptTask id="script_task" name="Script Task">
|
||||
<bpmn:incoming>Flow_0sw85uk</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0s9lss3</bpmn:outgoing>
|
||||
<bpmn:script>x=1</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:endEvent id="Event_0ryttlc">
|
||||
<bpmn:incoming>Flow_02xy1ag</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_02xy1ag" sourceRef="gateway" targetRef="Event_0ryttlc">
|
||||
<bpmn:conditionExpression>x==2</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:sequenceFlow id="Flow_1dk6oyl" sourceRef="Event_17ujsfj" targetRef="subprocess" />
|
||||
<bpmn:sequenceFlow id="Flow_0sw85uk" sourceRef="subprocess" targetRef="script_task" />
|
||||
<bpmn:subProcess id="subprocess" name="Subprocess">
|
||||
<bpmn:incoming>Flow_1dk6oyl</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_11uu31d</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0sw85uk</bpmn:outgoing>
|
||||
<bpmn:startEvent id="Event_17df4es">
|
||||
<bpmn:outgoing>Flow_0ih1i19</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0ih1i19" sourceRef="Event_17df4es" targetRef="subprocess_manual_task" />
|
||||
<bpmn:endEvent id="Event_1ehwj0c">
|
||||
<bpmn:incoming>Flow_0dua5j8</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0dua5j8" sourceRef="subprocess_manual_task" targetRef="Event_1ehwj0c" />
|
||||
<bpmn:manualTask id="subprocess_manual_task" name="Subprocess Manual Task">
|
||||
<bpmn:extensionElements>
|
||||
<spiffworkflow:instructionsForEndUser>HEY MANUAL</spiffworkflow:instructionsForEndUser>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0ih1i19</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0dua5j8</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
</bpmn:subProcess>
|
||||
<bpmn:sequenceFlow id="Flow_11uu31d" sourceRef="gateway" targetRef="subprocess" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="test_loopback_to_subprocess">
|
||||
<bpmndi:BPMNShape id="Event_17ujsfj_di" bpmnElement="Event_17ujsfj">
|
||||
<dc:Bounds x="122" y="-168" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_16ouwyf_di" bpmnElement="gateway" isMarkerVisible="true">
|
||||
<dc:Bounds x="565" y="-175" width="50" height="50" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="568" y="-118" width="44" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1tvzm43_di" bpmnElement="script_task">
|
||||
<dc:Bounds x="370" y="-190" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0ryttlc_di" bpmnElement="Event_0ryttlc">
|
||||
<dc:Bounds x="712" y="-168" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_10og25a_di" bpmnElement="subprocess">
|
||||
<dc:Bounds x="210" y="-190" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0s9lss3_di" bpmnElement="Flow_0s9lss3">
|
||||
<di:waypoint x="470" y="-150" />
|
||||
<di:waypoint x="565" y="-150" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_02xy1ag_di" bpmnElement="Flow_02xy1ag">
|
||||
<di:waypoint x="615" y="-150" />
|
||||
<di:waypoint x="712" y="-150" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1dk6oyl_di" bpmnElement="Flow_1dk6oyl">
|
||||
<di:waypoint x="158" y="-150" />
|
||||
<di:waypoint x="210" y="-150" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0sw85uk_di" bpmnElement="Flow_0sw85uk">
|
||||
<di:waypoint x="310" y="-150" />
|
||||
<di:waypoint x="370" y="-150" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_11uu31d_di" bpmnElement="Flow_11uu31d">
|
||||
<di:waypoint x="590" y="-175" />
|
||||
<di:waypoint x="590" y="-250" />
|
||||
<di:waypoint x="438" y="-250" />
|
||||
<di:waypoint x="303" y="-189" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_0d2d8pf">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_0ez33hq" bpmnElement="subprocess">
|
||||
<bpmndi:BPMNShape id="Event_17df4es_di" bpmnElement="Event_17df4es">
|
||||
<dc:Bounds x="212" y="172" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_1ehwj0c_di" bpmnElement="Event_1ehwj0c">
|
||||
<dc:Bounds x="452" y="172" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0va03mf_di" bpmnElement="subprocess_manual_task">
|
||||
<dc:Bounds x="300" y="150" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0ih1i19_di" bpmnElement="Flow_0ih1i19">
|
||||
<di:waypoint x="248" y="190" />
|
||||
<di:waypoint x="300" y="190" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0dua5j8_di" bpmnElement="Flow_0dua5j8">
|
||||
<di:waypoint x="400" y="190" />
|
||||
<di:waypoint x="452" y="190" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
|
@ -7,8 +7,8 @@
|
|||
<bpmn:endEvent id="end_event_of_manual_task_model">
|
||||
<bpmn:incoming>Flow_0nnh2x9</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0nnh2x9" sourceRef="Activity_Hello" targetRef="end_event_of_manual_task_model" />
|
||||
<bpmn:manualTask id="Activity_Hello" name="Hello">
|
||||
<bpmn:sequenceFlow id="Flow_0nnh2x9" sourceRef="manual_task_one" targetRef="end_event_of_manual_task_model" />
|
||||
<bpmn:manualTask id="manual_task_one" name="Hello">
|
||||
<bpmn:extensionElements>
|
||||
<spiffworkflow:instructionsForEndUser>## Hello</spiffworkflow:instructionsForEndUser>
|
||||
</bpmn:extensionElements>
|
||||
|
@ -16,7 +16,7 @@
|
|||
<bpmn:outgoing>Flow_0nnh2x9</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
<bpmn:sequenceFlow id="Flow_0stlaxe" sourceRef="StartEvent_1" targetRef="the_script" />
|
||||
<bpmn:sequenceFlow id="Flow_1pmem7s" sourceRef="the_script" targetRef="Activity_Hello" />
|
||||
<bpmn:sequenceFlow id="Flow_1pmem7s" sourceRef="the_script" targetRef="manual_task_one" />
|
||||
<bpmn:scriptTask id="the_script">
|
||||
<bpmn:incoming>Flow_0stlaxe</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1pmem7s</bpmn:outgoing>
|
||||
|
@ -31,7 +31,7 @@
|
|||
<bpmndi:BPMNShape id="Event_0ia26nb_di" bpmnElement="end_event_of_manual_task_model">
|
||||
<dc:Bounds x="592" y="159" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1rcj16n_di" bpmnElement="Activity_Hello">
|
||||
<bpmndi:BPMNShape id="Activity_1rcj16n_di" bpmnElement="manual_task_one">
|
||||
<dc:Bounds x="420" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1vokg57_di" bpmnElement="the_script">
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0stlaxe</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:endEvent id="end_event_of_manual_task_model">
|
||||
<bpmn:endEvent id="end_event_of_manual_task_model" name="End Event Of Manual Task Model">
|
||||
<bpmn:incoming>Flow_1ygcsbt</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:manualTask id="manual_task" name="Hello">
|
||||
<bpmn:manualTask id="top_level_manual_task_two" name="Top Level Manual Task Two">
|
||||
<bpmn:extensionElements>
|
||||
<spiffworkflow:instructionsForEndUser>## Hello</spiffworkflow:instructionsForEndUser>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_1fktmf7</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_1t9ywmr</bpmn:incoming>
|
||||
<bpmn:incoming>Flow_0q30935</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_09gjylo</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
<bpmn:sequenceFlow id="Flow_0stlaxe" sourceRef="StartEvent_1" targetRef="top_level_script" />
|
||||
|
@ -21,9 +21,9 @@
|
|||
<bpmn:outgoing>Flow_1fktmf7</bpmn:outgoing>
|
||||
<bpmn:script>set_in_top_level_script = 1</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_1fktmf7" sourceRef="top_level_script" targetRef="manual_task" />
|
||||
<bpmn:sequenceFlow id="Flow_09gjylo" sourceRef="manual_task" targetRef="top_level_subprocess" />
|
||||
<bpmn:subProcess id="top_level_subprocess">
|
||||
<bpmn:sequenceFlow id="Flow_1fktmf7" sourceRef="top_level_script" targetRef="top_level_manual_task_one" />
|
||||
<bpmn:sequenceFlow id="Flow_09gjylo" sourceRef="top_level_manual_task_two" targetRef="top_level_subprocess" />
|
||||
<bpmn:subProcess id="top_level_subprocess" name="Top Level Subprocess">
|
||||
<bpmn:incoming>Flow_09gjylo</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0yxus36</bpmn:outgoing>
|
||||
<bpmn:startEvent id="Event_0g7txdo">
|
||||
|
@ -46,7 +46,7 @@ except:
|
|||
we_move_on = False</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
</bpmn:subProcess>
|
||||
<bpmn:callActivity id="top_level_call_activity" calledElement="test_process_to_call">
|
||||
<bpmn:callActivity id="top_level_call_activity" name="Top Level Call Activity" calledElement="test_process_to_call">
|
||||
<bpmn:incoming>Flow_0yxus36</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_187mcqe</bpmn:outgoing>
|
||||
</bpmn:callActivity>
|
||||
|
@ -60,13 +60,18 @@ except:
|
|||
<bpmn:conditionExpression>we_move_on == True</bpmn:conditionExpression>
|
||||
</bpmn:sequenceFlow>
|
||||
<bpmn:sequenceFlow id="Flow_1ygcsbt" sourceRef="top_level_process_script_after_gate" targetRef="end_event_of_manual_task_model" />
|
||||
<bpmn:scriptTask id="top_level_process_script_after_gate">
|
||||
<bpmn:scriptTask id="top_level_process_script_after_gate" name="Top Level Process Script After Gate">
|
||||
<bpmn:incoming>Flow_0lw7sda</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1ygcsbt</bpmn:outgoing>
|
||||
<bpmn:script>set_top_level_process_script_after_gate = 1</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:sequenceFlow id="Flow_1t9ywmr" sourceRef="Gateway_0p8naw0" targetRef="manual_task" />
|
||||
<bpmn:sequenceFlow id="Flow_1t9ywmr" sourceRef="Gateway_0p8naw0" targetRef="top_level_manual_task_two" />
|
||||
<bpmn:sequenceFlow id="Flow_0yxus36" sourceRef="top_level_subprocess" targetRef="top_level_call_activity" />
|
||||
<bpmn:sequenceFlow id="Flow_0q30935" sourceRef="top_level_manual_task_one" targetRef="top_level_manual_task_two" />
|
||||
<bpmn:manualTask id="top_level_manual_task_one" name="Top Level Manual Task One">
|
||||
<bpmn:incoming>Flow_1fktmf7</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_0q30935</bpmn:outgoing>
|
||||
</bpmn:manualTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="top_level_process">
|
||||
|
@ -74,25 +79,35 @@ except:
|
|||
<dc:Bounds x="179" y="159" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_0ia26nb_di" bpmnElement="end_event_of_manual_task_model">
|
||||
<dc:Bounds x="1092" y="159" width="36" height="36" />
|
||||
<dc:Bounds x="1212" y="159" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1200" y="202" width="67" height="40" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1rcj16n_di" bpmnElement="manual_task">
|
||||
<dc:Bounds x="400" y="137" width="100" height="80" />
|
||||
<bpmndi:BPMNShape id="Activity_1rcj16n_di" bpmnElement="top_level_manual_task_two">
|
||||
<dc:Bounds x="610" y="137" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1vokg57_di" bpmnElement="top_level_script">
|
||||
<dc:Bounds x="270" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_19a46sv_di" bpmnElement="top_level_subprocess">
|
||||
<dc:Bounds x="530" y="137" width="100" height="80" />
|
||||
<dc:Bounds x="740" y="137" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_04hrmow_di" bpmnElement="top_level_call_activity">
|
||||
<dc:Bounds x="680" y="137" width="100" height="80" />
|
||||
<dc:Bounds x="870" y="137" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Gateway_0p8naw0_di" bpmnElement="Gateway_0p8naw0" isMarkerVisible="true">
|
||||
<dc:Bounds x="835" y="152" width="50" height="50" />
|
||||
<dc:Bounds x="1005" y="152" width="50" height="50" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_1yhtryv_di" bpmnElement="top_level_process_script_after_gate">
|
||||
<dc:Bounds x="940" y="137" width="100" height="80" />
|
||||
<dc:Bounds x="1080" y="137" width="100" height="80" />
|
||||
<bpmndi:BPMNLabel />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0ctgju0_di" bpmnElement="top_level_manual_task_one">
|
||||
<dc:Bounds x="450" y="137" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Flow_0stlaxe_di" bpmnElement="Flow_0stlaxe">
|
||||
<di:waypoint x="215" y="177" />
|
||||
|
@ -100,33 +115,37 @@ except:
|
|||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1fktmf7_di" bpmnElement="Flow_1fktmf7">
|
||||
<di:waypoint x="370" y="177" />
|
||||
<di:waypoint x="400" y="177" />
|
||||
<di:waypoint x="450" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_09gjylo_di" bpmnElement="Flow_09gjylo">
|
||||
<di:waypoint x="500" y="177" />
|
||||
<di:waypoint x="530" y="177" />
|
||||
<di:waypoint x="710" y="177" />
|
||||
<di:waypoint x="740" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_187mcqe_di" bpmnElement="Flow_187mcqe">
|
||||
<di:waypoint x="780" y="177" />
|
||||
<di:waypoint x="835" y="177" />
|
||||
<di:waypoint x="970" y="177" />
|
||||
<di:waypoint x="1005" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0lw7sda_di" bpmnElement="Flow_0lw7sda">
|
||||
<di:waypoint x="885" y="177" />
|
||||
<di:waypoint x="940" y="177" />
|
||||
<di:waypoint x="1055" y="177" />
|
||||
<di:waypoint x="1080" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1ygcsbt_di" bpmnElement="Flow_1ygcsbt">
|
||||
<di:waypoint x="1040" y="177" />
|
||||
<di:waypoint x="1092" y="177" />
|
||||
<di:waypoint x="1180" y="177" />
|
||||
<di:waypoint x="1212" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1t9ywmr_di" bpmnElement="Flow_1t9ywmr">
|
||||
<di:waypoint x="860" y="152" />
|
||||
<di:waypoint x="860" y="100" />
|
||||
<di:waypoint x="450" y="100" />
|
||||
<di:waypoint x="450" y="137" />
|
||||
<di:waypoint x="1030" y="152" />
|
||||
<di:waypoint x="1030" y="100" />
|
||||
<di:waypoint x="660" y="100" />
|
||||
<di:waypoint x="660" y="137" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0yxus36_di" bpmnElement="Flow_0yxus36">
|
||||
<di:waypoint x="630" y="177" />
|
||||
<di:waypoint x="680" y="177" />
|
||||
<di:waypoint x="840" y="177" />
|
||||
<di:waypoint x="870" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_0q30935_di" bpmnElement="Flow_0q30935">
|
||||
<di:waypoint x="550" y="177" />
|
||||
<di:waypoint x="610" y="177" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
|
|
|
@ -2616,6 +2616,8 @@ class TestProcessApi(BaseTest):
|
|||
content_type="application/json",
|
||||
data=json.dumps(data),
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json is not None
|
||||
assert response.json["status"] == "complete"
|
||||
|
||||
response = client.get(
|
||||
|
@ -2641,9 +2643,9 @@ class TestProcessApi(BaseTest):
|
|||
) -> None:
|
||||
"""Test_script_unit_test_run."""
|
||||
process_group_id = "test_group"
|
||||
process_model_id = "process_navigation"
|
||||
bpmn_file_name = "process_navigation.bpmn"
|
||||
bpmn_file_location = "process_navigation"
|
||||
process_model_id = "manual_task"
|
||||
bpmn_file_name = "manual_task.bpmn"
|
||||
bpmn_file_location = "manual_task"
|
||||
process_model_identifier = self.create_group_and_model_with_bpmn(
|
||||
client=client,
|
||||
user=with_super_admin_user,
|
||||
|
@ -2674,25 +2676,11 @@ class TestProcessApi(BaseTest):
|
|||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
)
|
||||
|
||||
data = {
|
||||
"dateTime": "PT1H",
|
||||
"external": True,
|
||||
"internal": True,
|
||||
"label": "Event_0e4owa3",
|
||||
"typename": "TimerEventDefinition",
|
||||
}
|
||||
response = client.post(
|
||||
f"/v1.0/send-event/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}",
|
||||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
content_type="application/json",
|
||||
data=json.dumps(data),
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
f"/v1.0/process-instances/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}/task-info",
|
||||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
)
|
||||
assert len(response.json) == 9
|
||||
assert len(response.json) == 7
|
||||
human_task = next(task for task in response.json if task["bpmn_identifier"] == "manual_task_one")
|
||||
|
||||
response = client.post(
|
||||
|
@ -2711,7 +2699,7 @@ class TestProcessApi(BaseTest):
|
|||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert len(response.json) == 9
|
||||
assert len(response.json) == 7
|
||||
|
||||
def setup_initial_groups_for_move_tests(self, client: FlaskClient, with_super_admin_user: UserModel) -> None:
|
||||
"""Setup_initial_groups_for_move_tests."""
|
||||
|
|
|
@ -5,7 +5,8 @@ import pytest
|
|||
from flask import g
|
||||
from flask.app import Flask
|
||||
from flask.testing import FlaskClient
|
||||
from SpiffWorkflow.task import TaskState # type: ignore
|
||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||
from SpiffWorkflow.task import TaskState
|
||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
|
||||
|
||||
|
@ -256,7 +257,6 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
assert spiff_task is not None
|
||||
assert spiff_task.state == TaskState.COMPLETED
|
||||
|
||||
# TODO: FIX resetting a process instance to a task
|
||||
# def test_properly_resets_process_to_given_task(
|
||||
# self,
|
||||
# app: Flask,
|
||||
|
@ -275,6 +275,60 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
# assert finance_group is not None
|
||||
#
|
||||
# process_model = load_test_spec(
|
||||
# process_model_id="test_group/manual_task",
|
||||
# process_model_source_directory="manual_task",
|
||||
# )
|
||||
# process_instance = self.create_process_instance_from_process_model(
|
||||
# process_model=process_model, user=initiator_user
|
||||
# )
|
||||
# processor = ProcessInstanceProcessor(process_instance)
|
||||
# processor.do_engine_steps(save=True)
|
||||
# assert len(process_instance.active_human_tasks) == 1
|
||||
# initial_human_task_id = process_instance.active_human_tasks[0].id
|
||||
#
|
||||
# # save again to ensure we go attempt to process the human tasks again
|
||||
# processor.save()
|
||||
#
|
||||
# assert len(process_instance.active_human_tasks) == 1
|
||||
# assert initial_human_task_id == process_instance.active_human_tasks[0].id
|
||||
#
|
||||
# processor = ProcessInstanceProcessor(process_instance)
|
||||
# human_task_one = process_instance.active_human_tasks[0]
|
||||
# spiff_manual_task = processor.__class__.get_task_by_bpmn_identifier(
|
||||
# human_task_one.task_name, processor.bpmn_process_instance
|
||||
# )
|
||||
# assert spiff_manual_task is not None
|
||||
#
|
||||
# processor.suspend()
|
||||
# ProcessInstanceProcessor.reset_process(process_instance, str(spiff_manual_task.parent.id), commit=True)
|
||||
#
|
||||
# process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
|
||||
# processor = ProcessInstanceProcessor(process_instance)
|
||||
# processor.resume()
|
||||
# processor.do_engine_steps(save=True)
|
||||
# human_task_one = process_instance.active_human_tasks[0]
|
||||
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
|
||||
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
|
||||
# assert process_instance.status == "complete"
|
||||
#
|
||||
# def test_properly_resets_process_to_given_task_with_call_activity(
|
||||
# self,
|
||||
# app: Flask,
|
||||
# client: FlaskClient,
|
||||
# with_db_and_bpmn_file_cleanup: None,
|
||||
# with_super_admin_user: UserModel,
|
||||
# ) -> None:
|
||||
# self.create_process_group_with_api(client, with_super_admin_user, "test_group", "test_group")
|
||||
# initiator_user = self.find_or_create_user("initiator_user")
|
||||
# finance_user_three = self.find_or_create_user("testuser3")
|
||||
# assert initiator_user.principal is not None
|
||||
# assert finance_user_three.principal is not None
|
||||
# AuthorizationService.import_permissions_from_yaml_file()
|
||||
#
|
||||
# finance_group = GroupModel.query.filter_by(identifier="Finance Team").first()
|
||||
# assert finance_group is not None
|
||||
#
|
||||
# process_model = load_test_spec(
|
||||
# process_model_id="test_group/manual_task_with_subprocesses",
|
||||
# process_model_source_directory="manual_task_with_subprocesses",
|
||||
# )
|
||||
|
@ -283,33 +337,48 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
# )
|
||||
# processor = ProcessInstanceProcessor(process_instance)
|
||||
# processor.do_engine_steps(save=True)
|
||||
# # with open("before_reset.json", 'w') as f: f.write(json.dumps(processor.serialize(), indent=2))
|
||||
# assert len(process_instance.active_human_tasks) == 1
|
||||
# initial_human_task_id = process_instance.active_human_tasks[0].id
|
||||
#
|
||||
# # save again to ensure we go attempt to process the human tasks again
|
||||
# processor.save()
|
||||
#
|
||||
# assert len(process_instance.active_human_tasks) == 1
|
||||
# assert initial_human_task_id == process_instance.active_human_tasks[0].id
|
||||
#
|
||||
# processor = ProcessInstanceProcessor(process_instance)
|
||||
# human_task_one = process_instance.active_human_tasks[0]
|
||||
# spiff_manual_task = processor.__class__.get_task_by_bpmn_identifier(
|
||||
# human_task_one.task_name, processor.bpmn_process_instance
|
||||
# )
|
||||
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
|
||||
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
|
||||
# human_task_one = process_instance.active_human_tasks[0]
|
||||
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
|
||||
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
|
||||
#
|
||||
# # NOTES:
|
||||
# # somehow we are hosing the task state so that when completing tasks of a subprocess, the task AFTER the subprocess task
|
||||
# # is not marked READY but instead stays as FUTURE. Running things like:
|
||||
# # self.last_completed_spiff_task.task_spec._update(self.last_completed_spiff_task)
|
||||
# # and
|
||||
# # self.last_completed_spiff_task.task_spec._predict(self.last_completed_spiff_task, mask=TaskState.NOT_FINISHED_MASK)
|
||||
# # did not help.
|
||||
#
|
||||
# processor.suspend()
|
||||
# ProcessInstanceProcessor.reset_process(process_instance, str(spiff_manual_task.id), commit=True)
|
||||
# task_model_to_reset_to = (
|
||||
# TaskModel.query.join(TaskDefinitionModel)
|
||||
# .filter(TaskDefinitionModel.bpmn_identifier == "top_level_subprocess_script")
|
||||
# .order_by(TaskModel.id.desc()) # type: ignore
|
||||
# .first()
|
||||
# )
|
||||
# assert task_model_to_reset_to is not None
|
||||
# ProcessInstanceProcessor.reset_process(process_instance, task_model_to_reset_to.guid, commit=True)
|
||||
#
|
||||
# process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
|
||||
# processor = ProcessInstanceProcessor(process_instance)
|
||||
# processor.resume()
|
||||
# processor.do_engine_steps(save=True)
|
||||
#
|
||||
# assert len(process_instance.active_human_tasks) == 1
|
||||
# human_task_one = process_instance.active_human_tasks[0]
|
||||
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
|
||||
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
|
||||
# human_task_one = process_instance.active_human_tasks[0]
|
||||
# spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
|
||||
# ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
|
||||
#
|
||||
# assert process_instance.status == "complete"
|
||||
|
||||
def test_properly_saves_tasks_when_running(
|
||||
self,
|
||||
|
@ -358,54 +427,104 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
human_task_one = process_instance.active_human_tasks[0]
|
||||
spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
|
||||
ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
|
||||
human_task_one = process_instance.active_human_tasks[0]
|
||||
spiff_manual_task = processor.bpmn_process_instance.get_task_from_id(UUID(human_task_one.task_id))
|
||||
ProcessInstanceService.complete_form_task(processor, spiff_manual_task, {}, initiator_user, human_task_one)
|
||||
|
||||
# recreate variables to ensure all bpmn json was recreated from scratch from the db
|
||||
process_instance_relookup = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
|
||||
processor_final = ProcessInstanceProcessor(process_instance_relookup)
|
||||
assert process_instance_relookup.status == "complete"
|
||||
|
||||
first_data_set = {"set_in_top_level_script": 1}
|
||||
second_data_set = {
|
||||
**first_data_set,
|
||||
data_set_1 = {"set_in_top_level_script": 1}
|
||||
data_set_2 = {
|
||||
**data_set_1,
|
||||
**{"set_in_top_level_subprocess": 1, "we_move_on": False},
|
||||
}
|
||||
third_data_set = {
|
||||
**second_data_set,
|
||||
data_set_3 = {
|
||||
**data_set_2,
|
||||
**{
|
||||
"set_in_test_process_to_call_script": 1,
|
||||
"set_in_test_process_to_call_subprocess_subprocess_script": 1,
|
||||
"set_in_test_process_to_call_subprocess_script": 1,
|
||||
},
|
||||
}
|
||||
fourth_data_set = {**third_data_set, **{"a": 1, "we_move_on": True}}
|
||||
fifth_data_set = {**fourth_data_set, **{"validate_only": False, "set_top_level_process_script_after_gate": 1}}
|
||||
data_set_4 = {
|
||||
**data_set_3,
|
||||
**{
|
||||
"set_in_test_process_to_call_script": 1,
|
||||
},
|
||||
}
|
||||
data_set_5 = {**data_set_4, **{"a": 1, "we_move_on": True}}
|
||||
data_set_6 = {**data_set_5, **{"set_top_level_process_script_after_gate": 1}}
|
||||
data_set_7 = {**data_set_6, **{"validate_only": False, "set_top_level_process_script_after_gate": 1}}
|
||||
expected_task_data = {
|
||||
"top_level_script": first_data_set,
|
||||
"manual_task": first_data_set,
|
||||
"top_level_subprocess_script": second_data_set,
|
||||
"top_level_subprocess": second_data_set,
|
||||
"test_process_to_call_subprocess_script": third_data_set,
|
||||
"top_level_call_activity": third_data_set,
|
||||
"end_event_of_manual_task_model": third_data_set,
|
||||
"top_level_subprocess_script_second": fourth_data_set,
|
||||
"test_process_to_call_subprocess_script_second": fourth_data_set,
|
||||
"top_level_script": {"data": data_set_1, "bpmn_process_identifier": "top_level_process"},
|
||||
"top_level_manual_task_one": {"data": data_set_1, "bpmn_process_identifier": "top_level_process"},
|
||||
"top_level_manual_task_two": {"data": data_set_1, "bpmn_process_identifier": "top_level_process"},
|
||||
"top_level_subprocess_script": {
|
||||
"data": data_set_2,
|
||||
"bpmn_process_identifier": "top_level_subprocess",
|
||||
},
|
||||
"top_level_subprocess": {"data": data_set_2, "bpmn_process_identifier": "top_level_process"},
|
||||
"test_process_to_call_subprocess_script": {
|
||||
"data": data_set_3,
|
||||
"bpmn_process_identifier": "test_process_to_call_subprocess",
|
||||
},
|
||||
"top_level_call_activity": {"data": data_set_4, "bpmn_process_identifier": "top_level_process"},
|
||||
"top_level_manual_task_two_second": {
|
||||
"data": data_set_4,
|
||||
"bpmn_process_identifier": "top_level_process",
|
||||
},
|
||||
"top_level_subprocess_script_second": {
|
||||
"data": data_set_5,
|
||||
"bpmn_process_identifier": "top_level_subprocess",
|
||||
},
|
||||
"top_level_subprocess_second": {"data": data_set_5, "bpmn_process_identifier": "top_level_process"},
|
||||
"test_process_to_call_subprocess_script_second": {
|
||||
"data": data_set_5,
|
||||
"bpmn_process_identifier": "test_process_to_call_subprocess",
|
||||
},
|
||||
"top_level_call_activity_second": {
|
||||
"data": data_set_5,
|
||||
"bpmn_process_identifier": "top_level_process",
|
||||
},
|
||||
"end_event_of_manual_task_model": {"data": data_set_6, "bpmn_process_identifier": "top_level_process"},
|
||||
}
|
||||
|
||||
spiff_tasks_checked_once: list = []
|
||||
spiff_tasks_checked: list[str] = []
|
||||
|
||||
# TODO: also check task data here from the spiff_task directly to ensure we hydrated spiff correctly
|
||||
def assert_spiff_task_is_in_process(spiff_task_identifier: str, bpmn_process_identifier: str) -> None:
|
||||
if spiff_task.task_spec.name == spiff_task_identifier:
|
||||
expected_task_data_key = spiff_task.task_spec.name
|
||||
if spiff_task.task_spec.name in spiff_tasks_checked_once:
|
||||
def assert_spiff_task_is_in_process(spiff_task: SpiffTask) -> None:
|
||||
spiff_task_identifier = spiff_task.task_spec.name
|
||||
if spiff_task_identifier in expected_task_data:
|
||||
bpmn_process_identifier = expected_task_data[spiff_task_identifier]["bpmn_process_identifier"]
|
||||
expected_task_data_key = spiff_task_identifier
|
||||
if spiff_task_identifier in spiff_tasks_checked:
|
||||
expected_task_data_key = f"{spiff_task.task_spec.name}_second"
|
||||
|
||||
expected_python_env_data = expected_task_data[expected_task_data_key]
|
||||
assert expected_task_data_key not in spiff_tasks_checked
|
||||
|
||||
spiff_tasks_checked.append(expected_task_data_key)
|
||||
|
||||
expected_python_env_data = expected_task_data[expected_task_data_key]["data"]
|
||||
|
||||
base_failure_message = (
|
||||
f"Failed on {bpmn_process_identifier} - {spiff_task_identifier} - task data key"
|
||||
f" {expected_task_data_key}."
|
||||
)
|
||||
|
||||
# TODO: add back in when removing MAYBE and LIKELY tasks
|
||||
# count_failure_message = (
|
||||
# f"{base_failure_message} There are more than 2 entries of this task in the db."
|
||||
# " There should only ever be max 2."
|
||||
# )
|
||||
# task_models_with_bpmn_identifier_count = (
|
||||
# TaskModel.query.join(TaskDefinitionModel)
|
||||
# .filter(TaskModel.process_instance_id == process_instance_relookup.id)
|
||||
# .filter(TaskDefinitionModel.bpmn_identifier == spiff_task.task_spec.name)
|
||||
# .count()
|
||||
# )
|
||||
# assert task_models_with_bpmn_identifier_count < 3, count_failure_message
|
||||
task_model = TaskModel.query.filter_by(guid=str(spiff_task.id)).first()
|
||||
|
||||
assert task_model.start_in_seconds is not None
|
||||
|
@ -415,7 +534,9 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
task_definition = task_model.task_definition
|
||||
assert task_definition.bpmn_identifier == spiff_task_identifier
|
||||
assert task_definition.bpmn_name == spiff_task_identifier.replace("_", " ").title()
|
||||
assert task_definition.bpmn_process_definition.bpmn_identifier == bpmn_process_identifier
|
||||
assert (
|
||||
task_definition.bpmn_process_definition.bpmn_identifier == bpmn_process_identifier
|
||||
), base_failure_message
|
||||
|
||||
message = (
|
||||
f"{base_failure_message} Expected: {sorted(expected_python_env_data)}. Received:"
|
||||
|
@ -424,17 +545,12 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
# TODO: if we split out env data again we will need to use it here instead of json_data
|
||||
# assert task_model.python_env_data() == expected_python_env_data, message
|
||||
assert task_model.json_data() == expected_python_env_data, message
|
||||
spiff_tasks_checked_once.append(spiff_task.task_spec.name)
|
||||
|
||||
all_spiff_tasks = processor_final.bpmn_process_instance.get_tasks()
|
||||
assert len(all_spiff_tasks) > 1
|
||||
for spiff_task in all_spiff_tasks:
|
||||
assert spiff_task.state == TaskState.COMPLETED
|
||||
assert_spiff_task_is_in_process(
|
||||
"test_process_to_call_subprocess_script", "test_process_to_call_subprocess"
|
||||
)
|
||||
assert_spiff_task_is_in_process("top_level_subprocess_script", "top_level_subprocess")
|
||||
assert_spiff_task_is_in_process("top_level_script", "top_level_process")
|
||||
assert_spiff_task_is_in_process(spiff_task)
|
||||
|
||||
if spiff_task.task_spec.name == "top_level_call_activity":
|
||||
# the task id / guid of the call activity gets used as the guid of the bpmn process that it calls
|
||||
|
@ -462,7 +578,22 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
assert direct_parent_process is not None
|
||||
assert direct_parent_process.bpmn_process_definition.bpmn_identifier == "test_process_to_call"
|
||||
|
||||
assert processor.get_data() == fifth_data_set
|
||||
for task_bpmn_identifier in expected_task_data.keys():
|
||||
message = (
|
||||
f"Expected to have seen a task with a bpmn_identifier of {task_bpmn_identifier} but did not. "
|
||||
f"Only saw {sorted(spiff_tasks_checked)}"
|
||||
)
|
||||
assert task_bpmn_identifier in spiff_tasks_checked, message
|
||||
|
||||
# TODO: add back in when removing MAYBE and LIKELY tasks
|
||||
# task_models_that_are_predicted_count = (
|
||||
# TaskModel.query.filter(TaskModel.process_instance_id == process_instance_relookup.id)
|
||||
# .filter(TaskModel.state.in_(["LIKELY", "MAYBE"])) # type: ignore
|
||||
# .count()
|
||||
# )
|
||||
# assert task_models_that_are_predicted_count == 0
|
||||
|
||||
assert processor.get_data() == data_set_7
|
||||
|
||||
def test_does_not_recreate_human_tasks_on_multiple_saves(
|
||||
self,
|
||||
|
@ -569,14 +700,47 @@ class TestProcessInstanceProcessor(BaseTest):
|
|||
assert len(process_instance.human_tasks) == 2
|
||||
human_task_two = process_instance.active_human_tasks[0]
|
||||
|
||||
# this is just asserting the way the functionality currently works in spiff.
|
||||
# we would actually expect this to change one day if we stop reusing the same guid
|
||||
# when we re-do a task.
|
||||
# assert human_task_two.task_id == human_task_one.task_id
|
||||
|
||||
# EDIT: when using feature/remove-loop-reset branch of SpiffWorkflow, these should be different.
|
||||
assert human_task_two.task_id != human_task_one.task_id
|
||||
|
||||
def test_it_can_loopback_to_previous_bpmn_subprocess_with_gateway(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
) -> None:
|
||||
initiator_user = self.find_or_create_user("initiator_user")
|
||||
process_model = load_test_spec(
|
||||
process_model_id="test_group/loopback_to_subprocess",
|
||||
process_model_source_directory="loopback_to_subprocess",
|
||||
)
|
||||
process_instance = self.create_process_instance_from_process_model(
|
||||
process_model=process_model, user=initiator_user
|
||||
)
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
processor.do_engine_steps(save=True)
|
||||
|
||||
assert len(process_instance.active_human_tasks) == 1
|
||||
assert len(process_instance.human_tasks) == 1
|
||||
human_task_one = process_instance.active_human_tasks[0]
|
||||
|
||||
spiff_task = processor.get_task_by_guid(human_task_one.task_id)
|
||||
ProcessInstanceService.complete_form_task(processor, spiff_task, {}, initiator_user, human_task_one)
|
||||
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
assert len(process_instance.active_human_tasks) == 1
|
||||
assert len(process_instance.human_tasks) == 2
|
||||
human_task_two = process_instance.active_human_tasks[0]
|
||||
spiff_task = processor.get_task_by_guid(human_task_two.task_id)
|
||||
ProcessInstanceService.complete_form_task(processor, spiff_task, {}, initiator_user, human_task_two)
|
||||
|
||||
# ensure this does not raise a KeyError
|
||||
processor = ProcessInstanceProcessor(process_instance)
|
||||
assert len(process_instance.active_human_tasks) == 1
|
||||
assert len(process_instance.human_tasks) == 3
|
||||
human_task_three = process_instance.active_human_tasks[0]
|
||||
spiff_task = processor.get_task_by_guid(human_task_three.task_id)
|
||||
ProcessInstanceService.complete_form_task(processor, spiff_task, {}, initiator_user, human_task_three)
|
||||
|
||||
def test_task_data_is_set_even_if_process_instance_errors(
|
||||
self,
|
||||
app: Flask,
|
||||
|
|
|
@ -260,7 +260,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
return !taskToTimeTravelTo;
|
||||
};
|
||||
|
||||
const completionViewLink = (label: any, taskGuid: string) => {
|
||||
const queryParams = () => {
|
||||
const processIdentifier = searchParams.get('process_identifier');
|
||||
const callActivityTaskId = searchParams.get('bpmn_process_guid');
|
||||
const queryParamArray = [];
|
||||
|
@ -270,16 +270,19 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
if (callActivityTaskId) {
|
||||
queryParamArray.push(`bpmn_process_guid=${callActivityTaskId}`);
|
||||
}
|
||||
let queryParams = '';
|
||||
let queryParamString = '';
|
||||
if (queryParamArray.length > 0) {
|
||||
queryParams = `?${queryParamArray.join('&')}`;
|
||||
queryParamString = `?${queryParamArray.join('&')}`;
|
||||
}
|
||||
return queryParamString;
|
||||
};
|
||||
|
||||
const completionViewLink = (label: any, taskGuid: string) => {
|
||||
return (
|
||||
<Link
|
||||
reloadDocument
|
||||
data-qa="process-instance-step-link"
|
||||
to={`${processInstanceShowPageBaseUrl}/${taskGuid}${queryParams}`}
|
||||
to={`${processInstanceShowPageBaseUrl}/${taskGuid}${queryParams()}`}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
|
@ -287,7 +290,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
|
|||
};
|
||||
|
||||
const returnToProcessInstance = () => {
|
||||
window.location.href = processInstanceShowPageBaseUrl;
|
||||
window.location.href = `${processInstanceShowPageBaseUrl}${queryParams()}`;
|
||||
};
|
||||
|
||||
const resetProcessInstance = () => {
|
||||
|
|
Loading…
Reference in New Issue