Skip to content

Commit

Permalink
fix: transform feel script in output and output (#1055)
Browse files Browse the repository at this point in the history
* fix: transform feel script in output and output

* fix: moved fix to actual element visitor

* fix: added meaningful tests
  • Loading branch information
jonathanlukas authored Dec 3, 2024
1 parent c4d2634 commit efb0601
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,17 @@ public static Message inputOutputParameter(
.build());
}

public static Message inputOutputParameterFeelScript(
String elementLocalName, String parameterName, String feelScript) {
return INSTANCE.composeMessage(
"input-output-parameter-feel-script",
ContextBuilder.builder()
.entry("parameterName", parameterName)
.context(elementTransformedPrefix(elementLocalName))
.entry("feelScript", feelScript)
.build());
}

public static Message inputOutputParameterExecution(
String elementLocalName, String parameterName, String juelExpression, String feelExpression) {
return INSTANCE.composeMessage(
Expand Down Expand Up @@ -505,6 +516,10 @@ public static Message inputOutput() {
return INSTANCE.emptyMessage();
}

public static Message inputOutputScript() {
return INSTANCE.emptyMessage();
}

public static Message camundaScript(String script, String scriptFormat, String parentElement) {
return INSTANCE.composeMessage(
"camunda-script",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.camunda.community.migration.converter.visitor.impl.element;

import static org.camunda.community.migration.converter.NamespaceUri.*;

import org.camunda.bpm.model.xml.instance.DomElement;
import org.camunda.community.migration.converter.DomElementVisitorContext;
import org.camunda.community.migration.converter.convertible.AbstractDataMapperConvertible;
Expand All @@ -24,6 +26,10 @@ protected Message visitCamundaElement(DomElementVisitorContext context) {
DomElement element = context.getElement();
String name = element.getAttribute("name");
MappingDirection direction = findMappingDirection(element);
if (isScript(element)) {
// Scripts are handled in ScriptVisitor
return MessageFactory.inputOutputScript();
}
if (isNotStringOrExpression(element)) {
return MessageFactory.inputOutputParameterIsNoExpression(localName(), name);
}
Expand Down Expand Up @@ -61,6 +67,11 @@ protected Message visitCamundaElement(DomElementVisitorContext context) {
return resultMessage;
}

private boolean isScript(DomElement element) {
return element.getChildElements().stream()
.anyMatch(e -> e.getNamespaceURI().equals(CAMUNDA) && e.getLocalName().equals("script"));
}

private MappingDirection findMappingDirection(DomElement element) {
if (isInputParameter(element.getLocalName())) {
return MappingDirection.INPUT;
Expand All @@ -72,7 +83,7 @@ private MappingDirection findMappingDirection(DomElement element) {
}

private boolean isNotStringOrExpression(DomElement element) {
return element.getChildElements().size() > 0;
return !element.getChildElements().isEmpty();
}

private boolean isInputParameter(String localName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,84 @@
package org.camunda.community.migration.converter.visitor.impl.element;

import static org.camunda.community.migration.converter.NamespaceUri.*;

import java.util.List;
import org.camunda.bpm.model.xml.instance.DomElement;
import org.camunda.community.migration.converter.DomElementVisitorContext;
import org.camunda.community.migration.converter.convertible.AbstractDataMapperConvertible;
import org.camunda.community.migration.converter.convertible.AbstractDataMapperConvertible.MappingDirection;
import org.camunda.community.migration.converter.message.Message;
import org.camunda.community.migration.converter.message.MessageFactory;
import org.camunda.community.migration.converter.visitor.AbstractCamundaElementVisitor;

public class ScriptVisitor extends AbstractCamundaElementVisitor {
private static final String LOCAL_NAME_INPUT_PARAMETER = "inputParameter";
private static final String LOCAL_NAME_OUTPUT_PARAMETER = "outputParameter";

@Override
public String localName() {
return "script";
}

@Override
protected Message visitCamundaElement(DomElementVisitorContext context) {
DomElement script = context.getElement();
if (isInputOrOutput(script)) {
MappingDirection mappingDirection = findMappingDirection(script);
String targetName = findTargetName(script);
if (!isResource(script) && isFeelScript(script)) {
String feelScript = extractFeelScript(script);
context.addConversion(
AbstractDataMapperConvertible.class,
abstractTaskConversion ->
abstractTaskConversion.addZeebeIoMapping(mappingDirection, feelScript, targetName));
return MessageFactory.inputOutputParameterFeelScript(localName(), targetName, feelScript);
}
}
String scriptFormat = context.getElement().getAttribute("scriptFormat");
String script = detectScript(context);
String scriptContent = detectScript(context);
return MessageFactory.camundaScript(
script, scriptFormat, context.getElement().getParentElement().getLocalName());
scriptContent, scriptFormat, context.getElement().getParentElement().getLocalName());
}

private boolean isResource(DomElement script) {
return script.hasAttribute("resource");
}

private boolean isFeelScript(DomElement script) {
String scriptFormat = script.getAttribute("scriptFormat");
return "feel".equalsIgnoreCase(scriptFormat);
}

private boolean isInputOrOutput(DomElement element) {
if (element.getParentElement().getNamespaceURI().equals(CAMUNDA)) {
return List.of(LOCAL_NAME_INPUT_PARAMETER, LOCAL_NAME_OUTPUT_PARAMETER)
.contains(element.getParentElement().getLocalName());
}
return false;
}

private MappingDirection findMappingDirection(DomElement element) {
if (element.getParentElement().getLocalName().equals(LOCAL_NAME_INPUT_PARAMETER)) {
return MappingDirection.INPUT;
} else if (element.getParentElement().getLocalName().equals(LOCAL_NAME_OUTPUT_PARAMETER)) {
return MappingDirection.OUTPUT;
} else {
throw new IllegalStateException(
String.format(
"Unknown parent for input/output mapping: %s",
element.getParentElement().getLocalName()));
}
}

private String findTargetName(DomElement element) {
return element.getParentElement().getAttribute(CAMUNDA, "name");
}

private String detectScript(DomElementVisitorContext context) {
String resource = context.getElement().getAttribute("resource");
if (resource == null) {
return context.getElement().getTextContent();

} else {
return resource;
}
Expand All @@ -33,4 +88,8 @@ private String detectScript(DomElementVisitorContext context) {
public boolean canBeTransformed(DomElementVisitorContext context) {
return false;
}

private String extractFeelScript(DomElement script) {
return "=" + script.getTextContent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ called-element-ref-binding.severity=INFO
#
# Camunda Element
#
input-output-parameter-is-no-expression.message={{ templates.element-not-transformable-prefix }} Parameter '{{ parameterName }}': Only String or Expression is supported as Input/Output.
input-output-parameter-feel-script.message={{ templates.element-transformed-prefix }} Parameter '{{ parameterName }}': '{{ feelScript }}' has been mapped.
input-output-parameter-feel-script.severity=INFO
#
input-output-parameter-is-no-expression.message={{ templates.element-not-transformable-prefix }} Parameter '{{ parameterName }}': Only String, Expression or inline FEEL script is supported as Input/Output.
input-output-parameter-is-no-expression.severity=WARNING
#
input-output-parameter.message={{ templates.element-transformed-prefix }} Parameter '{{ parameterName }}': {{ templates.expression-transformation-result }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
Expand Down Expand Up @@ -47,6 +48,8 @@ public class BpmnConverterTest {
"form-ref-deployment.bpmn",
"decision-ref-version.bpmn",
"decision-ref-deployment.bpmn",
"feel_expr_not_tranformed.bpmn",
"decision-ref-deployment.bpmn",
"delegate.bpmn",
"decision-ref-deployment.bpmn",
"delegate-expression-listener.bpmn"
Expand Down Expand Up @@ -796,6 +799,44 @@ void testCalledElementRefDeploymentBindingConversion() {
.isEqualTo("deployment");
}

@Test
void testFeelScriptInputShouldBeTransformed() {
BpmnModelInstance modelInstance = loadAndConvert("feel_expr_not_tranformed.bpmn");
DomElement serviceTask = modelInstance.getDocument().getElementById("Activity_1s02kf9");
assertThat(serviceTask).isNotNull();
DomElement extensionElements =
serviceTask.getChildElementsByNameNs(BPMN, "extensionElements").get(0);
assertThat(extensionElements).isNotNull();
DomElement ioMapping = extensionElements.getChildElementsByNameNs(ZEEBE, "ioMapping").get(0);
assertThat(ioMapping).isNotNull();
DomElement input =
ioMapping.getChildElementsByNameNs(ZEEBE, "input").stream()
.filter(e -> e.getAttribute(ZEEBE, "target").equals("HinweisText"))
.findFirst()
.get();
assertThat(input).isNotNull();
assertThat(input.getAttribute(ZEEBE, "target")).isEqualTo("HinweisText");
assertThat(input.getAttribute(ZEEBE, "source"))
.isEqualTo("=\"Vorgang automatisiert durchgeführt und abgeschlossen\"");
}

@Test
void testNonFeelScriptInputShouldNotBeTransformed() {
BpmnModelInstance modelInstance = loadAndConvert("feel_expr_not_tranformed.bpmn");
DomElement serviceTask = modelInstance.getDocument().getElementById("Activity_1s02kf9");
assertThat(serviceTask).isNotNull();
DomElement extensionElements =
serviceTask.getChildElementsByNameNs(BPMN, "extensionElements").get(0);
assertThat(extensionElements).isNotNull();
DomElement ioMapping = extensionElements.getChildElementsByNameNs(ZEEBE, "ioMapping").get(0);
assertThat(ioMapping).isNotNull();
Optional<DomElement> input =
ioMapping.getChildElementsByNameNs(ZEEBE, "input").stream()
.filter(e -> e.getAttribute(ZEEBE, "target").equals("anotherReference"))
.findFirst();
assertThat(input).isEmpty();
}

@Test
void testDefaultResultVariable() {
BpmnModelInstance modelInstance = loadAndConvert("default-result-variable.bpmn");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,4 +607,18 @@ void shouldBuildDelegateExpressionAsJobTypeNull() {
"Delegate expression %s could not be transformed to job type. Please define manually.",
jobType);
}

@Test
void shouldBuildInputOutputParameterFeelScript() {
String elementLocalName = random();
String parameterName = random();
String feelScript = random();
Message message =
MessageFactory.inputOutputParameterFeelScript(elementLocalName, parameterName, feelScript);
assertNotNull(message);
assertThat(message.getMessage())
.isEqualTo(
"Element '%s' was transformed. Parameter '%s': '%s' has been mapped.",
elementLocalName, parameterName, feelScript);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1pyi0y7" 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="Process_0mwymxe" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_1wn0wn6</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_1wn0wn6" sourceRef="StartEvent_1" targetRef="Activity_1s02kf9" />
<bpmn:serviceTask id="Activity_1s02kf9" name="Service Task">
<bpmn:extensionElements>
<camunda:inputOutput>
<camunda:inputParameter name="HinweisText">
<camunda:script scriptFormat="feel">"Vorgang automatisiert durchgeführt und abgeschlossen"</camunda:script>
</camunda:inputParameter>
<camunda:inputParameter name="anotherReference">
<camunda:script scriptFormat="feel" resource="external.feel" />
</camunda:inputParameter>
</camunda:inputOutput>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1wn0wn6</bpmn:incoming>
<bpmn:outgoing>Flow_171de2x</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:endEvent id="Event_16xtit0">
<bpmn:incoming>Flow_171de2x</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_171de2x" sourceRef="Activity_1s02kf9" targetRef="Event_16xtit0" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0mwymxe">
<bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="StartEvent_1">
<dc:Bounds x="182" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0phr2zr_di" bpmnElement="Activity_1s02kf9">
<dc:Bounds x="270" y="80" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_16xtit0_di" bpmnElement="Event_16xtit0">
<dc:Bounds x="422" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1wn0wn6_di" bpmnElement="Flow_1wn0wn6">
<di:waypoint x="218" y="120" />
<di:waypoint x="270" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_171de2x_di" bpmnElement="Flow_171de2x">
<di:waypoint x="370" y="120" />
<di:waypoint x="422" y="120" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

0 comments on commit efb0601

Please sign in to comment.