diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 3d62f468..f4b7fdd0 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -469,6 +469,33 @@ paths: items: $ref: "#/components/schemas/Workflow" + /process-instances/{process_instance_id}/task/{task_id}/update: + parameters: + - name: process_instance_id + in: path + required: true + description: The unique id of the process instance + schema: + type: string + - name: task_id + in: path + required: true + description: The unique id of the task + schema: + type: string + post: + operationId: spiffworkflow_backend.routes.process_api_blueprint.update_task_data + summary: Update the task data for requested instance and task + tags: + - Process Instances + responses: + "200": + description: Task Updated Successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Workflow" + /process-models/{process_group_id}/{process_model_id}/script-unit-tests: parameters: - name: process_group_id diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index af73c3cd..1e95c575 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1493,3 +1493,44 @@ def _update_form_schema_with_task_data_as_needed( for o in value: if isinstance(o, dict): _update_form_schema_with_task_data_as_needed(o, task_data) + + +def update_task_data(process_instance_id: str, task_id: str, body: Dict) -> Response: + """Update task data.""" + process_instance = ProcessInstanceModel.query.filter( + ProcessInstanceModel.id == int(process_instance_id) + ).first() + if process_instance: + process_instance_bpmn_json_dict = json.loads(process_instance.bpmn_json) + if "new_task_data" in body: + new_task_data_str: str = body["new_task_data"] + new_task_data_dict = json.loads(new_task_data_str) + if task_id in process_instance_bpmn_json_dict["tasks"]: + process_instance_bpmn_json_dict["tasks"][task_id][ + "data" + ] = new_task_data_dict + process_instance.bpmn_json = json.dumps(process_instance_bpmn_json_dict) + db.session.add(process_instance) + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise ApiError( + error_code="update_task_data_error", + message=f"Could not update the Instance. Original error is {e}", + ) from e + else: + raise ApiError( + error_code="update_task_data_error", + message=f"Could not find Task: {task_id} in Instance: {process_instance_id}.", + ) + else: + raise ApiError( + error_code="update_task_data_error", + message=f"Could not update task data for Instance: {process_instance_id}, and Task: {task_id}.", + ) + return Response( + json.dumps(ProcessInstanceModelSchema().dump(process_instance)), + status=200, + mimetype="application/json", + ) diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index 70c38dba..42d1abb8 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -522,6 +522,10 @@ class ProcessInstanceProcessor: ).all() if len(active_tasks) > 0: for at in active_tasks: + active_task_users = at.active_task_users + for atu in active_task_users: + # don't trust sqlalchemy to cascade - ran into race condition here. + db.session.delete(atu) db.session.delete(at) db.session.add(self.process_instance_model) diff --git a/src/spiffworkflow_backend/services/secret_service.py b/src/spiffworkflow_backend/services/secret_service.py index b5557a49..2aaf223d 100644 --- a/src/spiffworkflow_backend/services/secret_service.py +++ b/src/spiffworkflow_backend/services/secret_service.py @@ -55,7 +55,7 @@ class SecretService: def get_secret(key: str) -> SecretModel: """Get_secret.""" secret = db.session.query(SecretModel).filter(SecretModel.key == key).first() - if secret is not None: + if isinstance(secret, SecretModel): return secret else: raise ApiError( diff --git a/src/spiffworkflow_backend/services/user_service.py b/src/spiffworkflow_backend/services/user_service.py index 6480fb0d..d4749e01 100644 --- a/src/spiffworkflow_backend/services/user_service.py +++ b/src/spiffworkflow_backend/services/user_service.py @@ -264,7 +264,7 @@ class UserService: .filter(PrincipalModel.user_id == user_id) .first() ) - if principal: + if isinstance(principal, PrincipalModel): return principal raise ApiError( error_code="no_principal_found", diff --git a/tests/spiffworkflow_backend/unit/test_active_task.py b/tests/spiffworkflow_backend/unit/test_active_task.py new file mode 100644 index 00000000..6381482b --- /dev/null +++ b/tests/spiffworkflow_backend/unit/test_active_task.py @@ -0,0 +1,49 @@ +"""Process Model.""" +from decimal import Decimal + +from flask.app import Flask +from flask_bpmn.models.db import db + +from spiffworkflow_backend.models.active_task import ActiveTaskModel +from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel +from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor +from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec + +from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel + + +class TestActiveTask(BaseTest): + + def test_can_create_and_delete_an_active_task ( + self, app: Flask, with_db_and_bpmn_file_cleanup: None + ) -> None: + process_model = load_test_spec( + "call_activity_test", + process_model_source_directory="call_activity_same_directory", + ) + + process_instance = self.create_process_instance_from_process_model( + process_model + ) + active_task = ActiveTaskModel( + process_instance_id=process_instance.id, + process_model_display_name="my shorts", + form_file_name="my_file_name", + ui_form_file_name="", + task_id="1234", + task_name="any old thing", + task_title="", + task_type="test type", + task_status="WAITING", + lane_assignment_id=None, + ) + initiator_user = self.find_or_create_user("initiator_user") + db.session.add(active_task) + db.session.commit() + active_task_user = ActiveTaskUserModel(active_task_id=active_task.id, user_id=initiator_user.id) + db.session.add(active_task_user) + db.session.commit() + processor = ProcessInstanceProcessor(process_instance) + processor.save() # This should clear out all active tasks and active task users. + assert(len(db.session.query(ActiveTaskModel).all()) == 0) \ No newline at end of file