From 2ebb3a14ce3c7a15029c62a911fa873346ed0f24 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 6 Jan 2023 16:21:29 -0500 Subject: [PATCH] do not allow sending messages to terminated and suspended process instances w/ burnettk --- src/spiffworkflow_backend/api.yml | 2 +- .../routes/messages_controller.py | 23 +++- .../integration/test_process_api.py | 117 ++++++++++++++++-- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 1055ba43..50fb6f87 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -1820,7 +1820,7 @@ paths: post: tags: - Messages - operationId: spiffworkflow_backend.routes.messages_controller.message_start + operationId: spiffworkflow_backend.routes.messages_controller.message_send summary: Instantiate and run a given process model with a message start event matching given identifier requestBody: content: diff --git a/src/spiffworkflow_backend/routes/messages_controller.py b/src/spiffworkflow_backend/routes/messages_controller.py index 51290770..30456c93 100644 --- a/src/spiffworkflow_backend/routes/messages_controller.py +++ b/src/spiffworkflow_backend/routes/messages_controller.py @@ -19,6 +19,7 @@ from spiffworkflow_backend.models.message_triggerable_process_model import ( ) 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.routes.process_api_blueprint import ( _find_process_instance_by_id_or_raise, ) @@ -90,7 +91,7 @@ def message_instance_list( # payload: dict, # process_instance_id: Optional[int], # } -def message_start( +def message_send( message_identifier: str, body: Dict[str, Any], ) -> flask.wrappers.Response: @@ -121,6 +122,26 @@ def message_start( body["process_instance_id"] ) + if process_instance.status == ProcessInstanceStatus.suspended.value: + raise ApiError( + error_code="process_instance_is_suspended", + message=( + f"Process Instance '{process_instance.id}' is suspended and cannot" + " accept messages.'" + ), + status_code=400, + ) + + if process_instance.status == ProcessInstanceStatus.terminated.value: + raise ApiError( + error_code="process_instance_is_terminated", + message=( + f"Process Instance '{process_instance.id}' is terminated and cannot" + " accept messages.'" + ), + status_code=400, + ) + message_instance = MessageInstanceModel.query.filter_by( process_instance_id=process_instance.id, message_model_id=message_model.id, diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index b7b3da25..f5248050 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1296,16 +1296,16 @@ class TestProcessApi(BaseTest): xml_file_contents = f_open.read() assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents - def test_message_start_when_starting_process_instance( + def test_message_send_when_starting_process_instance( self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - """Test_message_start_when_starting_process_instance.""" + """Test_message_send_when_starting_process_instance.""" # ensure process model is loaded - process_group_id = "test_message_start" + process_group_id = "test_message_send" process_model_id = "message_receiver" bpmn_file_name = "message_receiver.bpmn" bpmn_file_location = "message_send_one_conversation" @@ -1345,15 +1345,15 @@ class TestProcessApi(BaseTest): assert process_instance_data assert process_instance_data["the_payload"] == payload - def test_message_start_when_providing_message_to_running_process_instance( + def test_message_send_when_providing_message_to_running_process_instance( self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - """Test_message_start_when_providing_message_to_running_process_instance.""" - process_group_id = "test_message_start" + """Test_message_send_when_providing_message_to_running_process_instance.""" + process_group_id = "test_message_send" process_model_id = "message_sender" bpmn_file_name = "message_sender.bpmn" bpmn_file_location = "message_send_one_conversation" @@ -1412,6 +1412,105 @@ class TestProcessApi(BaseTest): assert process_instance_data assert process_instance_data["the_payload"] == payload + def test_message_send_errors_when_providing_message_to_suspended_process_instance( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_message_send_when_providing_message_to_running_process_instance.""" + process_group_id = "test_message_send" + process_model_id = "message_sender" + bpmn_file_name = "message_sender.bpmn" + bpmn_file_location = "message_send_one_conversation" + process_model_identifier = self.create_group_and_model_with_bpmn( + client, + with_super_admin_user, + process_group_id=process_group_id, + process_model_id=process_model_id, + bpmn_file_name=bpmn_file_name, + bpmn_file_location=bpmn_file_location, + ) + + message_model_identifier = "message_response" + payload = { + "the_payload": { + "topica": "the_payload.topica_string", + "topicb": "the_payload.topicb_string", + "andThis": "another_item_non_key", + } + } + response = self.create_process_instance_from_process_model_id_with_api( + client, + process_model_identifier, + self.logged_in_headers(with_super_admin_user), + ) + assert response.json is not None + process_instance_id = response.json["id"] + + response = client.post( + f"/v1.0/process-instances/{self.modify_process_identifier_for_path_param(process_model_identifier)}/{process_instance_id}/run", + headers=self.logged_in_headers(with_super_admin_user), + ) + + assert response.status_code == 200 + assert response.json is not None + + process_instance = ProcessInstanceModel.query.filter_by( + id=process_instance_id + ).first() + processor = ProcessInstanceProcessor(process_instance) + + processor.suspend() + response = client.post( + f"/v1.0/messages/{message_model_identifier}", + content_type="application/json", + headers=self.logged_in_headers(with_super_admin_user), + data=json.dumps( + {"payload": payload, "process_instance_id": process_instance_id} + ), + ) + assert response.status_code == 400 + assert response.json + assert response.json["error_code"] == "process_instance_is_suspended" + + processor.resume() + response = client.post( + f"/v1.0/messages/{message_model_identifier}", + content_type="application/json", + headers=self.logged_in_headers(with_super_admin_user), + data=json.dumps( + {"payload": payload, "process_instance_id": process_instance_id} + ), + ) + assert response.status_code == 200 + json_data = response.json + assert json_data + assert json_data["status"] == "complete" + process_instance_id = json_data["id"] + process_instance = ProcessInstanceModel.query.filter_by( + id=process_instance_id + ).first() + assert process_instance + processor = ProcessInstanceProcessor(process_instance) + process_instance_data = processor.get_data() + assert process_instance_data + assert process_instance_data["the_payload"] == payload + + processor.terminate() + response = client.post( + f"/v1.0/messages/{message_model_identifier}", + content_type="application/json", + headers=self.logged_in_headers(with_super_admin_user), + data=json.dumps( + {"payload": payload, "process_instance_id": process_instance_id} + ), + ) + assert response.status_code == 400 + assert response.json + assert response.json["error_code"] == "process_instance_is_terminated" + def test_process_instance_can_be_terminated( self, app: Flask, @@ -1419,9 +1518,9 @@ class TestProcessApi(BaseTest): with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - """Test_message_start_when_providing_message_to_running_process_instance.""" + """Test_message_send_when_providing_message_to_running_process_instance.""" # this task will wait on a catch event - process_group_id = "test_message_start" + process_group_id = "test_message_send" process_model_id = "message_sender" bpmn_file_name = "message_sender.bpmn" bpmn_file_location = "message_send_one_conversation" @@ -2188,7 +2287,7 @@ class TestProcessApi(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_can_get_message_instances_by_process_instance_id.""" - process_group_id = "test_message_start" + process_group_id = "test_message_send" process_model_id = "message_receiver" bpmn_file_name = "message_receiver.bpmn" bpmn_file_location = "message_send_one_conversation"