Skip to content

Commit

Permalink
Convert execution.getVariable("a") to a valid FEEL expression (#1092)
Browse files Browse the repository at this point in the history
  • Loading branch information
ingorichtsmeier authored Jan 10, 2025
1 parent 2d8ded0 commit c504854
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExpressionTransformationResult {

private static final Logger LOG = LoggerFactory.getLogger(ExpressionTransformationResult.class);

private static final Pattern methodInvocationPattern = Pattern.compile("\\.[\\w]*\\(.*\\)");
private static final Pattern executionPattern = Pattern.compile("execution\\.");
private static final Pattern executionGetVariablePattern =
Pattern.compile("execution.getVariable");
private final String juelExpression;
private final String feelExpression;

Expand All @@ -23,12 +30,29 @@ public String getFeelExpression() {
}

public Boolean hasMethodInvocation() {
if (hasExecutionGetVariable()) {
return false;
}
Matcher m = methodInvocationPattern.matcher(juelExpression);
return m.find();
boolean methodMatch = m.find();
LOG.debug("{} contains method invocation: {}", juelExpression, methodMatch);
return methodMatch;
}

public Boolean hasExecution() {
public Boolean hasExecutionOnly() {
if (hasExecutionGetVariable()) {
return false;
}
Matcher m = executionPattern.matcher(juelExpression);
return m.find();
boolean executionOnlyMatch = m.find();
LOG.debug("{} contains execution only: {}", juelExpression, executionOnlyMatch);
return executionOnlyMatch;
}

public Boolean hasExecutionGetVariable() {
Matcher m = executionGetVariablePattern.matcher(juelExpression);
boolean executionGetVariableMatch = m.find();
LOG.debug("{} contains execution.getVariable: {}", juelExpression, executionGetVariableMatch);
return executionGetVariableMatch;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ private String handleExpression(String expression) {
.replaceAll("!\\(([^\\(\\)]*)\\)", "not($1)")
.replaceAll(" && ", " and ")
.replaceAll(" \\|\\| ", " or ")
.replaceAll("'", "\"");
.replaceAll("'", "\"")
.replaceAll("execution\\.getVariable\\(\"(.*)\"\\)", "$1");
// increment all indexes
Pattern pattern = Pattern.compile("\\[(\\d*)\\]");
Matcher m = pattern.matcher(replaced);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected void visitBpmnElement(DomElementVisitorContext context) {
.getBpmnMultiInstanceLoopCharacteristics()
.setCompletionCondition(transformationResult.getFeelExpression()));
Message message;
if (transformationResult.hasExecution()) {
if (transformationResult.hasExecutionOnly()) {
message =
MessageFactory.completionConditionExecution(
transformationResult.getJuelExpression(), transformationResult.getFeelExpression());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private void handleJuelExpression(DomElementVisitorContext context) {
context.addConversion(
SequenceFlowConvertible.class,
conversion -> conversion.setConditionExpression(transformationResult.getFeelExpression()));
if (transformationResult.hasExecution()) {
if (transformationResult.hasExecutionOnly()) {
context.addMessage(
MessageFactory.conditionExpressionExecution(
transformationResult.getJuelExpression(), transformationResult.getFeelExpression()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protected Message visitSupportedAttribute(DomElementVisitorContext context, Stri
.setInputCollection(transformationResult.getFeelExpression()));
context.addMessage(MessageFactory.collectionHint());
Message message;
if (transformationResult.hasExecution()) {
if (transformationResult.hasExecutionOnly()) {
message =
MessageFactory.collectionExecution(
attributeLocalName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected Message visitCamundaElement(DomElementVisitorContext context) {
abstractTaskConversion.addZeebeIoMapping(
direction, transformationResult.getFeelExpression(), name));
Message resultMessage;
if (transformationResult.hasExecution()) {
if (transformationResult.hasExecutionOnly()) {
resultMessage =
MessageFactory.inputOutputParameterExecution(
localName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,21 @@ void testInternalScript_8_1() {
.isEqualTo("Script was set to header 'script'. Please review.");
}

@Test
void testExcutionGetVariable() {
BpmnDiagramCheckResult result = loadAndCheck("expression-get-variable.bpmn");
List<BpmnElementCheckMessage> equalsYesMessage =
result.getResult("GetVariableEqualsYesFlow").getMessages();
assertThat(equalsYesMessage).hasSize(1);
assertThat(equalsYesMessage.get(0).getSeverity()).isEqualTo(Severity.REVIEW);
assertThat(equalsYesMessage.get(0).getMessage()).contains("-> '=exampleVar = \"yes\"");
List<BpmnElementCheckMessage> notEqualsYesMessage =
result.getResult("GetVariableNotEqualsYesFlow").getMessages();
assertThat(notEqualsYesMessage).hasSize(1);
assertThat(notEqualsYesMessage.get(0).getSeverity()).isEqualTo(Severity.REVIEW);
assertThat(notEqualsYesMessage.get(0).getMessage()).contains("-> '=exampleVar != \"yes\"");
}

@Test
void testExpressionWithMethodInvocation() {
BpmnDiagramCheckResult result = loadAndCheck("expression-method-invocation.bpmn");
Expand All @@ -338,9 +353,9 @@ void testExpressionWithMethodInvocation() {
List<BpmnElementCheckMessage> executionIsUsedMessage =
result.getResult("ExecutionIsUsedSequenceFlow").getMessages();
assertThat(executionIsUsedMessage).hasSize(1);
assertThat(executionIsUsedMessage.get(0).getSeverity()).isEqualTo(Severity.TASK);
assertThat(executionIsUsedMessage.get(0).getSeverity()).isEqualTo(Severity.REVIEW);
assertThat(executionIsUsedMessage.get(0).getMessage())
.contains("'execution' is not available in FEEL");
.contains("-> '=input != null and input > 5");

List<BpmnElementCheckMessage> methodInvocationIsUsedMessage =
result.getResult("MethodInvocationIsUsedSequenceFlow").getMessages();
Expand Down Expand Up @@ -401,13 +416,12 @@ void testMultiInstanceConfigurationWithExecution() {
assertThat(messages.get(0).getSeverity()).isEqualTo(Severity.TASK);
assertThat(messages.get(0).getMessage()).contains("Collecting results");

assertThat(messages.get(1).getSeverity()).isEqualTo(Severity.TASK);
assertThat(messages.get(1).getMessage())
.contains(Arrays.asList("collection", "'execution' is not available in FEEL"));
assertThat(messages.get(1).getSeverity()).isEqualTo(Severity.REVIEW);
assertThat(messages.get(1).getMessage()).contains(Arrays.asList("collection", "-> '=myList'"));

assertThat(messages.get(2).getSeverity()).isEqualTo(Severity.TASK);
assertThat(messages.get(2).getSeverity()).isEqualTo(Severity.REVIEW);
assertThat(messages.get(2).getMessage())
.contains(Arrays.asList("Completion condition", "'execution' is not available in FEEL"));
.contains(Arrays.asList("Completion condition", "-> '=complete = true'"));

assertThat(messages.get(3).getSeverity()).isEqualTo(Severity.INFO);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.camunda.community.migration.converter.expression.ExpressionTransformer;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;

public class ExpressionTransformerTest {
Expand Down Expand Up @@ -57,7 +58,8 @@ public Stream<DynamicContainer> shouldResolveExpression() {
expression("${empty donut || coffee}").isMappedTo("=donut=null or coffee"),
expression("${not empty donut || coffee}").isMappedTo("=not(donut=null) or coffee"),
expression("${not(empty donut || coffee)}").isMappedTo("=not(donut=null or coffee)"),
expression("${execution.getVariable(\"a\")}").hasUsedExecution(true),
expression("${execution.getVariable(\"a\")}").isMappedTo("=a"),
expression("${execution.getProcessInstanceId()}").hasUsedExecution(true),
expression("${myexecutionContext.isSpecial()}").hasUsedExecution(false),
expression("${var.getSomething()}").hasMethodInvocation(true),
expression("${!dauerbuchungVoat21Ids.isEmpty()}").hasMethodInvocation(true),
Expand Down Expand Up @@ -108,8 +110,19 @@ public ExpressionTestBuilder hasUsedExecution(boolean expected) {
tests.add(
DynamicTest.dynamicTest(
String.format("Expect %s execution used", expected ? "a" : "no"),
() -> assertThat(result.hasExecution()).isEqualTo(expected)));
() -> assertThat(result.hasExecutionOnly()).isEqualTo(expected)));
return this;
}
}

@Test
public void testExpressions() {
ExpressionTransformationResult underTest =
new ExpressionTransformationResult("execution.", null);
assertThat(underTest.hasExecutionOnly()).isEqualTo(true);
underTest = new ExpressionTransformationResult("execution.getVariable", null);
assertThat(underTest.hasExecutionOnly()).isEqualTo(false);
underTest = new ExpressionTransformationResult("execution.getProcessInstanceId()", null);
assertThat(underTest.hasExecutionOnly()).isEqualTo(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?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:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1ortpt7" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.29.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.22.0">
<bpmn:process id="GetVariableExpressionProcess" name="GetVariable expression process" isExecutable="true" camunda:historyTimeToLive="30">
<bpmn:startEvent id="StartEvent_1" name="Expression getVariable should be tested">
<bpmn:outgoing>Flow_1c8n89f</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:exclusiveGateway id="Gateway_1flm2ff" name="exampleVar is not null and equal to &#34;yes&#34;?">
<bpmn:incoming>Flow_1c8n89f</bpmn:incoming>
<bpmn:outgoing>GetVariableEqualsYesFlow</bpmn:outgoing>
<bpmn:outgoing>GetVariableNotEqualsYesFlow</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1c8n89f" sourceRef="StartEvent_1" targetRef="Gateway_1flm2ff" />
<bpmn:endEvent id="Event_14xfi06" name="Expression getVariable evaluated">
<bpmn:incoming>GetVariableEqualsYesFlow</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="GetVariableEqualsYesFlow" name="yes" sourceRef="Gateway_1flm2ff" targetRef="Event_14xfi06">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${execution.getVariable("exampleVar") == "yes"}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:endEvent id="Event_0t9rfk6" name="Expression getVariable returned">
<bpmn:incoming>GetVariableNotEqualsYesFlow</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="GetVariableNotEqualsYesFlow" name="no" sourceRef="Gateway_1flm2ff" targetRef="Event_0t9rfk6">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${execution.getVariable("exampleVar") != "yes"}</bpmn:conditionExpression>
</bpmn:sequenceFlow>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="GetVariableExpressionProcess">
<bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
<dc:Bounds x="182" y="162" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="160" y="205" width="81" height="40" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_1flm2ff_di" bpmnElement="Gateway_1flm2ff" isMarkerVisible="true">
<dc:Bounds x="275" y="155" width="50" height="50" />
<bpmndi:BPMNLabel>
<dc:Bounds x="256" y="105" width="88" height="40" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_14xfi06_di" bpmnElement="Event_14xfi06">
<dc:Bounds x="382" y="162" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="373" y="205" width="55" height="40" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0t9rfk6_di" bpmnElement="Event_0t9rfk6">
<dc:Bounds x="382" y="272" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="373" y="315" width="55" height="40" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1c8n89f_di" bpmnElement="Flow_1c8n89f">
<di:waypoint x="218" y="180" />
<di:waypoint x="275" y="180" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1pahf39_di" bpmnElement="GetVariableEqualsYesFlow">
<di:waypoint x="325" y="180" />
<di:waypoint x="382" y="180" />
<bpmndi:BPMNLabel>
<dc:Bounds x="345" y="162" width="18" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1t756j0_di" bpmnElement="GetVariableNotEqualsYesFlow">
<di:waypoint x="300" y="205" />
<di:waypoint x="300" y="290" />
<di:waypoint x="382" y="290" />
<bpmndi:BPMNLabel>
<dc:Bounds x="334" y="273" width="13" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

0 comments on commit c504854

Please sign in to comment.