Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

propogate uncaught events out of workflow #428

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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')

Loading