Added support for Easy Rules with Aviator integration. Aviator is widely used in China among both large enterprises and individual developers. For more details, please visit: https://github.com/killme2008/aviatorscript

pull/424/head
xiemin04 11 months ago
parent d445083153
commit ef6202ca56

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules</artifactId>
<version>4.1.1-SNAPSHOT</version>
</parent>
<artifactId>easy-rules-aviator</artifactId>
<packaging>jar</packaging>
<name>Easy Rules Aviator module</name>
<description>Aviator integration module</description>
<properties>
<aviator-version>5.4.1</aviator-version>
</properties>
<scm>
<url>git@github.com:j-easy/easy-rules.git</url>
<connection>scm:git:git@github.com:j-easy/easy-rules.git</connection>
<developerConnection>scm:git:git@github.com:j-easy/easy-rules.git</developerConnection>
<tag>HEAD</tag>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/j-easy/easy-rules/issues</url>
</issueManagement>
<ciManagement>
<system>Github Actions</system>
<url>https://github.com/j-easy/easy-rules/actions</url>
</ciManagement>
<developers>
<developer>
<id>benas</id>
<name>Mahmoud Ben Hassine</name>
<url>http://benas.github.io</url>
<email>mahmoud.benhassine@icloud.com</email>
<roles>
<role>Lead developer</role>
</roles>
</developer>
</developers>
<licenses>
<license>
<name>MIT License</name>
<url>http://opensource.org/licenses/mit-license.php</url>
</license>
</licenses>
<dependencies>
<!-- production dependencies -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-support</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>${aviator-version}</version>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>${system-lambda.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<header>${project.parent.basedir}/licence-header-template.txt</header>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,63 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import com.googlecode.aviator.Expression;
import org.jeasy.rules.api.Action;
import org.jeasy.rules.api.Facts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.jeasy.rules.aviator.AviatorRule.DEFAULT_AVIATOR;
/**
* author peterxiemin(bbcoder1987@gmail.com)
*/
public class AviatorAction implements Action {
public static final Logger LOGGER = LoggerFactory.getLogger(AviatorAction.class.getName());
private final String expression;
private final Expression compileExpression;
/**
* Create a new {@link AviatorAction}.
*
* @param expression the action written in expression language
*/
public AviatorAction(String expression) {
this.expression = expression;
this.compileExpression = DEFAULT_AVIATOR.compile(expression);
}
@Override
public void execute(Facts facts) throws Exception {
try {
compileExpression.execute(facts.asMap());
} catch (Exception e) {
LOGGER.error("Unable to evaluate expression: '" + expression + "' on facts: " + facts);
throw e;
}
}
}

@ -0,0 +1,64 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import com.googlecode.aviator.Expression;
import org.jeasy.rules.api.Condition;
import org.jeasy.rules.api.Facts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.jeasy.rules.aviator.AviatorRule.DEFAULT_AVIATOR;
/**
* author peterxiemin(bbcoder1987@gmail.com)
*/
public class AviatorCondition implements Condition {
private static final Logger LOGGER = LoggerFactory.getLogger(AviatorCondition.class.getName());
private final String expression;
private final Expression compileExpression;
/**
* Create a new {@link AviatorCondition}.
* @param expression
* the condition written in expression language
*/
public AviatorCondition(String expression) {
this.expression = expression;
this.compileExpression = DEFAULT_AVIATOR.compile(expression);
}
/**
* Evaluate the rule.
* @param facts known when evaluating the rule.
* @return true if the rule should be applied, false otherwise.
*/
@Override
public boolean evaluate(Facts facts) {
return (Boolean) compileExpression.execute(facts.asMap());
}
}

@ -0,0 +1,131 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.runtime.JavaMethodReflectionFunctionMissing;
import org.jeasy.rules.api.Action;
import org.jeasy.rules.api.Condition;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.core.BasicRule;
import java.util.ArrayList;
import java.util.List;
/**
* author peterxiemin(bbcoder1987@gmail.com)
*/
public class AviatorRule extends BasicRule {
public static final AviatorEvaluatorInstance DEFAULT_AVIATOR;
static {
DEFAULT_AVIATOR = AviatorEvaluator.getInstance();
DEFAULT_AVIATOR.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance());
}
private Condition condition = Condition.FALSE;
private final List<Action> actions = new ArrayList<>();
/**
* Create a new Aviator rule.
*/
public AviatorRule() {
super(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
}
/**
* set rule name
* @param name
* @return this rule
*/
public AviatorRule name(String name) {
this.name = name;
return this;
}
/**
* set rule description
* @param description
* @return this rule
*/
public AviatorRule description(String description) {
this.description = description;
return this;
}
/**
* set rule priority
* @param priority
* @return this rule
*/
public AviatorRule priority(int priority) {
this.priority = priority;
return this;
}
/**
* set rule condition
* @param condition
* @return this rule
*/
public AviatorRule when(String condition) {
this.condition = new AviatorCondition(condition);
return this;
}
/**
* set rule action
* @param action
* @return this rule
*/
public AviatorRule then(String action) {
actions.add(new AviatorAction(action));
return this;
}
/**
* evaluate the rule
* @param facts
* @return
*/
@Override
public boolean evaluate(Facts facts) {
return condition.evaluate(facts);
}
/**
* execute the rule
* @param facts
* @throws Exception
*/
@Override
public void execute(Facts facts) throws Exception {
for (Action action : actions) {
action.execute(facts);
}
}
}

@ -0,0 +1,106 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.support.AbstractRuleFactory;
import org.jeasy.rules.support.RuleDefinition;
import org.jeasy.rules.support.reader.JsonRuleDefinitionReader;
import org.jeasy.rules.support.reader.RuleDefinitionReader;
import org.jeasy.rules.support.reader.YamlRuleDefinitionReader;
import java.io.Reader;
import java.util.List;
/**
* author peterxiemin(bbcoder1987@gmail.com)
*/
public class AviatorRuleFactory extends AbstractRuleFactory {
private final RuleDefinitionReader reader;
/**
* Create a new {@link AviatorRuleFactory} with a given reader.
*
* @param reader used to read rule definitions
* @see YamlRuleDefinitionReader
* @see JsonRuleDefinitionReader
*/
public AviatorRuleFactory(RuleDefinitionReader reader) {
this.reader = reader;
}
/**
* Create a new {@link AviatorRule} from a Reader.
*
* The rule descriptor should contain a single rule definition.
* If no rule definitions are found, a {@link IllegalArgumentException} will be thrown.
* If more than a rule is defined in the descriptor, the first rule will be returned.
*
* @param ruleDescriptor descriptor of rule definition
* @return a new rule
* @throws Exception if unable to create the rule from the descriptor
*/
public Rule createRule(Reader ruleDescriptor) throws Exception {
List<RuleDefinition> ruleDefinitions = reader.read(ruleDescriptor);
if (ruleDefinitions.isEmpty()) {
throw new IllegalArgumentException("rule descriptor is empty");
}
return createRule(ruleDefinitions.get(0));
}
/**
* Create a new {@link AviatorRule} from a {@link RuleDefinition}.
*
* @param rulesDescriptor definition of the rule
* @return a new rule
*/
public Rules createRules(Reader rulesDescriptor) throws Exception {
Rules rules = new Rules();
List<RuleDefinition> ruleDefinitions = reader.read(rulesDescriptor);
for (RuleDefinition ruleDefinition : ruleDefinitions) {
rules.register(createRule(ruleDefinition));
}
return rules;
}
/**
* Create a simple rule from a rule definition.
* @param ruleDefinition
* @return
*/
@Override
protected Rule createSimpleRule(RuleDefinition ruleDefinition) {
AviatorRule aviatorRule = new AviatorRule()
.name(ruleDefinition.getName())
.description(ruleDefinition.getDescription())
.priority(ruleDefinition.getPriority())
.when(ruleDefinition.getCondition());
for (String action : ruleDefinition.getActions()) {
aviatorRule.then(action);
}
return aviatorRule;
}
}

@ -0,0 +1,27 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* This package contains classes to support <a href="https://github.com/mvel/mvel">MVEL</a>.
*/
package org.jeasy.rules.aviator;

@ -0,0 +1,71 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import org.jeasy.rules.api.Facts;
import org.junit.Test;
import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOutNormalized;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
/**
* Author peterxiemin(bbcoder1987@gmail.com)
* Tests for {@link AviatorAction}.
*/
public class AviatorActionTest {
@Test
public void shouldExecuteExpressionSuccessfully() throws Exception {
Person person = new Person("foo", 20);
Facts facts = new Facts();
facts.put("person", person);
AviatorAction personAction = new AviatorAction("setAdult(person, true);");
personAction.execute(facts);
assertThat(person.isAdult()).isTrue();
}
@Test
public void shouldThrowExceptionWhenExpressionEvaluationFails() {
Person person = new Person("foo", 20);
Facts facts = new Facts();
facts.put("person", person);
AviatorAction aviatorAction = new AviatorAction("setBlah(person);");
assertThatThrownBy(() -> aviatorAction.execute(facts))
.isInstanceOf(Exception.class)
.hasMessageContaining("No matching method setBlah found taking 0 args for class org.jeasy.rules.aviator.Person");
}
@Test
public void shouldExecuteExpressionWithCustomFunction() throws Exception {
Facts facts = new Facts();
AviatorAction aviatorAction = new AviatorAction("fn hello() { println('Hello from EasyRules&Aviator!'); }; " +
"hello();");
String output = tapSystemOutNormalized(
() -> aviatorAction.execute(facts));
aviatorAction.execute(facts);
assertThat(output).isEqualTo("Hello from EasyRules&Aviator!\n");
}
}

@ -0,0 +1,70 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import org.jeasy.rules.api.Facts;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class AviatorConditionTest {
@Test
public void testAviatorExpressionEvaluation() {
// given
AviatorCondition isAdult = new AviatorCondition("person.age > 18");
Facts facts = new Facts();
facts.put("person", new Person("foo", 20));
// when
boolean evaluationResult = isAdult.evaluate(facts);
// then
assertThat(evaluationResult).isTrue();
}
@Test(expected = RuntimeException.class)
public void whenDeclaredFactIsNotPresent_thenShouldThrowRuntimeException() {
// given
AviatorCondition isHot = new AviatorCondition("temperature > 30");
Facts facts = new Facts();
// when
boolean evaluationResult = isHot.evaluate(facts);
// then
// expected exception
}
@Test
public void testAviatorExpressionEvaluationWithJavaUtilClass() {
AviatorCondition aviatorCondition = new AviatorCondition("let d = new java.util.Random(123); " +
"return nextBoolean(d)" +
";");
boolean evaluationResult = aviatorCondition.evaluate(new Facts());
assertThat(evaluationResult).isTrue();
}
}

@ -0,0 +1,178 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import org.assertj.core.api.Assertions;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.support.composite.UnitRuleGroup;
import org.jeasy.rules.support.reader.JsonRuleDefinitionReader;
import org.jeasy.rules.support.reader.YamlRuleDefinitionReader;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(Parameterized.class)
public class AviatorRuleFactoryTest {
@Parameterized.Parameter(0)
public AviatorRuleFactory factory;
@Parameterized.Parameter(1)
public String fileExtension;
@Parameterized.Parameters
public static Collection<Object[]> parameters() {
return Arrays.asList(new Object[][]{
{new AviatorRuleFactory(new YamlRuleDefinitionReader()), "yml"},
{new AviatorRuleFactory(new JsonRuleDefinitionReader()), "json"},
});
}
@Test
public void testCreateRules() throws Exception {
// given
String rulesDescriptor = "src/test/resources/rules." + fileExtension;
// when
Rules rules = factory.createRules(new FileReader(rulesDescriptor));
// then
Assertions.assertThat(rules).hasSize(2);
Iterator<Rule> iterator = rules.iterator();
Rule rule = iterator.next();
Assertions.assertThat(rule).isNotNull();
Assertions.assertThat(rule.getName()).isEqualTo("adult rule");
Assertions.assertThat(rule.getDescription()).isEqualTo("when age is greater than 18, then mark as adult");
Assertions.assertThat(rule.getPriority()).isEqualTo(1);
rule = iterator.next();
Assertions.assertThat(rule).isNotNull();
Assertions.assertThat(rule.getName()).isEqualTo("weather rule");
Assertions.assertThat(rule.getDescription()).isEqualTo("when it rains, then take an umbrella");
Assertions.assertThat(rule.getPriority()).isEqualTo(2);
}
@Test
public void testRuleCreationFromFileReader() throws Exception {
// given
Reader adultRuleDescriptorAsReader = new FileReader("src/test/resources/adult-rule." + fileExtension);
// when
Rule adultRule = factory.createRule(adultRuleDescriptorAsReader);
// then
assertThat(adultRule.getName()).isEqualTo("adult rule");
assertThat(adultRule.getDescription()).isEqualTo("when age is greater than 18, then mark as adult");
assertThat(adultRule.getPriority()).isEqualTo(1);
}
@Test
public void testRuleCreationFromStringReader() throws Exception {
// given
Path ruleDescriptor = Paths.get("src/test/resources/adult-rule." + fileExtension);
Reader adultRuleDescriptorAsReader = new StringReader(new String(Files.readAllBytes(ruleDescriptor)));
// when
Rule adultRule = factory.createRule(adultRuleDescriptorAsReader);
// then
assertThat(adultRule.getName()).isEqualTo("adult rule");
assertThat(adultRule.getDescription()).isEqualTo("when age is greater than 18, then mark as adult");
assertThat(adultRule.getPriority()).isEqualTo(1);
}
@Test
public void testRuleCreationFromFileReader_withCompositeRules() throws Exception {
// given
File rulesDescriptor = new File("src/test/resources/composite-rules." + fileExtension);
// when
Rules rules = factory.createRules(new FileReader(rulesDescriptor));
// then
assertThat(rules).hasSize(2);
Iterator<Rule> iterator = rules.iterator();
Rule rule = iterator.next();
assertThat(rule).isNotNull();
assertThat(rule.getName()).isEqualTo("Movie id rule");
assertThat(rule.getDescription()).isEqualTo("description");
assertThat(rule.getPriority()).isEqualTo(1);
assertThat(rule).isInstanceOf(UnitRuleGroup.class);
rule = iterator.next();
assertThat(rule).isNotNull();
assertThat(rule.getName()).isEqualTo("weather rule");
assertThat(rule.getDescription()).isEqualTo("when it rains, then take an umbrella");
assertThat(rule.getPriority()).isEqualTo(1);
}
@Test
public void testRuleCreationFromFileReader_withInvalidCompositeRuleType() {
// given
File rulesDescriptor = new File("src/test/resources/composite-rule-invalid-composite-rule-type." + fileExtension);
// when
Assertions.assertThatThrownBy(() -> factory.createRule(new FileReader(rulesDescriptor)))
// then
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid composite rule type, must be one of [UnitRuleGroup, ConditionalRuleGroup, ActivationRuleGroup]");
}
@Test
public void testRuleCreationFromFileReader_withEmptyComposingRules() {
// given
File rulesDescriptor = new File("src/test/resources/composite-rule-invalid-empty-composing-rules." + fileExtension);
// when
Assertions.assertThatThrownBy(() -> factory.createRule(new FileReader(rulesDescriptor)))
// then
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Composite rules must have composing rules specified");
}
@Test
public void testRuleCreationFromFileReader_withNonCompositeRuleDeclaresComposingRules() {
// given
File rulesDescriptor = new File("src/test/resources/non-composite-rule-with-composing-rules." + fileExtension);
// when
Assertions.assertThatThrownBy(() -> factory.createRule(new FileReader(rulesDescriptor)))
// then
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Non-composite rules cannot have composing rules");
}
}

@ -0,0 +1,68 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
import org.jeasy.rules.api.Facts;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
public class AviatorRuleTest {
private Facts facts = new Facts();
private AviatorRule aviatorRule = new AviatorRule().name("rn").description("rd").priority(1);
@Before
public void setUp() {
aviatorRule.when("person.age > 18");
aviatorRule.then("setAdult(person, true);");
}
@Test
public void whenTheRuleIsTriggered_thenConditionShouldBeEvaluated() {
// given
facts.put("person", new Person("foo", 20));
// when
boolean evaluationResult = aviatorRule.evaluate(facts);
// then
assertThat(evaluationResult).isTrue();
}
@Test
public void whenTheConditionIsTrue_thenActionsShouldBeExecuted() throws Exception {
// given
Person foo = new Person("foo", 20);
facts.put("person", foo);
// when
aviatorRule.execute(facts);
// then
assertThat(foo.isAdult()).isTrue();
}
}

@ -0,0 +1,60 @@
/*
* The MIT License
*
* Copyright (c) 2021, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.aviator;
public class Person {
private String name;
private int age;
private boolean isAdult;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isAdult() {
return isAdult;
}
public void setAdult(boolean adult) {
isAdult = adult;
}
}

@ -0,0 +1,11 @@
[
{
"name": "adult rule",
"description": "when age is greater than 18, then mark as adult",
"priority": 1,
"condition": "person.age > 18",
"actions": [
"setAdult(person, true);"
]
}
]

@ -0,0 +1,6 @@
name: adult rule
description: when age is greater than 18, then mark as adult
priority: 1
condition: "person.age > 18"
actions:
- "setAdult(person, true);"

@ -0,0 +1,23 @@
[
{
"name": "invalid rule",
"compositeRuleType": "foo",
"priority": 1,
"composingRules": [
{
"name": "rule1",
"condition": "true",
"actions": [
"p(\"\");"
]
},
{
"name": "rule2",
"condition": "false",
"actions": [
"p(\"\");"
]
}
]
}
]

@ -0,0 +1,12 @@
name: invalid rule
compositeRuleType: foo
priority: 1
composingRules:
- name: rule1
condition: "true"
actions:
- "p(\"\");"
- name: rule2
condition: "false"
actions:
- "p(\"\");"

@ -0,0 +1,8 @@
[
{
"name": "invalid rule",
"compositeRuleType": "UnitRuleGroup",
"priority": 1,
"composingRules": []
}
]

@ -0,0 +1,4 @@
name: invalid rule
compositeRuleType: UnitRuleGroup
priority: 1
composingRules:

@ -0,0 +1,36 @@
[
{
"name": "Movie id rule",
"compositeRuleType": "UnitRuleGroup",
"priority": 1,
"composingRules": [
{
"name": "Time is evening",
"description": "If it's later than 7pm",
"priority": 1,
"condition": "day.hour > 19",
"actions": [
"shouldProvideId(person, true);"
]
},
{
"name": "Movie is rated R",
"description": "If the movie is rated R",
"priority": 1,
"condition": "movie.rating == R",
"actions": [
"shouldProvideId(person, true);"
]
}
]
},
{
"name": "weather rule",
"description": "when it rains, then take an umbrella",
"priority": 1,
"condition": "rain == True",
"actions": [
"p(\"It rains, take an umbrella!\");"
]
}
]

@ -0,0 +1,23 @@
name: Movie id rule
compositeRuleType: UnitRuleGroup
priority: 1
composingRules:
- name: Time is evening
description: If it's later than 7pm
priority: 1
condition: "day.hour > 19"
actions:
- "shouldProvideId(person, true);"
- name: Movie is rated R
description: If the movie is rated R
priority: 1
condition: "movie.rating == R"
actions:
- "shouldProvideId(person, true);"
---
name: weather rule
description: when it rains, then take an umbrella
priority: 1
condition: "rain == True"
actions:
- "p(\"It rains, take an umbrella!\");"

@ -0,0 +1,30 @@
[
{
"name": "Movie id rule",
"priority": 1,
"condition": "true",
"actions": [
"p(\"\");"
],
"composingRules": [
{
"name": "Time is evening",
"description": "If it's later than 7pm",
"priority": 1,
"condition": "day.hour > 19",
"actions": [
"shouldProvideId(person, true);"
]
},
{
"name": "Movie is rated R",
"description": "If the movie is rated R",
"priority": 1,
"condition": "movie.rating == R",
"actions": [
"shouldProvideId(person, true);"
]
}
]
}
]

@ -0,0 +1,18 @@
name: Movie id rule
priority: 1
condition: "true"
actions:
- "p(\"\");"
composingRules:
- name: Time is evening
description: If it's later than 7pm
priority: 1
condition: "day.hour > 19"
actions:
- "shouldProvideId(person, true);"
- name: Movie is rated R
description: If the movie is rated R
priority: 1
condition: "movie.rating == R"
actions:
- "shouldProvideId(person, true);"

@ -0,0 +1,20 @@
[
{
"name": "adult rule",
"description": "when age is greater than 18, then mark as adult",
"priority": 1,
"condition": "person.age > 18",
"actions": [
"setAdult(person, true);"
]
},
{
"name": "weather rule",
"description": "when it rains, then take an umbrella",
"priority": 2,
"condition": "rain == true",
"actions": [
"p(\"It rains, take an umbrella!\");"
]
}
]

@ -0,0 +1,14 @@
---
name: adult rule
description: when age is greater than 18, then mark as adult
priority: 1
condition: "person.age > 18"
actions:
- "setAdult(person, true);"
---
name: weather rule
description: when it rains, then take an umbrella
priority: 2
condition: "rain == true"
actions:
- "p(\"It rains, take an umbrella!\");"

@ -20,6 +20,7 @@
<module>easy-rules-support</module>
<module>easy-rules-spel</module>
<module>easy-rules-jexl</module>
<module>easy-rules-aviator</module>
</modules>
<packaging>pom</packaging>
@ -168,7 +169,7 @@
<version>${maven-license-plugin.version}</version>
<configuration>
<properties>
<currentYear>2020</currentYear>
<currentYear>2021</currentYear>
</properties>
<strictCheck>true</strictCheck>
<includes>

Loading…
Cancel
Save