Skip to content

Commit

Permalink
DROOLS-1663 Kie DMN doesn't support IMPORT decisions between DMN files (
Browse files Browse the repository at this point in the history
apache#1923)

* DROOLS-1663 Kie DMN doesn't support IMPORT decisions between DMN files

Add support for simple (mainly hardcoded) Decision imports

* Small refactoring and Provide required data for cycle detection tests
  • Loading branch information
tarilabs authored and mariofusco committed May 31, 2018
1 parent d399eb0 commit 8d589c5
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ public Decision getDecision() {
return decision;
}

public void setDecision(Decision decision) {
this.decision = decision;
}

public DMNExpressionEvaluator getEvaluator() {
return evaluator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ public DMNModel compile(Definitions dmndefs, Collection<DMNModel> dmnModels) {
model.setImportAliasForNS(iAlias, located.getNamespace(), located.getName());
importFromModel(model, located);
}
} else {
MsgUtil.reportMessage(logger,
DMNMessage.Severity.ERROR,
null,
model,
null,
null,
Msg.IMPORT_TYPE_UNKNOWN,
i.getImportType());
}
}
}
Expand All @@ -197,6 +206,9 @@ private void importFromModel(DMNModelImpl model, DMNModel m) {
for (BusinessKnowledgeModelNode bkm : m.getBusinessKnowledgeModels()) {
model.addBusinessKnowledgeModel(bkm);
}
for (DecisionNode dn : m.getDecisions()) {
model.addDecision(dn);
}
}

private void processItemDefinitions(DMNCompilerContext ctx, DMNFEELHelper feel, DMNModelImpl model, Definitions dmndefs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,16 @@ public void compileEvaluator(DMNNode node, DMNCompilerImpl compiler, DMNCompiler
Map<String, DMNType> importedTypes = new HashMap<>();
for( DMNNode dep : di.getDependencies().values() ) {
if( dep instanceof DecisionNode ) {
ctx.setVariable( dep.getName(), ((DecisionNode) dep).getResultType() );
if (dep.getModelNamespace().equals(model.getNamespace())) {
ctx.setVariable(dep.getName(), ((DecisionNode) dep).getResultType());
} else {
// then the Decision dependency is an imported Decision.
Optional<String> alias = model.getImportAliasFor(dep.getModelNamespace(), dep.getModelName());
if (alias.isPresent()) {
CompositeTypeImpl importedComposite = (CompositeTypeImpl) importedTypes.computeIfAbsent(alias.get(), a -> new CompositeTypeImpl());
importedComposite.addField(dep.getName(), ((DecisionNode) dep).getResultType());
}
}
} else if( dep instanceof InputDataNode ) {
ctx.setVariable( dep.getName(), ((InputDataNode) dep).getType() );
} else if( dep instanceof BusinessKnowledgeModelNode ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static ImportType whichImportType(Import _import) {
switch (_import.getImportType()) {
case "http://www.omg.org/spec/DMN/20151101/dmn.xsd":
case "http://www.omg.org/spec/DMN1-2Alpha/20160929/MODEL":
case "http://www.omg.org/spec/DMN/20180521/MODEL/":
return ImportType.DMN;
default:
return ImportType.UNKNOWN;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public DecisionNode getDecisionByName(String name) {
return null;
}
for ( DecisionNode dn : this.decisions.values() ) {
if ( dn.getName() != null && name.equals( dn.getName() ) ) {
if (dn.getName() != null && name.equals(dn.getName()) && getNamespace().equals(dn.getModelNamespace())) {
return dn;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ private boolean isNodeValueDefined(DMNResultImpl result, DMNNode node) {

private boolean evaluateDecision(DMNContext context, DMNResultImpl result, DecisionNode d, boolean typeCheck) {
DecisionNodeImpl decision = (DecisionNodeImpl) d;
if( result.getContext().isDefined( decision.getName() ) ) {
if (isNodeValueDefined(result, decision)) {
// already resolved
return true;
} else {
Expand Down Expand Up @@ -341,7 +341,7 @@ private boolean evaluateDecision(DMNContext context, DMNResultImpl result, Decis
getIdentifier( dep ),
e.getMessage() );
}
if( ! result.getContext().isDefined( dep.getName() ) ) {
if (!isNodeValueDefined(result, dep)) {
if( dep instanceof DecisionNode ) {
if (!evaluateDecision(context, result, (DecisionNode) dep, typeCheck)) {
missingInput = true;
Expand Down Expand Up @@ -429,7 +429,14 @@ private boolean evaluateDecision(DMNContext context, DMNResultImpl result, Decis
return false;
}

result.getContext().set( decision.getDecision().getVariable().getName(), value );
if (decision.getModelNamespace().equals(result.getModel().getNamespace())) {
result.getContext().set(decision.getDecision().getVariable().getName(), value);
} else {
DMNModelImpl model = (DMNModelImpl) result.getModel();
Optional<String> importAlias = model.getImportAliasFor(decision.getModelNamespace(), decision.getModelName());
Map<String, Object> aliasContext = (Map) result.getContext().getAll().computeIfAbsent(importAlias.get(), x -> new LinkedHashMap<>());
aliasContext.put(decision.getDecision().getVariable().getName(), value);
}
dr.setResult( value );
dr.setEvaluationStatus( DMNDecisionResult.DecisionEvaluationStatus.SUCCEEDED );
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
public final class Msg {
// consolidated
public static final Message2 UNSUPPORTED_ELEMENT = new Message2( DMNMessageType.UNSUPPORTED_ELEMENT, "Element %s with type='%s' is not supported." );
public static final Message1 IMPORT_TYPE_UNKNOWN = new Message1( DMNMessageType.INVALID_SYNTAX, "Import type unknown: '%s'." );
public static final Message2 IMPORT_NOT_FOUND_FOR_NODE = new Message2( DMNMessageType.IMPORT_NOT_FOUND, "Required import not found: %s for node '%s' " );
public static final Message2 IMPORT_NOT_FOUND_FOR_NODE_MISSING_ALIAS = new Message2( DMNMessageType.IMPORT_NOT_FOUND, "Required import not found: %s for node '%s'; missing DMN Import name alias." );
public static final Message2 REQ_INPUT_NOT_FOUND_FOR_NODE = new Message2( DMNMessageType.REQ_NOT_FOUND, "Required input '%s' not found on node '%s'" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
import org.kie.dmn.feel.lang.types.BuiltInType;
import org.kie.dmn.feel.marshaller.FEELStringMarshaller;
import org.kie.dmn.feel.util.EvalHelper;
import org.kie.dmn.model.v1_1.Decision;
import org.kie.dmn.model.v1_1.Definitions;
import org.kie.dmn.model.v1_1.ItemDefinition;
import org.mockito.ArgumentCaptor;

Expand Down Expand Up @@ -1446,13 +1448,32 @@ public void testInvalidFunction() {
FEELStringMarshaller.INSTANCE.marshall( Arrays.asList(decisionResults.get(0).getResult(), decisionResults.get(1).getResult()) );
}

private Definitions buildSimplifiedDefinitions(String namespace, String... decisions) {
Definitions def = new Definitions();
def.setNamespace(namespace);
for (String d : decisions) {
Decision dec = new Decision();
dec.setName(d);
def.getDrgElement().add(dec);
def.addChildren(dec);
dec.setParent(def);
}
return def;
}

private DecisionNodeImpl buildSimplifiedDecisionNode(Definitions def, String name) {
return new DecisionNodeImpl(def.getDrgElement().stream().filter(drg -> drg.getName().equals(name)).filter(Decision.class::isInstance).map(Decision.class::cast).findFirst().get());
}

@Test
public void testCycleDetection() {
DecisionNodeImpl a = new DecisionNodeImpl();
DecisionNodeImpl b = new DecisionNodeImpl();
Definitions defs = buildSimplifiedDefinitions("ns", "a", "b");
DecisionNodeImpl a = buildSimplifiedDecisionNode(defs, "a");
DecisionNodeImpl b = buildSimplifiedDecisionNode(defs, "b");
a.addDependency("b", b);
b.addDependency("a", b);
DMNModelImpl model = new DMNModelImpl();
model.setDefinitions(defs);
model.addDecision(a);
model.addDecision(b);
DMNRuntime runtime = DMNRuntimeUtil.createRuntime(this.getClass());
Expand All @@ -1462,9 +1483,11 @@ public void testCycleDetection() {

@Test
public void testCycleDetectionSelfReference() {
DecisionNodeImpl decision = new DecisionNodeImpl();
Definitions defs = buildSimplifiedDefinitions("ns", "self");
DecisionNodeImpl decision = buildSimplifiedDecisionNode(defs, "self");
decision.addDependency("self", decision);
DMNModelImpl model = new DMNModelImpl();
model.setDefinitions(defs);
model.addDecision(decision);
DMNRuntime runtime = DMNRuntimeUtil.createRuntime(this.getClass());
DMNResult result = runtime.evaluateAll(model, DMNFactory.newContext());
Expand All @@ -1473,31 +1496,35 @@ public void testCycleDetectionSelfReference() {

@Test
public void testSharedDependency() {
DecisionNodeImpl a = new DecisionNodeImpl();
DecisionNodeImpl b = new DecisionNodeImpl();
DecisionNodeImpl c = new DecisionNodeImpl();
a.addDependency("c", c);
b.addDependency("c", c);
DMNModelImpl model = new DMNModelImpl();
model.addDecision(a);
model.addDecision(b);
model.addDecision(c);
DMNRuntime runtime = DMNRuntimeUtil.createRuntime(this.getClass());
DMNResult result = runtime.evaluateAll(model, DMNFactory.newContext());
assertFalse(result.hasErrors());
Definitions defs = buildSimplifiedDefinitions("ns", "a", "b", "c");
DecisionNodeImpl a = buildSimplifiedDecisionNode(defs, "a");
DecisionNodeImpl b = buildSimplifiedDecisionNode(defs, "b");
DecisionNodeImpl c = buildSimplifiedDecisionNode(defs, "c");
a.addDependency("c", c);
b.addDependency("c", c);
DMNModelImpl model = new DMNModelImpl();
model.setDefinitions(defs);
model.addDecision(a);
model.addDecision(b);
model.addDecision(c);
DMNRuntime runtime = DMNRuntimeUtil.createRuntime(this.getClass());
DMNResult result = runtime.evaluateAll(model, DMNFactory.newContext());
assertFalse(result.hasErrors());
}

@Test
public void testCycleDetectionDeadlyDiamond() {
DecisionNodeImpl a = new DecisionNodeImpl();
DecisionNodeImpl b = new DecisionNodeImpl();
DecisionNodeImpl c = new DecisionNodeImpl();
DecisionNodeImpl d = new DecisionNodeImpl();
Definitions defs = buildSimplifiedDefinitions("ns", "a", "b", "c", "d");
DecisionNodeImpl a = buildSimplifiedDecisionNode(defs, "a");
DecisionNodeImpl b = buildSimplifiedDecisionNode(defs, "b");
DecisionNodeImpl c = buildSimplifiedDecisionNode(defs, "c");
DecisionNodeImpl d = buildSimplifiedDecisionNode(defs, "d");
a.addDependency("b", b);
a.addDependency("c", c);
b.addDependency("d", d);
c.addDependency("d", d);
DMNModelImpl model = new DMNModelImpl();
model.setDefinitions(defs);
model.addDecision(a);
model.addDecision(b);
model.addDecision(c);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,35 @@ public void testImportDependenciesForDTInAContext() {
assertThat(evaluateAll.getDecisionResultByName("A Decision Ctx with DT").getResult(), is("Respectfully, Hello John!"));
}

@Test
public void testImportHardcodedDecisions() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntimeWithAdditionalResources("Spell_Greeting.dmn",
this.getClass(),
"Import_Spell_Greeting.dmn");

DMNModel importedModel = runtime.getModel("http://www.trisotech.com/dmn/definitions/_88f4fc88-1eb2-4188-a721-5720cf5565ce",
"Spell Greeting");
assertThat(importedModel, notNullValue());
for (DMNMessage message : importedModel.getMessages()) {
LOG.debug("1 {}", message);
}

DMNModel dmnModel = runtime.getModel("http://www.trisotech.com/dmn/definitions/_d67f19e9-7835-4cad-9c80-16b8423cc392",
"Import Spell Greeting");
assertThat(dmnModel, notNullValue());
for (DMNMessage message : dmnModel.getMessages()) {
LOG.debug("2 {}", message);
}

DMNContext context = runtime.newContext();
context.set("Person Name", "John");

DMNResult evaluateAll = runtime.evaluateAll(dmnModel, context);
for (DMNMessage message : evaluateAll.getMessages()) {
LOG.debug("e {}", message);
}
LOG.debug("{}", evaluateAll);
assertThat(evaluateAll.getDecisionResultByName("Say the Greeting to Person").getResult(), is("Hello, John"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<dmn11:definitions xmlns="http://www.trisotech.com/dmn/definitions/_d67f19e9-7835-4cad-9c80-16b8423cc392" xmlns:feel="http://www.omg.org/spec/FEEL/20140401" xmlns:include1="http://www.trisotech.com/dmn/definitions/_88f4fc88-1eb2-4188-a721-5720cf5565ce" xmlns:tc="http://www.omg.org/spec/DMN/20160719/testcase" xmlns:triso="http://www.trisotech.com/2015/triso/modeling" xmlns:trisofeed="http://trisotech.com/feed" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exporter="DMN Modeler" exporterVersion="6.0.8.201805241511" id="_d67f19e9-7835-4cad-9c80-16b8423cc392" name="Import Spell Greeting" namespace="http://www.trisotech.com/dmn/definitions/_d67f19e9-7835-4cad-9c80-16b8423cc392" triso:logoChoice="Default" xmlns:dmn11="http://www.omg.org/spec/DMN/20151101/dmn.xsd">
<dmn11:extensionElements/>
<dmn11:import xmlns:drools="http://www.drools.org/kie/dmn/1.1" drools:modelName="Spell Greeting" drools:name="sg" importType="http://www.omg.org/spec/DMN/20180521/MODEL/" namespace="http://www.trisotech.com/dmn/definitions/_88f4fc88-1eb2-4188-a721-5720cf5565ce" triso:fileId="eyJmIjp7InNrdSI6IjlkM2ExMGEwLWM1YTUtNDIxNC04NmMyLTRhMTUxNTMwNWM1MCIsIm5hbWUiOiJTcGVsbCBHcmVldGluZyJ9fQ==" triso:fileName="Matteo Mortari/Spell Greeting"/>
<dmn11:inputData id="_2a802a5a-9b63-4a9d-a6cd-b9661d7ae285" name="Person Name">
<dmn11:variable id="_d8b2285e-79d0-426c-9158-ec9bf5697a3c" name="Person Name" typeRef="feel:string"/>
</dmn11:inputData>
<dmn11:decision id="_6938dac5-9d7d-4416-8bba-25b41de3b6df" name="Say the Greeting to Person">
<dmn11:variable id="_a3b88e24-65d6-49a2-a325-2efb7f339072" name="Say the Greeting to Person"/>
<dmn11:informationRequirement>
<dmn11:requiredInput href="#_2a802a5a-9b63-4a9d-a6cd-b9661d7ae285"/>
</dmn11:informationRequirement>
<dmn11:informationRequirement>
<dmn11:requiredDecision href="http://www.trisotech.com/dmn/definitions/_88f4fc88-1eb2-4188-a721-5720cf5565ce#_764902f0-f4bc-49d9-ba55-cc8f5216e52c"/>
</dmn11:informationRequirement>
<dmn11:literalExpression id="_80988e25-680c-49f4-a2ac-7cc79e0844c7">
<dmn11:text>sg.Spell Greeting + ", " + Person Name</dmn11:text>
</dmn11:literalExpression>
</dmn11:decision>
</dmn11:definitions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<dmn11:definitions xmlns="http://www.trisotech.com/dmn/definitions/_88f4fc88-1eb2-4188-a721-5720cf5565ce" xmlns:feel="http://www.omg.org/spec/FEEL/20140401" xmlns:tc="http://www.omg.org/spec/DMN/20160719/testcase" xmlns:triso="http://www.trisotech.com/2015/triso/modeling" xmlns:trisofeed="http://trisotech.com/feed" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exporter="DMN Modeler" exporterVersion="6.0.8.201805241511" id="_88f4fc88-1eb2-4188-a721-5720cf5565ce" name="Spell Greeting" namespace="http://www.trisotech.com/dmn/definitions/_88f4fc88-1eb2-4188-a721-5720cf5565ce" triso:logoChoice="Default" xmlns:dmn11="http://www.omg.org/spec/DMN/20151101/dmn.xsd">
<dmn11:extensionElements/>
<dmn11:decision id="_764902f0-f4bc-49d9-ba55-cc8f5216e52c" name="Spell Greeting">
<dmn11:variable id="_5e4b59f8-639d-42c6-971a-b7c47afe6ee5" name="Spell Greeting" typeRef="feel:string"/>
<dmn11:literalExpression id="_5acb13b8-37a8-4a13-98b8-f4c31f416365">
<dmn11:text>"Hello"</dmn11:text>
</dmn11:literalExpression>
</dmn11:decision>
</dmn11:definitions>

0 comments on commit 8d589c5

Please sign in to comment.