diff --git a/spiffworkflow-backend/migrations/versions/c6e246c3c04e_.py b/spiffworkflow-backend/migrations/versions/c6e246c3c04e_.py new file mode 100644 index 00000000..f9ffcfdc --- /dev/null +++ b/spiffworkflow-backend/migrations/versions/c6e246c3c04e_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: c6e246c3c04e +Revises: 6344d90d20fa +Create Date: 2024-02-19 16:41:52.728357 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c6e246c3c04e' +down_revision = '6344d90d20fa' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('message_triggerable_process_model', schema=None) as batch_op: + batch_op.add_column(sa.Column('file_name', sa.String(length=255), nullable=True)) + batch_op.create_index(batch_op.f('ix_message_triggerable_process_model_file_name'), ['file_name'], unique=False) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('message_triggerable_process_model', schema=None) as batch_op: + batch_op.drop_index(batch_op.f('ix_message_triggerable_process_model_file_name')) + batch_op.drop_column('file_name') + + # ### end Alembic commands ### diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/message_triggerable_process_model.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/message_triggerable_process_model.py index 5f12c3ab..9e89834c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/message_triggerable_process_model.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/message_triggerable_process_model.py @@ -8,5 +8,7 @@ class MessageTriggerableProcessModel(SpiffworkflowBaseDBModel): id = db.Column(db.Integer, primary_key=True) message_name: str = db.Column(db.String(255), index=True) process_model_identifier: str = db.Column(db.String(255), nullable=False, index=True) + file_name: str = db.Column(db.String(255), index=True) + updated_at_in_seconds: int = db.Column(db.Integer) created_at_in_seconds: int = db.Column(db.Integer) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index b62c55a3..3d55ee62 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -310,6 +310,9 @@ class SpecFileService(FileSystemService): @staticmethod def update_message_trigger_cache(ref: Reference) -> None: """Assure we know which messages can trigger the start of a process.""" + current_triggerable_processes = MessageTriggerableProcessModel.query.filter_by( + file_name=ref.file_name, process_model_identifier=ref.relative_location + ).all() for message_name in ref.start_messages: message_triggerable_process_model = MessageTriggerableProcessModel.query.filter_by( message_name=message_name, @@ -318,6 +321,7 @@ class SpecFileService(FileSystemService): message_triggerable_process_model = MessageTriggerableProcessModel( message_name=message_name, process_model_identifier=ref.relative_location, + file_name=ref.file_name, ) db.session.add(message_triggerable_process_model) else: @@ -325,6 +329,12 @@ class SpecFileService(FileSystemService): raise ProcessModelFileInvalidError( f"Message model is already used to start process model {ref.relative_location}" ) + elif message_triggerable_process_model.file_name is None: + message_triggerable_process_model.file_name = ref.file_name + db.session.add(message_triggerable_process_model) + current_triggerable_processes.remove(message_triggerable_process_model) + for trigger_pm in current_triggerable_processes: + db.session.delete(trigger_pm) @staticmethod def update_correlation_cache(ref: Reference) -> None: 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 78853cec..d440e6b8 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_message_service.py @@ -8,6 +8,7 @@ from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.services.message_service import MessageService from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService +from spiffworkflow_backend.services.spec_file_service import SpecFileService from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -252,3 +253,39 @@ class TestMessageService(BaseTest): assert len(message_instances) == 2 mi_statuses = [mi.status for mi in message_instances] assert mi_statuses == ["completed", "completed"] + + def test_can_delete_message_start_events_from_database_if_model_no_longer_references_it( + self, + app: Flask, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + process_model_without_message_start_event = load_test_spec( + "test_group/sample", + process_model_source_directory="sample", + ) + old_message_triggerable_process = MessageTriggerableProcessModel( + message_name="travel_start_test_v2", + process_model_identifier=process_model_without_message_start_event.id, + file_name=process_model_without_message_start_event.primary_file_name, + ) + db.session.add(old_message_triggerable_process) + db.session.commit() + message_triggerable_process_model = MessageTriggerableProcessModel.query.filter_by( + message_name="travel_start_test_v2" + ).first() + assert message_triggerable_process_model is not None + assert message_triggerable_process_model.process_model_identifier == process_model_without_message_start_event.id + + assert process_model_without_message_start_event.primary_file_name is not None + primary_file_contents = SpecFileService.get_data( + process_model_without_message_start_event, process_model_without_message_start_event.primary_file_name + ) + SpecFileService.update_file( + process_model_without_message_start_event, + process_model_without_message_start_event.primary_file_name, + primary_file_contents, + ) + message_triggerable_process_model = MessageTriggerableProcessModel.query.filter_by( + message_name="travel_start_test_v2" + ).first() + assert message_triggerable_process_model is None