allow option to complete single tasks with or without execution

This commit is contained in:
Elizabeth Esswein 2022-12-29 16:34:38 -05:00
parent e6391a2aa3
commit afb630c78d
4 changed files with 49 additions and 24 deletions

View File

@ -1565,7 +1565,7 @@ paths:
schema:
type: string
post:
operationId: spiffworkflow_backend.routes.process_api_blueprint.mark_task_complete
operationId: spiffworkflow_backend.routes.process_api_blueprint.manual_complete_task
summary: Mark a task complete without executing it
tags:
- Process Instances

View File

@ -28,11 +28,6 @@ from lxml import etree # type: ignore
from lxml.builder import ElementMaker # type: ignore
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.task import TaskState
from sqlalchemy import and_
from sqlalchemy import asc
from sqlalchemy import desc
from sqlalchemy import or_
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
ProcessEntityNotFoundError,
)
@ -99,6 +94,10 @@ from spiffworkflow_backend.services.secret_service import SecretService
from spiffworkflow_backend.services.service_task_service import ServiceTaskService
from spiffworkflow_backend.services.spec_file_service import SpecFileService
from spiffworkflow_backend.services.user_service import UserService
from sqlalchemy import and_
from sqlalchemy import asc
from sqlalchemy import desc
from sqlalchemy import or_
class TaskDataSelectOption(TypedDict):
@ -2189,23 +2188,24 @@ def send_bpmn_event(
)
def mark_task_complete(
def manual_complete_task(
modified_process_model_identifier: str,
process_instance_id: str,
task_id: str,
body: Dict,
) -> Response:
"""Mark a task complete without executing it."""
execute = body.get("execute", True)
process_instance = ProcessInstanceModel.query.filter(
ProcessInstanceModel.id == int(process_instance_id)
).first()
if process_instance:
processor = ProcessInstanceProcessor(process_instance)
processor.mark_task_complete(task_id)
processor.manual_complete_task(task_id, execute)
else:
raise ApiError(
error_code="send_bpmn_event_error",
message=f"Could not skip Task {task_id} in Instance {process_instance_id}",
error_code="complete_task",
message=f"Could not complete Task {task_id} in Instance {process_instance_id}",
)
return Response(
json.dumps(ProcessInstanceModelSchema().dump(process_instance)),

View File

@ -621,7 +621,7 @@ class ProcessInstanceProcessor:
db.session.add(pim)
db.session.commit()
def save(self) -> None:
def _save(self) -> None:
"""Saves the current state of this processor to the database."""
self.process_instance_model.bpmn_json = self.serialize()
@ -643,6 +643,9 @@ class ProcessInstanceProcessor:
db.session.add(self.process_instance_model)
db.session.commit()
def save(self) -> None:
"""Saves the current state and moves on to the next state."""
self._save()
human_tasks = HumanTaskModel.query.filter_by(
process_instance_id=self.process_instance_model.id
).all()
@ -729,17 +732,25 @@ class ProcessInstanceProcessor:
self.bpmn_process_instance.catch(event_definition)
self.do_engine_steps(save=True)
def mark_task_complete(self, task_id: str) -> None:
"""Mark the task complete without executing it."""
def manual_complete_task(self, task_id: str, execute: bool) -> None:
"""Mark the task complete optionally executing it."""
spiff_task = self.bpmn_process_instance.get_task(UUID(task_id))
spiff_task._set_state(TaskState.COMPLETED)
if execute:
current_app.logger.info(
f"Manually executing Task {spiff_task.task_spec.name} of process instance {self.process_instance_model.id}"
)
spiff_task.complete()
else:
current_app.logger.info(
f"Skipping Task {spiff_task.task_spec.name} of process instance {self.process_instance_model.id}"
)
spiff_task._set_state(TaskState.COMPLETED)
for child in spiff_task.children:
child.task_spec._update(child)
self.bpmn_process_instance.last_task = spiff_task
for child in spiff_task.children:
child.task_spec._update(child)
current_app.logger.info(
f"Task {spiff_task.task_spec.name} of process instance {self.process_instance_model.id} skipped"
)
self.do_engine_steps(save=True)
self._save()
# Saving the workflow seems to reset the status
self.suspend()
@staticmethod
def get_parser() -> MyCustomParser:

View File

@ -529,6 +529,8 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
// We actually could allow this for any waiting events
const taskTypes = ['Event Based Gateway'];
return (
processInstance &&
processInstance.status === 'waiting' &&
ability.can('POST', targetUris.processInstanceSendEventPath) &&
taskTypes.filter((t) => t === task.type).length > 0 &&
task.state === 'WAITING' &&
@ -536,8 +538,10 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
);
};
const canMarkTaskComplete = (task: any) => {
const canCompleteTask = (task: any) => {
return (
processInstance &&
processInstance.status === 'suspended' &&
ability.can('POST', targetUris.processInstanceCompleteTaskPath) &&
task.state === 'READY' &&
showingLastSpiffStep()
@ -546,6 +550,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const canResetProcess = (task: any) => {
return (
ability.can('POST', targetUris.processInstanceResetPath) &&
processInstance &&
processInstance.status === 'suspended' &&
task.state === 'READY' &&
@ -627,13 +632,14 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
});
};
const markTaskComplete = () => {
const completeTask = (execute: boolean) => {
const taskToUse: any = taskToDisplay;
HttpService.makeCallToBackend({
path: `/task-complete/${modifiedProcessModelId}/${params.process_instance_id}/${taskToUse.id}`,
httpMethod: 'POST',
successCallback: saveTaskDataResult,
failureCallback: saveTaskDataFailure,
postBody: { execute },
});
};
@ -705,15 +711,23 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
</Button>
);
}
if (canMarkTaskComplete(task)) {
if (canCompleteTask(task)) {
buttons.push(
<Button
data-qa="mark-task-complete-button"
onClick={() => markTaskComplete()}
onClick={() => completeTask(false)}
>
Mark Complete
</Button>
);
buttons.push(
<Button
data-qa="execute-task-complete-button"
onClick={() => completeTask(true)}
>
Execute Task
</Button>
);
}
if (canSendEvent(task)) {
buttons.push(