diff --git a/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/message/MessageFactory.java b/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/message/MessageFactory.java index e2ab02a4..cb769f75 100644 --- a/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/message/MessageFactory.java +++ b/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/message/MessageFactory.java @@ -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( @@ -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", diff --git a/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/InputOutputParameterVisitor.java b/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/InputOutputParameterVisitor.java index b5be6398..d751e188 100644 --- a/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/InputOutputParameterVisitor.java +++ b/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/InputOutputParameterVisitor.java @@ -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; @@ -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); } @@ -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; @@ -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) { diff --git a/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/ScriptVisitor.java b/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/ScriptVisitor.java index a2626437..df60c89b 100644 --- a/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/ScriptVisitor.java +++ b/backend-diagram-converter/core/src/main/java/org/camunda/community/migration/converter/visitor/impl/element/ScriptVisitor.java @@ -1,11 +1,20 @@ 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"; @@ -13,17 +22,63 @@ public String localName() { @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; } @@ -33,4 +88,8 @@ private String detectScript(DomElementVisitorContext context) { public boolean canBeTransformed(DomElementVisitorContext context) { return false; } + + private String extractFeelScript(DomElement script) { + return "=" + script.getTextContent(); + } } diff --git a/backend-diagram-converter/core/src/main/resources/message-templates.properties b/backend-diagram-converter/core/src/main/resources/message-templates.properties index d2844345..fc46a0f5 100644 --- a/backend-diagram-converter/core/src/main/resources/message-templates.properties +++ b/backend-diagram-converter/core/src/main/resources/message-templates.properties @@ -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 }} diff --git a/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/BpmnConverterTest.java b/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/BpmnConverterTest.java index 32d10a86..9be140b9 100644 --- a/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/BpmnConverterTest.java +++ b/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/BpmnConverterTest.java @@ -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; @@ -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" @@ -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 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"); diff --git a/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/message/MessageFactoryTest.java b/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/message/MessageFactoryTest.java index fe1af82a..bbe2c793 100644 --- a/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/message/MessageFactoryTest.java +++ b/backend-diagram-converter/core/src/test/java/org/camunda/community/migration/converter/message/MessageFactoryTest.java @@ -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); + } } diff --git a/backend-diagram-converter/core/src/test/resources/feel_expr_not_tranformed.bpmn b/backend-diagram-converter/core/src/test/resources/feel_expr_not_tranformed.bpmn new file mode 100644 index 00000000..a4d2f5ba --- /dev/null +++ b/backend-diagram-converter/core/src/test/resources/feel_expr_not_tranformed.bpmn @@ -0,0 +1,48 @@ + + + + + Flow_1wn0wn6 + + + + + + + "Vorgang automatisiert durchgeführt und abgeschlossen" + + + + + + + Flow_1wn0wn6 + Flow_171de2x + + + Flow_171de2x + + + + + + + + + + + + + + + + + + + + + + + + +