mirror of
https://github.com/sartography/spiff-arena.git
synced 2025-03-03 10:30:37 +00:00
167 lines
7.3 KiB
Python
167 lines
7.3 KiB
Python
"""Message_service."""
|
|
from typing import Optional
|
|
|
|
from spiffworkflow_backend.models.db import db
|
|
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
|
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.services.process_instance_processor import (
|
|
CustomBpmnScriptEngine,
|
|
)
|
|
from spiffworkflow_backend.services.process_instance_processor import (
|
|
ProcessInstanceProcessor,
|
|
)
|
|
from spiffworkflow_backend.services.process_instance_service import (
|
|
ProcessInstanceService,
|
|
)
|
|
|
|
|
|
class MessageServiceError(Exception):
|
|
"""MessageServiceError."""
|
|
|
|
|
|
class MessageService:
|
|
"""MessageService."""
|
|
|
|
@classmethod
|
|
def correlate_send_message(cls, message_instance_send: MessageInstanceModel) -> Optional[MessageInstanceModel]:
|
|
"""Connects the given send message to a 'receive' message if possible.
|
|
|
|
:param message_instance_send:
|
|
:return: the message instance that received this message.
|
|
"""
|
|
# Thread safe via db locking - don't try to progress the same send message over multiple instances
|
|
if message_instance_send.status != MessageStatuses.ready.value:
|
|
return None
|
|
message_instance_send.status = MessageStatuses.running.value
|
|
db.session.add(message_instance_send)
|
|
db.session.commit()
|
|
|
|
# Find available messages that might match
|
|
available_receive_messages = MessageInstanceModel.query.filter_by(
|
|
name=message_instance_send.name,
|
|
status=MessageStatuses.ready.value,
|
|
message_type=MessageTypes.receive.value,
|
|
).all()
|
|
message_instance_receive: Optional[MessageInstanceModel] = None
|
|
try:
|
|
for message_instance in available_receive_messages:
|
|
if message_instance.correlates(message_instance_send, CustomBpmnScriptEngine()):
|
|
message_instance_receive = message_instance
|
|
|
|
if message_instance_receive is None:
|
|
# Check for a message triggerable process and start that to create a new message_instance_receive
|
|
message_triggerable_process_model = MessageTriggerableProcessModel.query.filter_by(
|
|
message_name=message_instance_send.name
|
|
).first()
|
|
if message_triggerable_process_model:
|
|
receiving_process = MessageService.start_process_with_message(
|
|
message_triggerable_process_model, message_instance_send
|
|
)
|
|
message_instance_receive = MessageInstanceModel.query.filter_by(
|
|
process_instance_id=receiving_process.id,
|
|
message_type="receive",
|
|
status="ready",
|
|
).first()
|
|
else:
|
|
receiving_process = MessageService.get_process_instance_for_message_instance(message_instance_receive)
|
|
|
|
# Assure we can send the message, otherwise keep going.
|
|
if message_instance_receive is None or not receiving_process.can_receive_message():
|
|
message_instance_send.status = "ready"
|
|
message_instance_send.status = "ready"
|
|
db.session.add(message_instance_send)
|
|
db.session.commit()
|
|
return None
|
|
|
|
# Set the receiving message to running, so it is not altered elswhere ...
|
|
message_instance_receive.status = "running"
|
|
|
|
cls.process_message_receive(
|
|
receiving_process,
|
|
message_instance_receive,
|
|
message_instance_send.name,
|
|
message_instance_send.payload,
|
|
)
|
|
message_instance_receive.status = "completed"
|
|
message_instance_receive.counterpart_id = message_instance_send.id
|
|
db.session.add(message_instance_receive)
|
|
message_instance_send.status = "completed"
|
|
message_instance_send.counterpart_id = message_instance_receive.id
|
|
db.session.add(message_instance_send)
|
|
db.session.commit()
|
|
return message_instance_receive
|
|
|
|
except Exception as exception:
|
|
db.session.rollback()
|
|
message_instance_send.status = "failed"
|
|
message_instance_send.failure_cause = str(exception)
|
|
db.session.add(message_instance_send)
|
|
if message_instance_receive:
|
|
message_instance_receive.status = "failed"
|
|
message_instance_receive.failure_cause = str(exception)
|
|
db.session.add(message_instance_receive)
|
|
db.session.commit()
|
|
raise exception
|
|
|
|
@classmethod
|
|
def correlate_all_message_instances(cls) -> None:
|
|
"""Look at ALL the Send and Receive Messages and attempt to find correlations."""
|
|
message_instances_send = MessageInstanceModel.query.filter_by(message_type="send", status="ready").all()
|
|
|
|
for message_instance_send in message_instances_send:
|
|
cls.correlate_send_message(message_instance_send)
|
|
|
|
@staticmethod
|
|
def start_process_with_message(
|
|
message_triggerable_process_model: MessageTriggerableProcessModel,
|
|
message_instance: MessageInstanceModel,
|
|
) -> 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,
|
|
)
|
|
processor_receive = ProcessInstanceProcessor(process_instance_receive)
|
|
processor_receive.do_engine_steps(save=True)
|
|
return process_instance_receive
|
|
|
|
@staticmethod
|
|
def get_process_instance_for_message_instance(
|
|
message_instance_receive: MessageInstanceModel,
|
|
) -> ProcessInstanceModel:
|
|
"""Process_message_receive."""
|
|
process_instance_receive: ProcessInstanceModel = ProcessInstanceModel.query.filter_by(
|
|
id=message_instance_receive.process_instance_id
|
|
).first()
|
|
if process_instance_receive is None:
|
|
raise MessageServiceError(
|
|
(
|
|
(
|
|
"Process instance cannot be found for queued message:"
|
|
f" {message_instance_receive.id}.Tried with id"
|
|
f" {message_instance_receive.process_instance_id}"
|
|
),
|
|
)
|
|
)
|
|
return process_instance_receive
|
|
|
|
@staticmethod
|
|
def process_message_receive(
|
|
process_instance_receive: ProcessInstanceModel,
|
|
message_instance_receive: MessageInstanceModel,
|
|
message_model_name: str,
|
|
message_payload: dict,
|
|
) -> None:
|
|
"""process_message_receive."""
|
|
processor_receive = ProcessInstanceProcessor(process_instance_receive)
|
|
processor_receive.bpmn_process_instance.catch_bpmn_message(message_model_name, message_payload)
|
|
processor_receive.do_engine_steps(save=True)
|
|
message_instance_receive.status = MessageStatuses.completed.value
|
|
db.session.add(message_instance_receive)
|
|
db.session.commit()
|