This commit is contained in:
Dan 2023-03-09 16:10:31 -05:00
parent 300af1abae
commit 6871b43543
2 changed files with 61 additions and 49 deletions

View File

@ -1,19 +1,13 @@
"""Error_handling_service."""
import json
from typing import Union
from flask import current_app
from flask import g
from flask.wrappers import Response
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
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.models.process_instance import ProcessInstanceModelSchema
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.process_model import ProcessModelInfo
from spiffworkflow_backend.services.message_service import MessageService
@ -72,18 +66,19 @@ class ErrorHandlingService:
@staticmethod
def handle_system_notification(
error: Union[ApiError, Exception],
process_model: ProcessModelInfo,
_processor: ProcessInstanceProcessor
) -> Response:
"""Send a BPMN Message - which may kick off a waiting process. """
error: Union[ApiError, Exception],
process_model: ProcessModelInfo,
_processor: ProcessInstanceProcessor,
) -> None:
"""Send a BPMN Message - which may kick off a waiting process."""
message_text = (
f"There was an exception running process {process_model.id}.\nOriginal"
f" Error:\n{error.__repr__()}"
)
message_payload = {"message_text": message_text,
"recipients": process_model.exception_notification_addresses
}
message_payload = {
"message_text": message_text,
"recipients": process_model.exception_notification_addresses,
}
user_id = None
if "user" in g:
user_id = g.user.id

View File

@ -1,44 +1,52 @@
import pytest
from flask import Flask
from flask.testing import FlaskClient
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
from conftest import with_super_admin_user
from spiffworkflow_backend import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.models.process_model import ProcessModelInfo
from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService
from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor
from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService
from spiffworkflow_backend.services.process_instance_processor import (
ProcessInstanceProcessor,
)
from spiffworkflow_backend.services.process_instance_service import (
ProcessInstanceService,
)
from spiffworkflow_backend.services.process_model_service import ProcessModelService
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
class TestErrorHandlingService(BaseTest):
"""Error Handling does some crazy stuff man, like even firing off
messages in case a BPMN Task is waiting for that message. """
"""Error Handling does some crazy stuff man.
def run_process_model_and_handle_error(self, process_model: ProcessModelInfo, user: UserModel) -> Exception:
Like it can fire off BPMN messages in case a BPMN Task is waiting for that message.
"""
def run_process_model_and_handle_error(
self, process_model: ProcessModelInfo, user: UserModel
) -> ProcessInstanceModel:
process_instance = ProcessInstanceService.create_process_instance_from_process_model_identifier(
process_model.id, user)
pip = ProcessInstanceProcessor(process_instance)
process_model.id, user
)
pip = ProcessInstanceProcessor(process_instance)
with pytest.raises(ApiError) as e:
pip.do_engine_steps(save=True)
ErrorHandlingService().handle_error(pip, e)
ErrorHandlingService().handle_error(pip, e.value)
return process_instance
def test_handle_error_suspends_or_faults_process(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
) -> None:
""" Process Model in DB marked as suspended when error occurs"""
"""Process Model in DB marked as suspended when error occurs."""
process_model = load_test_spec(
"test_group/error_suspend",
process_model_source_directory="error",
@ -46,42 +54,51 @@ class TestErrorHandlingService(BaseTest):
)
# Process instance should be marked as errored by default.
process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user)
assert(ProcessInstanceStatus.error.value == process_instance.status)
process_instance = self.run_process_model_and_handle_error(
process_model, with_super_admin_user
)
assert ProcessInstanceStatus.error.value == process_instance.status
# If process model should be suspended on error, then that is what should happen.
process_model.fault_or_suspend_on_exception = "suspend"
ProcessModelService.save_process_model(process_model)
process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user)
assert(ProcessInstanceStatus.suspended.value == process_instance.status)
process_instance = self.run_process_model_and_handle_error(
process_model, with_super_admin_user
)
assert ProcessInstanceStatus.suspended.value == process_instance.status
def test_error_sends_bpmn_message(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel):
""" Process Model in DB marked as suspended when error occurs"""
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
) -> None:
"""Real BPMN Messages should get generated and processes should fire off and complete."""
process_model = load_test_spec(
"test_group/error_send_message_bpmn",
process_model_source_directory="error",
bpmn_file_name="error.bpmn", # Slightly misnamed, it sends and receives
)
""" Process Model that will listen for errors sent."""
process_model_error_handler = load_test_spec(
load_test_spec(
"test_group/admin_tools/error_handler",
process_model_source_directory="error",
bpmn_file_name="error_handler.bpmn", # Slightly misnamed, it sends and receives
)
process_model.exception_notification_addresses=["dan@ILoveToReadErrorsInMyEmails.com"]
process_model.exception_notification_addresses = [
"dan@ILoveToReadErrorsInMyEmails.com"
]
ProcessModelService.save_process_model(process_model)
# kick off the process and assure it got marked as an error.
process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user)
assert(ProcessInstanceStatus.error.value == process_instance.status)
process_instance = self.run_process_model_and_handle_error(
process_model, with_super_admin_user
)
assert ProcessInstanceStatus.error.value == process_instance.status
# Both send and receive messages should be generated, matched
# and considered complete.
messages = db.session.query(MessageInstanceModel).all()
assert(2 == len(messages))
assert("completed", messages[0].status)
assert("completed", messages[1].status)
assert 2 == len(messages)
assert "completed" == messages[0].status
assert "completed" == messages[1].status