diff --git a/spiffworkflow-backend/bin/query_tasks b/spiffworkflow-backend/bin/query_tasks index 8cd6984e1..43e701ae4 100755 --- a/spiffworkflow-backend/bin/query_tasks +++ b/spiffworkflow-backend/bin/query_tasks @@ -10,11 +10,11 @@ set -o errtrace -o errexit -o nounset -o pipefail mysql -uroot spiffworkflow_backend_unit_testing -e ' select * from process_instance; - select t.guid as task_guid, t.state as task_state, td.bpmn_identifier as task_id from task t + select t.guid as task_guid, t.state as task_state, td.bpmn_identifier as task_id, t.properties_json from task t join task_definition td on td.id = t.task_definition_id where process_instance_id=(select max(id) from process_instance); - select bp.guid as bp_guid, bpd.bpmn_identifier as bp_identifier from bpmn_process bp + select bp.guid as bp_guid, bpd.bpmn_identifier as bp_identifier, bp.properties_json from bpmn_process bp join bpmn_process_definition bpd on bpd.id = bp.bpmn_process_definition_id join bpmn_process bpb on bpb.id = bp.direct_parent_process_id join process_instance pi on bpb.id = pi.bpmn_process_id diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index ec64ad814..657965db7 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1207,15 +1207,18 @@ class ProcessInstanceProcessor: process_instance, ProcessInstanceEventType.process_instance_rewound_to_task.value, task_guid=to_task_guid ) processor = ProcessInstanceProcessor(process_instance) - deleted_tasks = processor.bpmn_process_instance.reset_from_task_id(UUID(to_task_guid)) - spiff_tasks = processor.bpmn_process_instance.get_tasks() + initial_spiff_tasks = processor.bpmn_process_instance.get_tasks() + + processor.bpmn_process_instance.reset_from_task_id(UUID(to_task_guid)) + current_spiff_tasks = processor.bpmn_process_instance.get_tasks() + deleted_spiff_tasks = [t for t in initial_spiff_tasks if t not in current_spiff_tasks] task_service = TaskService( process_instance=processor.process_instance_model, serializer=processor._serializer, bpmn_definition_to_task_definitions_mappings=processor.bpmn_definition_to_task_definitions_mappings, ) - task_service.update_all_tasks_from_spiff_tasks(spiff_tasks, deleted_tasks, start_time) + task_service.update_all_tasks_from_spiff_tasks(current_spiff_tasks, deleted_spiff_tasks, start_time) # Save the process processor.save() @@ -1662,9 +1665,7 @@ class ProcessInstanceProcessor: """Complete_task.""" task_model = TaskModel.query.filter_by(guid=human_task.task_id).first() if task_model is None: - raise TaskNotFoundError( - f"Cannot find a task with guid {self.process_instance_model.id} and task_id is {human_task.task_id}" - ) + raise TaskNotFoundError(f"Cannot find a task with guid {human_task.task_id}") task_model.start_in_seconds = time.time() self.bpmn_process_instance.run_task_from_id(spiff_task.id) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index d0b74d1dd..135838e30 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -29,6 +29,7 @@ from spiffworkflow_backend.models.process_instance_event import ProcessInstanceE from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.spec_reference import SpecReferenceNotFoundError from spiffworkflow_backend.models.task import TaskModel # noqa: F401 +from spiffworkflow_backend.models.task import TaskNotFoundError from spiffworkflow_backend.models.task_definition import TaskDefinitionModel from spiffworkflow_backend.services.process_instance_tmp_service import ProcessInstanceTmpService @@ -456,17 +457,27 @@ class TaskService: def update_all_tasks_from_spiff_tasks( self, spiff_tasks: list[SpiffTask], deleted_spiff_tasks: list[SpiffTask], start_time: float ) -> None: + """Update given spiff tasks in the database and remove deleted tasks.""" # Remove all the deleted/pruned tasks from the database. - deleted_task_ids = list(map(lambda t: str(t.id), deleted_spiff_tasks)) - tasks_to_clear = TaskModel.query.filter(TaskModel.guid.in_(deleted_task_ids)).all() # type: ignore + deleted_task_guids = list(map(lambda t: str(t.id), deleted_spiff_tasks)) + tasks_to_clear = TaskModel.query.filter(TaskModel.guid.in_(deleted_task_guids)).all() # type: ignore human_tasks_to_clear = HumanTaskModel.query.filter( - HumanTaskModel.task_id.in_(deleted_task_ids) # type: ignore + HumanTaskModel.task_id.in_(deleted_task_guids) # type: ignore ).all() # delete human tasks first to avoid potential conflicts when deleting tasks. # otherwise sqlalchemy returns several warnings. for task in human_tasks_to_clear + tasks_to_clear: db.session.delete(task) + db.session.commit() + + bpmn_processes_to_delete = ( + BpmnProcessModel.query.filter(BpmnProcessModel.guid.in_(deleted_task_guids)) # type: ignore + .order_by(BpmnProcessModel.id.desc()) # type: ignore + .all() + ) + for bpmn_process in bpmn_processes_to_delete: + db.session.delete(bpmn_process) # Note: Can't restrict this to definite, because some things are updated and are now CANCELLED # and other things may have been COMPLETED and are now MAYBE @@ -570,6 +581,10 @@ class TaskService: bpmn_process_identifiers: list[str] = [] if bpmn_process.guid: task_model = TaskModel.query.filter_by(guid=bpmn_process.guid).first() + if task_model is None: + raise TaskNotFoundError( + f"Cannot find the corresponding task for the bpmn process with guid {bpmn_process.guid}." + ) ( parent_bpmn_processes, _task_models_of_parent_bpmn_processes, diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 48e87877a..8650cc00b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import Editor from '@monaco-editor/react'; import { useParams, @@ -40,6 +40,7 @@ import { import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import { useUriListForPermissions } from '../hooks/UriListForPermissions'; import { + ErrorForDisplay, EventDefinition, PermissionsToCheck, ProcessData, @@ -128,12 +129,18 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { processInstanceLogListPageBaseUrl = `/admin/logs/${params.process_model_id}/${params.process_instance_id}`; } + const handleAddErrorInUseEffect = useCallback((value: ErrorForDisplay) => { + addError(value); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (!permissionsLoaded) { return undefined; } - const processTaskFailure = () => { + const processTaskFailure = (result: any) => { setTasksCallHadError(true); + handleAddErrorInUseEffect(result); }; const processTasksSuccess = (results: Task[]) => { if (params.to_task_guid) { @@ -190,6 +197,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { searchParams, taskListPath, variant, + handleAddErrorInUseEffect, ]); const deleteProcessInstance = () => {