Implement native BPMN workflows and guided modes
This commit is contained in:
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from collections import Counter
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
from xml.etree.ElementTree import ParseError
|
||||
from xml.etree.ElementTree import Element, ParseError
|
||||
|
||||
from defusedxml.ElementTree import fromstring
|
||||
from defusedxml.common import DefusedXmlException
|
||||
@@ -27,33 +27,45 @@ NATIVE_EXECUTION_ELEMENTS = frozenset(
|
||||
{
|
||||
"startEvent",
|
||||
"endEvent",
|
||||
"task",
|
||||
"manualTask",
|
||||
"userTask",
|
||||
"serviceTask",
|
||||
"businessRuleTask",
|
||||
"receiveTask",
|
||||
"sendTask",
|
||||
"exclusiveGateway",
|
||||
"parallelGateway",
|
||||
"sequenceFlow",
|
||||
"receiveTask",
|
||||
"intermediateCatchEvent",
|
||||
"intermediateThrowEvent",
|
||||
"sequenceFlow",
|
||||
}
|
||||
)
|
||||
|
||||
NATIVE_MAPPING_ELEMENTS = NATIVE_EXECUTION_ELEMENTS | frozenset(
|
||||
{
|
||||
"task",
|
||||
"definitions",
|
||||
"process",
|
||||
"collaboration",
|
||||
"documentation",
|
||||
"extensionElements",
|
||||
"incoming",
|
||||
"outgoing",
|
||||
"conditionExpression",
|
||||
"scriptTask",
|
||||
"serviceTask",
|
||||
"businessRuleTask",
|
||||
"receiveTask",
|
||||
"sendTask",
|
||||
"callActivity",
|
||||
"subProcess",
|
||||
"transaction",
|
||||
"adHocSubProcess",
|
||||
"exclusiveGateway",
|
||||
"parallelGateway",
|
||||
"inclusiveGateway",
|
||||
"eventBasedGateway",
|
||||
"complexGateway",
|
||||
"boundaryEvent",
|
||||
"eventSubProcess",
|
||||
"intermediateCatchEvent",
|
||||
"intermediateThrowEvent",
|
||||
"dataObject",
|
||||
"dataObjectReference",
|
||||
"dataStoreReference",
|
||||
@@ -64,6 +76,16 @@ NATIVE_MAPPING_ELEMENTS = NATIVE_EXECUTION_ELEMENTS | frozenset(
|
||||
"textAnnotation",
|
||||
"association",
|
||||
"group",
|
||||
"dataInputAssociation",
|
||||
"dataOutputAssociation",
|
||||
"choreography",
|
||||
"choreographyTask",
|
||||
"callChoreography",
|
||||
"subChoreography",
|
||||
"conversation",
|
||||
"callConversation",
|
||||
"subConversation",
|
||||
"conversationLink",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -116,7 +138,7 @@ def bpmn_support_level(element_type: str) -> SupportLevel:
|
||||
return "interchange_only"
|
||||
|
||||
|
||||
def inspect_bpmn_xml(xml: str) -> BpmnInspection:
|
||||
def parse_bpmn_xml(xml: str) -> Element:
|
||||
encoded = xml.encode("utf-8")
|
||||
if not encoded:
|
||||
raise BpmnInspectionError("BPMN XML is empty")
|
||||
@@ -134,6 +156,15 @@ def inspect_bpmn_xml(xml: str) -> BpmnInspection:
|
||||
raise BpmnInspectionError(
|
||||
"BPMN document root must be bpmn:definitions in the BPMN 2.0 model namespace"
|
||||
)
|
||||
if sum(1 for _item in root.iter()) > MAX_BPMN_ELEMENTS:
|
||||
raise BpmnInspectionError(
|
||||
f"BPMN document exceeds the {MAX_BPMN_ELEMENTS}-element inspection limit"
|
||||
)
|
||||
return root
|
||||
|
||||
|
||||
def inspect_bpmn_xml(xml: str) -> BpmnInspection:
|
||||
root = parse_bpmn_xml(xml)
|
||||
|
||||
diagnostics: list[BpmnDiagnostic] = []
|
||||
elements: list[BpmnElementInventoryItem] = []
|
||||
@@ -149,10 +180,6 @@ def inspect_bpmn_xml(xml: str) -> BpmnInspection:
|
||||
while stack:
|
||||
element, parent_type, parent_id = stack.pop()
|
||||
visited += 1
|
||||
if visited > MAX_BPMN_ELEMENTS:
|
||||
raise BpmnInspectionError(
|
||||
f"BPMN document exceeds the {MAX_BPMN_ELEMENTS}-element inspection limit"
|
||||
)
|
||||
element_namespace, element_type = _qualified_name(element.tag)
|
||||
element_id = _bounded_attribute(element.attrib.get("id"), 255)
|
||||
if element_namespace == BPMN_MODEL_NAMESPACE:
|
||||
@@ -268,17 +295,24 @@ def _collect_references(
|
||||
attributes: dict[str, str],
|
||||
references: list[tuple[str, str | None, str]],
|
||||
) -> None:
|
||||
fields: tuple[str, ...]
|
||||
if element_type == "sequenceFlow":
|
||||
fields = ("sourceRef", "targetRef")
|
||||
elif element_type == "messageFlow":
|
||||
fields = ("sourceRef", "targetRef", "messageRef")
|
||||
elif element_type == "participant":
|
||||
fields = ("processRef",)
|
||||
elif element_type == "lane":
|
||||
fields = ("partitionElementRef",)
|
||||
else:
|
||||
fields = ()
|
||||
fields_by_element = {
|
||||
"sequenceFlow": ("sourceRef", "targetRef"),
|
||||
"messageFlow": ("sourceRef", "targetRef", "messageRef"),
|
||||
"association": ("sourceRef", "targetRef"),
|
||||
"participant": ("processRef",),
|
||||
"lane": ("partitionElementRef",),
|
||||
"boundaryEvent": ("attachedToRef",),
|
||||
"dataInputAssociation": ("sourceRef", "targetRef"),
|
||||
"dataOutputAssociation": ("sourceRef", "targetRef"),
|
||||
"dataObjectReference": ("dataObjectRef",),
|
||||
"dataStoreReference": ("dataStoreRef",),
|
||||
"messageEventDefinition": ("messageRef", "operationRef"),
|
||||
"signalEventDefinition": ("signalRef",),
|
||||
"errorEventDefinition": ("errorRef",),
|
||||
"escalationEventDefinition": ("escalationRef",),
|
||||
"compensateEventDefinition": ("activityRef",),
|
||||
}
|
||||
fields = fields_by_element.get(element_type, ())
|
||||
for field in fields:
|
||||
value = attributes.get(field)
|
||||
if value:
|
||||
@@ -306,4 +340,5 @@ __all__ = [
|
||||
"NATIVE_MAPPING_ELEMENTS",
|
||||
"bpmn_support_level",
|
||||
"inspect_bpmn_xml",
|
||||
"parse_bpmn_xml",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user