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 682598819..8f522af9b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -661,35 +661,9 @@ def process_instance_reset( 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", - ) + processor = ProcessInstanceProcessor(process_instance) + processor.reset_process(spiff_step) + return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") def process_instance_log_list( 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 f9e6c6207..842bca15a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -622,7 +622,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() @@ -644,9 +644,6 @@ 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() @@ -733,6 +730,14 @@ class ProcessInstanceProcessor: self.bpmn_process_instance.catch(event_definition) self.do_engine_steps(save=True) + def add_step(self, step: Union[dict, None] = None) -> None: + """Add a spiff step.""" + self.increment_spiff_step() + if step is None: + step = self.spiff_step_details_mapping() + db.session.add(SpiffStepDetailsModel(**step)) + db.session.commit() + 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)) @@ -742,17 +747,52 @@ class ProcessInstanceProcessor: ) 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_logger = logging.getLogger("spiff") + spiff_logger.info(f"Skipped task", extra=spiff_task.log_info()) 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 - self._save() + self.add_step() + self.save() # Saving the workflow seems to reset the status self.suspend() + def reset_process(self, spiff_step: int) -> None: + """Reset a process to an earlier state.""" + + spiff_logger = logging.getLogger("spiff") + spiff_logger.info( + f"Process reset from step {spiff_step}", + extra=self.bpmn_process_instance.log_info(), + ) + + step_detail = ( + db.session.query(SpiffStepDetailsModel) + .filter( + SpiffStepDetailsModel.process_instance_id + == self.process_instance_model.id, + SpiffStepDetailsModel.spiff_step == spiff_step, + ) + .first() + ) + if step_detail is not None: + self.add_step( + { + "process_instance_id": self.process_instance_model.id, + "spiff_step": self.process_instance_model.spiff_step or 1, + "task_json": step_detail.task_json, + "timestamp": round(time.time()), + } + ) + + dct = self._serializer.workflow_to_dict(self.bpmn_process_instance) + dct["tasks"] = step_detail.task_json["tasks"] + dct["subprocesses"] = step_detail.task_json["subprocesses"] + self.bpmn_process_instance = self._serializer.workflow_from_dict(dct) + self.save() + self.suspend() + @staticmethod def get_parser() -> MyCustomParser: """Get_parser."""