Skip to content

Commit

Permalink
[incubator-kie-issues#1543] Add the "id" of executed rules to the Aft…
Browse files Browse the repository at this point in the history
…erEvaluateDecisionTableEvent (apache#6127)

* [incubator-kie-issues#1543] Add the "id" of executed rules to the AfterEvaluateDecisionTableEvent

* [incubator-kie-issues#1543] Fix TODO

* [incubator-kie-issues#1543] Fix license header

* [incubator-kie-issues#1543] Minor refactoring on unrelated test

* [incubator-kie-issues#1543] Fix as per PR review

---------

Co-authored-by: Gabriele-Cardosi <[email protected]>
  • Loading branch information
2 people authored and rgdoliveira committed Oct 24, 2024
1 parent cb15b1a commit 4569d01
Show file tree
Hide file tree
Showing 17 changed files with 369 additions and 296 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ default String getDecisionTableId() {
List<Integer> getMatches();

List<Integer> getSelected();

List<String> getMatchesIds();

List<String> getSelectedIds();
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ public EvaluatorResult evaluate(DMNRuntimeEventManager dmrem, DMNResult dmnr) {
LOG.debug("failed evaluate", e);
throw new RuntimeException(e);
} finally {
DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), node.getName(), dt.getId(), result, (r != null ? r.matchedRules : null), (r != null ? r.fired : null) );
DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), node.getName(), dt.getId(), result,
(r != null ? r.matchedRules : null),
(r != null ? r.fired : null),
(r != null ? r.matchedIds : null),
(r != null ? r.firedIds : null));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ public EvaluatorResult evaluate(DMNRuntimeEventManager dmrem, DMNResult dmnr) {
r = processEvents( events, dmrem, result, node );
return new EvaluatorResultImpl( dtr, r.hasErrors ? ResultType.FAILURE : ResultType.SUCCESS );
} finally {
DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), dt.getName(), dtNodeId, result, (r != null ? r.matchedRules : null), (r != null ? r.fired : null) );
DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), dt.getName(), dtNodeId, result,
(r != null ? r.matchedRules : null),
(r != null ? r.fired : null),
(r != null ? r.matchedIds : null),
(r != null ? r.firedIds : null));
}
}

Expand All @@ -112,8 +116,10 @@ public static EventResults processEvents(List<FEELEvent> events, DMNRuntimeEvent
for ( FEELEvent e : events ) {
if ( e instanceof DecisionTableRulesMatchedEvent ) {
r.matchedRules = ((DecisionTableRulesMatchedEvent) e).getMatches();
r.matchedIds = ((DecisionTableRulesMatchedEvent) e).getMatchesIds();
} else if ( e instanceof DecisionTableRulesSelectedEvent ) {
r.fired = ((DecisionTableRulesSelectedEvent) e).getFired();
r.firedIds = ((DecisionTableRulesSelectedEvent) e).getFiredIds();
} else if ( e.getSeverity() == FEELEvent.Severity.ERROR ) {
MsgUtil.reportMessage( logger,
DMNMessage.Severity.ERROR,
Expand Down Expand Up @@ -143,6 +149,8 @@ public static class EventResults {
public boolean hasErrors = false;
public List<Integer> matchedRules;
public List<Integer> fired;
public List<String> matchedIds;
public List<String> firedIds;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ protected DMNExpressionEvaluator compileDecisionTable(DMNCompilerContext ctx, DM
java.util.List<DTDecisionRule> rules = new ArrayList<>();
index = 0;
for ( DecisionRule dr : dt.getRule() ) {
DTDecisionRule rule = new DTDecisionRule( index );
DTDecisionRule rule = new DTDecisionRule( index, dr.getId() );
for ( int i = 0; i < dr.getInputEntry().size(); i++ ) {
UnaryTests ut = dr.getInputEntry().get(i);
final java.util.List<UnaryTest> tests;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ public EvaluatorResult evaluate(DMNRuntimeEventManager eventManager, DMNResult d
} finally {
evalCtx.exitFrame();
DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable(eventManager, node.getName(), decisionTableName, decisionTableId, dmnResult,
(eventResults != null ? eventResults.matchedRules : null), (eventResults != null ? eventResults.fired : null));
(eventResults != null ? eventResults.matchedRules : null),
(eventResults != null ? eventResults.fired : null),
(eventResults != null ? eventResults.matchedIds : null),
(eventResults != null ? eventResults.firedIds : null));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* "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
* 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
Expand All @@ -29,9 +29,9 @@
import org.drools.model.functions.Function1;
import org.kie.dmn.api.feel.runtime.events.FEELEvent;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.runtime.decisiontables.DTDecisionRule;
import org.kie.dmn.feel.runtime.decisiontables.DecisionTable;
import org.kie.dmn.feel.runtime.decisiontables.HitPolicy;
import org.kie.dmn.feel.runtime.decisiontables.Indexed;
import org.kie.dmn.feel.runtime.events.DecisionTableRulesMatchedEvent;
import org.kie.dmn.feel.runtime.events.HitPolicyViolationEvent;

Expand All @@ -49,16 +49,17 @@ static class Items {

public void addResult(ResultObject resultObject) {
resultGroupedByRow
.computeIfAbsent(resultObject.row, i -> new ArrayList<>(1)) // 10 (the default for Java) columns output are not that usual
.computeIfAbsent(resultObject.row, i -> new ArrayList<>(1)) // 10 (the default for Java) columns
// output are not that usual
.add(resultObject);
}

public void clearResults() {
resultGroupedByRow.clear();
}

List<Indexed> matches() {
return indexes().map(i -> (Indexed) () -> i).collect(toList());
List<DTDecisionRule> matches() {
return indexes().map(i -> new DTDecisionRule(i, null)).collect(toList());
}

private Stream<Integer> indexes() {
Expand Down Expand Up @@ -147,25 +148,39 @@ public Object applyHitPolicy(EvaluationContext evaluationContext,
}
events.add(new HitPolicyViolationEvent(
FEELEvent.Severity.WARN,
String.format("No rule matched for decision table '%s' and no default values were defined. Setting result to null.", decisionTable.getName()),
String.format("No rule matched for decision table '%s' and no default values were defined. " +
"Setting result to null.", decisionTable.getName()),
decisionTable.getName(),
Collections.emptyList()));
}

List<? extends Indexed> matchIndexes = items.matches();
evaluationContext.notifyEvt( () -> {
List<Integer> matchedIndexes = matchIndexes.stream().map( dr -> dr.getIndex() + 1 ).collect(Collectors.toList() );
return new DecisionTableRulesMatchedEvent(FEELEvent.Severity.INFO,
String.format("Rules matched for decision table '%s': %s", decisionTable.getName(), matchIndexes),
decisionTable.getName(),
decisionTable.getName(),
matchedIndexes );
}
List<DTDecisionRule> matchingDecisionRules = items.matches();
evaluationContext.notifyEvt(() -> {
List<Integer> matches = new ArrayList<>();
List<String> matchesId = new ArrayList<>();
matchingDecisionRules.forEach(dr -> {
matches.add(dr.getIndex() + 1);
if (dr.getId() != null && !dr.getId().isEmpty()) {
matchesId.add(dr.getId());
}
});
return new DecisionTableRulesMatchedEvent(FEELEvent.Severity.INFO,
String.format("Rules matched for " +
"decision " +
"table '%s': " +
"%s",
decisionTable.getName(), matchingDecisionRules),
decisionTable.getName(),
decisionTable.getName(),
matches,
matchesId);
}
);

List<Object> resultObjects = items.evaluateResults(evaluationContext);

Map<Integer, String> errorMessages = checkResults(decisionTable.getOutputs(), evaluationContext, matchIndexes, resultObjects );
Map<Integer, String> errorMessages = checkResults(decisionTable.getOutputs(), evaluationContext, matchingDecisionRules
, resultObjects);
if (!errorMessages.isEmpty()) {
List<Integer> offending = new ArrayList<>(errorMessages.keySet());
events.add(new HitPolicyViolationEvent(
Expand All @@ -178,6 +193,6 @@ public Object applyHitPolicy(EvaluationContext evaluationContext,
return null;
}

return hitPolicy.getDti().dti(evaluationContext, decisionTable, matchIndexes, resultObjects);
return hitPolicy.getDti().dti(evaluationContext, decisionTable, matchingDecisionRules, resultObjects);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@ public class AfterEvaluateDecisionTableEventImpl
private final DMNResult result;
private final List<Integer> matches;
private final List<Integer> fired;
private final List<String> matchesIds;
private final List<String> firedIds;

public AfterEvaluateDecisionTableEventImpl(String nodeName, String dtName, String dtId, DMNResult result, List<Integer> matches, List<Integer> fired) {
public AfterEvaluateDecisionTableEventImpl(String nodeName, String dtName, String dtId, DMNResult result, List<Integer> matches, List<Integer> fired, List<String> matchesIds, List<String> firedIds) {
this.nodeName = nodeName;
this.dtName = dtName;
this.dtId = dtId;
this.result = result;
this.matches = matches;
this.fired = fired;
this.matchesIds = matchesIds;
this.firedIds = firedIds;
}

@Override
Expand Down Expand Up @@ -73,9 +77,18 @@ public List<Integer> getSelected() {
return fired == null ? Collections.emptyList() : fired;
}

@Override
public List<String> getMatchesIds() {
return matchesIds == null ? Collections.emptyList() : matchesIds;
}

@Override
public List<String> getSelectedIds() {return firedIds == null ? Collections.emptyList() : firedIds;
}

@Override
public String toString() {
return "AfterEvaluateDecisionTableEvent{ nodeName='"+nodeName+"' decisionTableName='" + dtName + "' matches=" + getMatches() + " fired=" + getSelected() + " }";
return "AfterEvaluateDecisionTableEvent{ nodeName='"+nodeName+"' decisionTableName='" + dtName + "' matches=" + getMatches() + " fired=" + getSelected() + "' matchesIds=" + getMatchesIds() + " firedIds=" + getSelectedIds() + " }";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ public static void fireBeforeEvaluateDecisionTable( DMNRuntimeEventManager event
}
}

public static void fireAfterEvaluateDecisionTable( DMNRuntimeEventManager eventManager, String nodeName, String dtName, String dtId, DMNResult result, List<Integer> matches, List<Integer> fired ) {
public static void fireAfterEvaluateDecisionTable( DMNRuntimeEventManager eventManager, String nodeName, String dtName, String dtId, DMNResult result, List<Integer> matches, List<Integer> fired, List<String> matchedIds, List<String> firedIds ) {
if( eventManager.hasListeners() ) {
AfterEvaluateDecisionTableEvent event = new AfterEvaluateDecisionTableEventImpl(nodeName, dtName, dtId, result, matches, fired);
AfterEvaluateDecisionTableEvent event = new AfterEvaluateDecisionTableEventImpl(nodeName, dtName, dtId, result, matches, fired, matchedIds, firedIds);
notifyListeners(eventManager, l -> l.afterEvaluateDecisionTable(event));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.kie.dmn.api.core.ast.ItemDefNode;
import org.kie.dmn.api.core.event.AfterConditionalEvaluationEvent;
import org.kie.dmn.api.core.event.AfterEvaluateConditionalEvent;
import org.kie.dmn.api.core.event.AfterEvaluateDecisionTableEvent;
import org.kie.dmn.api.core.event.DMNRuntimeEventListener;
import org.kie.dmn.core.api.DMNFactory;
import org.kie.dmn.core.api.event.DefaultDMNRuntimeEventListener;
Expand Down Expand Up @@ -384,26 +385,36 @@ void typeConstraintsChecks(boolean useExecModelCompiler) {

@ParameterizedTest
@MethodSource("params")
void conditionalIfCheck(boolean useExecModelCompiler) {
void evaluationHitIdsCheck(boolean useExecModelCompiler) {
init(useExecModelCompiler);
final String ifElementId = "_3C702CE4-E5A0-4B6F-905D-C2621FFFA387";
final String thenElementId = "_6481FF12-61B5-451C-B775-4143D9B6CD6B";
final String elseElementId = "_2CD02CB2-6B56-45C4-B461-405E89D45633";
final String ruleId0 = "_1578BD9E-2BF9-4BFC-8956-1A736959C937";
final String ruleId1 = "_31CD7AA3-A806-4E7E-B512-821F82043620";
final String ruleId3 = "_2545E1A8-93D3-4C8A-A0ED-8AD8B10A58F9";
final String ruleId4 = "_510A50DA-D5A4-4F06-B0BE-7F8F2AA83740";
final DMNRuntime runtime = DMNRuntimeUtil.createRuntime("valid_models/DMNv1_5/RiskScore_Simple.dmn", this.getClass() );


final List<AfterEvaluateConditionalEvent> afterEvaluateConditionalEvents = new ArrayList<>();
final List<AfterConditionalEvaluationEvent> afterConditionalEvaluationEvents = new ArrayList<>();
final List<String> evaluateConditionalIds = new ArrayList<>();
final List<String> conditionalEvaluationIds = new ArrayList<>();
final List<String> executedRuleIds = new ArrayList<>();
runtime.addListener(new DefaultDMNRuntimeEventListener() {

@Override
public void afterConditionalEvaluation(AfterConditionalEvaluationEvent event) {
conditionalEvaluationIds.add(event.getExecutedId());
}

@Override
public void afterEvaluateConditional(AfterEvaluateConditionalEvent event) {
afterEvaluateConditionalEvents.add(event);
evaluateConditionalIds.add(event.getExecutedId());
}

@Override
public void afterConditionalEvaluation(AfterConditionalEvaluationEvent event) {
afterConditionalEvaluationEvents.add(event);
public void afterEvaluateDecisionTable(AfterEvaluateDecisionTableEvent event) {
executedRuleIds.addAll(event.getSelectedIds());
}

});
Expand All @@ -419,20 +430,23 @@ public void afterConditionalEvaluation(AfterConditionalEvaluationEvent event) {
final DMNResult dmnResult1 = runtime.evaluateAll( dmnModel, ctx1 );
assertThat(dmnResult1.hasErrors()).as(DMNRuntimeUtil.formatMessages(dmnResult1.getMessages())).isFalse();
assertThat( dmnResult1.getContext().get( "Risk Score" )).isEqualTo(BigDecimal.valueOf(50));
assertThat(afterEvaluateConditionalEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(ifElementId));
assertThat(afterConditionalEvaluationEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(elseElementId));
assertThat(evaluateConditionalIds).hasSize(1).allMatch(id -> id.equals(ifElementId));
assertThat(conditionalEvaluationIds).hasSize(1).allMatch(id -> id.equals(elseElementId));
assertThat(executedRuleIds).hasSize(2).contains(ruleId0, ruleId3);

//
afterEvaluateConditionalEvents.clear();
afterConditionalEvaluationEvents.clear();
evaluateConditionalIds.clear();
conditionalEvaluationIds.clear();
executedRuleIds.clear();
final DMNContext ctx2 = runtime.newContext();
ctx2.set("Credit Score", "Excellent");
ctx2.set("DTI", 10);
final DMNResult dmnResult2 = runtime.evaluateAll( dmnModel, ctx2 );
assertThat(dmnResult2.hasErrors()).as(DMNRuntimeUtil.formatMessages(dmnResult1.getMessages())).isFalse();
assertThat( dmnResult2.getContext().get( "Risk Score" )).isEqualTo(BigDecimal.valueOf(20));
assertThat(afterEvaluateConditionalEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(ifElementId));
assertThat(afterConditionalEvaluationEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(thenElementId));
assertThat(evaluateConditionalIds).hasSize(1).allMatch(id -> id.equals(ifElementId));
assertThat(conditionalEvaluationIds).hasSize(1).allMatch(id -> id.equals(thenElementId));
assertThat(executedRuleIds).hasSize(2).contains(ruleId1, ruleId4);
}

@ParameterizedTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ The class DecisionRule is used to model the rules in a decision table (see 8.2 N
*/
public class DTDecisionRule implements Indexed {
private int index;
private String id;
private List<UnaryTest> inputEntry;
private List<CompiledExpression> outputEntry;

public DTDecisionRule(int index) {
public DTDecisionRule(int index, String id) {
this.index = index;
this.id = id;
}

/**
Expand Down Expand Up @@ -86,4 +88,8 @@ public List<CompiledExpression> getOutputEntry() {
public int getIndex() {
return index;
}

public String getId() {
return id;
}
}
Loading

0 comments on commit 4569d01

Please sign in to comment.