From a4f6afe3694357fd390166d4b96dc649f06f42c5 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Mon, 19 Jun 2023 09:29:05 +0200 Subject: [PATCH 01/17] DROOLS-7475 Proposed feature for ['key'] accessor nesting the protoextractor into the maven build --- .gitignore | 2 + .../input.txt | 1 + .../pom.xml | 96 +++++++++++++++++ .../protoextractor/Protoextractor.g4 | 26 +++++ .../protoextractor/ASTProductionVisitor.java | 69 ++++++++++++ .../protoextractor/ExtractorParser.java | 24 +++++ .../protoextractor/ExtractorUtils.java | 21 ++++ .../protoextractor/ast/ASTBuilderFactory.java | 29 +++++ .../protoextractor/ast/ASTNode.java | 19 ++++ .../protoextractor/ast/AbstractNode.java | 102 ++++++++++++++++++ .../protoextractor/ast/DefaultedVisitor.java | 20 ++++ .../protoextractor/ast/ExtractorNode.java | 31 ++++++ .../protoextractor/ast/IdentifierNode.java | 28 +++++ .../protoextractor/ast/IndexAccessorNode.java | 29 +++++ .../ast/IntegerLiteralNode.java | 28 +++++ .../ast/SquaredAccessorNode.java | 29 +++++ .../protoextractor/ast/StringLiteralNode.java | 29 +++++ .../protoextractor/ast/TextValue.java | 7 ++ .../protoextractor/ast/Visitor.java | 9 ++ .../ExtractorPrototypeExpression.java | 46 ++++++++ .../NormalizedFieldRepresentationVisitor.java | 46 ++++++++ .../prototype/ValueExtractionVisitor.java | 70 ++++++++++++ .../protoextractor/ExtractorTest.java | 62 +++++++++++ .../prototype/PrototypeTest.java | 47 ++++++++ .../src/test/resources/mixedBag.json | 17 +++ .../src/test/resources/test1.json | 17 +++ pom.xml | 2 + 27 files changed, 906 insertions(+) create mode 100644 drools-ansible-rulebook-integration-protoextractor/input.txt create mode 100644 drools-ansible-rulebook-integration-protoextractor/pom.xml create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ASTProductionVisitor.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorParser.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorUtils.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTBuilderFactory.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IdentifierNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IndexAccessorNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IntegerLiteralNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/SquaredAccessorNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/StringLiteralNode.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/TextValue.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/NormalizedFieldRepresentationVisitor.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json diff --git a/.gitignore b/.gitignore index 372e03f0..f0f81b6b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ dependency-reduced-pom.xml #CI !.ci + +.antlr diff --git a/drools-ansible-rulebook-integration-protoextractor/input.txt b/drools-ansible-rulebook-integration-protoextractor/input.txt new file mode 100644 index 00000000..c8a152e5 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/input.txt @@ -0,0 +1 @@ +a.b["c"].d["e/asd"]['f'].g.h \ No newline at end of file diff --git a/drools-ansible-rulebook-integration-protoextractor/pom.xml b/drools-ansible-rulebook-integration-protoextractor/pom.xml new file mode 100644 index 00000000..f2ab8fbf --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/pom.xml @@ -0,0 +1,96 @@ + + + + drools-ansible-rulebook-integration + org.drools + 1.0.3-SNAPSHOT + + 4.0.0 + + drools-ansible-rulebook-integration-protoextractor + + Drools :: Ansible Rulebook Integration :: Protoextractor + + + + org.drools + drools-canonical-model + ${version.drools} + + + org.drools + drools-core + ${version.drools} + + + org.antlr + antlr4-runtime + ${version.org.antlr4} + + + com.fasterxml.jackson.core + jackson-databind + ${version.jackson} + + + org.drools + drools-model-compiler + ${version.drools} + test + + + + + + + org.antlr + antlr4-maven-plugin + ${version.org.antlr4} + + + + antlr4 + + + true + false + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + TestRigGui + none + + java + + + + + org.antlr.v4.gui.TestRig + + org.drools.ansible.rulebook.integration.protoextractor.Protoextractor + extractor + -gui + input.txt + + true + + + + org.antlr + antlr4 + ${version.org.antlr4} + + + + + + \ No newline at end of file diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 b/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 new file mode 100644 index 00000000..d4248b9e --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 @@ -0,0 +1,26 @@ +grammar Protoextractor; + +extractor : identifier ( chunk )* ; + +chunk : ('.' identifier) | squaredAccessor | indexAccessor ; + +squaredAccessor : '[' stringLiteral ']'; +indexAccessor : '[' integerLiteral ']'; + +stringLiteral : STRING1 | STRING2 ; +integerLiteral : SIGNED_INT ; + +// keep as := ID given this is matching upstream pyparsing, see lexer rule. +identifier : ID ; + +// ref https://pyparsing-docs.readthedocs.io/en/latest/pyparsing.html#pyparsing.pyparsing_common.identifier +ID : [\p{Letter}_][\p{Letter}_0-9]* ; + +// ref https://github.com/ansible/ansible-rulebook/blob/24444bfcefcd7e9551d708723b76f58c3de9e976/ansible_rulebook/condition_parser.py#L118-L123 +STRING1 : '\'' ~('\r' | '\n' | '"')* '\'' ; +STRING2 : '"' ~('\r' | '\n' | '"')* '"' ; + +SIGNED_INT : (MINUS|PLUS)? DIGITS; +MINUS : '-'; +PLUS : '+'; +DIGITS : [0-9]+; diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ASTProductionVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ASTProductionVisitor.java new file mode 100644 index 00000000..8a51b601 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ASTProductionVisitor.java @@ -0,0 +1,69 @@ +package org.drools.ansible.rulebook.integration.protoextractor; + +import java.util.ArrayList; +import java.util.List; + +import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.ChunkContext; +import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.ExtractorContext; +import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.IdentifierContext; +import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.IndexAccessorContext; +import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.IntegerLiteralContext; +import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.SquaredAccessorContext; +import org.drools.ansible.rulebook.integration.protoextractor.ProtoextractorParser.StringLiteralContext; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTBuilderFactory; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.IntegerLiteralNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.TextValue; + +public class ASTProductionVisitor extends ProtoextractorBaseVisitor { + + @Override + public ASTNode visitStringLiteral(StringLiteralContext ctx) { + return ASTBuilderFactory.newStringLiteralNode(ctx); + } + + @Override + public ASTNode visitIdentifier(IdentifierContext ctx) { + return ASTBuilderFactory.newIdentifierNode(ctx); + } + + @Override + public ASTNode visitIntegerLiteral(IntegerLiteralContext ctx) { + return ASTBuilderFactory.newIntegerLiteralNode(ctx); + } + + @Override + public ASTNode visitSquaredAccessor(SquaredAccessorContext ctx) { + TextValue str = (TextValue) ctx.stringLiteral().accept(this); + return ASTBuilderFactory.newSquaredAccessorNode(ctx, str); + } + + @Override + public ASTNode visitIndexAccessor(IndexAccessorContext ctx) { + IntegerLiteralNode idx = (IntegerLiteralNode) ctx.integerLiteral().accept(this); + return ASTBuilderFactory.newIndexAccessorNode(ctx, idx); + } + + @Override + public ASTNode visitChunk(ChunkContext ctx) { + if (ctx.identifier() != null) { + return ctx.identifier().accept(this); + } else if (ctx.squaredAccessor() != null) { + return ctx.squaredAccessor().accept(this); + } else if (ctx.indexAccessor() != null) { + return ctx.indexAccessor().accept(this); + } + throw new IllegalStateException("reached illegal state while parsing"); + } + + @Override + public ASTNode visitExtractor(ExtractorContext ctx) { + List values = new ArrayList<>(); + ASTNode value0 = ctx.identifier().accept(this); + values.add(value0); + for (ChunkContext chunk : ctx.chunk()) { + values.add(chunk.accept(this)); + } + return ASTBuilderFactory.newExtractorNode(ctx, values); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorParser.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorParser.java new file mode 100644 index 00000000..8599e76f --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorParser.java @@ -0,0 +1,24 @@ +package org.drools.ansible.rulebook.integration.protoextractor; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CodePointCharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; + +public class ExtractorParser { + private ExtractorParser() { + // only static methods. + } + + public static ExtractorNode parse(String expression) { + CodePointCharStream charStream = CharStreams.fromString(expression); + ProtoextractorLexer lexer = new ProtoextractorLexer(charStream); + CommonTokenStream tokens = new CommonTokenStream(lexer); + ProtoextractorParser parser = new ProtoextractorParser(tokens); + ParseTree tree = parser.extractor(); + ASTProductionVisitor visitor = new ASTProductionVisitor(); + ExtractorNode extractor = (ExtractorNode) visitor.visit(tree); + return extractor; + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorUtils.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorUtils.java new file mode 100644 index 00000000..e2d0c508 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorUtils.java @@ -0,0 +1,21 @@ +package org.drools.ansible.rulebook.integration.protoextractor; + +import java.util.List; + +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.NormalizedFieldRepresentationVisitor; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ValueExtractionVisitor; + +public class ExtractorUtils { + private ExtractorUtils() { + // only static methods. + } + + public static List getParts(ExtractorNode extractorNode) { + return new NormalizedFieldRepresentationVisitor().visit(extractorNode); + } + + public static Object getValueFrom(ExtractorNode extractorNode, Object readValue) { + return new ValueExtractionVisitor(readValue).visit(extractorNode); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTBuilderFactory.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTBuilderFactory.java new file mode 100644 index 00000000..0aa96bc1 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTBuilderFactory.java @@ -0,0 +1,29 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import java.util.List; + +import org.antlr.v4.runtime.ParserRuleContext; + +public class ASTBuilderFactory { + private ASTBuilderFactory() { + // only static utils methods. + } + public static StringLiteralNode newStringLiteralNode(ParserRuleContext ctx) { + return new StringLiteralNode( ctx ); + } + public static IntegerLiteralNode newIntegerLiteralNode(ParserRuleContext ctx) { + return new IntegerLiteralNode( ctx ); + } + public static IdentifierNode newIdentifierNode(ParserRuleContext ctx) { + return new IdentifierNode( ctx ); + } + public static SquaredAccessorNode newSquaredAccessorNode(ParserRuleContext ctx, TextValue value) { + return new SquaredAccessorNode( ctx, value ); + } + public static IndexAccessorNode newIndexAccessorNode(ParserRuleContext ctx, IntegerLiteralNode value) { + return new IndexAccessorNode( ctx, value ); + } + public static ExtractorNode newExtractorNode(ParserRuleContext ctx, List values) { + return new ExtractorNode( ctx, values ); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTNode.java new file mode 100644 index 00000000..3532a271 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ASTNode.java @@ -0,0 +1,19 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +public interface ASTNode { + int getStartChar(); + + int getEndChar(); + + int getStartLine(); + + int getStartColumn(); + + int getEndLine(); + + int getEndColumn(); + + String getText(); + + T accept(Visitor v); +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java new file mode 100644 index 00000000..98d2c674 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java @@ -0,0 +1,102 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Interval; + +public abstract class AbstractNode implements ASTNode { + protected final ASTNode[] EMPTY_CHILDREN = new ASTNode[0]; + private int startChar; + private int endChar; + private int startLine; + private int startColumn; + private int endLine; + private int endColumn; + + private String text; + + public AbstractNode( ParserRuleContext ctx ) { + // DO NOT keep the reference to `ParserRuleContext` to avoid unneeded retention of lexer structures. + this.setStartChar( ctx.getStart().getStartIndex() ); + this.setStartLine( ctx.getStart().getLine() ); + this.setStartColumn( ctx.getStart().getCharPositionInLine() ); + this.setEndChar( ctx.getStop().getStopIndex() ); + this.setEndLine( ctx.getStop().getLine() ); + this.setEndColumn( ctx.getStop().getCharPositionInLine() + ctx.getStop().getText().length() ); + this.setText( getOriginalText( ctx ) ); + } + + public static String getOriginalText(ParserRuleContext ctx) { + int a = ctx.start.getStartIndex(); + int b = ctx.stop.getStopIndex(); + Interval interval = new Interval( a, b ); + return ctx.getStart().getInputStream().getText( interval ); + } + + @Override + public int getStartChar() { + return startChar; + } + + public void setStartChar(int startChar) { + this.startChar = startChar; + } + + @Override + public int getEndChar() { + return endChar; + } + + public void setEndChar(int endChar) { + this.endChar = endChar; + } + + @Override + public int getStartLine() { + return startLine; + } + + public void setStartLine(int startLine) { + this.startLine = startLine; + } + + @Override + public int getStartColumn() { + return startColumn; + } + + public void setStartColumn(int startColumn) { + this.startColumn = startColumn; + } + + @Override + public int getEndLine() { + return endLine; + } + + public void setEndLine(int endLine) { + this.endLine = endLine; + } + + @Override + public int getEndColumn() { + return endColumn; + } + + public void setEndColumn(int endColumn) { + this.endColumn = endColumn; + } + + @Override + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public String toString() { + return getClass().getSimpleName()+"{" + text + "}"; + } +} \ No newline at end of file diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java new file mode 100644 index 00000000..c31723dc --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java @@ -0,0 +1,20 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +public abstract class DefaultedVisitor implements Visitor { + public abstract T defaultVisit(ASTNode n); + public T visit(ASTNode n) { + return defaultVisit(n); + } + public T visit(IdentifierNode n) { + return defaultVisit(n); + } + public T visit(SquaredAccessorNode n) { + return defaultVisit(n); + } + public T visit(IndexAccessorNode n) { + return defaultVisit(n); + } + public T visit(ExtractorNode n) { + return defaultVisit(n); + } +} \ No newline at end of file diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java new file mode 100644 index 00000000..dd014e4d --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java @@ -0,0 +1,31 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.antlr.v4.runtime.ParserRuleContext; + +public class ExtractorNode extends AbstractNode { + private final List values; + + public ExtractorNode(ParserRuleContext ctx, List values2) { + super(ctx); + Objects.requireNonNull(values2); + this.values = Collections.unmodifiableList(values2); + } + + public List getValues() { + return values; + } + + @Override + public String toString() { + return "ExtractorNode [values=" + values + "]"; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IdentifierNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IdentifierNode.java new file mode 100644 index 00000000..9cea5867 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IdentifierNode.java @@ -0,0 +1,28 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import org.antlr.v4.runtime.ParserRuleContext; + +public class IdentifierNode extends AbstractNode implements TextValue { + + private final String value; + + public IdentifierNode(ParserRuleContext ctx) { + super(ctx); + this.value = getOriginalText(ctx); + } + + @Override + public String getValue() { + return value; + } + + @Override + public String toString() { + return "IdentifierNode [value=" + value + "]"; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IndexAccessorNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IndexAccessorNode.java new file mode 100644 index 00000000..7080c5aa --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IndexAccessorNode.java @@ -0,0 +1,29 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import java.util.Objects; + +import org.antlr.v4.runtime.ParserRuleContext; + +public class IndexAccessorNode extends AbstractNode { + private final IntegerLiteralNode value; + + public IndexAccessorNode(ParserRuleContext ctx, IntegerLiteralNode value) { + super(ctx); + Objects.requireNonNull(value); + this.value = value; + } + + public Integer getValue() { + return this.value.getValue(); + } + + @Override + public String toString() { + return "IndexAccessorNode [value=" + value + "]"; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IntegerLiteralNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IntegerLiteralNode.java new file mode 100644 index 00000000..b7937a99 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/IntegerLiteralNode.java @@ -0,0 +1,28 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import org.antlr.v4.runtime.ParserRuleContext; + +public class IntegerLiteralNode extends AbstractNode { + + private final Integer value; + + public IntegerLiteralNode(ParserRuleContext ctx) { + super(ctx); + String originalText = getOriginalText(ctx); + this.value = Integer.valueOf(originalText); + } + + public Integer getValue() { + return value; + } + + @Override + public String toString() { + return "IntegerLiteralNode [value=" + value + "]"; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/SquaredAccessorNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/SquaredAccessorNode.java new file mode 100644 index 00000000..19feae39 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/SquaredAccessorNode.java @@ -0,0 +1,29 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import java.util.Objects; + +import org.antlr.v4.runtime.ParserRuleContext; + +public class SquaredAccessorNode extends AbstractNode { + private final TextValue value; + + public SquaredAccessorNode(ParserRuleContext ctx, TextValue value) { + super(ctx); + Objects.requireNonNull(value); + this.value = value; + } + + public String getValue() { + return this.value.getValue(); + } + + @Override + public String toString() { + return "SquaredAccessorNode [value=" + value + "]"; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/StringLiteralNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/StringLiteralNode.java new file mode 100644 index 00000000..1693dba6 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/StringLiteralNode.java @@ -0,0 +1,29 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +import org.antlr.v4.runtime.ParserRuleContext; + +public class StringLiteralNode extends AbstractNode implements TextValue { + + private final String value; + + public StringLiteralNode(ParserRuleContext ctx) { + super(ctx); + String originalText = getOriginalText(ctx); + this.value = originalText.substring(1, originalText.length()-1); + } + + @Override + public String getValue() { + return value; + } + + @Override + public String toString() { + return "StringLiteralNode [value=" + value + "]"; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/TextValue.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/TextValue.java new file mode 100644 index 00000000..225caff5 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/TextValue.java @@ -0,0 +1,7 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +public interface TextValue { + + String getValue(); + +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java new file mode 100644 index 00000000..4d352eca --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java @@ -0,0 +1,9 @@ +package org.drools.ansible.rulebook.integration.protoextractor.ast; + +public interface Visitor { + T visit(ASTNode n); + T visit(IdentifierNode n); + T visit(SquaredAccessorNode n); + T visit(IndexAccessorNode n); + T visit(ExtractorNode n); +} \ No newline at end of file diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java new file mode 100644 index 00000000..d57d06f8 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -0,0 +1,46 @@ +package org.drools.ansible.rulebook.integration.protoextractor.prototype; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; + +import org.drools.model.Prototype; +import org.drools.model.PrototypeExpression; +import org.drools.model.PrototypeFact; +import org.drools.model.functions.Function1; + +public class ExtractorPrototypeExpression implements PrototypeExpression { + private final ExtractorNode extractorNode; + + ExtractorPrototypeExpression(ExtractorNode extractorNode) { + this.extractorNode = extractorNode; + } + + @Override + public Function1 asFunction(Prototype prototype) { + return pf -> { + Map asMap = ((org.drools.base.facttemplates.Fact) pf).asMap(); + Object value = new ValueExtractionVisitor(asMap).visit(extractorNode); + return value != null ? value : Prototype.UNDEFINED_VALUE; + }; + } + + // TODO used for indexing, normalizing chunks. + public String getFieldName() { + return ExtractorUtils.getParts(extractorNode).stream().collect(Collectors.joining()); + } + + @Override + public String toString() { + return "ExtractorPrototypeExpression{" + extractorNode + "}"; + } + + @Override + public Collection getImpactedFields() { + return Collections.singletonList(ExtractorUtils.getParts(extractorNode).get(0)); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/NormalizedFieldRepresentationVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/NormalizedFieldRepresentationVisitor.java new file mode 100644 index 00000000..d0558b9b --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/NormalizedFieldRepresentationVisitor.java @@ -0,0 +1,46 @@ +package org.drools.ansible.rulebook.integration.protoextractor.prototype; + +import java.util.ArrayList; +import java.util.List; + +import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.IdentifierNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.IndexAccessorNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.SquaredAccessorNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.Visitor; + +public class NormalizedFieldRepresentationVisitor implements Visitor> { + List result = new ArrayList<>(); + + @Override + public List visit(ASTNode n) { + throw new UnsupportedOperationException("Unimplemented method 'visit'"); + } + + @Override + public List visit(IdentifierNode n) { + result.add(n.getValue()); + return result; + } + + @Override + public List visit(SquaredAccessorNode n) { + result.add(n.getValue()); + return result; + } + + @Override + public List visit(IndexAccessorNode n) { + result.add("("+n.getValue()+")"); + return result; + } + + @Override + public List visit(ExtractorNode n) { + for (ASTNode chunk : n.getValues()) { + chunk.accept(this); + } + return result; + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java new file mode 100644 index 00000000..02e60836 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java @@ -0,0 +1,70 @@ +package org.drools.ansible.rulebook.integration.protoextractor.prototype; + +import java.util.List; +import java.util.Map; + +import org.drools.ansible.rulebook.integration.protoextractor.ast.ASTNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.DefaultedVisitor; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.IdentifierNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.IndexAccessorNode; +import org.drools.ansible.rulebook.integration.protoextractor.ast.SquaredAccessorNode; + +public class ValueExtractionVisitor extends DefaultedVisitor { + private final Object original; + private Object cur; + + public ValueExtractionVisitor(Object original) { + this.original = original; + this.cur = this.original; + } + + @Override + public Object defaultVisit(ASTNode n) { + cur = null; + return cur; + } + + @Override + public Object visit(IdentifierNode n) { + cur = fromMap(cur, n.getValue()); + return cur; + } + + private static Object fromMap(Object in, String key) { + if (in instanceof Map) { + return ((Map) in).get(key); + } else { + return null; + } + } + + @Override + public Object visit(SquaredAccessorNode n) { + cur = fromMap(cur, n.getValue()); + return cur; + } + + @Override + public Object visit(IndexAccessorNode n) { + if (cur instanceof List) { + List theList = (List) cur; + int javaIdx = n.getValue() > 0 ? n.getValue() : theList.size() + n.getValue(); + if (javaIdx < theList.size()) { // avoid index out of bounds; + cur = theList.get(javaIdx); + } + } + return cur; + } + + @Override + public Object visit(ExtractorNode n) { + for (ASTNode chunk : n.getValues()) { + if (this.cur == null) { + break; + } + this.cur = chunk.accept(this); + } + return cur; + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java new file mode 100644 index 00000000..152ab74a --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java @@ -0,0 +1,62 @@ +package org.drools.ansible.rulebook.integration.protoextractor; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; + +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.junit.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class ExtractorTest { + + @Test + public void testBasic() throws Exception { + final String expression = "a.b[\"c\"].d[\"e/asd\"]['f'].g.h"; + + ExtractorNode extractor = ExtractorParser.parse(expression); + List parts = ExtractorUtils.getParts(extractor); + assertThat(parts) + .as("a series of string chunks which could be used for indexing") + .containsExactly("a", "b", "c", "d", "e/asd", "f", "g", "h"); + + final String JSON = Files.readString(Paths.get(ExtractorTest.class.getResource("/test1.json").toURI())); + Map readValue = new ObjectMapper().readValue(JSON, Map.class); + Object valueExtracted = ExtractorUtils.getValueFrom(extractor, readValue); + assertThat(valueExtracted) + .as("extractor can be used to extract value based on the path expression") + .isEqualTo(47); + } + + @Test + public void testMixedBag() throws Exception { + final String expression = "range[\"x\"][1][2].a[\"b\"]"; + + ExtractorNode extractor = ExtractorParser.parse(expression); + + final String JSON = Files.readString(Paths.get(ExtractorTest.class.getResource("/mixedBag.json").toURI())); + Map readValue = new ObjectMapper().readValue(JSON, Map.class); + Object valueExtracted = ExtractorUtils.getValueFrom(extractor, readValue); + assertThat(valueExtracted) + .as("extractor can be used to extract value based on the path expression") + .isEqualTo(47); + } + + @Test + public void testMixedBagReverse() throws Exception { + final String expression = "range[\"x\"][-1][2].a[\"b\"]"; + + ExtractorNode extractor = ExtractorParser.parse(expression); + + final String JSON = Files.readString(Paths.get(ExtractorTest.class.getResource("/mixedBag.json").toURI())); + Map readValue = new ObjectMapper().readValue(JSON, Map.class); + Object valueExtracted = ExtractorUtils.getValueFrom(extractor, readValue); + assertThat(valueExtracted) + .as("extractor can be used to extract value based on the path expression") + .isEqualTo(47); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java new file mode 100644 index 00000000..9077beac --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java @@ -0,0 +1,47 @@ +package org.drools.ansible.rulebook.integration.protoextractor.prototype; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; + +import org.drools.ansible.rulebook.integration.protoextractor.ExtractorParser; +import org.drools.base.facttemplates.Fact; +import org.drools.model.PrototypeDSL; +import org.drools.model.PrototypeFact; +import org.drools.modelcompiler.facttemplate.FactFactory; +import org.junit.Test; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class PrototypeTest { + + @Test + public void testBasic() throws Exception { + final String expression = "a.b[\"c\"].d[\"e/asd\"]['f'].g.h"; + + ExtractorPrototypeExpression expr = new ExtractorPrototypeExpression(ExtractorParser.parse(expression)); + assertThat(expr.getImpactedFields()) + .as("always the first of the chunks") + .isEqualTo(List.of("a")); + assertThat(expr.getFieldName()) + .as("TODO normalization of chunks used for indexing") + .isEqualTo("abcde/asdfgh"); + + final String JSON = Files.readString(Paths.get(PrototypeTest.class.getResource("/test1.json").toURI())); + final Map readValue = new ObjectMapper().readValue(JSON, new TypeReference>() {}); + final Fact mapBasedFact = FactFactory.createMapBasedFact(PrototypeDSL.prototype("test"), readValue); + assertThat(mapBasedFact.get("a")) + .as("sanity check on manually built drools Fact") + .isNotNull() + .isInstanceOf(Map.class); + + Object valueExtracted = expr.asFunction(null).apply((PrototypeFact) mapBasedFact); + assertThat(valueExtracted) + .as("ExtractorPrototypeExpression used to extract value based on the path expression") + .isEqualTo(47); + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json new file mode 100644 index 00000000..ca108357 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json @@ -0,0 +1,17 @@ +{ + "range": { + "x": [ + null, + [ + null, + null, + { + "a": { + "b": 47 + } + } + ] + ] + }, + "range2": null +} \ No newline at end of file diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json new file mode 100644 index 00000000..409877b5 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json @@ -0,0 +1,17 @@ +{ + "a": { + "b": { + "c": { + "d": { + "e/asd": { + "f": { + "g": { + "h": 47 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 023e92a7..cf1984df 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ pom + drools-ansible-rulebook-integration-protoextractor drools-ansible-rulebook-integration-api drools-ansible-rulebook-integration-runtime @@ -33,6 +34,7 @@ 3.20.2 2.13.1 20230227 + 4.10.1 3.0.0-M5 1.35 From 1cd8cf91794eb9d8368dd4ff78a1c6ce8f3383da Mon Sep 17 00:00:00 2001 From: tarilabs Date: Mon, 19 Jun 2023 19:21:17 +0200 Subject: [PATCH 02/17] WIP: broken but progressing Results : Failed tests: testWithSelectAttr(org.drools.ansible.rulebook.integration.api.NullTest): expected:<1> but was:<0> test(org.drools.ansible.rulebook.integration.api.NullTest): expected:<1> but was:<0> testRepeatedOnceWithin(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinWithOr(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinWithAnd(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testSelectOnNull(org.drools.ansible.rulebook.integration.api.SelectTest): expected:<1> but was:<0> Tests in error: testOnceWithinInRule(org.drools.ansible.rulebook.integration.api.OnceWithinTest): Cannot invoke "java.util.Map.get(Object)" because "map" is null testOnceWithinInCondition(org.drools.ansible.rulebook.integration.api.OnceWithinTest): Cannot invoke "java.util.Map.get(Object)" because "map" is null testOnceAfterWithOr(org.drools.ansible.rulebook.integration.api.OnceAfterTest): Cannot invoke "Object.toString()" because the return value of "org.drools.base.facttemplates.Fact.get(String)" is null Tests run: 138, Failures: 6, Errors: 3, Skipped: 0 --- .../pom.xml | 5 +++ .../conditions/ConditionExpression.java | 10 +++-- .../api/domain/constraints/ExistsField.java | 24 +++++++++- .../domain/constraints/NotExistsField.java | 3 +- .../domain/constraints/SelectConstraint.java | 5 ++- .../temporal/OnceAbstractTimeConstraint.java | 5 ++- .../api/rulesmodel/ParsedCondition.java | 3 +- .../api/rulesmodel/RulesModelUtil.java | 39 ++++++++-------- .../integration/api/AdditionTest.java | 3 +- .../integration/api/LogicalOperatorsTest.java | 5 ++- .../constraints/SelectConstraintTest.java | 9 ++-- .../durable/domain/DurableRule.java | 14 +++--- .../protoextractor/ast/AbstractNode.java | 15 +++++++ .../protoextractor/ast/ExtractorNode.java | 11 +++++ .../CompleteExtractorPrototypeExpression.java | 44 +++++++++++++++++++ .../ExtractorPrototypeExpression.java | 6 +-- .../ExtractorPrototypeExpressionUtils.java | 33 ++++++++++++++ 17 files changed, 186 insertions(+), 48 deletions(-) create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java create mode 100644 drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpressionUtils.java diff --git a/drools-ansible-rulebook-integration-api/pom.xml b/drools-ansible-rulebook-integration-api/pom.xml index 6c8da9b1..f3f21e95 100644 --- a/drools-ansible-rulebook-integration-api/pom.xml +++ b/drools-ansible-rulebook-integration-api/pom.xml @@ -14,6 +14,11 @@ Drools :: Ansible Rulebook Integration :: API + + org.drools + drools-ansible-rulebook-integration-protoextractor + ${project.version} + org.slf4j slf4j-simple diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java index 6be1258d..151250b8 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java @@ -6,6 +6,8 @@ import java.util.Map; import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpression; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils; import org.drools.model.PrototypeDSL; import org.drools.model.PrototypeExpression; import org.drools.model.PrototypeVariable; @@ -37,8 +39,8 @@ public ConditionExpression(PrototypeExpression prototypeExpression, boolean fiel public ConditionExpression composeWith(PrototypeExpression.BinaryOperation.Operator decodeBinaryOperator, ConditionExpression rhs) { if (isBeta() && rhs.isBeta() && !prototypeName.equals(rhs.getPrototypeName())) { - PrototypeExpression composed = prototypeField(betaVariable, getFieldName()) - .composeWith(decodeBinaryOperator, prototypeField(rhs.getBetaVariable(), rhs.getFieldName())); + PrototypeExpression composed = ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(betaVariable, getFieldName()) + .composeWith(decodeBinaryOperator, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(rhs.getBetaVariable(), rhs.getFieldName())); return new ConditionExpression(composed); } @@ -54,7 +56,7 @@ public boolean isBeta() { } public String getFieldName() { - return ((PrototypeExpression.PrototypeFieldValue) prototypeExpression).getFieldName(); + return ((ExtractorPrototypeExpression) prototypeExpression).getFieldName(); } public PrototypeExpression getPrototypeExpression() { @@ -142,7 +144,7 @@ private static ConditionExpression createFieldExpression(RuleGenerationContext r betaVariable = (PrototypeVariable) boundPattern.getFirstVariable(); } } - return new ConditionExpression(fieldName2PrototypeExpression(fieldName), true, prototypeName, betaVariable); + return new ConditionExpression(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(fieldName), true, prototypeName, betaVariable); } private static PrototypeExpression.BinaryOperation.Operator decodeBinaryOperator(String operator) { diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java index db3a48af..5c0d83bb 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java @@ -4,8 +4,11 @@ import java.util.function.BiPredicate; import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext; +import org.drools.ansible.rulebook.integration.api.domain.conditions.ConditionExpression; import org.drools.ansible.rulebook.integration.api.rulesmodel.ParsedCondition; import org.drools.model.ConstraintOperator; +import org.drools.model.Prototype; +import org.drools.model.PrototypeExpression; import org.drools.model.PrototypeFact; import static org.drools.ansible.rulebook.integration.api.domain.conditions.ConditionExpression.map2Expr; @@ -21,7 +24,7 @@ public enum ExistsField implements ConstraintOperator, ConditionFactory { @Override public BiPredicate asPredicate() { - return (t,v) -> ((PrototypeFact) t).has((String) v); + throw new UnsupportedOperationException("converted to make use of "+ExistsFieldUsingExtractor.class.getCanonicalName()); } @Override @@ -31,6 +34,23 @@ public String toString() { @Override public ParsedCondition createParsedCondition(RuleGenerationContext ruleContext, String expressionName, Map expression) { - return new ParsedCondition(thisPrototype(), this, fixedValue(map2Expr(ruleContext, expression).getFieldName())).withNotPattern(expressionName.equals(NEGATED_EXPRESSION_NAME)); + ConditionExpression map2Expr = map2Expr(ruleContext, expression); + PrototypeExpression usedProtototypeExpr = map2Expr.getPrototypeExpression(); + PrototypeExpression unused = fixedValue(map2Expr.getFieldName()); + return new ParsedCondition(thisPrototype(), new ExistsFieldUsingExtractor(usedProtototypeExpr), unused).withNotPattern(expressionName.equals(NEGATED_EXPRESSION_NAME)); + } + + public static class ExistsFieldUsingExtractor implements ConstraintOperator { + private final PrototypeExpression expr; + + public ExistsFieldUsingExtractor(PrototypeExpression expr) { + this.expr = expr; + } + + @Override + public BiPredicate asPredicate() { + return (t, v) -> expr.asFunction(null).apply((PrototypeFact) t) != Prototype.UNDEFINED_VALUE; + } + } } diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java index c678c860..f920a4e7 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/NotExistsField.java @@ -5,13 +5,14 @@ import org.drools.model.ConstraintOperator; import org.drools.model.PrototypeFact; +@Deprecated() // TODO seems no longer in-use? public enum NotExistsField implements ConstraintOperator { INSTANCE; @Override public BiPredicate asPredicate() { - return (t,v) -> !((PrototypeFact) t).has((String) v); + throw new UnsupportedOperationException("deprecated and should be no longer in use"); } @Override diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java index 9d38c747..46b6512b 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraint.java @@ -8,6 +8,7 @@ import org.drools.ansible.rulebook.integration.api.domain.conditions.ConditionExpression; import org.drools.ansible.rulebook.integration.api.rulesmodel.BetaParsedCondition; import org.drools.ansible.rulebook.integration.api.rulesmodel.ParsedCondition; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils; import org.drools.model.ConstraintOperator; import org.drools.model.Prototype; import org.drools.model.PrototypeDSL; @@ -63,8 +64,8 @@ private static ParsedCondition createSelectConditionWithLeftAndRightFields(RuleG PrototypeDSL.PrototypePatternDef rightPattern = ruleContext.getBoundPattern(rightPatternBindName); return rightPattern == null ? - new ParsedCondition(prototypeField(leftField), operator, prototypeField(rightField)) : - new BetaParsedCondition(prototypeField(leftField), operator, (PrototypeVariable) rightPattern.getFirstVariable(), prototypeField(rightField.substring(dotPos+1))); + new ParsedCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftField), operator, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(rightField)) : + new BetaParsedCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftField), operator, (PrototypeVariable) rightPattern.getFirstVariable(), ExtractorPrototypeExpressionUtils.prototypeFieldExtractorSkippingFirst(rightField)); } static ParsedCondition createSelectConditionWithFixedRight(ConditionExpression left, BiPredicate opPred, Object rhsValue, boolean positive) { 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 097cafab..3ac60f70 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,5 +1,6 @@ package org.drools.ansible.rulebook.integration.api.domain.temporal; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils; import org.drools.model.Index; import org.drools.model.PrototypeDSL; import org.drools.model.PrototypeVariable; @@ -40,9 +41,9 @@ protected PrototypeVariable getControlVariable() { 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( ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(unique), Index.ConstraintType.EQUAL, getPatternVariable(), ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(unique) ); } - controlPattern.expr( prototypeField("drools_rule_name"), Index.ConstraintType.EQUAL, fixedValue(ruleName) ); + controlPattern.expr( ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("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/rulesmodel/ParsedCondition.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/ParsedCondition.java index be59d3ac..104fbdbe 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/ParsedCondition.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesmodel/ParsedCondition.java @@ -2,6 +2,7 @@ import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext; import org.drools.ansible.rulebook.integration.api.domain.constraints.NegationOperator; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils; import org.drools.model.ConstraintOperator; import org.drools.model.PrototypeDSL; import org.drools.model.PrototypeExpression; @@ -22,7 +23,7 @@ public class ParsedCondition { private boolean negated = false; public ParsedCondition(String left, ConstraintOperator operator, Object right) { - this(prototypeField(left), operator, fixedValue(right)); + this(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(left), operator, fixedValue(right)); } public ParsedCondition(PrototypeExpression left, ConstraintOperator operator, PrototypeExpression right) { 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 070388db..e2521f76 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 @@ -20,43 +20,44 @@ public class RulesModelUtil { private RulesModelUtil() { } public static Fact mapToFact(Map factMap, boolean event) { - Fact fact = event ? createMapBasedEvent( getPrototype(DEFAULT_PROTOTYPE_NAME) ) : createMapBasedFact( getPrototype(DEFAULT_PROTOTYPE_NAME) ); - populateFact(fact, factMap, ""); - fact.set(ORIGINAL_MAP_FIELD, factMap); + Fact fact = event ? createMapBasedEvent( getPrototype(DEFAULT_PROTOTYPE_NAME), factMap ) : createMapBasedFact( getPrototype(DEFAULT_PROTOTYPE_NAME), factMap ); + // the culprit, disabled: + // populateFact(fact, factMap, ""); + // fact.set(ORIGINAL_MAP_FIELD, factMap); return fact; } - private static void populateFact(Fact fact, Map value, String fieldName) { - for (Map.Entry entry : value.entrySet()) { - String key = fieldName + entry.getKey(); - fact.set(key, entry.getValue()); - if (entry.getValue() instanceof Map) { - populateFact(fact, (Map) entry.getValue(), key + "."); - } - } - } + // private static void populateFact(Fact fact, Map value, String fieldName) { + // for (Map.Entry entry : value.entrySet()) { + // String key = fieldName + entry.getKey(); + // fact.set(key, entry.getValue()); + // if (entry.getValue() instanceof Map) { + // populateFact(fact, (Map) entry.getValue(), key + "."); + // } + // } + // } public static Object factToMap(Object fact) { if (fact instanceof Fact) { - return factToMap(((Fact) fact).asMap()); + return (((Fact) fact).asMap()); } if (fact instanceof Map) { - return factToMap(((Map) fact)); + return (((Map) fact)); } return fact; } - private static Map factToMap(Map factMap) { - Map map = (Map) factMap.get(ORIGINAL_MAP_FIELD); - return map != null ? map : factMap; - } + // private static Map factToMap(Map factMap) { + // Map map = (Map) factMap.get(ORIGINAL_MAP_FIELD); + // return map != null ? 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 map = (Map) event.asMap(); 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/AdditionTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AdditionTest.java index 198b04bc..af3600f1 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AdditionTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AdditionTest.java @@ -3,6 +3,7 @@ import java.util.List; import org.drools.ansible.rulebook.integration.api.domain.RulesSet; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils; import org.drools.model.Index; import org.junit.Test; import org.kie.api.runtime.rule.Match; @@ -70,7 +71,7 @@ public void testExecuteRules() { public void testExecuteRulesSet() { RulesSet rulesSet = new RulesSet(); rulesSet.addRule().withCondition().all() - .addSingleCondition(prototypeField("nested.i"), Index.ConstraintType.EQUAL, prototypeField("nested.j").add(fixedValue(1))); + .addSingleCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("nested.i"), Index.ConstraintType.EQUAL, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("nested.j").add(fixedValue(1))); RulesExecutor rulesExecutor = RulesExecutorFactory.createRulesExecutor(rulesSet); diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java index 8ec94611..7c783063 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/LogicalOperatorsTest.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.drools.ansible.rulebook.integration.api.domain.RuleMatch; import org.drools.ansible.rulebook.integration.api.domain.RulesSet; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils; import org.drools.model.Index; import org.drools.model.PrototypeFact; import org.junit.Test; @@ -351,8 +352,8 @@ public void testNegate() { public void testAny() { RulesSet rulesSet = new RulesSet(); rulesSet.addRule().withCondition().any() - .addSingleCondition(prototypeField("i"), Index.ConstraintType.EQUAL, fixedValue(0)).withPatternBinding("event") - .addSingleCondition(prototypeField("i"), Index.ConstraintType.EQUAL, fixedValue(1)).withPatternBinding("event"); + .addSingleCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("i"), Index.ConstraintType.EQUAL, fixedValue(0)).withPatternBinding("event") + .addSingleCondition(ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("i"), Index.ConstraintType.EQUAL, fixedValue(1)).withPatternBinding("event"); RulesExecutor rulesExecutor = RulesExecutorFactory.createRulesExecutor(rulesSet); checkAnyExecution(rulesExecutor); diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java index 6b6557b8..3993d610 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java @@ -23,6 +23,7 @@ import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext; import org.drools.ansible.rulebook.integration.api.rulesmodel.ParsedCondition; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpression; import org.drools.model.PrototypeExpression; import org.junit.Test; @@ -42,8 +43,8 @@ public void createParsedConditionWithSelectExpression() { ParsedCondition parsedCondition = SelectConstraint.INSTANCE.createParsedCondition(new RuleGenerationContext(), SelectConstraint.EXPRESSION_NAME, map); - assertThat(parsedCondition.getLeft()).isInstanceOf(PrototypeExpression.PrototypeFieldValue.class); - assertThat(((PrototypeExpression.PrototypeFieldValue)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels"); + assertThat(parsedCondition.getLeft()).isInstanceOf(ExtractorPrototypeExpression.class); + assertThat(((ExtractorPrototypeExpression)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels"); assertThat(parsedCondition.getRight()).isInstanceOf(PrototypeExpression.FixedValue.class); // rightValue is not important. "25" is already incorporated in the operator @@ -65,8 +66,8 @@ public void createParsedConditionWithSelectNotExpression() { ParsedCondition parsedCondition = SelectConstraint.INSTANCE.createParsedCondition(new RuleGenerationContext(), SelectConstraint.NEGATED_EXPRESSION_NAME, map); - assertThat(parsedCondition.getLeft()).isInstanceOf(PrototypeExpression.PrototypeFieldValue.class); - assertThat(((PrototypeExpression.PrototypeFieldValue)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels"); + assertThat(parsedCondition.getLeft()).isInstanceOf(ExtractorPrototypeExpression.class); + assertThat(((ExtractorPrototypeExpression)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels"); assertThat(parsedCondition.getRight()).isInstanceOf(PrototypeExpression.FixedValue.class); // rightValue is not important. "25" is already incorporated in the operator diff --git a/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java b/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java index 4289688c..5f146e72 100644 --- a/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java +++ b/drools-ansible-rulebook-integration-durable/src/main/java/org/drools/ansible/rulebook/integration/durable/domain/DurableRule.java @@ -205,22 +205,22 @@ private SimpleCondition createCondition(String binding, String leftValue, String String leftBinding = getBindingFromMap(l); if (leftBinding != null) { - PrototypeExpression rightExpression = prototypeField(((Map) l).get(leftBinding).toString()).composeWith(decodeBinaryOperator(rightKey), toPrototypeExpression(r)); - return new BetaExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), leftBinding, rightExpression); + PrototypeExpression rightExpression = ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(((Map) l).get(leftBinding).toString()).composeWith(decodeBinaryOperator(rightKey), toPrototypeExpression(r)); + return new BetaExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), leftBinding, rightExpression); } String rightBinding = getBindingFromMap(r); if (rightBinding != null) { - PrototypeExpression rightExpression = toPrototypeExpression(l).composeWith(decodeBinaryOperator(rightKey), prototypeField(((Map) r).get(rightBinding).toString())); - return new BetaExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), rightBinding, rightExpression); + PrototypeExpression rightExpression = toPrototypeExpression(l).composeWith(decodeBinaryOperator(rightKey), ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(((Map) r).get(rightBinding).toString())); + return new BetaExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), rightBinding, rightExpression); } PrototypeExpression rightExpression = toPrototypeExpression(l).composeWith(decodeBinaryOperator(rightKey), toPrototypeExpression(r)); - return new ExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), rightExpression); + return new ExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), rightExpression); } if (existingBindings.contains(rightKey)) { if (rightValue instanceof String) { - return new BetaExpressionCondition(binding, prototypeField(leftValue), decodeConstraintType(decodedOp), rightKey, prototypeField((String) rightValue)); + return new BetaExpressionCondition(binding, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(leftValue), decodeConstraintType(decodedOp), rightKey, ExtractorPrototypeExpressionUtils.prototypeFieldExtractor((String) rightValue)); } throw new UnsupportedOperationException(); } @@ -234,7 +234,7 @@ private PrototypeExpression toPrototypeExpression(Object value) { assert(map.size() == 1); String fieldName = (String) map.get("$m"); assert(fieldName != null); - return prototypeField(fieldName); + return ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(fieldName); } return fixedValue(value); } diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java index 98d2c674..11603256 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java @@ -14,6 +14,10 @@ public abstract class AbstractNode implements ASTNode { private String text; + protected AbstractNode( AbstractNode usingCtxFrom ) { + copyLocationAttributesFrom(usingCtxFrom); + } + public AbstractNode( ParserRuleContext ctx ) { // DO NOT keep the reference to `ParserRuleContext` to avoid unneeded retention of lexer structures. this.setStartChar( ctx.getStart().getStartIndex() ); @@ -25,6 +29,17 @@ public AbstractNode( ParserRuleContext ctx ) { this.setText( getOriginalText( ctx ) ); } + private AbstractNode copyLocationAttributesFrom(AbstractNode from) { + this.setStartChar(from.getStartChar()); + this.setStartLine(from.getStartLine()); + this.setStartColumn(from.getStartColumn()); + this.setEndChar(from.getEndChar()); + this.setEndLine(from.getEndLine()); + this.setEndColumn(from.getEndColumn()); + this.setText(from.getText()); + return this; + } + public static String getOriginalText(ParserRuleContext ctx) { int a = ctx.start.getStartIndex(); int b = ctx.stop.getStopIndex(); diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java index dd014e4d..7fb0d4f8 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/ExtractorNode.java @@ -15,6 +15,17 @@ public ExtractorNode(ParserRuleContext ctx, List values2) { this.values = Collections.unmodifiableList(values2); } + private ExtractorNode(ExtractorNode extractorNode, List skippingFirst) { + super(extractorNode); + Objects.requireNonNull(skippingFirst); + this.values = Collections.unmodifiableList(skippingFirst); + } + + public ExtractorNode cloneSkipFirst() { + final List skippingFirst = values.subList(1, values.size()); + return new ExtractorNode(this, skippingFirst); + } + public List getValues() { return values; } diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java new file mode 100644 index 00000000..50958275 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java @@ -0,0 +1,44 @@ +package org.drools.ansible.rulebook.integration.protoextractor.prototype; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.drools.model.PrototypeExpression.EvaluableExpression; +import org.drools.model.PrototypeFact; +import org.drools.model.PrototypeVariable; + +public class CompleteExtractorPrototypeExpression extends ExtractorPrototypeExpression implements EvaluableExpression { + private final PrototypeVariable protoVar; + + CompleteExtractorPrototypeExpression(PrototypeVariable protoVar, ExtractorNode extractorNode) { + super(extractorNode); + this.protoVar = protoVar; + } + + public PrototypeVariable getProtoVar() { + return protoVar; + } + + @Override + public boolean hasPrototypeVariable() { + return true; + } + + @Override + public Collection getPrototypeVariables() { + return Collections.singletonList(protoVar); + } + + @Override + public Object evaluate(Map factsMap) { + PrototypeFact prototypeFact = factsMap.get(protoVar); + return asFunction(null).apply(prototypeFact); + } + + @Override + public String toString() { + return "CompleteExtractorPrototypeExpression{" + extractorNode + "}"; + } +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java index d57d06f8..1dcc015d 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -7,14 +7,14 @@ import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils; import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; - +import org.drools.base.facttemplates.Fact; import org.drools.model.Prototype; import org.drools.model.PrototypeExpression; import org.drools.model.PrototypeFact; import org.drools.model.functions.Function1; public class ExtractorPrototypeExpression implements PrototypeExpression { - private final ExtractorNode extractorNode; + protected final ExtractorNode extractorNode; ExtractorPrototypeExpression(ExtractorNode extractorNode) { this.extractorNode = extractorNode; @@ -23,7 +23,7 @@ public class ExtractorPrototypeExpression implements PrototypeExpression { @Override public Function1 asFunction(Prototype prototype) { return pf -> { - Map asMap = ((org.drools.base.facttemplates.Fact) pf).asMap(); + Map asMap = ((Fact) pf).asMap(); Object value = new ValueExtractionVisitor(asMap).visit(extractorNode); return value != null ? value : Prototype.UNDEFINED_VALUE; }; diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpressionUtils.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpressionUtils.java new file mode 100644 index 00000000..51aa23f0 --- /dev/null +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpressionUtils.java @@ -0,0 +1,33 @@ +package org.drools.ansible.rulebook.integration.protoextractor.prototype; + +import org.drools.ansible.rulebook.integration.protoextractor.ExtractorParser; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.drools.model.PrototypeExpression; +import org.drools.model.PrototypeVariable; + +public class ExtractorPrototypeExpressionUtils { + private ExtractorPrototypeExpressionUtils() { + // only static methods. + } + + public static ExtractorPrototypeExpression prototypeFieldExtractor(String expression) { + ExtractorNode parse = ExtractorParser.parse(expression); + return new ExtractorPrototypeExpression(parse); + } + + /** + * old way of: `prototypeField(rightField.substring(dotPos+1))` + * given: m_0.a.b.c + * returns an extractor for: a.b.c + */ + public static ExtractorPrototypeExpression prototypeFieldExtractorSkippingFirst(String expression) { + ExtractorNode parse = ExtractorParser.parse(expression); + ExtractorNode parseSkipFirst = parse.cloneSkipFirst(); + return new ExtractorPrototypeExpression(parseSkipFirst); + } + + public static PrototypeExpression prototypeFieldExtractor(PrototypeVariable betaVariable, String expression) { + ExtractorNode parse = ExtractorParser.parse(expression); + return new CompleteExtractorPrototypeExpression(betaVariable, parse); + } +} From 7493fde37e09ad64745f15f6075de6eb8cb8165c Mon Sep 17 00:00:00 2001 From: tarilabs Date: Mon, 19 Jun 2023 19:37:20 +0200 Subject: [PATCH 03/17] WIP: broken but null != UNDEF Results : Failed tests: testRepeatedOnceWithin(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinWithOr(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinWithAnd(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> Tests in error: testOnceWithinInRule(org.drools.ansible.rulebook.integration.api.OnceWithinTest): Cannot invoke "java.util.Map.get(Object)" because "map" is null testOnceWithinInCondition(org.drools.ansible.rulebook.integration.api.OnceWithinTest): Cannot invoke "java.util.Map.get(Object)" because "map" is null testOnceAfterWithOr(org.drools.ansible.rulebook.integration.api.OnceAfterTest): Cannot invoke "Object.toString()" because the return value of "org.drools.base.facttemplates.Fact.get(String)" is null Tests run: 138, Failures: 3, Errors: 3, Skipped: 0 --- .../prototype/ExtractorPrototypeExpression.java | 2 +- .../prototype/ValueExtractionVisitor.java | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java index 1dcc015d..67c9e61c 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -25,7 +25,7 @@ public Function1 asFunction(Prototype prototype) { return pf -> { Map asMap = ((Fact) pf).asMap(); Object value = new ValueExtractionVisitor(asMap).visit(extractorNode); - return value != null ? value : Prototype.UNDEFINED_VALUE; + return value; }; } diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java index 02e60836..820be37a 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java @@ -9,6 +9,7 @@ import org.drools.ansible.rulebook.integration.protoextractor.ast.IdentifierNode; import org.drools.ansible.rulebook.integration.protoextractor.ast.IndexAccessorNode; import org.drools.ansible.rulebook.integration.protoextractor.ast.SquaredAccessorNode; +import org.drools.model.Prototype; public class ValueExtractionVisitor extends DefaultedVisitor { private final Object original; @@ -33,9 +34,14 @@ public Object visit(IdentifierNode n) { private static Object fromMap(Object in, String key) { if (in instanceof Map) { - return ((Map) in).get(key); + Map theMap = (Map) in; + if (theMap.containsKey(key)) { + return theMap.get(key); + } else { + return Prototype.UNDEFINED_VALUE; + } } else { - return null; + return Prototype.UNDEFINED_VALUE; } } @@ -52,7 +58,11 @@ public Object visit(IndexAccessorNode n) { int javaIdx = n.getValue() > 0 ? n.getValue() : theList.size() + n.getValue(); if (javaIdx < theList.size()) { // avoid index out of bounds; cur = theList.get(javaIdx); + } else { + cur = Prototype.UNDEFINED_VALUE; } + } else { + cur = Prototype.UNDEFINED_VALUE; } return cur; } @@ -60,7 +70,7 @@ public Object visit(IndexAccessorNode n) { @Override public Object visit(ExtractorNode n) { for (ASTNode chunk : n.getValues()) { - if (this.cur == null) { + if (this.cur == null || this.cur == Prototype.UNDEFINED_VALUE) { break; } this.cur = chunk.accept(this); From 7cd607392cef4df799332d8c808891bf85cdcae5 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Mon, 19 Jun 2023 20:34:16 +0200 Subject: [PATCH 04/17] WIP: broken but progressing Failed tests: testRepeatedOnceWithin(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinInRule(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinWithOr(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinWithAnd(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceWithinInCondition(org.drools.ansible.rulebook.integration.api.OnceWithinTest): expected:<0> but was:<1> testOnceAfterWithOr(org.drools.ansible.rulebook.integration.api.OnceAfterTest): expected:<2> but was:<1> Tests run: 138, Failures: 6, Errors: 0, Skipped: 0 --- .../api/rulesmodel/RulesModelUtil.java | 1 + .../integration/api/OnceAfterTest.java | 18 ++++++++++++------ .../integration/api/OnceWithinTest.java | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) 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 e2521f76..21f4a367 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 @@ -13,6 +13,7 @@ public class RulesModelUtil { + @Deprecated // no longer to be used 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"; 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 3eeb0e4e..33ef6096 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 @@ -2,6 +2,8 @@ import org.drools.ansible.rulebook.integration.api.domain.temporal.TimeAmount; import org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil; +import org.drools.ansible.rulebook.integration.protoextractor.ExtractorParser; +import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils; import org.drools.base.facttemplates.Fact; import org.junit.Test; import org.kie.api.runtime.rule.Match; @@ -96,12 +98,12 @@ public void testOnceAfterWithOr() { for (int i = 0; i < 3; i++) { Fact fact = (Fact) matchedRules.get(0).getDeclarationValue("m_" + i); - String host = fact.get("meta.hosts").toString(); + String host = evalAgainstFact(fact, "meta.hosts").toString(); assertTrue( host.equals( "h1" ) || host.equals( "h2" ) ); - String level = fact.get("alert.level").toString(); + String level = evalAgainstFact(fact, "alert.level").toString(); assertTrue( level.equals( "error" ) || level.equals( "warning" ) ); - Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD); + Map map = (Map) fact.asMap(); 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") ); @@ -116,12 +118,12 @@ public void testOnceAfterWithOr() { assertEquals("r1", matchedRules.get(0).getRule().getName()); Fact fact = (Fact) matchedRules.get(0).getDeclarationValue("m"); - String host = fact.get("meta.hosts").toString(); + String host = evalAgainstFact(fact, "meta.hosts").toString(); assertTrue(host.equals("h1")); - String level = fact.get("alert.level").toString(); + String level = evalAgainstFact(fact, "alert.level").toString(); assertTrue(level.equals("warning")); - Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD); + Map map = (Map) fact.asMap(); 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") ); @@ -129,4 +131,8 @@ public void testOnceAfterWithOr() { rulesExecutor.dispose(); } + + private static Object evalAgainstFact(Fact fact, String expr) { + return ExtractorUtils.getValueFrom(ExtractorParser.parse(expr), fact.asMap()); + } } 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 ca5ac43c..cef579d8 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 @@ -122,7 +122,7 @@ private void onceWithinTest(String json) { assertEquals(1, matchedRules.size()); Fact fact = (Fact) matchedRules.get(0).getDeclarationValue("singleton"); - Map map = (Map) fact.get(RulesModelUtil.ORIGINAL_MAP_FIELD); + Map map = (Map) fact.asMap(); 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") ); From 941af73e7a9fcfe98593667f84e580c1683cb2af Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 07:41:10 +0200 Subject: [PATCH 05/17] ALL tests passing --- .../temporal/OnceAbstractTimeConstraint.java | 48 +++++++++++++++++-- .../domain/temporal/OnceAfterDefinition.java | 11 +++-- .../domain/temporal/OnceWithinDefinition.java | 11 +++-- .../CompleteExtractorPrototypeExpression.java | 2 +- .../ExtractorPrototypeExpression.java | 2 +- 5 files changed, 60 insertions(+), 14 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 3ac60f70..b35ffa96 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,8 +1,15 @@ package org.drools.ansible.rulebook.integration.api.domain.temporal; +import org.drools.ansible.rulebook.integration.protoextractor.ExtractorParser; +import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils; +import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpression; import org.drools.ansible.rulebook.integration.protoextractor.prototype.ExtractorPrototypeExpressionUtils; +import org.drools.base.facttemplates.Fact; import org.drools.model.Index; import org.drools.model.PrototypeDSL; +import org.drools.model.PrototypeExpression; +import org.drools.model.PrototypeFact; import org.drools.model.PrototypeVariable; import java.util.List; @@ -19,13 +26,13 @@ public abstract class OnceAbstractTimeConstraint implements TimeConstraint { protected String ruleName; protected final TimeAmount timeAmount; - protected final List groupByAttributes; + protected final List groupByAttributes; protected PrototypeDSL.PrototypePatternDef guardedPattern; private PrototypeVariable controlVariable; - public OnceAbstractTimeConstraint(TimeAmount timeAmount, List groupByAttributes) { + public OnceAbstractTimeConstraint(TimeAmount timeAmount, List groupByAttributes) { this.timeAmount = timeAmount; this.groupByAttributes = groupByAttributes; } @@ -40,8 +47,11 @@ protected PrototypeVariable getControlVariable() { protected PrototypeDSL.PrototypePatternDef createControlPattern() { PrototypeDSL.PrototypePatternDef controlPattern = protoPattern(variable(getPrototype(SYNTHETIC_PROTOTYPE_NAME))); - for (String unique : groupByAttributes) { - controlPattern.expr( ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(unique), Index.ConstraintType.EQUAL, getPatternVariable(), ExtractorPrototypeExpressionUtils.prototypeFieldExtractor(unique) ); + for (GroupByAttribute unique : groupByAttributes) { + controlPattern.expr( prototypeField(unique.getKey()), // intentional, the control fact has the "group by" key string as-is (not structured), so we reference it for the left part + Index.ConstraintType.EQUAL, + getPatternVariable(), + unique.asPrototypeExpression() ); // on the right, we need extractor to check the real fact/event attribute value } controlPattern.expr( ExtractorPrototypeExpressionUtils.prototypeFieldExtractor("drools_rule_name"), Index.ConstraintType.EQUAL, fixedValue(ruleName) ); this.controlVariable = (PrototypeVariable) controlPattern.getFirstVariable(); @@ -63,4 +73,34 @@ static String sanitizeAttributeName(String name) { } return name; } + + public static class GroupByAttribute { + private final String key; + private final ExtractorNode extractor; + + private GroupByAttribute(String key, ExtractorNode extractor) { + this.key = key; + this.extractor = extractor; + } + + public static GroupByAttribute from(String expr) { + return new GroupByAttribute(expr, ExtractorParser.parse(expr)); + } + + public String getKey() { + return key; + } + + public PrototypeExpression asPrototypeExpression() { + return new ExtractorPrototypeExpression(extractor); + } + + public Object evalExtractorOnFact(PrototypeFact fact) { + return evalExtractorOnFact((Fact) fact); + } + + public Object evalExtractorOnFact(Fact fact) { + return ExtractorUtils.getValueFrom(extractor, fact.asMap()); + } + } } 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 a72d7b28..ec2fe7a5 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 @@ -140,7 +140,7 @@ private static Object controlFact2Event(Fact fact) { return writeMetaDataOnEvent((Fact) fact.get("event"), ruleEngineMeta); } - public OnceAfterDefinition(TimeAmount timeAmount, List groupByAttributes) { + public OnceAfterDefinition(TimeAmount timeAmount, List groupByAttributes) { super(timeAmount, groupByAttributes); } @@ -194,8 +194,8 @@ public List getControlRules(RuleGenerationContext ruleContext) { not( createControlPattern() ), on(getPatternVariable()).execute((drools, event) -> { Event controlEvent = createMapBasedEvent( controlPrototype ); - for (String unique : groupByAttributes) { - controlEvent.set(unique, event.get(unique)); + for (GroupByAttribute unique : groupByAttributes) { + controlEvent.set(unique.getKey(), unique.evalExtractorOnFact(event)); } controlEvent.set("drools_rule_name", ruleName); controlEvent.set( "event", event ); @@ -250,7 +250,10 @@ public String toString() { } public static OnceAfterDefinition parseOnceAfter(String onceWithin, List groupByAttributes) { - List sanitizedAttributes = groupByAttributes.stream().map(OnceAbstractTimeConstraint::sanitizeAttributeName).collect(toList()); + List sanitizedAttributes = groupByAttributes.stream() + .map(OnceAbstractTimeConstraint::sanitizeAttributeName) + .map(GroupByAttribute::from) + .collect(toList()); return new OnceAfterDefinition(parseTimeAmount(onceWithin), sanitizedAttributes); } } 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 349cb101..aff4bb26 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 @@ -70,7 +70,7 @@ public class OnceWithinDefinition extends OnceAbstractTimeConstraint { public static final String KEYWORD = "once_within"; - public OnceWithinDefinition(TimeAmount timeAmount, List groupByAttributes) { + public OnceWithinDefinition(TimeAmount timeAmount, List groupByAttributes) { super(timeAmount, groupByAttributes); } @@ -89,8 +89,8 @@ public void executeTimeConstraintConsequence(Drools drools, Object... facts) { Event controlEvent = createMapBasedEvent( getPrototype(SYNTHETIC_PROTOTYPE_NAME) ) .withExpiration(timeAmount.getAmount(), timeAmount.getTimeUnit()); Fact fact = (Fact) facts[0]; - for (String unique : groupByAttributes) { - controlEvent.set(unique, fact.get(unique)); + for (GroupByAttribute unique : groupByAttributes) { + controlEvent.set(unique.getKey(), unique.evalExtractorOnFact(fact)); } controlEvent.set("drools_rule_name", ruleName); drools.insert(controlEvent); @@ -127,7 +127,10 @@ public String toString() { } public static OnceWithinDefinition parseOnceWithin(String onceWithin, List groupByAttributes) { - List sanitizedAttributes = groupByAttributes.stream().map(OnceAbstractTimeConstraint::sanitizeAttributeName).collect(toList()); + List sanitizedAttributes = groupByAttributes.stream() + .map(OnceAbstractTimeConstraint::sanitizeAttributeName) + .map(GroupByAttribute::from) + .collect(toList()); return new OnceWithinDefinition(parseTimeAmount(onceWithin), sanitizedAttributes); } } diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java index 50958275..82ecb70f 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java @@ -12,7 +12,7 @@ public class CompleteExtractorPrototypeExpression extends ExtractorPrototypeExpression implements EvaluableExpression { private final PrototypeVariable protoVar; - CompleteExtractorPrototypeExpression(PrototypeVariable protoVar, ExtractorNode extractorNode) { + public CompleteExtractorPrototypeExpression(PrototypeVariable protoVar, ExtractorNode extractorNode) { super(extractorNode); this.protoVar = protoVar; } diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java index 67c9e61c..e1466b2a 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -16,7 +16,7 @@ public class ExtractorPrototypeExpression implements PrototypeExpression { protected final ExtractorNode extractorNode; - ExtractorPrototypeExpression(ExtractorNode extractorNode) { + public ExtractorPrototypeExpression(ExtractorNode extractorNode) { this.extractorNode = extractorNode; } From 7db369bea6201a5c0f262d279f0c8e432ae87201 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 07:44:47 +0200 Subject: [PATCH 06/17] small refactor --- .../prototype/ExtractorPrototypeExpression.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java index e1466b2a..e19d1616 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -15,9 +15,13 @@ public class ExtractorPrototypeExpression implements PrototypeExpression { protected final ExtractorNode extractorNode; + protected final String computedFieldName; + protected final Collection computedImpactedFields; public ExtractorPrototypeExpression(ExtractorNode extractorNode) { this.extractorNode = extractorNode; + this.computedFieldName = ExtractorUtils.getParts(extractorNode).stream().collect(Collectors.joining()); + this.computedImpactedFields = Collections.singletonList(ExtractorUtils.getParts(extractorNode).get(0)); } @Override @@ -31,7 +35,7 @@ public Function1 asFunction(Prototype prototype) { // TODO used for indexing, normalizing chunks. public String getFieldName() { - return ExtractorUtils.getParts(extractorNode).stream().collect(Collectors.joining()); + return this.computedFieldName; } @Override @@ -41,6 +45,6 @@ public String toString() { @Override public Collection getImpactedFields() { - return Collections.singletonList(ExtractorUtils.getParts(extractorNode).get(0)); + return this.computedImpactedFields; } } From b1cf0d77372579a46620d1458511cbc526bd56d9 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 07:56:23 +0200 Subject: [PATCH 07/17] cleanup RulesModelUtil --- .../api/rulesmodel/RulesModelUtil.java | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) 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 21f4a367..7aa3d895 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 @@ -22,37 +22,16 @@ private RulesModelUtil() { } public static Fact mapToFact(Map factMap, boolean event) { Fact fact = event ? createMapBasedEvent( getPrototype(DEFAULT_PROTOTYPE_NAME), factMap ) : createMapBasedFact( getPrototype(DEFAULT_PROTOTYPE_NAME), factMap ); - // the culprit, disabled: - // populateFact(fact, factMap, ""); - // fact.set(ORIGINAL_MAP_FIELD, factMap); return fact; } - // private static void populateFact(Fact fact, Map value, String fieldName) { - // for (Map.Entry entry : value.entrySet()) { - // String key = fieldName + entry.getKey(); - // fact.set(key, entry.getValue()); - // if (entry.getValue() instanceof Map) { - // populateFact(fact, (Map) entry.getValue(), key + "."); - // } - // } - // } - public static Object factToMap(Object fact) { if (fact instanceof Fact) { - return (((Fact) fact).asMap()); - } - if (fact instanceof Map) { - return (((Map) fact)); + return ((Fact) fact).asMap(); } return fact; } - // private static Map factToMap(Map factMap) { - // Map map = (Map) factMap.get(ORIGINAL_MAP_FIELD); - // return map != null ? map : factMap; - // } - public static Map asFactMap(String json) { return new JSONObject(json).toMap(); } From 5f6004c445121f112d706f897e540a9ad6914fd4 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 08:24:02 +0200 Subject: [PATCH 08/17] refactor, small fix for array access, tests --- .../pom.xml | 2 +- .../protoextractor/ast/AbstractNode.java | 2 +- .../protoextractor/ast/DefaultedVisitor.java | 2 +- .../protoextractor/ast/Visitor.java | 2 +- .../prototype/ValueExtractionVisitor.java | 2 +- .../protoextractor/ExtractorTest.java | 38 +++++++++++++++++++ .../src/test/resources/mixedBag.json | 2 +- .../src/test/resources/test1.json | 2 +- 8 files changed, 45 insertions(+), 7 deletions(-) diff --git a/drools-ansible-rulebook-integration-protoextractor/pom.xml b/drools-ansible-rulebook-integration-protoextractor/pom.xml index f2ab8fbf..374e99ae 100644 --- a/drools-ansible-rulebook-integration-protoextractor/pom.xml +++ b/drools-ansible-rulebook-integration-protoextractor/pom.xml @@ -93,4 +93,4 @@ - \ No newline at end of file + diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java index 11603256..89781df4 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/AbstractNode.java @@ -114,4 +114,4 @@ public void setText(String text) { public String toString() { return getClass().getSimpleName()+"{" + text + "}"; } -} \ No newline at end of file +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java index c31723dc..d4ccf913 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/DefaultedVisitor.java @@ -17,4 +17,4 @@ public T visit(IndexAccessorNode n) { public T visit(ExtractorNode n) { return defaultVisit(n); } -} \ No newline at end of file +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java index 4d352eca..28a74890 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/ast/Visitor.java @@ -6,4 +6,4 @@ public interface Visitor { T visit(SquaredAccessorNode n); T visit(IndexAccessorNode n); T visit(ExtractorNode n); -} \ No newline at end of file +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java index 820be37a..419760ec 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java @@ -55,7 +55,7 @@ public Object visit(SquaredAccessorNode n) { public Object visit(IndexAccessorNode n) { if (cur instanceof List) { List theList = (List) cur; - int javaIdx = n.getValue() > 0 ? n.getValue() : theList.size() + n.getValue(); + int javaIdx = n.getValue() >= 0 ? n.getValue() : theList.size() + n.getValue(); if (javaIdx < theList.size()) { // avoid index out of bounds; cur = theList.get(javaIdx); } else { diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java index 152ab74a..5afcd262 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/ExtractorTest.java @@ -8,6 +8,7 @@ import java.util.Map; import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; +import org.drools.model.Prototype; import org.junit.Test; import com.fasterxml.jackson.databind.ObjectMapper; @@ -59,4 +60,41 @@ public void testMixedBagReverse() throws Exception { .as("extractor can be used to extract value based on the path expression") .isEqualTo(47); } + + @Test + public void testMixedBagNullRange2() throws Exception { + assertThat(valueFromMixedBag("range2")) + .as("the key `range2` exists as a first level entry in the json, but the value is the null literal") + .isNull(); + } + + @Test + public void testMixedBagUndefFirstLevel() throws Exception { + assertThat(valueFromMixedBag("unexisting") == Prototype.UNDEFINED_VALUE) + .as("this key does not exists at all in the json") + .isTrue(); + } + + @Test + public void testMixedBagArrayIndexExistsButNullValue() throws Exception { + assertThat(valueFromMixedBag("range.x[0]")) + .as("this is accessing index 0 so the first element in a json array of size 2, but the first element in the json array is the null literal") + .isNull(); + } + + @Test + public void testMixedBagArrayIndexUnexisting() throws Exception { + assertThat(valueFromMixedBag("range.x[999]") == Prototype.UNDEFINED_VALUE) + .as("the json array is of size 2, so this 999 index position is undef") + .isTrue(); + } + + private Object valueFromMixedBag(String expression) throws Exception { + ExtractorNode extractor = ExtractorParser.parse(expression); + + final String JSON = Files.readString(Paths.get(ExtractorTest.class.getResource("/mixedBag.json").toURI())); + Map readValue = new ObjectMapper().readValue(JSON, Map.class); + Object valueExtracted = ExtractorUtils.getValueFrom(extractor, readValue); + return valueExtracted; + } } diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json index ca108357..68ae4aeb 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/mixedBag.json @@ -14,4 +14,4 @@ ] }, "range2": null -} \ No newline at end of file +} diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json index 409877b5..9ae62c02 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/resources/test1.json @@ -14,4 +14,4 @@ } } } -} \ No newline at end of file +} From 2ebf1f44355d1d506e27c733e70b42f6c6930435 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 08:46:29 +0200 Subject: [PATCH 09/17] fix grammar typo --- .../rulebook/integration/protoextractor/Protoextractor.g4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 b/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 index d4248b9e..80e07683 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/antlr4/org/drools/ansible/rulebook/integration/protoextractor/Protoextractor.g4 @@ -17,7 +17,7 @@ identifier : ID ; ID : [\p{Letter}_][\p{Letter}_0-9]* ; // ref https://github.com/ansible/ansible-rulebook/blob/24444bfcefcd7e9551d708723b76f58c3de9e976/ansible_rulebook/condition_parser.py#L118-L123 -STRING1 : '\'' ~('\r' | '\n' | '"')* '\'' ; +STRING1 : '\'' ~('\r' | '\n' | '\'')* '\'' ; STRING2 : '"' ~('\r' | '\n' | '"')* '"' ; SIGNED_INT : (MINUS|PLUS)? DIGITS; From bc9cb3639f8787aa5ea9cc73f94dce0716d74804 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 09:36:29 +0200 Subject: [PATCH 10/17] refactor ExistsField as discussed with Mario --- .../api/domain/constraints/ExistsField.java | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java index 5c0d83bb..4a539d09 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/constraints/ExistsField.java @@ -4,15 +4,11 @@ import java.util.function.BiPredicate; import org.drools.ansible.rulebook.integration.api.domain.RuleGenerationContext; -import org.drools.ansible.rulebook.integration.api.domain.conditions.ConditionExpression; import org.drools.ansible.rulebook.integration.api.rulesmodel.ParsedCondition; import org.drools.model.ConstraintOperator; import org.drools.model.Prototype; -import org.drools.model.PrototypeExpression; -import org.drools.model.PrototypeFact; import static org.drools.ansible.rulebook.integration.api.domain.conditions.ConditionExpression.map2Expr; -import static org.drools.model.PrototypeExpression.fixedValue; import static org.drools.model.PrototypeExpression.thisPrototype; public enum ExistsField implements ConstraintOperator, ConditionFactory { @@ -24,7 +20,7 @@ public enum ExistsField implements ConstraintOperator, ConditionFactory { @Override public BiPredicate asPredicate() { - throw new UnsupportedOperationException("converted to make use of "+ExistsFieldUsingExtractor.class.getCanonicalName()); + return (t, v) -> v != Prototype.UNDEFINED_VALUE; // actually, always true from caller: https://github.com/kiegroup/drools/blob/9de8d0b54b364bda1b1d76d81923c8bfc060c2f8/drools-model/drools-canonical-model/src/main/java/org/drools/model/PrototypeDSL.java#L273 } @Override @@ -34,23 +30,6 @@ public String toString() { @Override public ParsedCondition createParsedCondition(RuleGenerationContext ruleContext, String expressionName, Map expression) { - ConditionExpression map2Expr = map2Expr(ruleContext, expression); - PrototypeExpression usedProtototypeExpr = map2Expr.getPrototypeExpression(); - PrototypeExpression unused = fixedValue(map2Expr.getFieldName()); - return new ParsedCondition(thisPrototype(), new ExistsFieldUsingExtractor(usedProtototypeExpr), unused).withNotPattern(expressionName.equals(NEGATED_EXPRESSION_NAME)); - } - - public static class ExistsFieldUsingExtractor implements ConstraintOperator { - private final PrototypeExpression expr; - - public ExistsFieldUsingExtractor(PrototypeExpression expr) { - this.expr = expr; - } - - @Override - public BiPredicate asPredicate() { - return (t, v) -> expr.asFunction(null).apply((PrototypeFact) t) != Prototype.UNDEFINED_VALUE; - } - + return new ParsedCondition(thisPrototype(), this, map2Expr(ruleContext, expression).getPrototypeExpression()).withNotPattern(expressionName.equals(NEGATED_EXPRESSION_NAME)); } } From 0f41f50052adc3dc3a6668ab1375c6a805fff0e2 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 14:03:29 +0200 Subject: [PATCH 11/17] implement code review feedback --- .../protoextractor/prototype/ValueExtractionVisitor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java index 419760ec..598e3de5 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ValueExtractionVisitor.java @@ -22,8 +22,7 @@ public ValueExtractionVisitor(Object original) { @Override public Object defaultVisit(ASTNode n) { - cur = null; - return cur; + throw new UnsupportedOperationException("this visitor implemented all visit methods"); } @Override From f6ccd3735b13703c595e46039cf76849386e098e Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 14:16:02 +0200 Subject: [PATCH 12/17] implement code review feedback --- .../prototype/CompleteExtractorPrototypeExpression.java | 2 +- .../prototype/ExtractorPrototypeExpression.java | 9 ++++++++- .../protoextractor/prototype/PrototypeTest.java | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java index 82ecb70f..7a9e3406 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/CompleteExtractorPrototypeExpression.java @@ -34,7 +34,7 @@ public Collection getPrototypeVariables() { @Override public Object evaluate(Map factsMap) { PrototypeFact prototypeFact = factsMap.get(protoVar); - return asFunction(null).apply(prototypeFact); + return asFunction(IGNORED).apply(prototypeFact); } @Override diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java index e19d1616..a0f56cd6 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -9,11 +9,18 @@ import org.drools.ansible.rulebook.integration.protoextractor.ast.ExtractorNode; import org.drools.base.facttemplates.Fact; import org.drools.model.Prototype; +import org.drools.model.PrototypeDSL; import org.drools.model.PrototypeExpression; import org.drools.model.PrototypeFact; import org.drools.model.functions.Function1; public class ExtractorPrototypeExpression implements PrototypeExpression { + /** + * This is okay for ansible-integration work as prototype do not define accessor programmatically, + * but having prototype definition ignored in {@link #asFunction(Prototype)} call, ought to be revised + * if porting this module into Drools and generalizing beyond usage in ansible-integration. + */ + public static final Prototype IGNORED = PrototypeDSL.prototype("IGNORED"); protected final ExtractorNode extractorNode; protected final String computedFieldName; protected final Collection computedImpactedFields; @@ -28,7 +35,7 @@ public ExtractorPrototypeExpression(ExtractorNode extractorNode) { public Function1 asFunction(Prototype prototype) { return pf -> { Map asMap = ((Fact) pf).asMap(); - Object value = new ValueExtractionVisitor(asMap).visit(extractorNode); + Object value = ExtractorUtils.getValueFrom(extractorNode, asMap); return value; }; } diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java index 9077beac..21b3472c 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java @@ -39,7 +39,7 @@ public void testBasic() throws Exception { .isNotNull() .isInstanceOf(Map.class); - Object valueExtracted = expr.asFunction(null).apply((PrototypeFact) mapBasedFact); + Object valueExtracted = expr.asFunction(ExtractorPrototypeExpression.IGNORED).apply((PrototypeFact) mapBasedFact); assertThat(valueExtracted) .as("ExtractorPrototypeExpression used to extract value based on the path expression") .isEqualTo(47); From b536459df5a73ce8fd20cab9415e272df07f6f1f Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 14:22:14 +0200 Subject: [PATCH 13/17] code review feedback impl about ORIGINAL_MAP_FIELD --- .../integration/api/rulesengine/RulesExecutorSession.java | 3 +-- .../rulebook/integration/api/rulesmodel/RulesModelUtil.java | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java index d7f37078..61549144 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java @@ -16,7 +16,6 @@ import java.util.function.BiPredicate; import java.util.stream.Collectors; -import static org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil.ORIGINAL_MAP_FIELD; import static org.drools.ansible.rulebook.integration.api.rulesmodel.RulesModelUtil.mapToFact; @@ -107,7 +106,7 @@ private static boolean isKeyToBeIgnored(String wmFactKey, String... keysToExclud } } } - return wmFactKey.equals(ORIGINAL_MAP_FIELD); + return false; } int fireAllRules() { 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 7aa3d895..58730683 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 @@ -12,9 +12,6 @@ import static org.drools.modelcompiler.facttemplate.FactFactory.createMapBasedFact; public class RulesModelUtil { - - @Deprecated // no longer to be used - 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"; From b2602bb2a23dd0701719073502824225802199e4 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 15:59:10 +0200 Subject: [PATCH 14/17] BREAKING to investigate indexing issue --- .../integration/api/RulesExecutor.java | 5 ++ .../rulesengine/AbstractRulesEvaluator.java | 6 +++ .../api/rulesengine/RulesEvaluator.java | 3 ++ .../api/rulesengine/RulesExecutorSession.java | 4 ++ .../rulebook/integration/api/AlphaTest.java | 49 +++++++++++++++++-- .../integration/api/TimedOutTest.java | 2 + .../ExtractorPrototypeExpression.java | 4 +- 7 files changed, 67 insertions(+), 6 deletions(-) diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java index fc4f8034..f7590baa 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/RulesExecutor.java @@ -4,6 +4,7 @@ import org.drools.ansible.rulebook.integration.api.rulesengine.RulesExecutorSession; import org.drools.ansible.rulebook.integration.api.rulesengine.SessionStats; import org.drools.base.facttemplates.Fact; +import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.Match; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,4 +96,8 @@ public String getAllFactsAsJson() { public CompletableFuture> advanceTime(long amount, TimeUnit unit ) { return rulesEvaluator.advanceTime(amount, unit ); } + + public KieSession asKieSession() { + return rulesEvaluator.asKieSession(); + } } diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java index ee8b4542..fc24cd4e 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/AbstractRulesEvaluator.java @@ -19,6 +19,7 @@ import org.drools.ansible.rulebook.integration.api.io.RuleExecutorChannel; import org.drools.base.facttemplates.Fact; import org.drools.core.common.InternalFactHandle; +import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.Match; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -247,4 +248,9 @@ private List atomicRuleEvaluation(boolean processEventInsertion, Supplier return matches; } + + @Override + public KieSession asKieSession() { + return rulesExecutorSession.asKieSession(); + } } diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java index 4d71ae88..a37cbc3a 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesEvaluator.java @@ -1,6 +1,7 @@ package org.drools.ansible.rulebook.integration.api.rulesengine; import org.drools.ansible.rulebook.integration.api.RulesExecutorContainer; +import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.Match; import java.util.Collection; @@ -41,4 +42,6 @@ public interface RulesEvaluator { static RulesEvaluator createRulesEvaluator( RulesExecutorSession rulesExecutorSession, boolean async ) { return async ? new AsyncRulesEvaluator(rulesExecutorSession) : new SyncRulesEvaluator(rulesExecutorSession); } + + KieSession asKieSession(); } diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java index 61549144..db0c9ebc 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/rulesengine/RulesExecutorSession.java @@ -164,4 +164,8 @@ void setExecuteActions(boolean executeActions) { public boolean isMatchMultipleRules() { return rulesSet.isMatchMultipleRules(); } + + public KieSession asKieSession() { + return kieSession; + } } diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java index d439ee01..37133fbb 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/AlphaTest.java @@ -1,11 +1,27 @@ package org.drools.ansible.rulebook.integration.api; -import java.util.List; - +import org.drools.ansible.rulebook.integration.api.rulesmodel.PrototypeFactory; +import org.drools.base.definitions.InternalKnowledgePackage; +import org.drools.base.facttemplates.FactTemplate; +import org.drools.base.facttemplates.FactTemplateObjectType; +import org.drools.base.rule.IndexableConstraint; +import org.drools.base.util.index.ConstraintTypeOperator; +import org.drools.core.reteoo.AlphaNode; +import org.drools.core.reteoo.EntryPointNode; +import org.drools.core.reteoo.ObjectSink; +import org.drools.core.reteoo.ObjectTypeNode; +import org.drools.core.reteoo.Rete; +import org.drools.kiesession.rulebase.InternalKnowledgeBase; +import org.drools.model.Prototype; +import org.drools.modelcompiler.facttemplate.FactFactory; import org.junit.Test; +import org.kie.api.definition.rule.Rule; import org.kie.api.runtime.rule.Match; +import java.util.List; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class AlphaTest { @@ -31,7 +47,7 @@ public void testEqualsWithFixedValue() { " ]\n" + " },\n" + " \"enabled\": true,\n" + - " \"name\": null\n" + + " \"name\": \"R1\"\n" + " }\n" + " }\n" + " ]\n" + @@ -39,6 +55,9 @@ public void testEqualsWithFixedValue() { RulesExecutor rulesExecutor = RulesExecutorFactory.createFromJson(json); + Rete rete = ((InternalKnowledgeBase) rulesExecutor.asKieSession().getKieBase()).getRete(); + assertConstraintType(rete, "R1", ConstraintTypeOperator.EQUAL); + List matchedRules = rulesExecutor.processEvents("{ \"i\": 3 }").join(); assertEquals(0, matchedRules.size()); @@ -47,11 +66,33 @@ public void testEqualsWithFixedValue() { matchedRules = rulesExecutor.processEvents("{ \"i\": 3, \"j\": 3 }").join(); assertEquals(1, matchedRules.size()); - assertEquals("r_0", matchedRules.get(0).getRule().getName()); + assertEquals("R1", matchedRules.get(0).getRule().getName()); rulesExecutor.dispose(); } + private void assertConstraintType(Rete rete, String ruleName, ConstraintTypeOperator expectedType) { + boolean asserted = false; + assertEquals("expecting only 1 pkg.", 1, rete.getRuleBase().getPackages().length); + InternalKnowledgePackage ipkg = rete.getRuleBase().getPackages()[0]; + Prototype default_proto = PrototypeFactory.getPrototype(PrototypeFactory.DEFAULT_PROTOTYPE_NAME); + FactTemplate factTemplate = FactFactory.prototypeToFactTemplate(default_proto, ipkg); + EntryPointNode epn = rete.getEntryPointNodes().values().iterator().next(); + ObjectTypeNode otn = epn.getObjectTypeNodes().get(new FactTemplateObjectType(factTemplate)); + ObjectSink[] sinks = otn.getObjectSinkPropagator().getSinks(); + for (ObjectSink objectSink : sinks) { + AlphaNode alphaNode = (AlphaNode) objectSink; + assertEquals("expecting that one rule has one AlphaNode.", 1, alphaNode.getAssociatedRules().length); + Rule rule = alphaNode.getAssociatedRules()[0]; + if (rule.getName().equals(ruleName)) { + IndexableConstraint constraint = (IndexableConstraint) alphaNode.getConstraint(); + assertEquals(expectedType, constraint.getConstraintType()); + asserted = true; + } + } + assertTrue(asserted); + } + @Test public void testEqualsOn2Fields() { String json = diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java index 250e89b2..9e8959c0 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java @@ -12,6 +12,7 @@ import org.drools.ansible.rulebook.integration.api.rulesengine.SessionStats; import org.drools.base.facttemplates.Fact; import org.json.JSONObject; +import org.junit.Ignore; import org.junit.Test; import org.kie.api.runtime.rule.Match; @@ -183,6 +184,7 @@ public void testSimpleTimedOut() { assertEquals(0, stats.getEventsSuppressed()); } + @Ignore // TODO NOOOOOO @Test public void testTimedOutWithAutomaticClockAdvance() throws IOException { String json = diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java index a0f56cd6..4713449f 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -11,10 +11,11 @@ import org.drools.model.Prototype; import org.drools.model.PrototypeDSL; import org.drools.model.PrototypeExpression; +import org.drools.model.PrototypeExpression.IndexableExpression; import org.drools.model.PrototypeFact; import org.drools.model.functions.Function1; -public class ExtractorPrototypeExpression implements PrototypeExpression { +public class ExtractorPrototypeExpression implements PrototypeExpression, IndexableExpression { /** * This is okay for ansible-integration work as prototype do not define accessor programmatically, * but having prototype definition ignored in {@link #asFunction(Prototype)} call, ought to be revised @@ -40,7 +41,6 @@ public Function1 asFunction(Prototype prototype) { }; } - // TODO used for indexing, normalizing chunks. public String getFieldName() { return this.computedFieldName; } From 08c78d324ce07a7d2acd92d2b04729f3dca93eb9 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Tue, 20 Jun 2023 17:21:07 +0200 Subject: [PATCH 15/17] revert the disabled commit --- .../drools/ansible/rulebook/integration/api/TimedOutTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java index 9e8959c0..dfa15c27 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java @@ -183,8 +183,7 @@ public void testSimpleTimedOut() { assertEquals(1, stats.getEventsProcessed()); assertEquals(0, stats.getEventsSuppressed()); } - - @Ignore // TODO NOOOOOO + @Test public void testTimedOutWithAutomaticClockAdvance() throws IOException { String json = From 4dc46563be9b8807ebfd5b97cf148ef6afba6cd2 Mon Sep 17 00:00:00 2001 From: tarilabs Date: Wed, 21 Jun 2023 12:23:25 +0200 Subject: [PATCH 16/17] implement code review feedback --- .../api/domain/conditions/ConditionExpression.java | 2 +- .../api/domain/constraints/SelectConstraintTest.java | 4 ++-- .../prototype/ExtractorPrototypeExpression.java | 9 +++++---- .../protoextractor/prototype/PrototypeTest.java | 5 +++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java index 151250b8..bd6a302a 100644 --- a/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java +++ b/drools-ansible-rulebook-integration-api/src/main/java/org/drools/ansible/rulebook/integration/api/domain/conditions/ConditionExpression.java @@ -56,7 +56,7 @@ public boolean isBeta() { } public String getFieldName() { - return ((ExtractorPrototypeExpression) prototypeExpression).getFieldName(); + return prototypeExpression.getIndexingKey().orElseThrow(IllegalStateException::new); } public PrototypeExpression getPrototypeExpression() { diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java index 3993d610..89a7fd94 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/domain/constraints/SelectConstraintTest.java @@ -44,7 +44,7 @@ public void createParsedConditionWithSelectExpression() { ParsedCondition parsedCondition = SelectConstraint.INSTANCE.createParsedCondition(new RuleGenerationContext(), SelectConstraint.EXPRESSION_NAME, map); assertThat(parsedCondition.getLeft()).isInstanceOf(ExtractorPrototypeExpression.class); - assertThat(((ExtractorPrototypeExpression)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels"); + assertThat(parsedCondition.getLeft().getIndexingKey()).isPresent().contains("levels"); assertThat(parsedCondition.getRight()).isInstanceOf(PrototypeExpression.FixedValue.class); // rightValue is not important. "25" is already incorporated in the operator @@ -67,7 +67,7 @@ public void createParsedConditionWithSelectNotExpression() { ParsedCondition parsedCondition = SelectConstraint.INSTANCE.createParsedCondition(new RuleGenerationContext(), SelectConstraint.NEGATED_EXPRESSION_NAME, map); assertThat(parsedCondition.getLeft()).isInstanceOf(ExtractorPrototypeExpression.class); - assertThat(((ExtractorPrototypeExpression)parsedCondition.getLeft()).getFieldName()).isEqualTo("levels"); + assertThat(parsedCondition.getLeft().getIndexingKey()).isPresent().contains("levels"); assertThat(parsedCondition.getRight()).isInstanceOf(PrototypeExpression.FixedValue.class); // rightValue is not important. "25" is already incorporated in the operator diff --git a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java index 4713449f..7d663e88 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/main/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/ExtractorPrototypeExpression.java @@ -3,6 +3,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import org.drools.ansible.rulebook.integration.protoextractor.ExtractorUtils; @@ -11,11 +12,10 @@ import org.drools.model.Prototype; import org.drools.model.PrototypeDSL; import org.drools.model.PrototypeExpression; -import org.drools.model.PrototypeExpression.IndexableExpression; import org.drools.model.PrototypeFact; import org.drools.model.functions.Function1; -public class ExtractorPrototypeExpression implements PrototypeExpression, IndexableExpression { +public class ExtractorPrototypeExpression implements PrototypeExpression { /** * This is okay for ansible-integration work as prototype do not define accessor programmatically, * but having prototype definition ignored in {@link #asFunction(Prototype)} call, ought to be revised @@ -41,8 +41,9 @@ public Function1 asFunction(Prototype prototype) { }; } - public String getFieldName() { - return this.computedFieldName; + @Override + public Optional getIndexingKey() { + return Optional.of(this.computedFieldName); } @Override diff --git a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java index 21b3472c..2f9a177f 100644 --- a/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java +++ b/drools-ansible-rulebook-integration-protoextractor/src/test/java/org/drools/ansible/rulebook/integration/protoextractor/prototype/PrototypeTest.java @@ -27,9 +27,10 @@ public void testBasic() throws Exception { assertThat(expr.getImpactedFields()) .as("always the first of the chunks") .isEqualTo(List.of("a")); - assertThat(expr.getFieldName()) + assertThat(expr.getIndexingKey()) .as("TODO normalization of chunks used for indexing") - .isEqualTo("abcde/asdfgh"); + .isPresent() + .contains("abcde/asdfgh"); final String JSON = Files.readString(Paths.get(PrototypeTest.class.getResource("/test1.json").toURI())); final Map readValue = new ObjectMapper().readValue(JSON, new TypeReference>() {}); From 896b9259d363e4f3b5f3cd0aabf559e74a80498d Mon Sep 17 00:00:00 2001 From: tarilabs Date: Wed, 21 Jun 2023 15:34:50 +0200 Subject: [PATCH 17/17] code review feedback --- .../api/domain/temporal/OnceAbstractTimeConstraint.java | 5 +++++ .../ansible/rulebook/integration/api/TimedOutTest.java | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) 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 b35ffa96..d372769a 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 @@ -102,5 +102,10 @@ public Object evalExtractorOnFact(PrototypeFact fact) { public Object evalExtractorOnFact(Fact fact) { return ExtractorUtils.getValueFrom(extractor, fact.asMap()); } + + @Override + public String toString() { + return "GroupByAttribute [key=" + key + ", extractor=" + extractor + "]"; + } } } diff --git a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java index dfa15c27..fc7f385c 100644 --- a/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java +++ b/drools-ansible-rulebook-integration-api/src/test/java/org/drools/ansible/rulebook/integration/api/TimedOutTest.java @@ -12,7 +12,6 @@ import org.drools.ansible.rulebook.integration.api.rulesengine.SessionStats; import org.drools.base.facttemplates.Fact; import org.json.JSONObject; -import org.junit.Ignore; import org.junit.Test; import org.kie.api.runtime.rule.Match;