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

View File

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