From 9bce6f38c3d7dc03c6307055360048767fde7dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20H=2E=20Rapat=C3=A3o?= Date: Sat, 6 Jul 2024 14:34:15 +0100 Subject: [PATCH] feat: enable library user to extend Engines to have customizable operators (#27) This change enables each engine to have its operator without enforcing others to have the same capabilities. All built-in operations are still supported, and it doesn't restrict having others built-in operators in the future. By having this change, any library users can now start customizing the existing operators and even creating new operators without enforcing to have them in the library code base. --- JSON.md | 490 +++++++++--------- README.md | 47 +- .../projects/ruleset/engine/Evaluator.kt | 21 +- .../ruleset/engine/context/EvalContext.kt | 8 +- .../ruleset/engine/types/Expression.kt | 26 +- .../projects/ruleset/engine/types/Operator.kt | 55 -- .../engine/types/builder/BetweenBuilder.kt | 7 +- .../engine/types/builder/ExpressionBuilder.kt | 36 +- .../builder/extensions/BetweenExtensions.kt | 5 +- .../builder/extensions/ContainsExtensions.kt | 4 +- .../builder/extensions/WithExtensions.kt | 5 +- .../engine/types/errors/UnknownOperator.kt | 8 + .../types/operators/BuiltInOperators.kt | 13 + .../types/operators/ContainsOperator.kt | 9 + .../types/operators/EndsWithOperator.kt | 8 + .../engine/types/operators/EqualsOperator.kt | 8 + .../operators/GreaterOrEqualThanOperator.kt | 9 + .../types/operators/GreaterThanOperator.kt | 8 + .../operators/LessOrEqualThanOperator.kt | 9 + .../types/operators/LessThanOperator.kt | 8 + .../types/operators/NotEqualsOperator.kt | 8 + .../engine/types/operators/Operator.kt | 23 + .../types/operators/StartsWithOperator.kt | 8 + .../evaluator/graaljs/GraalJSContext.kt | 24 +- .../evaluator/graaljs/GraalJSEvaluator.kt | 18 +- .../engine/evaluator/graaljs/Operators.kt | 80 +++ .../engine/evaluator/graaljs/Parser.kt | 39 -- .../ruleset/jackson/ExpressionMixin.kt | 9 - .../jackson/SerializationExamplesBuilder.kt | 5 +- .../ruleset/jackson/SerializationTest.kt | 2 +- .../engine/evaluator/kotlin/KotlinContext.kt | 11 +- .../evaluator/kotlin/KotlinEvaluator.kt | 18 +- .../engine/evaluator/kotlin/Operators.kt | 69 +++ .../ruleset/engine/evaluator/kotlin/Parser.kt | 32 -- .../engine/evaluator/rhino/Operators.kt | 82 +++ .../ruleset/engine/evaluator/rhino/Parser.kt | 38 -- .../engine/evaluator/rhino/RhinoContext.kt | 27 +- .../engine/evaluator/rhino/RhinoEvaluator.kt | 16 +- .../ruleset/engine/BaseEvaluatorTest.kt | 16 + .../ruleset/engine/types/ExpressionTest.kt | 62 ++- 40 files changed, 811 insertions(+), 560 deletions(-) delete mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Operator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/errors/UnknownOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/BuiltInOperators.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/ContainsOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EndsWithOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EqualsOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterOrEqualThanOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterThanOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessOrEqualThanOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessThanOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/NotEqualsOperator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/Operator.kt create mode 100644 core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/StartsWithOperator.kt create mode 100644 graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Operators.kt delete mode 100644 graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Parser.kt create mode 100644 kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Operators.kt delete mode 100644 kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Parser.kt create mode 100644 rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Operators.kt delete mode 100644 rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Parser.kt diff --git a/JSON.md b/JSON.md index 8db25fa..de3624b 100644 --- a/JSON.md +++ b/JSON.md @@ -9,7 +9,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -17,7 +17,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : "true" } ``` @@ -25,7 +25,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : 10 } ``` @@ -33,7 +33,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : "\"value\"" } ``` @@ -41,7 +41,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : "value" } ``` @@ -51,7 +51,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : true } ``` @@ -59,7 +59,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "true" } ``` @@ -67,7 +67,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : 10 } ``` @@ -75,7 +75,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "\"value\"" } ``` @@ -83,7 +83,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "value" } ``` @@ -93,7 +93,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 } ``` @@ -103,7 +103,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 } ``` @@ -113,7 +113,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -123,7 +123,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ``` @@ -133,7 +133,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "STARTS_WITH", + "operator" : "starts_with", "right" : "\"value\"" } ``` @@ -143,7 +143,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "ENDS_WITH", + "operator" : "ends_with", "right" : "\"value\"" } ``` @@ -153,7 +153,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "field", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"value\"" } ``` @@ -164,11 +164,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -180,11 +180,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -196,11 +196,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -212,29 +212,29 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ], "anyMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ], "noneMatch" : [ { "left" : "field", - "operator" : "EQUALS", + "operator" : "equals", "right" : true }, { "left" : "price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10.0 } ] } @@ -248,7 +248,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "EQUALS", + "operator" : "equals", "right" : 10 } ``` @@ -256,7 +256,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "EQUALS", + "operator" : "equals", "right" : 0 } ``` @@ -264,7 +264,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : 0 } ``` @@ -272,7 +272,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : 10 } ``` @@ -280,7 +280,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : true, - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -288,7 +288,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : false, - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -296,7 +296,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : true, - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -304,7 +304,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : false, - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -312,7 +312,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -320,7 +320,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -328,7 +328,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.falseValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : true } ``` @@ -336,7 +336,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.falseValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : false } ``` @@ -344,7 +344,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "EQUALS", + "operator" : "equals", "right" : "true" } ``` @@ -352,7 +352,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : "false" } ``` @@ -360,7 +360,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.trueValue", - "operator" : "NOT_EQUALS", + "operator" : "not_equals", "right" : false } ``` @@ -368,7 +368,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.field.that.dont.exist", - "operator" : "EQUALS", + "operator" : "equals", "right" : "10", "onFailure" : "TRUE" } @@ -377,7 +377,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.field.that.dont.exist", - "operator" : "EQUALS", + "operator" : "equals", "right" : "10", "onFailure" : "FALSE" } @@ -387,11 +387,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -401,11 +401,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -415,11 +415,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -429,11 +429,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -443,11 +443,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 9 } ] } @@ -457,11 +457,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 9 } ] } @@ -471,11 +471,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 9 } ] } @@ -485,11 +485,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 9 } ] } @@ -499,11 +499,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -513,11 +513,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -527,11 +527,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -541,11 +541,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 11 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -555,11 +555,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -569,11 +569,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -583,11 +583,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 15 } ] } @@ -597,11 +597,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 15 } ] } @@ -611,11 +611,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ] } @@ -625,11 +625,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ] } @@ -639,11 +639,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ] } @@ -653,11 +653,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 5 }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ] } @@ -666,7 +666,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 0 } ``` @@ -674,7 +674,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 10 } ``` @@ -682,7 +682,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 20 } ``` @@ -690,7 +690,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 20 } ``` @@ -698,7 +698,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 30, - "operator" : "GREATER_THAN", + "operator" : "greater_than", "right" : 20 } ``` @@ -706,7 +706,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 100 } ``` @@ -714,7 +714,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 1 } ``` @@ -722,7 +722,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -730,7 +730,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 2, - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -738,7 +738,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 20, - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -746,7 +746,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_THAN", + "operator" : "less_than", "right" : 10 } ``` @@ -754,7 +754,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 100 } ``` @@ -762,7 +762,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ``` @@ -770,7 +770,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 1 } ``` @@ -778,7 +778,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 100 } ``` @@ -786,7 +786,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 10 } ``` @@ -794,7 +794,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : 1 } ``` @@ -802,7 +802,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 1 } ``` @@ -810,7 +810,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 } ``` @@ -818,7 +818,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 100 } ``` @@ -826,7 +826,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 1 } ``` @@ -834,7 +834,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 10 } ``` @@ -842,7 +842,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : 10, - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : 100 } ``` @@ -851,7 +851,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -861,7 +861,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -871,11 +871,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "10000" } ] } @@ -885,11 +885,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -899,11 +899,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -913,11 +913,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -927,12 +927,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -943,12 +943,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -959,12 +959,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -975,12 +975,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -991,12 +991,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1007,12 +1007,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1023,24 +1023,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1051,24 +1051,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1079,24 +1079,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1107,24 +1107,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1135,7 +1135,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1145,7 +1145,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1155,11 +1155,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1169,11 +1169,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1183,11 +1183,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1197,11 +1197,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1211,12 +1211,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1227,12 +1227,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1243,12 +1243,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1259,12 +1259,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1275,12 +1275,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1291,12 +1291,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1307,24 +1307,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1335,24 +1335,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1363,24 +1363,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1391,24 +1391,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1419,7 +1419,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } @@ -1429,7 +1429,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1439,11 +1439,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1453,11 +1453,11 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } @@ -1467,12 +1467,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1483,12 +1483,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1499,12 +1499,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1515,12 +1515,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1531,12 +1531,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1547,12 +1547,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1563,12 +1563,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1579,12 +1579,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1595,12 +1595,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1611,12 +1611,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1627,12 +1627,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1643,12 +1643,12 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1659,24 +1659,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1687,24 +1687,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1715,24 +1715,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1743,24 +1743,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] } ] @@ -1771,24 +1771,24 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" }, { "anyMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "allMatch" : [ { "left" : "item.price", - "operator" : "GREATER_OR_EQUAL_THAN", + "operator" : "greater_or_equal_than", "right" : "1000" } ] }, { "noneMatch" : [ { "left" : "item.price", - "operator" : "LESS_OR_EQUAL_THAN", + "operator" : "less_or_equal_than", "right" : "1000" } ] } ] @@ -1798,7 +1798,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.arrTags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"in_array\"" } ``` @@ -1806,7 +1806,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.arrTags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"not_in_array\"" } ``` @@ -1814,7 +1814,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "STARTS_WITH", + "operator" : "starts_with", "right" : "\"product\"" } ``` @@ -1822,7 +1822,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "STARTS_WITH", + "operator" : "starts_with", "right" : "\"name\"" } ``` @@ -1830,7 +1830,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "ENDS_WITH", + "operator" : "ends_with", "right" : "\"name\"" } ``` @@ -1838,7 +1838,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "ENDS_WITH", + "operator" : "ends_with", "right" : "\"product\"" } ``` @@ -1846,7 +1846,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"duct\"" } ``` @@ -1854,7 +1854,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.name", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"different value\"" } ``` @@ -1862,7 +1862,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.tags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"test\"" } ``` @@ -1870,7 +1870,7 @@ To see more details, check its source: [here](src/test/kotlin/com/rapatao/projec ```json { "left" : "item.tags", - "operator" : "CONTAINS", + "operator" : "contains", "right" : "\"different value\"" } ``` diff --git a/README.md b/README.md index 48c76aa..f6aaf2a 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ import com.rapatao.projects.ruleset.engine.types.builder.equalsTo val rule = "item.price" equalsTo 0 val input = mapOf("item" to mapOf("price" to 0)) -val evaluator: Evaluator = ... +val evaluator: Evaluator = ... val result = evaluator.evaluate(rule, input) println(result) // true @@ -130,24 +130,47 @@ val result2 = evaluator.evaluate(rule, Input(item = Item(price = 0.0))) println(result) // true ``` -## Supported operations (expressions) +## Expressions (Rule) -The engine only supports `boolean` evaluations, which means that all operations must results in a boolean value. +In the context of the engine, an expression is a decision table, where many statements can be executed using defined +operators, resulting in a `boolean`, where `true` means that the given input data matches, and `false` when it doesn't +match. All provided operations can be created using the builder: `com.rapatao.projects.ruleset.engine.types.builder.ExpressionBuilder` +### Operators + +The engine provides many built-in operators, but it also allows adding new ones or event overwriting the existing one. + +#### Built-in operators + | operator | description | |-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| EQUALS | Represents the equality operator (==), used to check if two values are equal. | -| NOT_EQUALS | Represents the inequality operator (!=), used to check if two values are not equal. | -| GREATER_THAN | Represents the greater than operator (>), used to compare if one value is greater than another. | -| GREATER_OR_EQUAL_THAN | Represents the greater than or equal to operator (>=), used to compare if one value is greater than or equal to another. | -| LESS_THAN | Represents the less than operator (<), used to compare if one value is less than another. | -| LESS_OR_EQUAL_THAN | Represents the less than or equal to operator (<=), used to compare if one value is less than or equal to another. | -| STARTS_WITH | Represents the operation to check if a string starts with a specified sequence of characters. | -| ENDS_WITH | Represents the operation to check if a string ends with a specified sequence of characters. | -| CONTAINS | Represents the operation to check if a string contains a specified sequence of characters or if an array contains a particular element. | +| equals | Represents the equality operator (==), used to check if two values are equal. | +| not_equals | Represents the inequality operator (!=), used to check if two values are not equal. | +| greater_than | Represents the greater than operator (>), used to compare if one value is greater than another. | +| greater_or_equal_than | Represents the greater than or equal to operator (>=), used to compare if one value is greater than or equal to another. | +| less_than | Represents the less than operator (<), used to compare if one value is less than another. | +| less_or_equal_than | Represents the less than or equal to operator (<=), used to compare if one value is less than or equal to another. | +| starts_with | Represents the operation to check if a string starts with a specified sequence of characters. | +| ends_with | Represents the operation to check if a string ends with a specified sequence of characters. | +| contains | Represents the operation to check if a string contains a specified sequence of characters or if an array contains a particular element. | + +#### Customizing the operators + +It is possible to create custom operators by creating an implementation of the +interface `com.rapatao.projects.ruleset.engine.types.operators.Operators`. + +The function `name()` identifies the operator, which is used when evaluating the expressions. The engine supports a +single Operator per name, which means that it is not possible to have more than one using the same name. + +> Each built-in operator has its own class and all of them are located at the +> package `com.rapatao.projects.ruleset.engine.types.operators`. To override then it is not mandatory to use these base +> classes, it only need to have the same name as the built-in operator. + +There is no validation related to duplicated operator names, since it is required to allow overriding the built-in +operator by one implemented by the user of this library. ### Examples diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt index f26636b..ce476b4 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/Evaluator.kt @@ -3,11 +3,17 @@ package com.rapatao.projects.ruleset.engine import com.rapatao.projects.ruleset.engine.context.EvalContext import com.rapatao.projects.ruleset.engine.types.Expression import com.rapatao.projects.ruleset.engine.types.OnFailure +import com.rapatao.projects.ruleset.engine.types.errors.UnknownOperator +import com.rapatao.projects.ruleset.engine.types.operators.Operator /** * The Evaluator is a base class used to evaluate a given rule expression against input data. */ -abstract class Evaluator { +abstract class Evaluator( + operators: List, +) { + + private val declaredOperators = operators.associateBy { it.name().lowercase() } /** * Evaluates the given rule expression against the provided input data. @@ -48,6 +54,13 @@ abstract class Evaluator { */ abstract fun name(): String + /** + * Return the operator implementation for the given name. + * + * @return The operator. + */ + fun operator(name: String): Operator? = declaredOperators[name] + private fun List.processNoneMatch(context: EvalContext): Boolean { return this.none { usingFailureWrapper(it.onFailure) { @@ -89,7 +102,11 @@ abstract class Evaluator { private fun Expression.processExpression(context: EvalContext): Boolean { return usingFailureWrapper(this.onFailure) { - context.process(this) + requireNotNull(this.operator) { "expression operator must not be null" } + + val operator = operator(this.operator) ?: throw UnknownOperator(this.operator) + + context.process(this.left, operator, this.right) } } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt index 598fccb..3e5ad8d 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/context/EvalContext.kt @@ -1,6 +1,6 @@ package com.rapatao.projects.ruleset.engine.context -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator /** * Represents an evaluation context for processing expressions. @@ -8,11 +8,9 @@ import com.rapatao.projects.ruleset.engine.types.Expression fun interface EvalContext { /** - * Processes an expression. + * Process the expression using the given operator * - * @param expression the expression to process * @return true if the expression is successfully processed, false otherwise - * @throws Exception if the expression processing fails and onFailure is set to THROW */ - fun process(expression: Expression): Boolean + fun process(left: Any?, operator: Operator, right: Any?): Boolean } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt index c5148c6..e226421 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Expression.kt @@ -1,5 +1,7 @@ package com.rapatao.projects.ruleset.engine.types +import com.rapatao.projects.ruleset.engine.Evaluator + /** * Represents an expression used in a logical query. * @@ -19,7 +21,7 @@ data class Expression( val anyMatch: List? = null, val noneMatch: List? = null, val left: Any? = null, - val operator: Operator? = null, + val operator: String? = null, val right: Any? = null, val onFailure: OnFailure = OnFailure.THROW, ) { @@ -32,15 +34,25 @@ data class Expression( * * @return Boolean value indicating whether the object is valid. */ - fun isValid(): Boolean { - val any = anyMatch?.map { it.isValid() }?.firstOrNull { !it } ?: true - val none = noneMatch?.map { it.isValid() }?.firstOrNull { !it } ?: true - val all = allMatch?.map { it.isValid() }?.firstOrNull { !it } ?: true + fun isValid(engine: Evaluator): Boolean { + val any = anyMatch?.map { it.isValid(engine) }?.firstOrNull { !it } ?: true + val none = noneMatch?.map { it.isValid(engine) }?.firstOrNull { !it } ?: true + val all = allMatch?.map { it.isValid(engine) }?.firstOrNull { !it } ?: true + + val something = (any && none && all) || parseable() + + return isValidGroup() && something && isValidOperator(engine) + } + + private fun isValidOperator(engine: Evaluator): Boolean { + val validOperator = operator == null || engine.operator(operator) != null + return validOperator + } + private fun isValidGroup(): Boolean { val has = anyMatch == null && noneMatch == null && allMatch == null && parseable() val group = anyMatch != null || noneMatch != null || allMatch != null - val something = (any && none && all) || parseable() - return (has || group) && something + return (has || group) } } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Operator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Operator.kt deleted file mode 100644 index ffa52ea..0000000 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/Operator.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.rapatao.projects.ruleset.engine.types - -/** - * This enum class defines a set of operators that can be utilized in different contexts such as string comparison, - * numerical comparison, or array element verification. - */ -enum class Operator { - /** - * Represents the equality operator (==), used to check if two values are equal. - */ - EQUALS, - - /** - * Represents the inequality operator (!=), used to check if two values are not equal. - */ - NOT_EQUALS, - - /** - * Represents the greater than operator (>), used to compare if one value is greater than another. - */ - GREATER_THAN, - - /** - * Represents the greater than or equal to operator (>=), used to compare if one value is greater than or equal to - * another. - */ - GREATER_OR_EQUAL_THAN, - - /** - * Represents the less than operator (<), used to compare if one value is less than another. - */ - LESS_THAN, - - /** - * Represents the less than or equal to operator (<=), used to compare if one value is less than or equal to - * another. - */ - LESS_OR_EQUAL_THAN, - - /** - * Represents the operation to check if a string starts with a specified sequence of characters. - */ - STARTS_WITH, - - /** - * Represents the operation to check if a string ends with a specified sequence of characters. - */ - ENDS_WITH, - - /** - * Represents the operation to check if a string contains a specified sequence of characters or if an array contains - * a particular element. - */ - CONTAINS, -} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt index be2a9f1..2ce579b 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/BetweenBuilder.kt @@ -1,7 +1,6 @@ package com.rapatao.projects.ruleset.engine.types.builder import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator /** * A builder class for constructing expressions representing a between condition. @@ -10,7 +9,7 @@ import com.rapatao.projects.ruleset.engine.types.Operator * @property from The starting value of the range. * @property operator The comparison operator for the condition. */ -data class BetweenBuilder(val left: Any, val from: Any, val operator: Operator) { +data class BetweenBuilder(val left: Any, val from: Any, val operator: String) { /** * Creates a non-inclusive range expression using the given `to` value. * @@ -19,7 +18,7 @@ data class BetweenBuilder(val left: Any, val from: Any, val operator: Operator) */ infix fun to(to: Any): Expression = MatcherBuilder.allMatch( ExpressionBuilder.expression(left = left, operator = operator, right = from), - ExpressionBuilder.expression(left = left, operator = Operator.LESS_THAN, right = to), + ExpressionBuilder.expression(left = left, operator = "less_than", right = to), ) /** @@ -30,7 +29,7 @@ data class BetweenBuilder(val left: Any, val from: Any, val operator: Operator) */ infix fun toInclusive(to: Any): Expression = MatcherBuilder.allMatch( ExpressionBuilder.expression(left = left, operator = operator, right = from), - ExpressionBuilder.expression(left = left, operator = Operator.LESS_OR_EQUAL_THAN, right = to), + ExpressionBuilder.expression(left = left, operator = "less_or_equal_than", right = to), ) } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt index 4df5cf8..a721f9c 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/ExpressionBuilder.kt @@ -1,16 +1,6 @@ package com.rapatao.projects.ruleset.engine.types.builder import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator -import com.rapatao.projects.ruleset.engine.types.Operator.CONTAINS -import com.rapatao.projects.ruleset.engine.types.Operator.ENDS_WITH -import com.rapatao.projects.ruleset.engine.types.Operator.EQUALS -import com.rapatao.projects.ruleset.engine.types.Operator.GREATER_OR_EQUAL_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.GREATER_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.LESS_OR_EQUAL_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.LESS_THAN -import com.rapatao.projects.ruleset.engine.types.Operator.NOT_EQUALS -import com.rapatao.projects.ruleset.engine.types.Operator.STARTS_WITH /** * A utility class for building expressions using different operators. @@ -48,7 +38,7 @@ object ExpressionBuilder { * @param right The right operand of the expression. * @return The created expression. */ - fun expression(left: Any?, operator: Operator, right: Any?) = Expression( + fun expression(left: Any?, operator: String, right: Any?) = Expression( left = left, operator = operator, right = right ) @@ -66,7 +56,7 @@ object ExpressionBuilder { * @param right The right operand to compare. * @return The expression representing the "equals to" comparison. */ - infix fun equalsTo(right: Any?) = Expression(left = left, operator = EQUALS, right = right) + infix fun equalsTo(right: Any?) = Expression(left = left, operator = "equals", right = right) /** * Creates an expression representing the inequality comparison between the left-hand side and the right-hand @@ -75,7 +65,7 @@ object ExpressionBuilder { * @param right The right-hand side of the comparison. * @return A new expression representing the inequality comparison. */ - infix fun notEqualsTo(right: Any?) = Expression(left = left, operator = NOT_EQUALS, right = right) + infix fun notEqualsTo(right: Any?) = Expression(left = left, operator = "not_equals", right = right) /** * Creates an Expression object representing the greater-than comparison between the left operand and the right @@ -86,7 +76,7 @@ object ExpressionBuilder { * @return the Expression object representing the greater-than comparison */ infix fun greaterThan(right: Any) = - Expression(left = left, operator = GREATER_THAN, right = right) + Expression(left = left, operator = "greater_than", right = right) /** * Creates an expression that represents the "greater than or equal to" operation @@ -95,7 +85,7 @@ object ExpressionBuilder { * @return an expression representing the "greater than or equal to" operation */ infix fun greaterOrEqualThan(right: Any) = - Expression(left = left, operator = GREATER_OR_EQUAL_THAN, right = right) + Expression(left = left, operator = "greater_or_equal_than", right = right) /** * Creates an 'less than' expression with the specified 'right' value. @@ -103,7 +93,7 @@ object ExpressionBuilder { * @param right The right value of the expression. * @return The created expression. */ - infix fun lessThan(right: Any) = Expression(left = left, operator = LESS_THAN, right = right) + infix fun lessThan(right: Any) = Expression(left = left, operator = "less_than", right = right) /** * Creates an expression that represents the less than or equal to operation. @@ -112,21 +102,21 @@ object ExpressionBuilder { * @return An Expression object representing the less than or equal to operation. */ infix fun lessOrEqualThan(right: Any) = - Expression(left = left, operator = LESS_OR_EQUAL_THAN, right = right) + Expression(left = left, operator = "less_or_equal_than", right = right) /** * Checks if the left operand represents a boolean with value true. * * @return The boolean expression representing if the condition is true. */ - fun isTrue() = Expression(left = left, operator = EQUALS, right = true) + fun isTrue() = Expression(left = left, operator = "equals", right = true) /** * Checks if the left operand represents a boolean with value false. * * @return The boolean expression representing if the condition is false. */ - fun isFalse() = Expression(left = left, operator = EQUALS, right = false) + fun isFalse() = Expression(left = left, operator = "equals", right = false) /** * Creates an expression that checks if the left operand starts with the specified right operand. @@ -135,7 +125,7 @@ object ExpressionBuilder { * @return An Expression object representing the "starts with" condition. */ fun startsWith(right: Any?) = Expression( - left = left, operator = STARTS_WITH, right = right + left = left, operator = "starts_with", right = right ) /** @@ -145,7 +135,7 @@ object ExpressionBuilder { * @return An Expression object representing the "ends with" condition. */ fun endsWith(right: Any?) = Expression( - left = left, operator = ENDS_WITH, right = right + left = left, operator = "ends_with", right = right ) /** @@ -154,10 +144,10 @@ object ExpressionBuilder { * If the left operand is a list, it checks if the list contains the right operand. * * @param right The value to be checked for containment within the left operand. Can be of any type. - * @return An [Expression] object representing the containment check, with the operator set to CONTAINS. + * @return An [Expression] object representing the containment check, with the operator set to "contains". */ fun contains(right: Any?) = Expression( - left = left, operator = CONTAINS, right = right + left = left, operator = "contains", right = right ) } } diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt index b1c22ee..6744965 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/BetweenExtensions.kt @@ -1,6 +1,5 @@ package com.rapatao.projects.ruleset.engine.types.builder.extensions -import com.rapatao.projects.ruleset.engine.types.Operator import com.rapatao.projects.ruleset.engine.types.builder.BetweenBuilder /** @@ -10,7 +9,7 @@ import com.rapatao.projects.ruleset.engine.types.builder.BetweenBuilder * @return A [BetweenBuilder] object to build the between condition. */ infix fun Any.from(from: Any): BetweenBuilder = BetweenBuilder( - left = this, from = from, operator = Operator.GREATER_THAN, + left = this, from = from, operator = "greater_than", ) /** @@ -20,5 +19,5 @@ infix fun Any.from(from: Any): BetweenBuilder = BetweenBuilder( * @return The [BetweenBuilder] instance for further chaining */ infix fun Any.fromInclusive(from: Any): BetweenBuilder = BetweenBuilder( - left = this, from = from, operator = Operator.GREATER_OR_EQUAL_THAN, + left = this, from = from, operator = "greater_or_equal_than", ) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt index 37e007b..de7b271 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/ContainsExtensions.kt @@ -8,7 +8,7 @@ import com.rapatao.projects.ruleset.engine.types.builder.ExpressionBuilder * * @receiver The string in which to check for the presence of the substring. * @param right The substring to search for within the current string. - * @return An [Expression] object representing the containment check, with the operator set to CONTAINS. + * @return An [Expression] object representing the containment check, with the operator set to "contains". */ infix fun String.expContains(right: String): Expression = ExpressionBuilder.left(this).contains(right) @@ -17,6 +17,6 @@ infix fun String.expContains(right: String): Expression = ExpressionBuilder.left * * @receiver The list in which to check for the presence of the element. * @param right The element to search for within the current list. - * @return An [Expression] object representing the containment check, with the operator set to CONTAINS. + * @return An [Expression] object representing the containment check, with the operator set to "contains". */ infix fun List.expContains(right: Any): Expression = ExpressionBuilder.left(this).contains(right) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt index 3233b9c..f581026 100644 --- a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/builder/extensions/WithExtensions.kt @@ -1,14 +1,13 @@ package com.rapatao.projects.ruleset.engine.types.builder.extensions import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator import com.rapatao.projects.ruleset.engine.types.builder.ExpressionBuilder /** * Creates an [Expression] object that represents a "starts with" operation. * * @param right The value to compare with the left string to check if it starts with it. - * @return An [Expression] object with the left string, the [Operator.STARTS_WITH] operator, and the right value. + * @return An [Expression] object with the left string, the "starts_with" operator, and the right value. */ infix fun String.startsWith(right: Any): Expression = ExpressionBuilder.left(this).startsWith(right) @@ -16,6 +15,6 @@ infix fun String.startsWith(right: Any): Expression = ExpressionBuilder.left(thi * Creates an [Expression] object that represents a "ends with" operation. * * @param right The value to compare with the left string to check if it ends with it. - * @return An [Expression] object with the left string, the [Operator.ENDS_WITH] operator, and the right value. + * @return An [Expression] object with the left string, the "ends_with" operator, and the right value. */ infix fun String.endsWith(right: Any): Expression = ExpressionBuilder.left(this).endsWith(right) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/errors/UnknownOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/errors/UnknownOperator.kt new file mode 100644 index 0000000..0c25777 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/errors/UnknownOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.errors + +/** + * Throw when an expression is using an unrecognized operator + */ +class UnknownOperator(name: String) : RuntimeException( + "Unknown operator: $name" +) diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/BuiltInOperators.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/BuiltInOperators.kt new file mode 100644 index 0000000..54a5d21 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/BuiltInOperators.kt @@ -0,0 +1,13 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +object BuiltInOperators { + const val EQUALS = "equals" + const val NOT_EQUALS = "not_equals" + const val GREATER_THAN = "greater_than" + const val GREATER_OR_EQUAL_THAN = "greater_or_equal_than" + const val LESS_THAN = "less_than" + const val LESS_OR_EQUAL_THAN = "less_or_equal_than" + const val STARTS_WITH = "starts_with" + const val ENDS_WITH = "ends_with" + const val CONTAINS = "contains" +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/ContainsOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/ContainsOperator.kt new file mode 100644 index 0000000..ffbf04b --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/ContainsOperator.kt @@ -0,0 +1,9 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the operation to check if a string contains a specified sequence of characters or if an array contains + * a particular element. + */ +abstract class ContainsOperator : Operator { + override fun name(): String = BuiltInOperators.CONTAINS +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EndsWithOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EndsWithOperator.kt new file mode 100644 index 0000000..2fc6867 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EndsWithOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the operation to check if a string ends with a specified sequence of characters. + */ +abstract class EndsWithOperator : Operator { + override fun name(): String = BuiltInOperators.ENDS_WITH +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EqualsOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EqualsOperator.kt new file mode 100644 index 0000000..5acbf77 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/EqualsOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the equality operator (==), used to check if two values are equal. + */ +abstract class EqualsOperator : Operator { + override fun name(): String = BuiltInOperators.EQUALS +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterOrEqualThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterOrEqualThanOperator.kt new file mode 100644 index 0000000..c8e0904 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterOrEqualThanOperator.kt @@ -0,0 +1,9 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the greater than or equal to operator (>=), used to compare if one value is greater than or equal to + * another. + */ +abstract class GreaterOrEqualThanOperator : Operator { + override fun name(): String = BuiltInOperators.GREATER_OR_EQUAL_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterThanOperator.kt new file mode 100644 index 0000000..912545d --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/GreaterThanOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the greater than operator (>), used to compare if one value is greater than another. + */ +abstract class GreaterThanOperator : Operator { + override fun name(): String = BuiltInOperators.GREATER_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessOrEqualThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessOrEqualThanOperator.kt new file mode 100644 index 0000000..e38cda1 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessOrEqualThanOperator.kt @@ -0,0 +1,9 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the less than or equal to operator (<=), used to compare if one value is less than or equal to + * another. + */ +abstract class LessOrEqualThanOperator : Operator { + override fun name(): String = BuiltInOperators.LESS_OR_EQUAL_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessThanOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessThanOperator.kt new file mode 100644 index 0000000..ba686e8 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/LessThanOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the less than operator (<), used to compare if one value is less than another. + */ +abstract class LessThanOperator : Operator { + override fun name(): String = BuiltInOperators.LESS_THAN +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/NotEqualsOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/NotEqualsOperator.kt new file mode 100644 index 0000000..f793560 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/NotEqualsOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the inequality operator (!=), used to check if two values are not equal. + */ +abstract class NotEqualsOperator : Operator { + override fun name(): String = BuiltInOperators.NOT_EQUALS +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/Operator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/Operator.kt new file mode 100644 index 0000000..5e7062d --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/Operator.kt @@ -0,0 +1,23 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +import com.rapatao.projects.ruleset.engine.context.EvalContext + +/** + * This interface defines how the Engines must validate the expression for a given Operator + */ +interface Operator { + + /** + * Compare the given operands + * + * @return true when equal, otherwise false + */ + fun process(context: EvalContext, left: Any?, right: Any?): Boolean + + /** + * Returns the operator representation name, such as "equals", "not_equals", etc. + * + * @return The operator name + */ + fun name(): String +} diff --git a/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/StartsWithOperator.kt b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/StartsWithOperator.kt new file mode 100644 index 0000000..a493e56 --- /dev/null +++ b/core/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/operators/StartsWithOperator.kt @@ -0,0 +1,8 @@ +package com.rapatao.projects.ruleset.engine.types.operators + +/** + * Represents the operation to check if a string starts with a specified sequence of characters. + */ +abstract class StartsWithOperator : Operator { + override fun name(): String = BuiltInOperators.STARTS_WITH +} diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt index a009659..4e47b6e 100644 --- a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt +++ b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSContext.kt @@ -1,9 +1,8 @@ package com.rapatao.projects.ruleset.engine.evaluator.graaljs import com.rapatao.projects.ruleset.engine.context.EvalContext -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.graalvm.polyglot.Context -import org.graalvm.polyglot.Source /** * GraalJSContext is a class that implements the EvalContext interface. @@ -15,15 +14,8 @@ class GraalJSContext( private val context: Context, ) : EvalContext { - /** - * Processes an expression. - * - * @param expression the expression to process - * @return true if the expression is successfully processed, false otherwise - * @throws Exception if the expression processing fails and onFailure is set to THROW - */ - override fun process(expression: Expression): Boolean { - return context.eval(expression.asScript()).asBoolean() + override fun process(left: Any?, operator: Operator, right: Any?): Boolean { + return operator.process(this, left, right) } /** @@ -32,14 +24,4 @@ class GraalJSContext( * @return the Graal JS context. */ fun context() = context - - private fun Expression.asScript(): Source { - val script = Parser.parse(this) - - return Source.newBuilder( - "js", - "true == ($script)", - script - ).buildLiteral() - } } diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt index f12b44c..ee07362 100644 --- a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt +++ b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/GraalJSEvaluator.kt @@ -4,6 +4,7 @@ import com.rapatao.projects.ruleset.engine.Evaluator import com.rapatao.projects.ruleset.engine.context.EvalContext import com.rapatao.projects.ruleset.engine.evaluator.graaljs.parameters.MapInjector import com.rapatao.projects.ruleset.engine.evaluator.graaljs.parameters.TypedInjector +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.graalvm.polyglot.Context import org.graalvm.polyglot.Engine import org.graalvm.polyglot.HostAccess @@ -26,8 +27,21 @@ open class GraalJSEvaluator( .engine(engine) .option("js.ecmascript-version", "2023") .allowHostAccess(HostAccess.ALL).allowHostClassLookup { true } - .option("js.nashorn-compat", "true").allowExperimentalOptions(true) -) : Evaluator() { + .option("js.nashorn-compat", "true").allowExperimentalOptions(true), + operators: List = listOf(), +) : Evaluator( + operators = listOf( + Equals(), + NotEquals(), + GreaterThan(), + GreaterOrEqualThan(), + LessThan(), + LessOrEqualThan(), + StartsWith(), + EndsWith(), + Contains(), + ) + operators, +) { override fun call(inputData: Any, block: EvalContext.() -> T): T = createContext().let { diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Operators.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Operators.kt new file mode 100644 index 0000000..ee7f279 --- /dev/null +++ b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Operators.kt @@ -0,0 +1,80 @@ +package com.rapatao.projects.ruleset.engine.evaluator.graaljs + +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.ContainsOperator +import com.rapatao.projects.ruleset.engine.types.operators.EndsWithOperator +import com.rapatao.projects.ruleset.engine.types.operators.EqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.NotEqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.StartsWithOperator +import org.graalvm.polyglot.Source + +internal class Equals : EqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) == ($right)") +} + +internal class NotEquals : NotEqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) != ($right)") +} + +internal class GreaterThan : GreaterThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) > ($right)") +} + +internal class GreaterOrEqualThan : GreaterOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) >= ($right)") +} + +internal class LessThan : LessThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) < ($right)") +} + +internal class LessOrEqualThan : LessOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) <= ($right)") +} + +internal class StartsWith : StartsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).startsWith(($right))") +} + +internal class EndsWith : EndsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).endsWith(($right))") +} + +internal class Contains : ContainsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate( + """ + (function() { + if (Array.isArray(${left})) { + return ${left}.includes(${right}) + } else { + return ${left}.indexOf(${right}) !== -1 + } + })() + """.trimIndent() + ) +} + +private fun EvalContext.evaluate(content: String): Boolean { + val graalJSContext = this as GraalJSContext + + return graalJSContext.context().eval( + Source.newBuilder( + "js", + "true == ($content)", + content, + ).buildLiteral() + ).asBoolean() +} diff --git a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Parser.kt b/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Parser.kt deleted file mode 100644 index a3df66a..0000000 --- a/graaljs-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/graaljs/Parser.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.rapatao.projects.ruleset.engine.evaluator.graaljs - -import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator - -internal object Parser { - - fun parse(expression: Expression): String { - return when (expression.operator) { - Operator.EQUALS -> "==".formatComparison(expression) - Operator.NOT_EQUALS -> "!=".formatComparison(expression) - Operator.GREATER_THAN -> ">".formatComparison(expression) - Operator.GREATER_OR_EQUAL_THAN -> ">=".formatComparison(expression) - Operator.LESS_THAN -> "<".formatComparison(expression) - Operator.LESS_OR_EQUAL_THAN -> "<=".formatComparison(expression) - Operator.STARTS_WITH -> "startsWith".formatWithOperation(expression) - Operator.ENDS_WITH -> "endsWith".formatWithOperation(expression) - Operator.CONTAINS -> formatContainsOperation(expression) - null -> error("when evaluation an expression, the operator cannot be null") - } - } - - private fun String.formatComparison(expression: Expression) = - "(${expression.left}) $this (${expression.right})" - - private fun String.formatWithOperation(expression: Expression) = - "${expression.left}.${this}(${expression.right})" - - private fun formatContainsOperation(expression: Expression) = - """ - (function() { - if (Array.isArray(${expression.left})) { - return ${expression.left}.includes(${expression.right}) - } else { - return ${expression.left}.indexOf(${expression.right}) !== -1 - } - })() - """.trimIndent() -} diff --git a/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt b/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt index 98e3bdb..9532fd2 100644 --- a/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt +++ b/jackson/src/main/kotlin/com/rapatao/projects/ruleset/jackson/ExpressionMixin.kt @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonFormat import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonInclude import com.rapatao.projects.ruleset.engine.types.OnFailure -import com.rapatao.projects.ruleset.engine.types.Operator /** * This interface represents a mixin for JSON serialization and deserialization of expressions. @@ -23,14 +22,6 @@ interface ExpressionMixin { ) fun onFailure(): OnFailure - @JsonFormat( - with = [ - JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES, - JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES - ], - ) - fun operator(): Operator - @JsonIgnore fun isValid(): Boolean } diff --git a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt index ae0c0be..4110289 100644 --- a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt +++ b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationExamplesBuilder.kt @@ -3,6 +3,7 @@ package com.rapatao.projects.ruleset.jackson import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.rapatao.projects.ruleset.engine.cases.TestData +import com.rapatao.projects.ruleset.engine.evaluator.kotlin.KotlinEvaluator import com.rapatao.projects.ruleset.engine.types.Expression import com.rapatao.projects.ruleset.engine.types.builder.MatcherBuilder.allMatch import com.rapatao.projects.ruleset.engine.types.builder.MatcherBuilder.anyMatch @@ -31,6 +32,8 @@ internal class SerializationExamplesBuilder { .setSerializationInclusion(JsonInclude.Include.NON_NULL) .addMixIn(Expression::class.java, ExpressionMixin::class.java) + private val engine = KotlinEvaluator() + private val cases = listOf( "* equalsTo: ", "field" equalsTo true, @@ -107,7 +110,7 @@ internal class SerializationExamplesBuilder { .filterIsInstance() .filter { // remove from serialization example rules that one of the operands is null - it.isValid() && + it.isValid(engine) && ( (it.left != null && it.right != null) || !it.noneMatch.isNullOrEmpty() || !it.allMatch.isNullOrEmpty() || !it.anyMatch.isNullOrEmpty() diff --git a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt index b5da54b..a0ad110 100644 --- a/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt +++ b/jackson/src/test/kotlin/com/rapatao/projects/ruleset/jackson/SerializationTest.kt @@ -44,7 +44,7 @@ class SerializationTest { val json = """ { "left": "field", - "operator": "EQUALS", + "operator": "equals", "right": 10 } """.trimIndent() diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt index 9a6991b..a72f3c6 100644 --- a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt +++ b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinContext.kt @@ -1,8 +1,7 @@ package com.rapatao.projects.ruleset.engine.evaluator.kotlin import com.rapatao.projects.ruleset.engine.context.EvalContext -import com.rapatao.projects.ruleset.engine.evaluator.kotlin.Parser.parse -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator import java.math.BigDecimal /** @@ -15,12 +14,8 @@ class KotlinContext( private val inputData: Map ) : EvalContext { - override fun process(expression: Expression): Boolean { - return parse( - expression, - expression.left.asValue(), - expression.right.asValue() - ) + override fun process(left: Any?, operator: Operator, right: Any?): Boolean { + return operator.process(this, left.asValue(), right.asValue()) } private fun Any?.asValue(): Any? { diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt index 257abd4..80d89eb 100644 --- a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt +++ b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/KotlinEvaluator.kt @@ -2,6 +2,7 @@ package com.rapatao.projects.ruleset.engine.evaluator.kotlin import com.rapatao.projects.ruleset.engine.Evaluator import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.Operator import kotlin.reflect.full.memberProperties /** @@ -9,7 +10,22 @@ import kotlin.reflect.full.memberProperties * * Supported types: Java primitive types, boolean, string, number types, maps, lists and arrays. */ -open class KotlinEvaluator : Evaluator() { +open class KotlinEvaluator( + operators: List = listOf(), +) : Evaluator( + operators = listOf( + Equals(), + NotEquals(), + GreaterThan(), + GreaterOrEqualThan(), + LessThan(), + LessOrEqualThan(), + StartsWith(), + EndsWith(), + Contains(), + ) + operators, +) { + override fun call(inputData: Any, block: (context: EvalContext) -> T): T { return block(KotlinContext( mutableMapOf().apply { diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Operators.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Operators.kt new file mode 100644 index 0000000..1e5f94d --- /dev/null +++ b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Operators.kt @@ -0,0 +1,69 @@ +package com.rapatao.projects.ruleset.engine.evaluator.kotlin + +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.ContainsOperator +import com.rapatao.projects.ruleset.engine.types.operators.EndsWithOperator +import com.rapatao.projects.ruleset.engine.types.operators.EqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.NotEqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.StartsWithOperator + +internal class Equals : EqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left == right +} + +internal class NotEquals : NotEqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left != right +} + +internal class GreaterThan : GreaterThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() > right +} + +internal class GreaterOrEqualThan : GreaterOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() >= right +} + +internal class LessThan : LessThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() < right +} + +internal class LessOrEqualThan : LessOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.comparable() <= right +} + +internal class StartsWith : StartsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.toString().startsWith(right.toString()) +} + +internal class EndsWith : EndsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.toString().endsWith(right.toString()) +} + +internal class Contains : ContainsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + left.checkContains(right) +} + +@Suppress("UNCHECKED_CAST") +private fun T.comparable() = this as Comparable + +private fun Any?.checkContains(value: Any?): Boolean { + return when { + this is String && value is String -> this.contains(value) + this is Collection<*> -> this.contains(value) + this is Array<*> -> this.contains(value) + else -> throw UnsupportedOperationException("contains doesn't support ${this?.javaClass} type") + } +} diff --git a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Parser.kt b/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Parser.kt deleted file mode 100644 index 7095428..0000000 --- a/kotlin-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/kotlin/Parser.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.rapatao.projects.ruleset.engine.evaluator.kotlin - -import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator - -internal object Parser { - fun parse(expression: Expression, left: Any?, right: Any?) = - when (expression.operator) { - Operator.EQUALS -> left == right - Operator.NOT_EQUALS -> left != right - Operator.GREATER_THAN -> left.comparable() > right - Operator.GREATER_OR_EQUAL_THAN -> left.comparable() >= right - Operator.LESS_THAN -> left.comparable() < right - Operator.LESS_OR_EQUAL_THAN -> left.comparable() <= right - Operator.STARTS_WITH -> left.toString().startsWith(right.toString()) - Operator.ENDS_WITH -> left.toString().endsWith(right.toString()) - Operator.CONTAINS -> left.checkContains(right) - null -> error("when evaluation an expression, the operator cannot be null") - } - - @Suppress("UNCHECKED_CAST") - private fun T.comparable() = this as Comparable - - private fun Any?.checkContains(value: Any?): Boolean { - return when { - this is String && value is String -> this.contains(value) - this is Collection<*> -> this.contains(value) - this is Array<*> -> this.contains(value) - else -> throw UnsupportedOperationException("contains doesn't support ${this?.javaClass} type") - } - } -} diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Operators.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Operators.kt new file mode 100644 index 0000000..fe5c90d --- /dev/null +++ b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Operators.kt @@ -0,0 +1,82 @@ +package com.rapatao.projects.ruleset.engine.evaluator.rhino + +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.ContainsOperator +import com.rapatao.projects.ruleset.engine.types.operators.EndsWithOperator +import com.rapatao.projects.ruleset.engine.types.operators.EqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.GreaterThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessOrEqualThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.LessThanOperator +import com.rapatao.projects.ruleset.engine.types.operators.NotEqualsOperator +import com.rapatao.projects.ruleset.engine.types.operators.StartsWithOperator + +internal class Equals : EqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) == ($right)") +} + +internal class NotEquals : NotEqualsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) != ($right)") +} + +internal class GreaterThan : GreaterThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) > ($right)") +} + +internal class GreaterOrEqualThan : GreaterOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) >= ($right)") +} + +internal class LessThan : LessThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) < ($right)") +} + +internal class LessOrEqualThan : LessOrEqualThanOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left) <= ($right)") +} + +internal class StartsWith : StartsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).startsWith(($right))") +} + +internal class EndsWith : EndsWithOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate("($left).endsWith(($right))") +} + +internal class Contains : ContainsOperator() { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = + context.evaluate( + """ + (function() { + if (Array.isArray(${left})) { + return ${left}.includes(${right}) + } else { + return ${left}.indexOf(${right}) !== -1 + } + })() + """.trimIndent() + ) +} + +private fun EvalContext.evaluate(content: String): Boolean { + val rhinoContext = this as RhinoContext + + return true == rhinoContext.context() + .compileString( + "true == ($content)", + content, + 0, + null, + ).exec( + rhinoContext.context(), + rhinoContext.scope(), + ) +} diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Parser.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Parser.kt deleted file mode 100644 index 3fa02ea..0000000 --- a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/Parser.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.rapatao.projects.ruleset.engine.evaluator.rhino - -import com.rapatao.projects.ruleset.engine.types.Expression -import com.rapatao.projects.ruleset.engine.types.Operator - -internal object Parser { - fun parse(expression: Expression): String { - return when (expression.operator) { - Operator.EQUALS -> "==".formatComparison(expression) - Operator.NOT_EQUALS -> "!=".formatComparison(expression) - Operator.GREATER_THAN -> ">".formatComparison(expression) - Operator.GREATER_OR_EQUAL_THAN -> ">=".formatComparison(expression) - Operator.LESS_THAN -> "<".formatComparison(expression) - Operator.LESS_OR_EQUAL_THAN -> "<=".formatComparison(expression) - Operator.STARTS_WITH -> "startsWith".formatWithOperation(expression) - Operator.ENDS_WITH -> "endsWith".formatWithOperation(expression) - Operator.CONTAINS -> formatContainsOperation(expression) - null -> error("when evaluation an expression, the operator cannot be null") - } - } - - private fun String.formatComparison(expression: Expression) = - "(${expression.left}) $this (${expression.right})" - - private fun String.formatWithOperation(expression: Expression) = - "${expression.left}.${this}(${expression.right})" - - private fun formatContainsOperation(expression: Expression) = - """ - (function() { - if (Array.isArray(${expression.left})) { - return ${expression.left}.includes(${expression.right}) - } else { - return ${expression.left}.indexOf(${expression.right}) !== -1 - } - })() - """.trimIndent() -} diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt index 4c5c953..6a524a9 100644 --- a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt +++ b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoContext.kt @@ -1,9 +1,8 @@ package com.rapatao.projects.ruleset.engine.evaluator.rhino import com.rapatao.projects.ruleset.engine.context.EvalContext -import com.rapatao.projects.ruleset.engine.types.Expression +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.mozilla.javascript.Context -import org.mozilla.javascript.Script import org.mozilla.javascript.ScriptableObject /** @@ -18,17 +17,8 @@ class RhinoContext( private val scope: ScriptableObject, ) : EvalContext { - /** - * Processes an expression. - * - * @param expression the expression to process - * @return true if the expression is successfully processed, false otherwise - * @throws Exception if the expression processing fails and onFailure is set to THROW - */ - override fun process(expression: Expression): Boolean { - return true == expression.asScript(context) - .exec(context, scope) - } + override fun process(left: Any?, operator: Operator, right: Any?): Boolean = + operator.process(this, left, right) /** * Returns the Rhino context. @@ -43,15 +33,4 @@ class RhinoContext( * @return The Rhino scope. */ fun scope() = scope - - private fun Expression.asScript(context: Context): Script { - val script = Parser.parse(this) - - return context.compileString( - "true == ($script)", - script, - 0, - null, - ) - } } diff --git a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt index 55c8d11..f126257 100644 --- a/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt +++ b/rhino-evaluator/src/main/kotlin/com/rapatao/projects/ruleset/engine/evaluator/rhino/RhinoEvaluator.kt @@ -4,6 +4,7 @@ import com.rapatao.projects.ruleset.engine.Evaluator import com.rapatao.projects.ruleset.engine.context.EvalContext import com.rapatao.projects.ruleset.engine.evaluator.rhino.parameters.MapInjector import com.rapatao.projects.ruleset.engine.evaluator.rhino.parameters.TypedInjector +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.mozilla.javascript.Context import org.mozilla.javascript.ScriptableObject @@ -12,7 +13,20 @@ import org.mozilla.javascript.ScriptableObject */ open class RhinoEvaluator( private val contextFactory: RhinoContextFactory = RhinoContextFactory(), -) : Evaluator() { + operators: List = listOf(), +) : Evaluator( + operators = listOf( + Equals(), + NotEquals(), + GreaterThan(), + GreaterOrEqualThan(), + LessThan(), + LessOrEqualThan(), + StartsWith(), + EndsWith(), + Contains(), + ) + operators, +) { override fun call( inputData: Any, diff --git a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt index 8cef73d..0e8cedf 100644 --- a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt +++ b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/BaseEvaluatorTest.kt @@ -7,9 +7,11 @@ import com.rapatao.projects.ruleset.engine.types.OnFailure import com.rapatao.projects.ruleset.engine.types.builder.MatcherBuilder.allMatch import com.rapatao.projects.ruleset.engine.types.builder.extensions.equalsTo import com.rapatao.projects.ruleset.engine.types.builder.extensions.ifFail +import com.rapatao.projects.ruleset.engine.types.operators.BuiltInOperators import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.emptyString import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasItem import org.hamcrest.Matchers.not import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -17,6 +19,7 @@ import org.junit.jupiter.api.assertThrows import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import kotlin.reflect.full.memberProperties abstract class BaseEvaluatorTest( private val evaluator: Evaluator @@ -127,4 +130,17 @@ abstract class BaseEvaluatorTest( fun assertEvaluatorMustHaveName() { assertThat(evaluator.name(), not(emptyString())) } + + @Test + @DisplayName("all built-in operators should have at least one test case") + fun assertBuiltInImplementations() { + val operatorsTested: Set = TestData.cases().flatMap { it.get().toList() } + .filterIsInstance() + .mapNotNull { it.operator } + .toSet() + + BuiltInOperators::class.memberProperties.forEach { + assertThat(operatorsTested, hasItem(it.call())) + } + } } diff --git a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt index 64575e2..3181ebb 100644 --- a/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt +++ b/tests/src/main/kotlin/com/rapatao/projects/ruleset/engine/types/ExpressionTest.kt @@ -1,5 +1,8 @@ package com.rapatao.projects.ruleset.engine.types +import com.rapatao.projects.ruleset.engine.Evaluator +import com.rapatao.projects.ruleset.engine.context.EvalContext +import com.rapatao.projects.ruleset.engine.types.operators.Operator import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.junit.jupiter.api.DisplayName @@ -7,63 +10,78 @@ import org.junit.jupiter.api.Test class ExpressionTest { + private val dummyEval: Evaluator = object : Evaluator( + listOf( + object : Operator { + override fun process(context: EvalContext, left: Any?, right: Any?): Boolean = true + override fun name(): String = "equals" + } + ) + ) { + override fun call(inputData: Any, block: (context: EvalContext) -> T): T { + TODO("Not yet implemented") + } + + override fun name(): String = "test" + } + @Test @DisplayName("empty expression must be invalid") fun assertEmptyExpression() { val exp = Expression() - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } @Test @DisplayName("expression with operator is valid") fun assertExpressionValid() { val exp = Expression( - operator = Operator.EQUALS, + operator = "equals", ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("no operator, but with noneMatch is valid") fun assertNoOperatorByNoneMatch() { val exp = Expression( - noneMatch = listOf(Expression(operator = Operator.EQUALS)), + noneMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("no operator, but with anyMatch is valid") fun assertNoOperatorButWithAnyMatch() { val exp = Expression( - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("no operator, but with allMatch is valid") fun assertNoOperatorButWithAllMatch() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @DisplayName("is valid if group and operator is set and valid") fun assertIsValidIfGroupAndOperatorIsSetAndValid() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), - noneMatch = listOf(Expression(operator = Operator.EQUALS)), - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), + noneMatch = listOf(Expression(operator = "equals")), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(true)) + assertThat(exp.isValid(dummyEval), equalTo(true)) } @Test @@ -71,34 +89,34 @@ class ExpressionTest { fun assertIsInvalidIfAllMatchContainsAnInvalidExpression() { val exp = Expression( allMatch = listOf(Expression()), - noneMatch = listOf(Expression(operator = Operator.EQUALS)), - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + noneMatch = listOf(Expression(operator = "equals")), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } @Test @DisplayName("is invalid if noneMatch contains an invalid expression") fun assertIsInvalidIfNoneMatchContainsInvalidExpression() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), noneMatch = listOf(Expression()), - anyMatch = listOf(Expression(operator = Operator.EQUALS)), + anyMatch = listOf(Expression(operator = "equals")), ) - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } @Test @DisplayName("is invalid if anyMatch contains an invalid expression") fun assertIsInvalidIfAnyMatchContainsInvalidExpression() { val exp = Expression( - allMatch = listOf(Expression(operator = Operator.EQUALS)), - noneMatch = listOf(Expression(operator = Operator.EQUALS)), + allMatch = listOf(Expression(operator = "equals")), + noneMatch = listOf(Expression(operator = "equals")), anyMatch = listOf(Expression()), ) - assertThat(exp.isValid(), equalTo(false)) + assertThat(exp.isValid(dummyEval), equalTo(false)) } }