diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py index ad6f2c51..58c90ba9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/message_service.py @@ -1,5 +1,6 @@ from SpiffWorkflow.bpmn import BpmnEvent # type: ignore from SpiffWorkflow.bpmn.specs.event_definitions.message import CorrelationProperty # type: ignore +from SpiffWorkflow.bpmn.specs.mixins import StartEventMixin # type: ignore from SpiffWorkflow.spiff.specs.event_definitions import MessageEventDefinition # type: ignore from spiffworkflow_backend.models.db import db @@ -8,6 +9,7 @@ from spiffworkflow_backend.models.message_instance import MessageStatuses from spiffworkflow_backend.models.message_instance import MessageTypes from spiffworkflow_backend.models.message_triggerable_process_model import MessageTriggerableProcessModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import CustomBpmnScriptEngine from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService @@ -51,7 +53,7 @@ class MessageService: ).first() if message_triggerable_process_model: receiving_process = MessageService.start_process_with_message( - message_triggerable_process_model, message_instance_send + message_triggerable_process_model, message_instance_send.user ) message_instance_receive = MessageInstanceModel.query.filter_by( process_instance_id=receiving_process.id, @@ -106,20 +108,37 @@ class MessageService: for message_instance_send in message_instances_send: cls.correlate_send_message(message_instance_send) - @staticmethod + @classmethod def start_process_with_message( + cls, message_triggerable_process_model: MessageTriggerableProcessModel, - message_instance: MessageInstanceModel, + user: UserModel, ) -> ProcessInstanceModel: """Start up a process instance, so it is ready to catch the event.""" process_instance_receive = ProcessInstanceService.create_process_instance_from_process_model_identifier( message_triggerable_process_model.process_model_identifier, - message_instance.user, + user, ) processor_receive = ProcessInstanceProcessor(process_instance_receive) + cls._cancel_non_matching_start_events(processor_receive, message_triggerable_process_model) processor_receive.do_engine_steps(save=True) return process_instance_receive + @classmethod + def _cancel_non_matching_start_events( + cls, processor_receive: ProcessInstanceProcessor, message_triggerable_process_model: MessageTriggerableProcessModel + ) -> None: + """Cancel any start event that does not match the start event that triggered this. + + After that SpiffWorkflow and the WorkflowExecutionService can figure it out. + """ + start_tasks = processor_receive.bpmn_process_instance.get_tasks(spec_class=StartEventMixin) + for start_task in start_tasks: + if not isinstance(start_task.task_spec.event_definition, MessageEventDefinition): + start_task.cancel() + elif start_task.task_spec.event_definition.name != message_triggerable_process_model.message_name: + start_task.cancel() + @staticmethod def get_process_instance_for_message_instance( message_instance_receive: MessageInstanceModel, diff --git a/spiffworkflow-backend/tests/data/multiple_message_start_events/multiple-message-start-events.bpmn b/spiffworkflow-backend/tests/data/multiple_message_start_events/multiple-message-start-events.bpmn new file mode 100644 index 00000000..ff5d3faa --- /dev/null +++ b/spiffworkflow-backend/tests/data/multiple_message_start_events/multiple-message-start-events.bpmn @@ -0,0 +1,141 @@ + + + + + + message_test_pur_id + + + message_test_trvl_id + + + + + Flow_18sbiwh + + + + Flow_0kepd4s + + + + Flow_0a541po + + + Flow_18sbiwh + Flow_0kepd4s + Flow_0a541po + Flow_18oryqi + + + + The process instance completed successfully. + + Flow_17u2bz9 + + + + placeholder + + Flow_18oryqi + Flow_17u2bz9 + a = 1 + + + + + + + + + + incoming_pur_data_test + + + + + incoming_trvl_data_test + + + + + {"status": "open", +"message_test_trvl_id": incoming_trvl_data_test["message_test_trvl_id"]} + + + + + + + + incoming_pur_data_test["message_test_pur_id"] + + + + + message_test_trvl_id + + + message_test_trvl_id + + + + + {"status": "open", +"message_test_pur_id": incoming_pur_data_test["message_test_pur_id"]} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py index 3ed20ab8..e4ae8262 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py @@ -1,6 +1,7 @@ from flask import Flask from flask.testing import FlaskClient from spiffworkflow_backend.models.message_instance import MessageInstanceModel +from spiffworkflow_backend.models.message_triggerable_process_model import MessageTriggerableProcessModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.services.message_service import MessageService from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor @@ -186,3 +187,23 @@ class TestMessageService(BaseTest): assert len(process_instance_result) == 3 for process_instance in process_instance_result: assert process_instance.status == "complete" + + def test_can_send_to_correct_start_event_if_there_are_multiple( + self, + app: Flask, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + load_test_spec( + "test_group/multiple_message_start_events", + process_model_source_directory="multiple_message_start_events", + ) + user = self.find_or_create_user() + message_triggerable_process_model = MessageTriggerableProcessModel.query.filter_by( + message_name="travel_start_test_v2" + ).first() + assert message_triggerable_process_model is not None + + MessageService.start_process_with_message(message_triggerable_process_model, user) + message_instances = MessageInstanceModel.query.all() + assert len(message_instances) == 1 + assert message_instances[0].name == "travel_start_test_v2"