From 0edb8904708a84e453fcc7b2bdf1d21a6b0f7520 Mon Sep 17 00:00:00 2001 From: Elizabeth Esswein Date: Thu, 29 Dec 2022 15:26:29 -0500 Subject: [PATCH] add endpoint to reset process to earlier step --- .../src/spiffworkflow_backend/api.yml | 33 ++++++++++++++++ .../routes/process_api_blueprint.py | 39 +++++++++++++++++++ .../src/hooks/UriListForPermissions.tsx | 1 + .../src/routes/ProcessInstanceShow.tsx | 32 ++++++++++++++- 4 files changed, 103 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index ea23d2fe..2a247863 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -1060,6 +1060,39 @@ paths: schema: $ref: "#/components/schemas/OkTrue" + /process-instance-reset/{modified_process_model_identifier}/{process_instance_id}/{spiff_step}: + parameters: + - name: modified_process_model_identifier + in: path + required: true + description: The modified process model id + schema: + type: string + - name: process_instance_id + in: path + required: true + description: The unique id of an existing process instance. + schema: + type: integer + - name: spiff_step + in: query + required: false + description: Reset the process to this state + schema: + type: integer + post: + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_instance_reset + summary: Reset a process instance to an earlier step + tags: + - Process Instances + responses: + "200": + description: Empty ok true response on successful resume. + content: + application/json: + schema: + $ref: "#/components/schemas/OkTrue" + /process-instances/reports: parameters: - name: page diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index ee3e4c62..b8db2dab 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -651,6 +651,45 @@ def process_instance_resume( return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") +def process_instance_reset( + process_instance_id: int, + modified_process_model_identifier: str, + spiff_step: int = 0, +) -> flask.wrappers.Response: + process_instance = ProcessInstanceService().get_process_instance( + process_instance_id + ) + step_detail = ( + db.session.query(SpiffStepDetailsModel) + .filter( + SpiffStepDetailsModel.process_instance_id == process_instance.id, + SpiffStepDetailsModel.spiff_step == spiff_step, + ) + .first() + ) + if step_detail is not None and process_instance.bpmn_json is not None: + bpmn_json = json.loads(process_instance.bpmn_json) + bpmn_json["tasks"] = step_detail.task_json["tasks"] + bpmn_json["subprocesses"] = step_detail.task_json["subprocesses"] + process_instance.bpmn_json = json.dumps(bpmn_json) + + db.session.add(process_instance) + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise ApiError( + error_code="reset_process_instance_error", + message=f"Could not update the Instance. Original error is {e}", + ) from e + + return Response( + json.dumps(ProcessInstanceModelSchema().dump(process_instance)), + status=200, + mimetype="application/json", + ) + + def process_instance_log_list( modified_process_model_identifier: str, process_instance_id: int, diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx index c9ef03e0..f8e5f07f 100644 --- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx +++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx @@ -16,6 +16,7 @@ export const useUriListForPermissions = () => { processInstanceReportListPath: '/v1.0/process-instances/reports', processInstanceResumePath: `/v1.0/process-instance-resume/${params.process_model_id}/${params.process_instance_id}`, processInstanceSuspendPath: `/v1.0/process-instance-suspend/${params.process_model_id}/${params.process_instance_id}`, + processInstanceResetPath: `/v1.0/process-instance-reset/${params.process_model_id}/${params.process_instance_id}`, processInstanceTaskListDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`, processInstanceSendEventPath: `/v1.0/send-event/${params.process_model_id}/${params.process_instance_id}`, processInstanceCompleteTaskPath: `/v1.0/complete-task/${params.process_model_id}/${params.process_instance_id}`, diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 03f8bd67..d984b664 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -90,6 +90,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { [`${targetUris.processInstanceResumePath}`]: ['POST'], [`${targetUris.processInstanceSuspendPath}`]: ['POST'], [`${targetUris.processInstanceTerminatePath}`]: ['POST'], + [targetUris.processInstanceResetPath]: ['POST'], [targetUris.messageInstanceListPath]: ['GET'], [targetUris.processInstanceActionPath]: ['DELETE'], [targetUris.processInstanceLogListPath]: ['GET'], @@ -261,6 +262,14 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { return spiffStepLink(, 1); }; + const resetProcessInstance = () => { + HttpService.makeCallToBackend({ + path: `${targetUris.processInstanceResetPath}/${currentSpiffStep()}`, + successCallback: refreshPage, + httpMethod: 'POST', + }); + }; + const getInfoTag = () => { if (!processInstance) { return null; @@ -535,6 +544,15 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { ); }; + const canResetProcess = (task: any) => { + return ( + processInstance && + processInstance.status === 'suspended' && + task.state === 'READY' && + !showingLastSpiffStep() + ); + }; + const getEvents = (task: any) => { const handleMessage = (eventDefinition: any) => { if (eventDefinition.typename === 'MessageEventDefinition') { @@ -619,7 +637,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { }); }; - const taskDataButtons = (task: any) => { + const taskDisplayButtons = (task: any) => { const buttons = []; if ( @@ -707,6 +725,16 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { ); } + if (canResetProcess(task)) { + buttons.push( + + ); + } } return buttons; @@ -771,7 +799,7 @@ export default function ProcessInstanceShow({ variant }: OwnProps) { > {taskToUse.name} ({taskToUse.type}): {taskToUse.state} - {taskDataButtons(taskToUse)} + {taskDisplayButtons(taskToUse)} {selectingEvent ? eventSelector(candidateEvents)