This is an automated email from the ASF dual-hosted git repository.
yamer pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git
The following commit(s) were added to refs/heads/main by this push:
new 842a2ca88f [incubator-kie-issue#2052] Decision Services doesn't work
with unnamed imported Elements (#6425)
842a2ca88f is described below
commit 842a2ca88fb88cd9c3dcb79dfafc5cef8a9c44e5
Author: Yeser Amer <[email protected]>
AuthorDate: Mon Aug 18 22:15:41 2025 +0200
[incubator-kie-issue#2052] Decision Services doesn't work with unnamed
imported Elements (#6425)
* Bug fix - Decision Services doesn't work with unnamed imported Elements
* Code Refactoring and Junits
* Unit test for inputQualifiedNamePrefix method
* Fixing review comments
* Fixing review comments
* Change Request
---------
Co-authored-by: ChinchuAjith <[email protected]>
---
.../dmn/core/compiler/DecisionServiceCompiler.java | 116 ++++++++--------
.../core/compiler/DecisionServiceCompilerTest.java | 147 +++++++++++++++++++++
2 files changed, 206 insertions(+), 57 deletions(-)
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DecisionServiceCompiler.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DecisionServiceCompiler.java
index 7d25b47256..d99ba14127 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DecisionServiceCompiler.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DecisionServiceCompiler.java
@@ -53,6 +53,8 @@ import org.kie.dmn.model.api.ItemDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.kie.dmn.core.compiler.UnnamedImportUtils.isInUnnamedImport;
+
public class DecisionServiceCompiler implements DRGElementCompiler {
private static final Logger LOG =
LoggerFactory.getLogger(DecisionServiceCompiler.class);
@@ -103,12 +105,13 @@ public class DecisionServiceCompiler implements
DRGElementCompiler {
* The qualified name of an element named E that is defined in the same
decision model as S is simply E.
* Otherwise, the qualified name is I.E, where I is the name of the import
element that refers to the model where E is defined.
*/
- private static String inputQualifiedNamePrefix(DMNNode input, DMNModelImpl
model) {
- if (input.getModelNamespace().equals(model.getNamespace())) {
+ static String inputQualifiedNamePrefix(DMNNode input, DMNModelImpl model)
{
+ if (input.getModelNamespace().equals(model.getNamespace()) ||
isInUnnamedImport(input, model)) {
return null;
} else {
- Optional<String> importAlias =
model.getImportAliasFor(input.getModelNamespace(), input.getModelName());
- if (importAlias.isEmpty()) {
+ try {
+ return getInputNamePrefix(input, model);
+ } catch (IllegalStateException e) {
MsgUtil.reportMessage(LOG,
DMNMessage.Severity.ERROR,
((DMNBaseNode)input).getSource(),
@@ -120,71 +123,74 @@ public class DecisionServiceCompiler implements
DRGElementCompiler {
((DMNBaseNode)input).getSource());
return null;
}
- return importAlias.get();
}
+ }
+
+ private static String getInputNamePrefix(DMNNode input, DMNModelImpl
model) {
+ Optional<String> importAlias =
model.getImportAliasFor(input.getModelNamespace(), input.getModelName());
+ if (importAlias.isEmpty()) {
+ throw new IllegalStateException("Missing import alias for model "
+ input.getModelName() +
+ "with namespace " + input.getModelNamespace());
+ }
+ return importAlias.get();
}
@Override
public void compileEvaluator(DMNNode node, DMNCompilerImpl compiler,
DMNCompilerContext ctx, DMNModelImpl model) {
DecisionServiceNodeImpl ni = (DecisionServiceNodeImpl) node;
-
List<DSFormalParameter> parameters = new ArrayList<>();
-
+ processInputData(ni, model, parameters);
+ processInputDecisions(ni, model, parameters);
+ validateEncapsulatedDecision(ni, model);
+ List<DecisionNode> outputDecisions = getOutputDecisions(ni, model);
+
+ boolean coerceSingleton = ((DMNCompilerConfigurationImpl)
compiler.getDmnCompilerConfig()).getOption(CoerceDecisionServiceSingletonOutputOption.class).isCoerceSingleton();
+ DMNDecisionServiceFunctionDefinitionEvaluator exprEvaluator = new
DMNDecisionServiceFunctionDefinitionEvaluator(ni, parameters, coerceSingleton);
+ ni.setEvaluator(exprEvaluator);
+
+ if (ni.getType() != null) {
+ checkFnConsistency(model, ni, ni.getType(), outputDecisions);
+ }
+ }
+
+ private void processInputData(DecisionServiceNodeImpl ni, DMNModelImpl
model, List<DSFormalParameter> parameters) {
for (DMNElementReference er : ni.getDecisionService().getInputData()) {
String id = DMNCompilerImpl.getId(er);
InputDataNode input = model.getInputById(id);
- String inputNamePrefix = inputQualifiedNamePrefix(input, model);
if (input != null) {
+ String inputNamePrefix = inputQualifiedNamePrefix(input,
model);
ni.addInputParameter(inputNamePrefix != null ? inputNamePrefix
+ "." + input.getName() : input.getName(), input);
parameters.add(new DSFormalParameter(inputNamePrefix,
input.getName(), input.getType()));
} else {
- MsgUtil.reportMessage(LOG,
- DMNMessage.Severity.ERROR,
- ni.getDecisionService(),
- model,
- null,
- null,
- Msg.REFERENCE_NOT_FOUND_FOR_DS,
- id,
- node.getName());
+ reportReferenceError(ni, model, id);
}
}
+ }
+
+ private void processInputDecisions(DecisionServiceNodeImpl ni,
DMNModelImpl model, List<DSFormalParameter> parameters) {
for (DMNElementReference er :
ni.getDecisionService().getInputDecision()) {
String id = DMNCompilerImpl.getId(er);
DecisionNode input = model.getDecisionById(id);
- String inputNamePrefix = inputQualifiedNamePrefix(input, model);
if (input != null) {
+ String inputNamePrefix = inputQualifiedNamePrefix(input,
model);
ni.addInputParameter(inputNamePrefix != null ? inputNamePrefix
+ "." + input.getName() : input.getName(), input);
parameters.add(new DSFormalParameter(inputNamePrefix,
input.getName(), input.getResultType()));
} else {
- MsgUtil.reportMessage(LOG,
- DMNMessage.Severity.ERROR,
- ni.getDecisionService(),
- model,
- null,
- null,
- Msg.REFERENCE_NOT_FOUND_FOR_DS,
- id,
- node.getName());
+ reportReferenceError(ni, model, id);
}
}
+ }
+
+ private void validateEncapsulatedDecision(DecisionServiceNodeImpl ni,
DMNModelImpl model) {
for (DMNElementReference er :
ni.getDecisionService().getEncapsulatedDecision()) {
String id = DMNCompilerImpl.getId(er);
- DecisionNode input = model.getDecisionById(id);
- if (input != null) {
- // nothing to do.
- } else {
- MsgUtil.reportMessage(LOG,
- DMNMessage.Severity.ERROR,
- ni.getDecisionService(),
- model,
- null,
- null,
- Msg.REFERENCE_NOT_FOUND_FOR_DS,
- id,
- node.getName());
+ if (model.getDecisionById(id) == null) {
+ reportReferenceError(ni, model, id);
}
}
+ }
+
+ private List<DecisionNode> getOutputDecisions(DecisionServiceNodeImpl ni,
DMNModelImpl model) {
List<DecisionNode> outputDecisions = new ArrayList<>();
for (DMNElementReference er :
ni.getDecisionService().getOutputDecision()) {
String id = DMNCompilerImpl.getId(er);
@@ -192,26 +198,22 @@ public class DecisionServiceCompiler implements
DRGElementCompiler {
if (outDecision != null) {
outputDecisions.add(outDecision);
} else {
- MsgUtil.reportMessage(LOG,
- DMNMessage.Severity.ERROR,
- ni.getDecisionService(),
- model,
- null,
- null,
- Msg.REFERENCE_NOT_FOUND_FOR_DS,
- id,
- node.getName());
+ reportReferenceError(ni, model, id);
}
}
+ return outputDecisions;
+ }
- boolean coerceSingleton = ((DMNCompilerConfigurationImpl)
compiler.getDmnCompilerConfig()).getOption(CoerceDecisionServiceSingletonOutputOption.class).isCoerceSingleton();
-
- DMNDecisionServiceFunctionDefinitionEvaluator exprEvaluator = new
DMNDecisionServiceFunctionDefinitionEvaluator(ni, parameters, coerceSingleton);
- ni.setEvaluator(exprEvaluator);
-
- if (ni.getType() != null) {
- checkFnConsistency(model, ni, ni.getType(), outputDecisions);
- }
+ private void reportReferenceError(DecisionServiceNodeImpl ni, DMNModelImpl
model, String id) {
+ MsgUtil.reportMessage(LOG,
+ DMNMessage.Severity.ERROR,
+ ni.getDecisionService(),
+ model,
+ null,
+ null,
+ Msg.REFERENCE_NOT_FOUND_FOR_DS,
+ id,
+ ni.getName());
}
private void checkFnConsistency(DMNModelImpl model,
DecisionServiceNodeImpl ni, DMNType type, List<DecisionNode> outputDecisions) {
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/compiler/DecisionServiceCompilerTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/compiler/DecisionServiceCompilerTest.java
new file mode 100644
index 0000000000..8004538c47
--- /dev/null
+++
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/compiler/DecisionServiceCompilerTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kie.dmn.core.compiler;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.kie.dmn.api.core.DMNMessage;
+import org.kie.dmn.api.core.ast.DMNNode;
+import org.kie.dmn.core.BaseInterpretedVsCompiledTest;
+import org.kie.dmn.core.ast.DMNBaseNode;
+import org.kie.dmn.core.impl.DMNModelImpl;
+import org.kie.dmn.core.util.Msg;
+import org.kie.dmn.core.util.MsgUtil;
+import org.kie.dmn.model.api.Definitions;
+import org.kie.dmn.model.api.Import;
+import org.kie.dmn.model.api.NamedElement;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import javax.xml.namespace.QName;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+
+public class DecisionServiceCompilerTest extends BaseInterpretedVsCompiledTest
{
+
+ @ParameterizedTest
+ @MethodSource("params")
+ void inputQualifiedNamePrefixWithSameNameSpace() {
+ DMNNode input = mock(DMNNode.class);
+ DMNModelImpl model = mock(DMNModelImpl.class);
+
+ when(input.getModelNamespace()).thenReturn("modelNamespace");
+ when(model.getNamespace()).thenReturn("modelNamespace");
+
+ String result =
DecisionServiceCompiler.inputQualifiedNamePrefix(input, model);
+ assertThat(result).isNull();
+
+ }
+
+ @ParameterizedTest
+ @MethodSource("params")
+ void inputQualifiedNamePrefixWithUnnamedImportTrue() {
+
+ DMNNode input = mock(DMNNode.class);
+ when(input.getModelNamespace()).thenReturn("nodeNamespace");
+ when(input.getName()).thenReturn("inputName");
+
+ Import imported = mock(Import.class);
+ when(imported.getName()).thenReturn("");
+ when(imported.getNamespace()).thenReturn("nodeNamespace");
+ Definitions definitions = mock(Definitions.class);
+ when(definitions.getImport()).thenReturn(List.of(imported));
+
+ DMNModelImpl model = mock(DMNModelImpl.class);
+ when(model.getNamespace()).thenReturn("modelNamespace");
+ when(model.getDefinitions()).thenReturn(definitions);
+
+ String result =
DecisionServiceCompiler.inputQualifiedNamePrefix(input, model);
+ assertThat(result).isNull();
+
+ }
+
+ @ParameterizedTest
+ @MethodSource("params")
+ void inputQualifiedNamePrefixWithImportAlias() {
+ DMNNode input = mock(DMNNode.class);
+ when(input.getModelNamespace()).thenReturn("nodeNamespace");
+ when(input.getName()).thenReturn("inputName");
+
+ Import imported = mock(Import.class);
+ when(imported.getNamespace()).thenReturn("importedNamespace");
+ Definitions definitions = mock(Definitions.class);
+ when(definitions.getImport()).thenReturn(List.of(imported));
+
+ DMNModelImpl model = mock(DMNModelImpl.class);
+ when(model.getNamespace()).thenReturn("modelNamespace");
+ when(model.getDefinitions()).thenReturn(definitions);
+ when(model.getImportAliasFor(any(),
any())).thenReturn(Optional.of("inputName"));
+
+ String result =
DecisionServiceCompiler.inputQualifiedNamePrefix(input, model);
+ assertThat(result).isNotNull();
+ assertThat(result).isEqualTo("inputName");
+
+ }
+
+ @ParameterizedTest
+ @MethodSource("params")
+ void inputQualifiedNamePrefixWithEmptyImportAlias() {
+ DMNBaseNode input = mock(DMNBaseNode.class);
+ when(input.getModelNamespace()).thenReturn("nodeNamespace");
+ when(input.getName()).thenReturn("inputName");
+ when(input.getModelName()).thenReturn("modelname");
+ NamedElement source = mock(NamedElement.class);
+ when(input.getSource()).thenReturn(source);
+
+ DMNModelImpl model = mock(DMNModelImpl.class);
+ when(model.getNamespace()).thenReturn("modelNamespace");
+ when(model.getImportAliasFor(any(),
any())).thenReturn(Optional.empty());
+
+ try (MockedStatic<MsgUtil> msgUtilMock = mockStatic(MsgUtil.class)) {
+ try (MockedStatic<UnnamedImportUtils> unnamedImportMock =
mockStatic(UnnamedImportUtils.class)) {
+ unnamedImportMock.when(() ->
UnnamedImportUtils.isInUnnamedImport(input, model)).thenReturn(false);
+ String result =
DecisionServiceCompiler.inputQualifiedNamePrefix(input, model);
+ assertThat(result).isNull();
+ // Verify error was reported
+ msgUtilMock.verify(() -> MsgUtil.reportMessage(
+ any(),
+ eq(DMNMessage.Severity.ERROR),
+ eq(source),
+ eq(model),
+ isNull(),
+ isNull(),
+ eq(Msg.IMPORT_NOT_FOUND_FOR_NODE_MISSING_ALIAS),
+ eq(new QName("nodeNamespace", "modelname")),
+ eq(source)
+ ));
+ }
+ }
+
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]