Pause/resume process instances (#2)

Collab with @cullerton
This commit is contained in:
jbirddog 2022-10-20 16:44:08 -04:00 committed by GitHub
parent 6eaae753d9
commit 5af593e8af
7 changed files with 171 additions and 1 deletions

View File

@ -670,6 +670,72 @@ paths:
schema:
$ref: "#/components/schemas/OkTrue"
/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/suspend:
parameters:
- name: process_group_id
in: path
required: true
description: The unique id of an existing process group
schema:
type: string
- name: process_model_id
in: path
required: true
description: The unique id of an existing process model.
schema:
type: string
- name: process_instance_id
in: path
required: true
description: The unique id of an existing process instance.
schema:
type: integer
post:
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_suspend
summary: Suspend a process instance
tags:
- Process Instances
responses:
"200":
description: Empty ok true response on successful suspension.
content:
application/json:
schema:
$ref: "#/components/schemas/OkTrue"
/process-models/{process_group_id}/{process_model_id}/process-instances/{process_instance_id}/resume:
parameters:
- name: process_group_id
in: path
required: true
description: The unique id of an existing process group
schema:
type: string
- name: process_model_id
in: path
required: true
description: The unique id of an existing process model.
schema:
type: string
- name: process_instance_id
in: path
required: true
description: The unique id of an existing process instance.
schema:
type: integer
post:
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_resume
summary: Resume a process instance
tags:
- Process Instances
responses:
"200":
description: Empty ok true response on successful resume.
content:
application/json:
schema:
$ref: "#/components/schemas/OkTrue"
/process-models/{process_group_id}/{process_model_id}/process-instances/reports:
parameters:
- name: process_group_id

View File

@ -61,6 +61,7 @@ class ActiveTaskModel(SpiffworkflowBaseDBModel):
task.task_status,
data=task_data,
process_instance_id=task.process_instance_id,
process_instance_status=task.status,
)
if hasattr(task, "process_model_display_name"):
new_task.process_model_display_name = task.process_model_display_name

View File

@ -111,6 +111,7 @@ class Task:
process_name: str = "",
properties: Union[dict, None] = None,
process_instance_id: Union[int, None] = None,
process_instance_status: Union[str, None] = None,
process_model_display_name: Union[str, None] = None,
process_group_identifier: Union[str, None] = None,
process_model_identifier: Union[str, None] = None,
@ -134,6 +135,7 @@ class Task:
self.data = {}
self.process_instance_id = process_instance_id
self.process_instance_status = process_instance_status
self.process_group_identifier = process_group_identifier
self.process_model_identifier = process_model_identifier
self.process_model_display_name = process_model_display_name
@ -178,6 +180,7 @@ class Task:
"process_name": self.process_name,
"properties": self.properties,
"process_instance_id": self.process_instance_id,
"process_instance_status": self.process_instance_status,
"process_model_display_name": self.process_model_display_name,
"process_group_identifier": self.process_group_identifier,
"process_model_identifier": self.process_model_identifier,

View File

@ -45,6 +45,7 @@ from spiffworkflow_backend.models.process_group import ProcessGroupSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.process_instance_report import (
ProcessInstanceReportModel,
)
@ -452,6 +453,34 @@ def process_instance_terminate(
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
def process_instance_suspend(
process_group_id: str,
process_model_id: str,
process_instance_id: int,
) -> flask.wrappers.Response:
"""Process_instance_suspend."""
process_instance = ProcessInstanceService().get_process_instance(
process_instance_id
)
processor = ProcessInstanceProcessor(process_instance)
processor.suspend()
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
def process_instance_resume(
process_group_id: str,
process_model_id: str,
process_instance_id: int,
) -> flask.wrappers.Response:
"""Process_instance_resume."""
process_instance = ProcessInstanceService().get_process_instance(
process_instance_id
)
processor = ProcessInstanceProcessor(process_instance)
processor.resume()
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
def process_instance_log_list(
process_group_id: str,
process_model_id: str,
@ -895,6 +924,7 @@ def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Res
.add_columns(
ProcessInstanceModel.process_model_identifier,
ProcessInstanceModel.process_group_identifier,
ProcessInstanceModel.status,
ActiveTaskModel.task_data,
ActiveTaskModel.task_name,
ActiveTaskModel.task_title,
@ -947,6 +977,14 @@ def process_instance_task_list(
def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response:
"""Task_show."""
process_instance = find_process_instance_by_id_or_raise(process_instance_id)
if process_instance.status == ProcessInstanceStatus.suspended.value:
raise ApiError(
error_code="error_suspended",
message="The process instance is suspended",
status_code=400,
)
process_model = get_process_model(
process_instance.process_model_identifier,
process_instance.process_group_identifier,

View File

@ -1133,3 +1133,15 @@ class ProcessInstanceProcessor:
self.process_instance_model.status = "terminated"
db.session.add(self.process_instance_model)
db.session.commit()
def suspend(self) -> None:
"""Suspend."""
self.process_instance_model.status = ProcessInstanceStatus.suspended.value
db.session.add(self.process_instance_model)
db.session.commit()
def resume(self) -> None:
"""Resume."""
self.process_instance_model.status = ProcessInstanceStatus.waiting.value
db.session.add(self.process_instance_model)
db.session.commit()

View File

@ -62,8 +62,13 @@ export default function HomePage() {
{rowToUse.title}
</td>
<td>{rowToUse.state}</td>
<td>{rowToUse.process_instance_status}</td>
<td>
<Button variant="primary" href={taskUrl}>
<Button
variant="primary"
href={taskUrl}
hidden={rowToUse.process_instance_status === 'suspended'}
>
Complete Task
</Button>
</td>
@ -78,6 +83,7 @@ export default function HomePage() {
<th>Process Instance</th>
<th>Task Name</th>
<th>Status</th>
<th>Process Instance Status</th>
<th>Actions</th>
</tr>
</thead>

View File

@ -53,6 +53,22 @@ export default function ProcessInstanceShow() {
});
};
const suspendProcessInstance = () => {
HttpService.makeCallToBackend({
path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}/suspend`,
successCallback: refreshPage,
httpMethod: 'POST',
});
};
const resumeProcessInstance = () => {
HttpService.makeCallToBackend({
path: `/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/${params.process_instance_id}/resume`,
successCallback: refreshPage,
httpMethod: 'POST',
});
};
const getTaskIds = () => {
const taskIds = { completed: [], readyOrWaiting: [] };
if (tasks) {
@ -126,6 +142,32 @@ export default function ProcessInstanceShow() {
return <div />;
};
const suspendButton = (processInstanceToUse: any) => {
if (
['complete', 'terminated', 'faulted', 'suspended'].indexOf(
processInstanceToUse.status
) === -1
) {
return (
<Button onClick={suspendProcessInstance} variant="warning">
Suspend
</Button>
);
}
return <div />;
};
const resumeButton = (processInstanceToUse: any) => {
if (processInstanceToUse.status === 'suspended') {
return (
<Button onClick={resumeProcessInstance} variant="warning">
Resume
</Button>
);
}
return <div />;
};
const handleClickedDiagramTask = (shapeElement: any) => {
if (tasks) {
const matchingTask = tasks.find(
@ -217,6 +259,8 @@ export default function ProcessInstanceShow() {
buttonLabel="Delete"
/>
{terminateButton(processInstanceToUse)}
{suspendButton(processInstanceToUse)}
{resumeButton(processInstanceToUse)}
</Stack>
{getInfoTag(processInstanceToUse)}
{taskDataDisplayArea()}