ensure we remove corresponding bpmn processes when removing tasks from a process reset

This commit is contained in:
jasquat 2023-05-11 09:32:07 -04:00
parent 7c555c62a4
commit 7ef3e62207
4 changed files with 37 additions and 13 deletions

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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 = () => {