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

Convert execution.getVariable("a") to a valid FEEL expression #1092

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
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>
Loading