Skip to content

Commit 7a1ec7e

Browse files
committed
feat: resolve types for path expressions
1 parent c9e9626 commit 7a1ec7e

File tree

3 files changed

+130
-7
lines changed

3 files changed

+130
-7
lines changed

lib/zeebe/util/feelUtility.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,12 @@ function getScriptExpression(variable, origin) {
278278
function findUnresolvedVariables(node) {
279279
const results = [];
280280

281+
if (node.name === 'PathExpression') {
282+
const [ object ] = node.children;
283+
results.push(...findUnresolvedVariables(object));
284+
return results;
285+
}
286+
281287
results.push(...(node.children.flatMap(findUnresolvedVariables)));
282288

283289
if (node.name === 'VariableName' && !node.value) {

test/fixtures/zeebe/type-resolution.bpmn

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?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:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_varResolution" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.44.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.9.0">
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:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_varResolution" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.42.0" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.9.0">
33
<bpmn:process id="Process_varResolution" isExecutable="true">
4+
<!-- Literal type outputs -->
45
<bpmn:serviceTask id="literalNullTask" name="Literal Null">
56
<bpmn:extensionElements>
67
<zeebe:ioMapping>
@@ -12,7 +13,7 @@
1213
<bpmn:serviceTask id="literalStringTask" name="Literal String">
1314
<bpmn:extensionElements>
1415
<zeebe:ioMapping>
15-
<zeebe:output source="=&#34;hello&#34;" target="outString" />
16+
<zeebe:output source="=&quot;hello&quot;" target="outString" />
1617
</zeebe:ioMapping>
1718
</bpmn:extensionElements>
1819
<bpmn:incoming>Flow_1</bpmn:incoming>
@@ -39,7 +40,7 @@
3940
<bpmn:serviceTask id="literalContextTask" name="Literal Context">
4041
<bpmn:extensionElements>
4142
<zeebe:ioMapping>
42-
<zeebe:output source="={&#34;key&#34;: &#34;value&#34;}" target="outContext" />
43+
<zeebe:output source="={&quot;key&quot;: &quot;value&quot;}" target="outContext" />
4344
</zeebe:ioMapping>
4445
</bpmn:extensionElements>
4546
<bpmn:incoming>Flow_4</bpmn:incoming>
@@ -55,13 +56,36 @@
5556
<bpmn:outgoing>Flow_6</bpmn:outgoing>
5657
<bpmn:outgoing>Flow_1u936n5</bpmn:outgoing>
5758
</bpmn:serviceTask>
58-
<bpmn:serviceTask id="passthroughProducerTask" name="Passthrough Producer">
59+
<!-- Path expression resolution -->
60+
<bpmn:serviceTask id="contextProducerTask" name="Context Producer">
5961
<bpmn:extensionElements>
6062
<zeebe:ioMapping>
61-
<zeebe:output source="=&#34;world&#34;" target="strVar" />
63+
<zeebe:output source="={&quot;name&quot;: &quot;alice&quot;, &quot;count&quot;: 42, &quot;flag&quot;: true, &quot;nested&quot;: {&quot;deep&quot;: null}}" target="ctxVar" />
6264
</zeebe:ioMapping>
6365
</bpmn:extensionElements>
6466
<bpmn:incoming>Flow_6</bpmn:incoming>
67+
<bpmn:outgoing>Flow_7</bpmn:outgoing>
68+
</bpmn:serviceTask>
69+
<bpmn:serviceTask id="pathConsumerTask" name="Path Consumer">
70+
<bpmn:extensionElements>
71+
<zeebe:ioMapping>
72+
<zeebe:input source="=ctxVar.name" target="pathString" />
73+
<zeebe:input source="=ctxVar.count" target="pathNumber" />
74+
<zeebe:input source="=ctxVar.flag" target="pathBoolean" />
75+
<zeebe:input source="=ctxVar.nested.deep" target="pathDeepNull" />
76+
</zeebe:ioMapping>
77+
</bpmn:extensionElements>
78+
<bpmn:incoming>Flow_7</bpmn:incoming>
79+
<bpmn:outgoing>Flow_8</bpmn:outgoing>
80+
</bpmn:serviceTask>
81+
<!-- Variable passthrough -->
82+
<bpmn:serviceTask id="passthroughProducerTask" name="Passthrough Producer">
83+
<bpmn:extensionElements>
84+
<zeebe:ioMapping>
85+
<zeebe:output source="=&quot;world&quot;" target="strVar" />
86+
</zeebe:ioMapping>
87+
</bpmn:extensionElements>
88+
<bpmn:incoming>Flow_8</bpmn:incoming>
6589
<bpmn:outgoing>Flow_9</bpmn:outgoing>
6690
</bpmn:serviceTask>
6791
<bpmn:serviceTask id="passthroughConsumerTask" name="Passthrough Consumer">
@@ -73,6 +97,7 @@
7397
<bpmn:incoming>Flow_9</bpmn:incoming>
7498
<bpmn:outgoing>Flow_10</bpmn:outgoing>
7599
</bpmn:serviceTask>
100+
<!-- Unresolved variable -->
76101
<bpmn:serviceTask id="unresolvedConsumerTask" name="Unresolved Consumer">
77102
<bpmn:extensionElements>
78103
<zeebe:ioMapping>
@@ -81,12 +106,15 @@
81106
</bpmn:extensionElements>
82107
<bpmn:incoming>Flow_10</bpmn:incoming>
83108
</bpmn:serviceTask>
109+
<!-- Sequence Flows -->
84110
<bpmn:sequenceFlow id="Flow_1" sourceRef="literalNullTask" targetRef="literalStringTask" />
85111
<bpmn:sequenceFlow id="Flow_2" sourceRef="literalStringTask" targetRef="literalNumberTask" />
86112
<bpmn:sequenceFlow id="Flow_3" sourceRef="literalNumberTask" targetRef="literalBooleanTask" />
87113
<bpmn:sequenceFlow id="Flow_4" sourceRef="literalBooleanTask" targetRef="literalContextTask" />
88114
<bpmn:sequenceFlow id="Flow_5" sourceRef="literalContextTask" targetRef="emptySourceTask" />
89-
<bpmn:sequenceFlow id="Flow_6" sourceRef="emptySourceTask" targetRef="passthroughProducerTask" />
115+
<bpmn:sequenceFlow id="Flow_6" sourceRef="emptySourceTask" targetRef="contextProducerTask" />
116+
<bpmn:sequenceFlow id="Flow_7" sourceRef="contextProducerTask" targetRef="pathConsumerTask" />
117+
<bpmn:sequenceFlow id="Flow_8" sourceRef="pathConsumerTask" targetRef="passthroughProducerTask" />
90118
<bpmn:sequenceFlow id="Flow_9" sourceRef="passthroughProducerTask" targetRef="passthroughConsumerTask" />
91119
<bpmn:sequenceFlow id="Flow_10" sourceRef="passthroughConsumerTask" targetRef="unresolvedConsumerTask" />
92120
<bpmn:serviceTask id="useUnknownVariable" name="Use unknown input">
@@ -101,6 +129,7 @@
101129
</bpmn:process>
102130
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
103131
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_varResolution">
132+
<!-- Literal type outputs -->
104133
<bpmndi:BPMNShape id="literalNullTask_di" bpmnElement="literalNullTask">
105134
<dc:Bounds x="160" y="100" width="100" height="80" />
106135
</bpmndi:BPMNShape>
@@ -119,12 +148,21 @@
119148
<bpmndi:BPMNShape id="emptySourceTask_di" bpmnElement="emptySourceTask">
120149
<dc:Bounds x="560" y="250" width="100" height="80" />
121150
</bpmndi:BPMNShape>
151+
<!-- Path expression resolution -->
152+
<bpmndi:BPMNShape id="contextProducerTask_di" bpmnElement="contextProducerTask">
153+
<dc:Bounds x="160" y="400" width="100" height="80" />
154+
</bpmndi:BPMNShape>
155+
<bpmndi:BPMNShape id="pathConsumerTask_di" bpmnElement="pathConsumerTask">
156+
<dc:Bounds x="360" y="400" width="100" height="80" />
157+
</bpmndi:BPMNShape>
158+
<!-- Variable passthrough -->
122159
<bpmndi:BPMNShape id="passthroughProducerTask_di" bpmnElement="passthroughProducerTask">
123160
<dc:Bounds x="560" y="400" width="100" height="80" />
124161
</bpmndi:BPMNShape>
125162
<bpmndi:BPMNShape id="passthroughConsumerTask_di" bpmnElement="passthroughConsumerTask">
126163
<dc:Bounds x="160" y="550" width="100" height="80" />
127164
</bpmndi:BPMNShape>
165+
<!-- Unresolved variable -->
128166
<bpmndi:BPMNShape id="unresolvedConsumerTask_di" bpmnElement="unresolvedConsumerTask">
129167
<dc:Bounds x="360" y="550" width="100" height="80" />
130168
</bpmndi:BPMNShape>
@@ -158,7 +196,14 @@
158196
<di:waypoint x="610" y="330" />
159197
<di:waypoint x="610" y="370" />
160198
<di:waypoint x="210" y="370" />
161-
<di:waypoint x="210" y="440" />
199+
<di:waypoint x="210" y="400" />
200+
</bpmndi:BPMNEdge>
201+
<bpmndi:BPMNEdge id="Flow_7_di" bpmnElement="Flow_7">
202+
<di:waypoint x="260" y="440" />
203+
<di:waypoint x="360" y="440" />
204+
</bpmndi:BPMNEdge>
205+
<bpmndi:BPMNEdge id="Flow_8_di" bpmnElement="Flow_8">
206+
<di:waypoint x="460" y="440" />
162207
<di:waypoint x="560" y="440" />
163208
</bpmndi:BPMNEdge>
164209
<bpmndi:BPMNEdge id="Flow_9_di" bpmnElement="Flow_9">

test/spec/zeebe/ZeebeVariableResolver.spec.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,78 @@ describe('ZeebeVariableResolver', function() {
18501850
});
18511851

18521852

1853+
describe('path expression resolution', function() {
1854+
1855+
it('should resolve path to string property', inject(async function(elementRegistry, variableResolver) {
1856+
1857+
// given
1858+
const task = elementRegistry.get('pathConsumerTask');
1859+
1860+
// when
1861+
const variables = await variableResolver.getVariablesForElement(task);
1862+
1863+
// then
1864+
expect(variables).to.variableInclude({
1865+
name: 'pathString',
1866+
type: 'String',
1867+
scope: 'pathConsumerTask'
1868+
});
1869+
}));
1870+
1871+
1872+
it('should resolve path to number property', inject(async function(elementRegistry, variableResolver) {
1873+
1874+
// given
1875+
const task = elementRegistry.get('pathConsumerTask');
1876+
1877+
// when
1878+
const variables = await variableResolver.getVariablesForElement(task);
1879+
1880+
// then
1881+
expect(variables).to.variableInclude({
1882+
name: 'pathNumber',
1883+
type: 'Number',
1884+
scope: 'pathConsumerTask'
1885+
});
1886+
}));
1887+
1888+
1889+
it('should resolve path to boolean property', inject(async function(elementRegistry, variableResolver) {
1890+
1891+
// given
1892+
const task = elementRegistry.get('pathConsumerTask');
1893+
1894+
// when
1895+
const variables = await variableResolver.getVariablesForElement(task);
1896+
1897+
// then
1898+
expect(variables).to.variableInclude({
1899+
name: 'pathBoolean',
1900+
type: 'Boolean',
1901+
scope: 'pathConsumerTask'
1902+
});
1903+
}));
1904+
1905+
1906+
it('should resolve deep path to null property', inject(async function(elementRegistry, variableResolver) {
1907+
1908+
// given
1909+
const task = elementRegistry.get('pathConsumerTask');
1910+
1911+
// when
1912+
const variables = await variableResolver.getVariablesForElement(task);
1913+
1914+
// then
1915+
expect(variables).to.variableInclude({
1916+
name: 'pathDeepNull',
1917+
type: '',
1918+
scope: 'pathConsumerTask'
1919+
});
1920+
}));
1921+
1922+
});
1923+
1924+
18531925
describe('variable passthrough', function() {
18541926

18551927
it('should resolve passthrough variable to source type', inject(async function(elementRegistry, variableResolver) {

0 commit comments

Comments
 (0)