Skip to content

Commit

Permalink
Merge pull request #428 from sartography/feature/error-event-handling
Browse files Browse the repository at this point in the history
propogate uncaught events out of workflow
  • Loading branch information
essweine authored Aug 22, 2024
2 parents 14c84aa + 101e8f6 commit 579bac4
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 23 deletions.
40 changes: 17 additions & 23 deletions SpiffWorkflow/bpmn/specs/event_definitions/item_aware_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,38 @@ def reset(self, my_task):
my_task.internal_data.pop(self.name, None)
super().reset(my_task)

class ErrorEventDefinition(ItemAwareEventDefinition):
"""
Error events can occur only in subprocesses and as subprocess boundary events. They're
matched by code rather than name.
"""

class CodeEventDefinition(ItemAwareEventDefinition):

def __init__(self, name, code=None, **kwargs):
super(ErrorEventDefinition, self).__init__(name, **kwargs)
super().__init__(name, **kwargs)
self.code = code

def throw(self, my_task):
payload = deepcopy(my_task.data)
event = BpmnEvent(self, payload=payload, target=my_task.workflow)
my_task.workflow.top_workflow.catch(event)

def details(self, my_task):
return PendingBpmnEvent(self.name, self.__class__.__name__, self.code)

def __eq__(self, other):
return super().__eq__(other) and self.code in [None, other.code]


class EscalationEventDefinition(ItemAwareEventDefinition):
class ErrorEventDefinition(CodeEventDefinition):
"""
Error events can occur only in subprocesses and as subprocess boundary events. They're
matched by code rather than name.
"""
pass

class EscalationEventDefinition(CodeEventDefinition):
"""
Escalation events have names, though they don't seem to be used for anything. Instead
the spec says that the escalation code should be matched.
"""

def __init__(self, name, code=None, **kwargs):
"""
Constructor.
:param escalation_code: The escalation code this event should
react to. If None then all escalations will activate this event.
"""
super(EscalationEventDefinition, self).__init__(name, **kwargs)
self.code = code

def details(self, my_task):
return PendingBpmnEvent(self.name, self.__class__.__name__, self.code)

def __eq__(self, other):
return super().__eq__(other) and self.code in [None, other.code]
pass


class SignalEventDefinition(ItemAwareEventDefinition):
Expand Down
4 changes: 4 additions & 0 deletions SpiffWorkflow/bpmn/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from SpiffWorkflow.bpmn.specs.mixins.events.event_types import CatchingEvent
from SpiffWorkflow.bpmn.specs.mixins.events.start_event import StartEvent
from SpiffWorkflow.bpmn.specs.mixins.subworkflow_task import CallActivity
from SpiffWorkflow.bpmn.specs.event_definitions.item_aware_event import CodeEventDefinition

from SpiffWorkflow.bpmn.specs.control import BoundaryEventSplit

Expand Down Expand Up @@ -113,6 +114,9 @@ def catch(self, event):
if event.target is not None:
# This limits results to tasks in the specified workflow
tasks = event.target.get_tasks(skip_subprocesses=True, state=TaskState.NOT_FINISHED_MASK, catches_event=event)
if isinstance(event.event_definition, CodeEventDefinition) and len(tasks) == 0:
event.target = event.target.parent_workflow
self.catch(event)
else:
self.update_collaboration(event)
tasks = self.get_tasks(state=TaskState.NOT_FINISHED_MASK, catches_event=event)
Expand Down
58 changes: 58 additions & 0 deletions tests/SpiffWorkflow/bpmn/data/uncaught_escalation.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1qnx3d3" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.0.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.17.0">
<bpmn:process id="top_level" isExecutable="true" camunda:versionTag="1">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_0vt1twq</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:endEvent id="Event_0yxpeto">
<bpmn:incoming>Flow_1udyjxo</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0vt1twq" sourceRef="StartEvent_1" targetRef="subprocess" />
<bpmn:sequenceFlow id="Flow_1udyjxo" sourceRef="subprocess" targetRef="Event_0yxpeto" />
<bpmn:subProcess id="subprocess" name="Subprocess">
<bpmn:incoming>Flow_0vt1twq</bpmn:incoming>
<bpmn:outgoing>Flow_1udyjxo</bpmn:outgoing>
<bpmn:startEvent id="Event_16026un">
<bpmn:outgoing>Flow_0w1tuap</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0w1tuap" sourceRef="Event_16026un" targetRef="Event_0rc21k6" />
<bpmn:endEvent id="Event_0rc21k6">
<bpmn:incoming>Flow_0w1tuap</bpmn:incoming>
<bpmn:escalationEventDefinition id="EscalationEventDefinition_0e8lzpf" escalationRef="Escalation_0ftd3m0" />
</bpmn:endEvent>
</bpmn:subProcess>
</bpmn:process>
<bpmn:escalation id="Escalation_0ftd3m0" name="Escalation_0ftd3m0" escalationCode="escalation-1" />
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="top_level">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="-98" y="40" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0yxpeto_di" bpmnElement="Event_0yxpeto">
<dc:Bounds x="332" y="40" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_12pv6kk_di" bpmnElement="subprocess" isExpanded="true">
<dc:Bounds x="0" y="-15" width="270" height="145" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_16026un_di" bpmnElement="Event_16026un">
<dc:Bounds x="52" y="42" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0ewztjn_di" bpmnElement="Event_0rc21k6">
<dc:Bounds x="172" y="42" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0w1tuap_di" bpmnElement="Flow_0w1tuap">
<di:waypoint x="88" y="60" />
<di:waypoint x="172" y="60" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0vt1twq_di" bpmnElement="Flow_0vt1twq">
<di:waypoint x="-62" y="58" />
<di:waypoint x="0" y="58" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1udyjxo_di" bpmnElement="Flow_1udyjxo">
<di:waypoint x="270" y="58" />
<di:waypoint x="332" y="58" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
17 changes: 17 additions & 0 deletions tests/SpiffWorkflow/bpmn/events/UncaughtEscalationTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import unittest

from SpiffWorkflow import TaskState
from SpiffWorkflow.bpmn import BpmnWorkflow

from ..BpmnWorkflowTestCase import BpmnWorkflowTestCase

class UncaughtEscalationTest(BpmnWorkflowTestCase):

def test_uncaught_escalation(self):
spec, subprocess_specs = self.load_workflow_spec('uncaught_escalation.bpmn', 'top_level')
workflow = BpmnWorkflow(spec, subprocess_specs)
workflow.do_engine_steps()
self.assertTrue(workflow.completed)
event = workflow.get_events()[0]
self.assertEqual(event.event_definition.code, 'escalation-1')

0 comments on commit 579bac4

Please sign in to comment.