Skip to content

Commit 579bac4

Browse files
authored
Merge pull request #428 from sartography/feature/error-event-handling
propogate uncaught events out of workflow
2 parents 14c84aa + 101e8f6 commit 579bac4

File tree

4 files changed

+96
-23
lines changed

4 files changed

+96
-23
lines changed

SpiffWorkflow/bpmn/specs/event_definitions/item_aware_event.py

+17-23
Original file line numberDiff line numberDiff line change
@@ -26,44 +26,38 @@ def reset(self, my_task):
2626
my_task.internal_data.pop(self.name, None)
2727
super().reset(my_task)
2828

29-
class ErrorEventDefinition(ItemAwareEventDefinition):
30-
"""
31-
Error events can occur only in subprocesses and as subprocess boundary events. They're
32-
matched by code rather than name.
33-
"""
29+
30+
class CodeEventDefinition(ItemAwareEventDefinition):
3431

3532
def __init__(self, name, code=None, **kwargs):
36-
super(ErrorEventDefinition, self).__init__(name, **kwargs)
33+
super().__init__(name, **kwargs)
3734
self.code = code
3835

36+
def throw(self, my_task):
37+
payload = deepcopy(my_task.data)
38+
event = BpmnEvent(self, payload=payload, target=my_task.workflow)
39+
my_task.workflow.top_workflow.catch(event)
40+
3941
def details(self, my_task):
4042
return PendingBpmnEvent(self.name, self.__class__.__name__, self.code)
4143

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

4547

46-
class EscalationEventDefinition(ItemAwareEventDefinition):
48+
class ErrorEventDefinition(CodeEventDefinition):
49+
"""
50+
Error events can occur only in subprocesses and as subprocess boundary events. They're
51+
matched by code rather than name.
52+
"""
53+
pass
54+
55+
class EscalationEventDefinition(CodeEventDefinition):
4756
"""
4857
Escalation events have names, though they don't seem to be used for anything. Instead
4958
the spec says that the escalation code should be matched.
5059
"""
51-
52-
def __init__(self, name, code=None, **kwargs):
53-
"""
54-
Constructor.
55-
56-
:param escalation_code: The escalation code this event should
57-
react to. If None then all escalations will activate this event.
58-
"""
59-
super(EscalationEventDefinition, self).__init__(name, **kwargs)
60-
self.code = code
61-
62-
def details(self, my_task):
63-
return PendingBpmnEvent(self.name, self.__class__.__name__, self.code)
64-
65-
def __eq__(self, other):
66-
return super().__eq__(other) and self.code in [None, other.code]
60+
pass
6761

6862

6963
class SignalEventDefinition(ItemAwareEventDefinition):

SpiffWorkflow/bpmn/workflow.py

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from SpiffWorkflow.bpmn.specs.mixins.events.event_types import CatchingEvent
2525
from SpiffWorkflow.bpmn.specs.mixins.events.start_event import StartEvent
2626
from SpiffWorkflow.bpmn.specs.mixins.subworkflow_task import CallActivity
27+
from SpiffWorkflow.bpmn.specs.event_definitions.item_aware_event import CodeEventDefinition
2728

2829
from SpiffWorkflow.bpmn.specs.control import BoundaryEventSplit
2930

@@ -113,6 +114,9 @@ def catch(self, event):
113114
if event.target is not None:
114115
# This limits results to tasks in the specified workflow
115116
tasks = event.target.get_tasks(skip_subprocesses=True, state=TaskState.NOT_FINISHED_MASK, catches_event=event)
117+
if isinstance(event.event_definition, CodeEventDefinition) and len(tasks) == 0:
118+
event.target = event.target.parent_workflow
119+
self.catch(event)
116120
else:
117121
self.update_collaboration(event)
118122
tasks = self.get_tasks(state=TaskState.NOT_FINISHED_MASK, catches_event=event)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<bpmn:process id="top_level" isExecutable="true" camunda:versionTag="1">
4+
<bpmn:startEvent id="StartEvent_1">
5+
<bpmn:outgoing>Flow_0vt1twq</bpmn:outgoing>
6+
</bpmn:startEvent>
7+
<bpmn:endEvent id="Event_0yxpeto">
8+
<bpmn:incoming>Flow_1udyjxo</bpmn:incoming>
9+
</bpmn:endEvent>
10+
<bpmn:sequenceFlow id="Flow_0vt1twq" sourceRef="StartEvent_1" targetRef="subprocess" />
11+
<bpmn:sequenceFlow id="Flow_1udyjxo" sourceRef="subprocess" targetRef="Event_0yxpeto" />
12+
<bpmn:subProcess id="subprocess" name="Subprocess">
13+
<bpmn:incoming>Flow_0vt1twq</bpmn:incoming>
14+
<bpmn:outgoing>Flow_1udyjxo</bpmn:outgoing>
15+
<bpmn:startEvent id="Event_16026un">
16+
<bpmn:outgoing>Flow_0w1tuap</bpmn:outgoing>
17+
</bpmn:startEvent>
18+
<bpmn:sequenceFlow id="Flow_0w1tuap" sourceRef="Event_16026un" targetRef="Event_0rc21k6" />
19+
<bpmn:endEvent id="Event_0rc21k6">
20+
<bpmn:incoming>Flow_0w1tuap</bpmn:incoming>
21+
<bpmn:escalationEventDefinition id="EscalationEventDefinition_0e8lzpf" escalationRef="Escalation_0ftd3m0" />
22+
</bpmn:endEvent>
23+
</bpmn:subProcess>
24+
</bpmn:process>
25+
<bpmn:escalation id="Escalation_0ftd3m0" name="Escalation_0ftd3m0" escalationCode="escalation-1" />
26+
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
27+
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="top_level">
28+
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
29+
<dc:Bounds x="-98" y="40" width="36" height="36" />
30+
</bpmndi:BPMNShape>
31+
<bpmndi:BPMNShape id="Event_0yxpeto_di" bpmnElement="Event_0yxpeto">
32+
<dc:Bounds x="332" y="40" width="36" height="36" />
33+
</bpmndi:BPMNShape>
34+
<bpmndi:BPMNShape id="Activity_12pv6kk_di" bpmnElement="subprocess" isExpanded="true">
35+
<dc:Bounds x="0" y="-15" width="270" height="145" />
36+
<bpmndi:BPMNLabel />
37+
</bpmndi:BPMNShape>
38+
<bpmndi:BPMNShape id="Event_16026un_di" bpmnElement="Event_16026un">
39+
<dc:Bounds x="52" y="42" width="36" height="36" />
40+
</bpmndi:BPMNShape>
41+
<bpmndi:BPMNShape id="Event_0ewztjn_di" bpmnElement="Event_0rc21k6">
42+
<dc:Bounds x="172" y="42" width="36" height="36" />
43+
</bpmndi:BPMNShape>
44+
<bpmndi:BPMNEdge id="Flow_0w1tuap_di" bpmnElement="Flow_0w1tuap">
45+
<di:waypoint x="88" y="60" />
46+
<di:waypoint x="172" y="60" />
47+
</bpmndi:BPMNEdge>
48+
<bpmndi:BPMNEdge id="Flow_0vt1twq_di" bpmnElement="Flow_0vt1twq">
49+
<di:waypoint x="-62" y="58" />
50+
<di:waypoint x="0" y="58" />
51+
</bpmndi:BPMNEdge>
52+
<bpmndi:BPMNEdge id="Flow_1udyjxo_di" bpmnElement="Flow_1udyjxo">
53+
<di:waypoint x="270" y="58" />
54+
<di:waypoint x="332" y="58" />
55+
</bpmndi:BPMNEdge>
56+
</bpmndi:BPMNPlane>
57+
</bpmndi:BPMNDiagram>
58+
</bpmn:definitions>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import unittest
2+
3+
from SpiffWorkflow import TaskState
4+
from SpiffWorkflow.bpmn import BpmnWorkflow
5+
6+
from ..BpmnWorkflowTestCase import BpmnWorkflowTestCase
7+
8+
class UncaughtEscalationTest(BpmnWorkflowTestCase):
9+
10+
def test_uncaught_escalation(self):
11+
spec, subprocess_specs = self.load_workflow_spec('uncaught_escalation.bpmn', 'top_level')
12+
workflow = BpmnWorkflow(spec, subprocess_specs)
13+
workflow.do_engine_steps()
14+
self.assertTrue(workflow.completed)
15+
event = workflow.get_events()[0]
16+
self.assertEqual(event.event_definition.code, 'escalation-1')
17+

0 commit comments

Comments
 (0)