From d95f9f0352699de9b9cdaa86378e4ff9135f0441 Mon Sep 17 00:00:00 2001 From: mariofusco Date: Tue, 30 May 2023 18:14:25 +0200 Subject: [PATCH] add rule engine meta data to once_after match --- .../temporal/OnceAbstractTimeConstraint.java | 11 ++++++++-- .../domain/temporal/OnceAfterDefinition.java | 22 +++++++++++++++---- .../domain/temporal/OnceWithinDefinition.java | 13 ++++++++--- .../api/domain/temporal/TimeAmount.java | 2 +- .../api/rulesmodel/RulesModelUtil.java | 10 +++++++++ .../integration/api/OnceAfterTest.java | 13 +++++++++++ .../integration/api/OnceWithinTest.java | 15 ++++++++++--- 7 files changed, 73 insertions(+), 13 deletions(-) diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java index cbe36da8..097cafab 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAbstractTimeConstraint.java @@ -1,11 +1,11 @@ package org.drools.ansible.rulebook.integration.api.domain.temporal; -import java.util.List; - import org.drools.model.Index; import org.drools.model.PrototypeDSL; import org.drools.model.PrototypeVariable; +import java.util.List; + import static org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory.SYNTHETIC_PROTOTYPE_NAME; import static org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory.getPrototype; import static org.drools.model.PrototypeDSL.protoPattern; @@ -22,6 +22,8 @@ public abstract class OnceAbstractTimeConstraint implements TimeConstraint { protected PrototypeDSL.PrototypePatternDef guardedPattern; + private PrototypeVariable controlVariable; + public OnceAbstractTimeConstraint(TimeAmount timeAmount, List groupByAttributes) { this.timeAmount = timeAmount; this.groupByAttributes = groupByAttributes; @@ -31,12 +33,17 @@ protected PrototypeVariable getPatternVariable() { return (PrototypeVariable) guardedPattern.getFirstVariable(); } + protected PrototypeVariable getControlVariable() { + return controlVariable; + } + protected PrototypeDSL.PrototypePatternDef createControlPattern() { PrototypeDSL.PrototypePatternDef controlPattern = protoPattern(variable(getPrototype(SYNTHETIC_PROTOTYPE_NAME))); for (String unique : groupByAttributes) { controlPattern.expr( prototypeField(unique), Index.ConstraintType.EQUAL, getPatternVariable(), prototypeField(unique) ); } controlPattern.expr( prototypeField("drools_rule_name"), Index.ConstraintType.EQUAL, fixedValue(ruleName) ); + this.controlVariable = (PrototypeVariable) controlPattern.getFirstVariable(); return controlPattern; } diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java index a8c15831..61735b54 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceAfterDefinition.java @@ -6,7 +6,6 @@ import org.drools.core.facttemplates.Event; import org.drools.core.facttemplates.Fact; import org.drools.model.Drools; -import org.drools.model.DroolsEntryPoint; import org.drools.model.Index; import org.drools.model.Prototype; import org.drools.model.PrototypeDSL; @@ -21,7 +20,9 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static java.util.stream.Collectors.toList; import static org.drools.ansible.rulebook.integration.api.domain.temporal.TimeAmount.parseTimeAmount; @@ -29,6 +30,7 @@ import static org.drools.ansible.rulebook.integration.api.rulesengine.RegisterOnlyAgendaFilter.SYNTHETIC_RULE_TAG; import static org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory.SYNTHETIC_PROTOTYPE_NAME; import static org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory.getPrototype; +import static org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil.writeMetaDataOnEvent; import static org.drools.model.DSL.accFunction; import static org.drools.model.DSL.accumulate; import static org.drools.model.DSL.declarationOf; @@ -121,16 +123,23 @@ private static Match transformOnceAfterMatch(Match match) { EmptyMatchDecorator rewrittenMatch = new EmptyMatchDecorator(match); Collection results = ((Collection) match.getDeclarationValue("results")); if (results.size() == 1) { - rewrittenMatch.withBoundObject("m", results.iterator().next().get("event")); + rewrittenMatch.withBoundObject("m", controlFact2Event(results.iterator().next())); } else { int i = 0; for (Fact fact : results) { - rewrittenMatch.withBoundObject("m_" + i++, fact.get("event")); + rewrittenMatch.withBoundObject("m_" + i++, controlFact2Event(fact)); } } return rewrittenMatch; } + private static Object controlFact2Event(Fact fact) { + Map ruleEngineMeta = new HashMap(); + ruleEngineMeta.put("once_after_time_window", fact.get("once_after_time_window")); + ruleEngineMeta.put("events_in_window", fact.get("events_in_window")); + return writeMetaDataOnEvent((Fact) fact.get("event"), ruleEngineMeta); + } + public OnceAfterDefinition(TimeAmount timeAmount, List groupByAttributes) { super(timeAmount, groupByAttributes); } @@ -190,6 +199,8 @@ public List getControlRules(RuleGenerationContext ruleContext) { } controlEvent.set("drools_rule_name", ruleName); controlEvent.set( "event", event ); + controlEvent.set( "once_after_time_window", timeAmount.toString() ); + controlEvent.set( "events_in_window", 1 ); drools.insert(controlEvent); drools.delete(event); }) @@ -223,7 +234,10 @@ public List getControlRules(RuleGenerationContext ruleContext) { .build( guardedPattern, createControlPattern(), - on(getPatternVariable()).execute(DroolsEntryPoint::delete) + on(getPatternVariable(), getControlVariable()).execute((drools, event, control) -> { + control.set( "events_in_window", ((int) control.get("events_in_window")) + 1 ); + drools.delete(event); + }) ) ); diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java index 3c585ed5..069c3d6e 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/OnceWithinDefinition.java @@ -1,8 +1,5 @@ package org.drools.ansible.rulebook.integration.api.domain.temporal; -import java.util.Collections; -import java.util.List; - import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext; import org.drools.core.facttemplates.Event; import org.drools.core.facttemplates.Fact; @@ -14,11 +11,17 @@ import org.drools.model.view.CombinedExprViewItem; import org.drools.model.view.ViewItem; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static java.util.stream.Collectors.toList; import static org.drools.ansible.rulebook.integration.api.domain.temporal.TimeAmount.parseTimeAmount; import static org.drools.ansible.rulebook.integration.api.rulesengine.RegisterOnlyAgendaFilter.SYNTHETIC_RULE_TAG; import static org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory.SYNTHETIC_PROTOTYPE_NAME; import static org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory.getPrototype; +import static org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil.writeMetaDataOnEvent; import static org.drools.model.DSL.not; import static org.drools.model.DSL.on; import static org.drools.model.PatternDSL.rule; @@ -92,6 +95,10 @@ public void executeTimeConstraintConsequence(Drools drools, Object... facts) { controlEvent.set("drools_rule_name", ruleName); drools.insert(controlEvent); drools.delete(fact); + + Map ruleEngineMeta = new HashMap(); + ruleEngineMeta.put("once_within_time_window", timeAmount.toString()); + writeMetaDataOnEvent(fact, ruleEngineMeta); } @Override diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/TimeAmount.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/TimeAmount.java index d4ce5897..83e223f2 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/TimeAmount.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/temporal/TimeAmount.java @@ -21,7 +21,7 @@ public TimeUnit getTimeUnit() { @Override public String toString() { - return "TimeAmount{ " + amount + " " + timeUnit + " }"; + return amount + " " + timeUnit; } public static TimeAmount parseTimeAmount(String timeAmount) { diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java index 12477a63..979993c9 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/RulesModelUtil.java @@ -3,6 +3,7 @@ import org.drools.core.facttemplates.Fact; import org.json.JSONObject; +import java.util.HashMap; import java.util.Map; import static org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory.DEFAULT_PROTOTYPE_NAME; @@ -13,6 +14,8 @@ public class RulesModelUtil { public static final String ORIGINAL_MAP_FIELD = "$_ORIGINAL_$_MAP_$_FIELD_$"; + public static final String META_FIELD = "meta"; + public static final String RULE_ENGINE_META_FIELD = "rule_engine"; private RulesModelUtil() { } @@ -51,4 +54,11 @@ private static Map factToMap(Map factMap) { public static Map asFactMap(String json) { return new JSONObject(json).toMap(); } + + public static Fact writeMetaDataOnEvent(Fact event, Map ruleEngineMeta) { + Map map = (Map) event.get(ORIGINAL_MAP_FIELD); + Map meta = (Map) map.computeIfAbsent(META_FIELD, x -> new HashMap<>()); + meta.put(RULE_ENGINE_META_FIELD, ruleEngineMeta); + return event; + } } diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java index cadcf250..b7c22e61 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceAfterTest.java @@ -1,10 +1,13 @@ package org.drools.ansible.rulebook.integration.api; +import org.drools.ansible.rulebook.integration.api.domain.temporal.TimeAmount; +import org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil; import org.drools.core.facttemplates.Fact; import org.junit.Test; import org.kie.api.runtime.rule.Match; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; @@ -97,6 +100,11 @@ public void testOnceAfterWithOr() { assertTrue( host.equals( "h1" ) || host.equals( "h2" ) ); String level = fact.get("alert.level").toString(); assertTrue( level.equals( "error" ) || level.equals( "warning" ) ); + + Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD); + Map ruleEngineMeta = (Map) ((Map)map.get(RulesModelUtil.META_FIELD)).get(RulesModelUtil.RULE_ENGINE_META_FIELD); + assertEquals( new TimeAmount(10, TimeUnit.SECONDS).toString(), ruleEngineMeta.get("once_after_time_window") ); + assertEquals( i == 0 ? 2 : 1, ruleEngineMeta.get("events_in_window") ); } for (int i = 0; i < 2; i++) { @@ -112,6 +120,11 @@ public void testOnceAfterWithOr() { assertTrue(host.equals("h1")); String level = fact.get("alert.level").toString(); assertTrue(level.equals("warning")); + + Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD); + Map ruleEngineMeta = (Map) ((Map)map.get(RulesModelUtil.META_FIELD)).get(RulesModelUtil.RULE_ENGINE_META_FIELD); + assertEquals( new TimeAmount(10, TimeUnit.SECONDS).toString(), ruleEngineMeta.get("once_after_time_window") ); + assertEquals( 1, ruleEngineMeta.get("events_in_window") ); } rulesExecutor.dispose(); diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java index bb1d58bc..2ff94948 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/OnceWithinTest.java @@ -1,11 +1,15 @@ package org.drools.ansible.rulebook.integration.api; -import java.util.List; -import java.util.concurrent.TimeUnit; - +import org.drools.ansible.rulebook.integration.api.domain.temporal.TimeAmount; +import org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil; +import org.drools.core.facttemplates.Fact; import org.junit.Test; import org.kie.api.runtime.rule.Match; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + import static org.junit.Assert.assertEquals; public class OnceWithinTest { @@ -117,6 +121,11 @@ private void onceWithinTest(String json) { List matchedRules = rulesExecutor.processEvents("{ \"sensu\": { \"process\": { \"type\":\"alert\" }, \"host\":\"h1\" } }").join(); assertEquals(1, matchedRules.size()); + Fact fact = (Fact) matchedRules.get(0).getDeclarationValue("singleton"); + Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD); + Map ruleEngineMeta = (Map) ((Map)map.get(RulesModelUtil.META_FIELD)).get(RulesModelUtil.RULE_ENGINE_META_FIELD); + assertEquals( new TimeAmount(10, TimeUnit.SECONDS).toString(), ruleEngineMeta.get("once_within_time_window") ); + rulesExecutor.advanceTime(3, TimeUnit.SECONDS); matchedRules = rulesExecutor.processEvents("{ \"sensu\": { \"process\": { \"type\":\"alert\" }, \"host\":\"h1\" } }").join();