From 84e80f8b33b416930e612272e54a5db85f456f09 Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Mon, 8 Apr 2019 00:59:18 +0200 Subject: [PATCH] Add support to read rules from json objects Resolves #202 --- easy-rules-mvel/pom.xml | 9 +- .../mvel/MVELJsonRuleDefinitionReader.java | 120 ++++++++++ .../jeasy/rules/mvel/MVELRuleDefinition.java | 10 +- .../rules/mvel/MVELRuleDefinitionReader.java | 88 ++------ .../org/jeasy/rules/mvel/MVELRuleFactory.java | 103 ++++++++- .../mvel/MVELYamlRuleDefinitionReader.java | 121 +++++++++++ .../MVELJsonRuleDefinitionReaderTest.java | 205 ++++++++++++++++++ .../rules/mvel/MVELJsonRuleFactoryTest.java | 170 +++++++++++++++ ... => MVELYamlRuleDefinitionReaderTest.java} | 31 +-- ...Test.java => MVELYamlRuleFactoryTest.java} | 21 +- .../adult-rule-with-default-values.json | 8 + .../resources/adult-rule-without-actions.json | 5 + .../adult-rule-without-condition.json | 7 + .../src/test/resources/adult-rule.json | 11 + ...site-rule-invalid-composite-rule-type.json | 23 ++ ...te-rule-invalid-empty-composing-rules.json | 8 + .../src/test/resources/composite-rules.json | 36 +++ ...composite-rule.yml => composite-rules.yml} | 0 ...n-composite-rule-with-composing-rules.json | 30 +++ .../src/test/resources/rules-empty.json | 1 + easy-rules-mvel/src/test/resources/rules.json | 20 ++ .../jeasy/rules/tutorials/shop/Launcher.java | 7 +- 22 files changed, 928 insertions(+), 106 deletions(-) create mode 100644 easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReader.java create mode 100644 easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELYamlRuleDefinitionReader.java create mode 100644 easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReaderTest.java create mode 100644 easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleFactoryTest.java rename easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/{MVELRuleDefinitionReaderTest.java => MVELYamlRuleDefinitionReaderTest.java} (86%) rename easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/{MVELRuleFactoryTest.java => MVELYamlRuleFactoryTest.java} (88%) create mode 100644 easy-rules-mvel/src/test/resources/adult-rule-with-default-values.json create mode 100644 easy-rules-mvel/src/test/resources/adult-rule-without-actions.json create mode 100644 easy-rules-mvel/src/test/resources/adult-rule-without-condition.json create mode 100644 easy-rules-mvel/src/test/resources/adult-rule.json create mode 100644 easy-rules-mvel/src/test/resources/composite-rule-invalid-composite-rule-type.json create mode 100644 easy-rules-mvel/src/test/resources/composite-rule-invalid-empty-composing-rules.json create mode 100644 easy-rules-mvel/src/test/resources/composite-rules.json rename easy-rules-mvel/src/test/resources/{composite-rule.yml => composite-rules.yml} (100%) create mode 100644 easy-rules-mvel/src/test/resources/non-composite-rule-with-composing-rules.json create mode 100644 easy-rules-mvel/src/test/resources/rules-empty.json create mode 100644 easy-rules-mvel/src/test/resources/rules.json diff --git a/easy-rules-mvel/pom.xml b/easy-rules-mvel/pom.xml index 49d1efa..f65bcd4 100644 --- a/easy-rules-mvel/pom.xml +++ b/easy-rules-mvel/pom.xml @@ -15,7 +15,7 @@ 2.4.3.Final 1.19.0 - 2.9.8 + 2.9.8 @@ -63,7 +63,12 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml - ${jackson-dataformat-yaml.version} + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} diff --git a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReader.java b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReader.java new file mode 100644 index 0000000..c23628f --- /dev/null +++ b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReader.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * + * Copyright (c) 2019, 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.mvel; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.jeasy.rules.api.Rule; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Rule definition reader based on Jackson. + * + * This reader expects an array of rule definitions as input even for a single rule. For example: + * + *
+ *     [{rule1}, {rule2}]
+ * 
+ * + * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) + */ +@SuppressWarnings("unchecked") +public class MVELJsonRuleDefinitionReader implements MVELRuleDefinitionReader { + + private ObjectMapper objectMapper; + + /** + * Create a new {@link MVELJsonRuleDefinitionReader}. + */ + public MVELJsonRuleDefinitionReader() { + objectMapper = new ObjectMapper(); + } + + /** + * Create a new {@link MVELJsonRuleDefinitionReader}. + * + * @param objectMapper to use to read rule definitions + */ + public MVELJsonRuleDefinitionReader(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public List read(Reader reader) throws Exception { + List ruleDefinitions = new ArrayList<>(); + Object[] rules = objectMapper.readValue(reader, Object[].class ); + + for (Object rule : rules) { + Map map = (Map) rule; + ruleDefinitions.add(createRuleDefinitionFrom(map)); + } + return ruleDefinitions; + } + + private MVELRuleDefinition createRuleDefinitionFrom(Map map) { + MVELRuleDefinition ruleDefinition = new MVELRuleDefinition(); + + String name = (String) map.get("name"); + ruleDefinition.setName(name != null ? name : Rule.DEFAULT_NAME); + + String description = (String) map.get("description"); + ruleDefinition.setDescription(description != null ? description : Rule.DEFAULT_DESCRIPTION); + + Integer priority = (Integer) map.get("priority"); + ruleDefinition.setPriority(priority != null ? priority : Rule.DEFAULT_PRIORITY); + + String compositeRuleType = (String) map.get("compositeRuleType"); + + String condition = (String) map.get("condition"); + if (condition == null && compositeRuleType == null) { + throw new IllegalArgumentException("The rule condition must be specified"); + } + ruleDefinition.setCondition(condition); + + List actions = (List) map.get("actions"); + if ((actions == null || actions.isEmpty()) && compositeRuleType == null) { + throw new IllegalArgumentException("The rule action(s) must be specified"); + } + ruleDefinition.setActions(actions); + + List composingRules = (List) map.get("composingRules"); + if (composingRules != null && compositeRuleType == null) { + throw new IllegalArgumentException("Non-composite rules cannot have composing rules"); + } else if ((composingRules == null || composingRules.isEmpty()) && compositeRuleType != null) { + throw new IllegalArgumentException("Composite rules must have composing rules specified"); + } else if (composingRules != null) { + List composingRuleDefinitions = new ArrayList<>(); + for (Object rule : composingRules){ + Map composingRuleMap = (Map) rule; + composingRuleDefinitions.add(createRuleDefinitionFrom(composingRuleMap)); + } + ruleDefinition.setComposingRules(composingRuleDefinitions); + ruleDefinition.setCompositeRuleType(compositeRuleType); + } + + return ruleDefinition; + } +} diff --git a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinition.java b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinition.java index a98ca0c..6ecdbe1 100644 --- a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinition.java +++ b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinition.java @@ -28,7 +28,15 @@ import org.jeasy.rules.api.Rule; import java.util.ArrayList; import java.util.List; -class MVELRuleDefinition { +/** + * Rule definition as defined in a rule description. + * This class encapsulates static definition of an {@link MVELRule}. + * + * This definition is produced by a {@link MVELRuleDefinitionReader}. + * + * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) + */ +public class MVELRuleDefinition { private String name = Rule.DEFAULT_NAME; private String description = Rule.DEFAULT_DESCRIPTION; diff --git a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinitionReader.java b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinitionReader.java index 09cb111..f427d3a 100644 --- a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinitionReader.java +++ b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleDefinitionReader.java @@ -23,76 +23,28 @@ */ package org.jeasy.rules.mvel; -import org.jeasy.rules.api.Rule; -import org.yaml.snakeyaml.Yaml; - import java.io.Reader; -import java.util.ArrayList; import java.util.List; -import java.util.Map; - -@SuppressWarnings("unchecked") -class MVELRuleDefinitionReader { - - private Yaml yaml = new Yaml(); - - MVELRuleDefinition read(Reader reader) { - Object object = yaml.load(reader); - Map map = (Map) object; - return createRuleDefinitionFrom(map); - } - - List readAll(Reader reader) { - List ruleDefinitions = new ArrayList<>(); - Iterable rules = yaml.loadAll(reader); - for (Object rule : rules) { - Map map = (Map) rule; - ruleDefinitions.add(createRuleDefinitionFrom(map)); - } - return ruleDefinitions; - } - - private static MVELRuleDefinition createRuleDefinitionFrom(Map map) { - MVELRuleDefinition ruleDefinition = new MVELRuleDefinition(); - - String name = (String) map.get("name"); - ruleDefinition.setName(name != null ? name : Rule.DEFAULT_NAME); - - String description = (String) map.get("description"); - ruleDefinition.setDescription(description != null ? description : Rule.DEFAULT_DESCRIPTION); - Integer priority = (Integer) map.get("priority"); - ruleDefinition.setPriority(priority != null ? priority : Rule.DEFAULT_PRIORITY); - - String compositeRuleType = (String) map.get("compositeRuleType"); - - String condition = (String) map.get("condition"); - if (condition == null && compositeRuleType == null) { - throw new IllegalArgumentException("The rule condition must be specified"); - } - ruleDefinition.setCondition(condition); - - List actions = (List) map.get("actions"); - if ((actions == null || actions.isEmpty()) && compositeRuleType == null) { - throw new IllegalArgumentException("The rule action(s) must be specified"); - } - ruleDefinition.setActions(actions); - - List composingRules = (List) map.get("composingRules"); - if (composingRules != null && compositeRuleType == null) { - throw new IllegalArgumentException("Non-composite rules cannot have composing rules"); - } else if (composingRules == null && compositeRuleType != null) { - throw new IllegalArgumentException("Composite rules must have composing rules specified"); - } else if (composingRules != null) { - List composingRuleDefinitions = new ArrayList<>(); - for (Object rule : composingRules){ - Map composingRuleMap = (Map) rule; - composingRuleDefinitions.add(createRuleDefinitionFrom(composingRuleMap)); - } - ruleDefinition.setComposingRules(composingRuleDefinitions); - ruleDefinition.setCompositeRuleType(compositeRuleType); - } +/** + * Strategy interface for {@link MVELRuleDefinition} readers. + * + * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) + * @see MVELJsonRuleDefinitionReader + * @see MVELYamlRuleDefinitionReader + */ +public interface MVELRuleDefinitionReader { + + /** + * Read a list of rule definitions from a rules descriptor. + * + * The descriptor is expected to contain a collection of rule definitions + * even for a single rule. + * + * @param reader of the rules descriptor + * @return a list of rule definitions + * @throws Exception if a problem occurs during rule defintion parsing + */ + List read(Reader reader) throws Exception; - return ruleDefinition; - } } diff --git a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleFactory.java b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleFactory.java index 75cd4f3..eb54da8 100644 --- a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleFactory.java +++ b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELRuleFactory.java @@ -42,7 +42,10 @@ import java.util.List; */ public class MVELRuleFactory { - private static final MVELRuleDefinitionReader READER = new MVELRuleDefinitionReader(); + // for backward compatibility only, will be removed in v3.4 + private static final MVELRuleDefinitionReader READER = new MVELYamlRuleDefinitionReader(); + + private MVELRuleDefinitionReader reader; private static final List ALLOWED_COMPOSITE_RULE_TYPES = Arrays.asList( UnitRuleGroup.class.getSimpleName(), @@ -50,26 +53,77 @@ public class MVELRuleFactory { ActivationRuleGroup.class.getSimpleName() ); + /** + * Create a new {@link MVELRuleFactory} with a given reader. + * + * @param reader to use to read rule definitions + * @see MVELYamlRuleDefinitionReader + * @see MVELJsonRuleDefinitionReader + */ + public MVELRuleFactory(MVELRuleDefinitionReader reader) { + this.reader = reader; + } + /** * Create a new {@link MVELRule} from a Reader. * * @param ruleDescriptor as a Reader * @return a new rule */ - public static Rule createRuleFrom(Reader ruleDescriptor) { - return createRuleFrom(ruleDescriptor, new ParserContext()); + public Rule createRule(Reader ruleDescriptor) throws Exception { + return createRule(ruleDescriptor, new ParserContext()); } /** * Create a new {@link MVELRule} 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 as a Reader + * @param parserContext the MVEL parser context + * @return a new rule + */ + public Rule createRule(Reader ruleDescriptor, ParserContext parserContext) throws Exception { + List ruleDefinitions = reader.read(ruleDescriptor); + if (ruleDefinitions.isEmpty()) { + throw new IllegalArgumentException("rule descriptor is empty"); + } + return createRule(ruleDefinitions.get(0), parserContext); + } + + /** + * Create a new {@link MVELRule} using a {@link MVELYamlRuleDefinitionReader}. + * + * @param ruleDescriptor as a Reader + * @return a new rule + * @deprecated Use {@link MVELRuleFactory#createRule(java.io.Reader)} instead. This method will be removed in v3.4. + */ + @Deprecated + public static Rule createRuleFrom(Reader ruleDescriptor) throws Exception { + return createRuleFrom(ruleDescriptor, new ParserContext()); + } + + /** + * Create a new {@link MVELRule} using a {@link MVELYamlRuleDefinitionReader}. + * + * 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 as a Reader * @param parserContext the MVEL parser context * @return a new rule + * @deprecated Use {@link MVELRuleFactory#createRule(java.io.Reader, org.mvel2.ParserContext)} instead. This method will be removed in v3.4. */ - public static Rule createRuleFrom(Reader ruleDescriptor, ParserContext parserContext) { - MVELRuleDefinition ruleDefinition = READER.read(ruleDescriptor); - return createRule(ruleDefinition, parserContext); + @Deprecated + public static Rule createRuleFrom(Reader ruleDescriptor, ParserContext parserContext) throws Exception { + List ruleDefinitions = READER.read(ruleDescriptor); + if (ruleDefinitions.isEmpty()) { + throw new IllegalArgumentException("rule descriptor is empty"); + } + return createRule(ruleDefinitions.get(0), parserContext); } /** @@ -78,8 +132,8 @@ public class MVELRuleFactory { * @param rulesDescriptor as a Reader * @return a set of rules */ - public static Rules createRulesFrom(Reader rulesDescriptor) { - return createRulesFrom(rulesDescriptor, new ParserContext()); + public Rules createRules(Reader rulesDescriptor) throws Exception { + return createRules(rulesDescriptor, new ParserContext()); } /** @@ -88,9 +142,38 @@ public class MVELRuleFactory { * @param rulesDescriptor as a Reader * @return a set of rules */ - public static Rules createRulesFrom(Reader rulesDescriptor, ParserContext parserContext) { + public Rules createRules(Reader rulesDescriptor, ParserContext parserContext) throws Exception { + Rules rules = new Rules(); + List ruleDefinition = reader.read(rulesDescriptor); + for (MVELRuleDefinition mvelRuleDefinition : ruleDefinition) { + rules.register(createRule(mvelRuleDefinition, parserContext)); + } + return rules; + } + + /** + * Create a set of {@link MVELRule} using a {@link MVELYamlRuleDefinitionReader}. + * + * @param rulesDescriptor as a Reader + * @return a set of rules + * @deprecated Use {@link MVELRuleFactory#createRules(java.io.Reader)} instead. This method will be removed in v3.4. + */ + @Deprecated + public static Rules createRulesFrom(Reader rulesDescriptor) throws Exception { + return createRulesFrom(rulesDescriptor, new ParserContext()); + } + + /** + * Create a set of {@link MVELRule} using a {@link MVELYamlRuleDefinitionReader}. + * + * @param rulesDescriptor as a Reader + * @return a set of rules + * @deprecated Use {@link MVELRuleFactory#createRules(java.io.Reader, org.mvel2.ParserContext)} instead. This method will be removed in v3.4. + */ + @Deprecated + public static Rules createRulesFrom(Reader rulesDescriptor, ParserContext parserContext) throws Exception { Rules rules = new Rules(); - List ruleDefinition = READER.readAll(rulesDescriptor); + List ruleDefinition = READER.read(rulesDescriptor); for (MVELRuleDefinition mvelRuleDefinition : ruleDefinition) { rules.register(createRule(mvelRuleDefinition, parserContext)); } diff --git a/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELYamlRuleDefinitionReader.java b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELYamlRuleDefinitionReader.java new file mode 100644 index 0000000..795dd00 --- /dev/null +++ b/easy-rules-mvel/src/main/java/org/jeasy/rules/mvel/MVELYamlRuleDefinitionReader.java @@ -0,0 +1,121 @@ +/** + * The MIT License + * + * Copyright (c) 2019, 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.mvel; + +import org.jeasy.rules.api.Rule; +import org.yaml.snakeyaml.Yaml; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Rule definition reader based on Jackson Yaml. + * + * This reader expects a collection of rule definitions as input even for a single rule. For example: + * + *
+ *     rule1
+ *     ---
+ *     rule2
+ * 
+ * + * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) + */ +@SuppressWarnings("unchecked") +public class MVELYamlRuleDefinitionReader implements MVELRuleDefinitionReader { + + private Yaml yaml; + + /** + * Create a new {@link MVELYamlRuleDefinitionReader}. + */ + public MVELYamlRuleDefinitionReader() { + yaml = new Yaml(); + } + + /** + * Create a new {@link MVELYamlRuleDefinitionReader}. + * + * @param yaml to use to read rule definitions + */ + public MVELYamlRuleDefinitionReader(Yaml yaml) { + this.yaml = yaml; + } + + public List read(Reader reader) { + List ruleDefinitions = new ArrayList<>(); + Iterable rules = yaml.loadAll(reader); + for (Object rule : rules) { + Map map = (Map) rule; + ruleDefinitions.add(createRuleDefinitionFrom(map)); + } + return ruleDefinitions; + } + + private MVELRuleDefinition createRuleDefinitionFrom(Map map) { + MVELRuleDefinition ruleDefinition = new MVELRuleDefinition(); + + String name = (String) map.get("name"); + ruleDefinition.setName(name != null ? name : Rule.DEFAULT_NAME); + + String description = (String) map.get("description"); + ruleDefinition.setDescription(description != null ? description : Rule.DEFAULT_DESCRIPTION); + + Integer priority = (Integer) map.get("priority"); + ruleDefinition.setPriority(priority != null ? priority : Rule.DEFAULT_PRIORITY); + + String compositeRuleType = (String) map.get("compositeRuleType"); + + String condition = (String) map.get("condition"); + if (condition == null && compositeRuleType == null) { + throw new IllegalArgumentException("The rule condition must be specified"); + } + ruleDefinition.setCondition(condition); + + List actions = (List) map.get("actions"); + if ((actions == null || actions.isEmpty()) && compositeRuleType == null) { + throw new IllegalArgumentException("The rule action(s) must be specified"); + } + ruleDefinition.setActions(actions); + + List composingRules = (List) map.get("composingRules"); + if (composingRules != null && compositeRuleType == null) { + throw new IllegalArgumentException("Non-composite rules cannot have composing rules"); + } else if (composingRules == null && compositeRuleType != null) { + throw new IllegalArgumentException("Composite rules must have composing rules specified"); + } else if (composingRules != null) { + List composingRuleDefinitions = new ArrayList<>(); + for (Object rule : composingRules){ + Map composingRuleMap = (Map) rule; + composingRuleDefinitions.add(createRuleDefinitionFrom(composingRuleMap)); + } + ruleDefinition.setComposingRules(composingRuleDefinitions); + ruleDefinition.setCompositeRuleType(compositeRuleType); + } + + return ruleDefinition; + } +} diff --git a/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReaderTest.java b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReaderTest.java new file mode 100644 index 0000000..a6a4311 --- /dev/null +++ b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleDefinitionReaderTest.java @@ -0,0 +1,205 @@ +/** + * The MIT License + * + * Copyright (c) 2019, 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.mvel; + +import org.jeasy.rules.api.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.FileReader; +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +// TODO use parametrized test to merge this test class with MVELYamlRuleDefinitionReaderTest +public class MVELJsonRuleDefinitionReaderTest { + + private MVELRuleDefinitionReader ruleDefinitionReader = new MVELJsonRuleDefinitionReader(); + + @Test + public void testRuleDefinitionReadingFromFile() throws Exception { + // given + File adultRuleDescriptor = new File("src/test/resources/adult-rule.json"); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + + // then + assertThat(ruleDefinitions).hasSize(1); + MVELRuleDefinition adultRuleDefinition = ruleDefinitions.get(0); + assertThat(adultRuleDefinition).isNotNull(); + assertThat(adultRuleDefinition.getName()).isEqualTo("adult rule"); + assertThat(adultRuleDefinition.getDescription()).isEqualTo("when age is greater then 18, then mark as adult"); + assertThat(adultRuleDefinition.getPriority()).isEqualTo(1); + assertThat(adultRuleDefinition.getCondition()).isEqualTo("person.age > 18"); + assertThat(adultRuleDefinition.getActions()).isEqualTo(Collections.singletonList("person.setAdult(true);")); + } + + @Test + public void testRuleDefinitionReadingFromString() throws Exception { + // given + String adultRuleDescriptor = new String(Files.readAllBytes(Paths.get("src/test/resources/adult-rule.json"))); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new StringReader(adultRuleDescriptor)); + + // then + assertThat(ruleDefinitions).hasSize(1); + MVELRuleDefinition adultRuleDefinition = ruleDefinitions.get(0); + assertThat(adultRuleDefinition).isNotNull(); + assertThat(adultRuleDefinition.getName()).isEqualTo("adult rule"); + assertThat(adultRuleDefinition.getDescription()).isEqualTo("when age is greater then 18, then mark as adult"); + assertThat(adultRuleDefinition.getPriority()).isEqualTo(1); + assertThat(adultRuleDefinition.getCondition()).isEqualTo("person.age > 18"); + assertThat(adultRuleDefinition.getActions()).isEqualTo(Collections.singletonList("person.setAdult(true);")); + } + + @Test + public void testRuleDefinitionReading_withDefaultValues() throws Exception { + // given + File adultRuleDescriptor = new File("src/test/resources/adult-rule-with-default-values.json"); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + + // then + assertThat(ruleDefinitions).hasSize(1); + MVELRuleDefinition adultRuleDefinition = ruleDefinitions.get(0); + assertThat(adultRuleDefinition).isNotNull(); + assertThat(adultRuleDefinition.getName()).isEqualTo(Rule.DEFAULT_NAME); + assertThat(adultRuleDefinition.getDescription()).isEqualTo(Rule.DEFAULT_DESCRIPTION); + assertThat(adultRuleDefinition.getPriority()).isEqualTo(Rule.DEFAULT_PRIORITY); + assertThat(adultRuleDefinition.getCondition()).isEqualTo("person.age > 18"); + assertThat(adultRuleDefinition.getActions()).isEqualTo(Collections.singletonList("person.setAdult(true);")); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidRuleDefinitionReading_whenNoCondition() throws Exception { + // given + File adultRuleDescriptor = new File("src/test/resources/adult-rule-without-condition.json"); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + + // then + // expected exception + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidRuleDefinitionReading_whenNoActions() throws Exception { + // given + File adultRuleDescriptor = new File("src/test/resources/adult-rule-without-actions.json"); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + + // then + // expected exception + } + + @Test + public void testRulesDefinitionReading() throws Exception { + // given + File rulesDescriptor = new File("src/test/resources/rules.json"); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(rulesDescriptor)); + + // then + assertThat(ruleDefinitions).hasSize(2); + MVELRuleDefinition ruleDefinition = ruleDefinitions.get(0); + assertThat(ruleDefinition).isNotNull(); + assertThat(ruleDefinition.getName()).isEqualTo("adult rule"); + assertThat(ruleDefinition.getDescription()).isEqualTo("when age is greater then 18, then mark as adult"); + assertThat(ruleDefinition.getPriority()).isEqualTo(1); + assertThat(ruleDefinition.getCondition()).isEqualTo("person.age > 18"); + assertThat(ruleDefinition.getActions()).isEqualTo(Collections.singletonList("person.setAdult(true);")); + + ruleDefinition = ruleDefinitions.get(1); + assertThat(ruleDefinition).isNotNull(); + assertThat(ruleDefinition.getName()).isEqualTo("weather rule"); + assertThat(ruleDefinition.getDescription()).isEqualTo("when it rains, then take an umbrella"); + assertThat(ruleDefinition.getPriority()).isEqualTo(2); + assertThat(ruleDefinition.getCondition()).isEqualTo("rain == true"); + assertThat(ruleDefinition.getActions()).isEqualTo(Collections.singletonList("System.out.println(\"It rains, take an umbrella!\");")); + } + + @Test + public void testEmptyRulesDefinitionReading() throws Exception { + // given + File rulesDescriptor = new File("src/test/resources/rules-empty.json"); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(rulesDescriptor)); + + // then + assertThat(ruleDefinitions).hasSize(0); + } + + @Test + public void testRuleDefinitionReading_withCompositeAndBasicRules() throws Exception { + // given + File compositeRuleDescriptor = new File("src/test/resources/composite-rules.json"); + + // when + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(compositeRuleDescriptor)); + + // then + assertThat(ruleDefinitions).hasSize(2); + + // then + MVELRuleDefinition ruleDefinition = ruleDefinitions.get(0); + assertThat(ruleDefinition).isNotNull(); + assertThat(ruleDefinition.getName()).isEqualTo("Movie id rule"); + assertThat(ruleDefinition.getDescription()).isEqualTo("description"); + assertThat(ruleDefinition.getPriority()).isEqualTo(1); + assertThat(ruleDefinition.getCompositeRuleType()).isEqualTo("UnitRuleGroup"); + assertThat(ruleDefinition.getComposingRules()).isNotEmpty(); + + List subrules = ruleDefinition.getComposingRules(); + assertThat(subrules).hasSize(2); + + MVELRuleDefinition subrule = subrules.get(0); + assertThat(subrule.getName()).isEqualTo("Time is evening"); + assertThat(subrule.getDescription()).isEqualTo("If it's later than 7pm"); + assertThat(subrule.getPriority()).isEqualTo(1); + + subrule = subrules.get(1); + assertThat(subrule.getName()).isEqualTo("Movie is rated R"); + assertThat(subrule.getDescription()).isEqualTo("If the movie is rated R"); + assertThat(subrule.getPriority()).isEqualTo(1); + + ruleDefinition = ruleDefinitions.get(1); + assertThat(ruleDefinition).isNotNull(); + assertThat(ruleDefinition.getName()).isEqualTo("weather rule"); + assertThat(ruleDefinition.getDescription()).isEqualTo("when it rains, then take an umbrella"); + assertThat(ruleDefinition.getComposingRules()).isEmpty(); + assertThat(ruleDefinition.getCondition()).isEqualTo("rain == True"); + assertThat(ruleDefinition.getActions()).isEqualTo(Collections.singletonList("System.out.println(\"It rains, take an umbrella!\");")); + } +} \ No newline at end of file diff --git a/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleFactoryTest.java b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleFactoryTest.java new file mode 100644 index 0000000..e1cc68e --- /dev/null +++ b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELJsonRuleFactoryTest.java @@ -0,0 +1,170 @@ +/** + * The MIT License + * + * Copyright (c) 2019, 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.mvel; + +import org.jeasy.rules.api.Rule; +import org.jeasy.rules.api.Rules; +import org.jeasy.rules.support.UnitRuleGroup; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +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.Paths; +import java.util.Iterator; + +import static org.assertj.core.api.Assertions.assertThat; + +// TODO use parametrized test to merge this test class with MVELYamlRuleFactoryTest +public class MVELJsonRuleFactoryTest { + + @org.junit.Rule + public ExpectedException expectedException = ExpectedException.none(); + + private MVELRuleFactory factory = new MVELRuleFactory(new MVELJsonRuleDefinitionReader()); + + @Test + public void testRulesCreation() throws Exception { + // given + File rulesDescriptor = new File("src/test/resources/rules.json"); + + // when + Rules rules = factory.createRules(new FileReader(rulesDescriptor)); + + // then + assertThat(rules).hasSize(2); + Iterator iterator = rules.iterator(); + + Rule rule = iterator.next(); + assertThat(rule).isNotNull(); + assertThat(rule.getName()).isEqualTo("adult rule"); + assertThat(rule.getDescription()).isEqualTo("when age is greater then 18, then mark as adult"); + assertThat(rule.getPriority()).isEqualTo(1); + + 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(2); + } + + @Test + public void testRuleCreationFromFileReader() throws Exception{ + // given + Reader adultRuleDescriptorAsReader = new FileReader("src/test/resources/adult-rule.json"); + + // when + Rule adultRule = factory.createRule(adultRuleDescriptorAsReader); + + // then + assertThat(adultRule.getName()).isEqualTo("adult rule"); + assertThat(adultRule.getDescription()).isEqualTo("when age is greater then 18, then mark as adult"); + assertThat(adultRule.getPriority()).isEqualTo(1); + } + + @Test + public void testRuleCreationFromStringReader() throws Exception{ + // given + Reader adultRuleDescriptorAsReader = new StringReader(new String(Files.readAllBytes(Paths.get("src/test/resources/adult-rule.json")))); + + // when + Rule adultRule = factory.createRule(adultRuleDescriptorAsReader); + + // then + assertThat(adultRule.getName()).isEqualTo("adult rule"); + assertThat(adultRule.getDescription()).isEqualTo("when age is greater then 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.json"); + + // when + Rules rules = factory.createRules(new FileReader(rulesDescriptor)); + + // then + assertThat(rules).hasSize(2); + Iterator 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() throws Exception { + // given + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Invalid composite rule type, must be one of [UnitRuleGroup, ConditionalRuleGroup, ActivationRuleGroup]"); + File rulesDescriptor = new File("src/test/resources/composite-rule-invalid-composite-rule-type.json"); + + // when + Rule rule = factory.createRule(new FileReader(rulesDescriptor)); + + // then + // expected exception + } + + @Test + public void testRuleCreationFromFileReader_withEmptyComposingRules() throws Exception { + // given + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Composite rules must have composing rules specified"); + File rulesDescriptor = new File("src/test/resources/composite-rule-invalid-empty-composing-rules.json"); + + // when + Rule rule = factory.createRule(new FileReader(rulesDescriptor)); + + // then + // expected exception + } + + @Test + public void testRuleCreationFromFileReader_withNonCompositeRuleDeclaresComposingRules() throws Exception { + // given + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Non-composite rules cannot have composing rules"); + File rulesDescriptor = new File("src/test/resources/non-composite-rule-with-composing-rules.json"); + + // when + Rule rule = factory.createRule(new FileReader(rulesDescriptor)); + + // then + // expected exception + } +} \ No newline at end of file diff --git a/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELRuleDefinitionReaderTest.java b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELYamlRuleDefinitionReaderTest.java similarity index 86% rename from easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELRuleDefinitionReaderTest.java rename to easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELYamlRuleDefinitionReaderTest.java index ac669fd..847fab7 100644 --- a/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELRuleDefinitionReaderTest.java +++ b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELYamlRuleDefinitionReaderTest.java @@ -24,7 +24,6 @@ package org.jeasy.rules.mvel; import org.jeasy.rules.api.Rule; -import org.jeasy.rules.api.Rules; import org.junit.Test; import java.io.File; @@ -33,14 +32,14 @@ import java.io.StringReader; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Collections; -import java.util.Iterator; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -public class MVELRuleDefinitionReaderTest { +// TODO use parametrized test to merge this test class with MVELJsonRuleDefinitionReaderTest +public class MVELYamlRuleDefinitionReaderTest { - private MVELRuleDefinitionReader ruleDefinitionReader = new MVELRuleDefinitionReader(); + private MVELRuleDefinitionReader ruleDefinitionReader = new MVELYamlRuleDefinitionReader(); @Test public void testRuleDefinitionReadingFromFile() throws Exception { @@ -48,9 +47,11 @@ public class MVELRuleDefinitionReaderTest { File adultRuleDescriptor = new File("src/test/resources/adult-rule.yml"); // when - MVELRuleDefinition adultRuleDefinition = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); // then + assertThat(ruleDefinitions).hasSize(1); + MVELRuleDefinition adultRuleDefinition = ruleDefinitions.get(0); assertThat(adultRuleDefinition).isNotNull(); assertThat(adultRuleDefinition.getName()).isEqualTo("adult rule"); assertThat(adultRuleDefinition.getDescription()).isEqualTo("when age is greater then 18, then mark as adult"); @@ -65,9 +66,11 @@ public class MVELRuleDefinitionReaderTest { String adultRuleDescriptor = new String(Files.readAllBytes(Paths.get("src/test/resources/adult-rule.yml"))); // when - MVELRuleDefinition adultRuleDefinition = ruleDefinitionReader.read(new StringReader(adultRuleDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new StringReader(adultRuleDescriptor)); // then + assertThat(ruleDefinitions).hasSize(1); + MVELRuleDefinition adultRuleDefinition = ruleDefinitions.get(0); assertThat(adultRuleDefinition).isNotNull(); assertThat(adultRuleDefinition.getName()).isEqualTo("adult rule"); assertThat(adultRuleDefinition.getDescription()).isEqualTo("when age is greater then 18, then mark as adult"); @@ -82,9 +85,11 @@ public class MVELRuleDefinitionReaderTest { File adultRuleDescriptor = new File("src/test/resources/adult-rule-with-default-values.yml"); // when - MVELRuleDefinition adultRuleDefinition = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); // then + assertThat(ruleDefinitions).hasSize(1); + MVELRuleDefinition adultRuleDefinition = ruleDefinitions.get(0); assertThat(adultRuleDefinition).isNotNull(); assertThat(adultRuleDefinition.getName()).isEqualTo(Rule.DEFAULT_NAME); assertThat(adultRuleDefinition.getDescription()).isEqualTo(Rule.DEFAULT_DESCRIPTION); @@ -99,7 +104,7 @@ public class MVELRuleDefinitionReaderTest { File adultRuleDescriptor = new File("src/test/resources/adult-rule-without-condition.yml"); // when - MVELRuleDefinition adultRuleDefinition = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); // then // expected exception @@ -111,7 +116,7 @@ public class MVELRuleDefinitionReaderTest { File adultRuleDescriptor = new File("src/test/resources/adult-rule-without-actions.yml"); // when - MVELRuleDefinition adultRuleDefinition = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(adultRuleDescriptor)); // then // expected exception @@ -123,7 +128,7 @@ public class MVELRuleDefinitionReaderTest { File rulesDescriptor = new File("src/test/resources/rules.yml"); // when - List ruleDefinitions = ruleDefinitionReader.readAll(new FileReader(rulesDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(rulesDescriptor)); // then assertThat(ruleDefinitions).hasSize(2); @@ -150,7 +155,7 @@ public class MVELRuleDefinitionReaderTest { File rulesDescriptor = new File("src/test/resources/rules-empty.yml"); // when - List ruleDefinitions = ruleDefinitionReader.readAll(new FileReader(rulesDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(rulesDescriptor)); // then assertThat(ruleDefinitions).hasSize(0); @@ -159,10 +164,10 @@ public class MVELRuleDefinitionReaderTest { @Test public void testRuleDefinitionReading_withCompositeAndBasicRules() throws Exception { // given - File compositeRuleDescriptor = new File("src/test/resources/composite-rule.yml"); + File compositeRuleDescriptor = new File("src/test/resources/composite-rules.yml"); // when - List ruleDefinitions = ruleDefinitionReader.readAll(new FileReader(compositeRuleDescriptor)); + List ruleDefinitions = ruleDefinitionReader.read(new FileReader(compositeRuleDescriptor)); // then assertThat(ruleDefinitions).hasSize(2); diff --git a/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELRuleFactoryTest.java b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELYamlRuleFactoryTest.java similarity index 88% rename from easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELRuleFactoryTest.java rename to easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELYamlRuleFactoryTest.java index 6018af5..98ea8b7 100644 --- a/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELRuleFactoryTest.java +++ b/easy-rules-mvel/src/test/java/org/jeasy/rules/mvel/MVELYamlRuleFactoryTest.java @@ -39,18 +39,21 @@ import java.util.Iterator; import static org.assertj.core.api.Assertions.assertThat; -public class MVELRuleFactoryTest { +// TODO use parametrized test to merge this test class with MVELJsonRuleFactoryTest +public class MVELYamlRuleFactoryTest { @org.junit.Rule public ExpectedException expectedException = ExpectedException.none(); + private MVELRuleFactory factory = new MVELRuleFactory(new MVELYamlRuleDefinitionReader()); + @Test public void testRulesCreation() throws Exception { // given File rulesDescriptor = new File("src/test/resources/rules.yml"); // when - Rules rules = MVELRuleFactory.createRulesFrom(new FileReader(rulesDescriptor)); + Rules rules = factory.createRules(new FileReader(rulesDescriptor)); // then assertThat(rules).hasSize(2); @@ -75,7 +78,7 @@ public class MVELRuleFactoryTest { Reader adultRuleDescriptorAsReader = new FileReader("src/test/resources/adult-rule.yml"); // when - Rule adultRule = MVELRuleFactory.createRuleFrom(adultRuleDescriptorAsReader); + Rule adultRule = factory.createRule(adultRuleDescriptorAsReader); // then assertThat(adultRule.getName()).isEqualTo("adult rule"); @@ -89,7 +92,7 @@ public class MVELRuleFactoryTest { Reader adultRuleDescriptorAsReader = new StringReader(new String(Files.readAllBytes(Paths.get("src/test/resources/adult-rule.yml")))); // when - Rule adultRule = MVELRuleFactory.createRuleFrom(adultRuleDescriptorAsReader); + Rule adultRule = factory.createRule(adultRuleDescriptorAsReader); // then assertThat(adultRule.getName()).isEqualTo("adult rule"); @@ -100,10 +103,10 @@ public class MVELRuleFactoryTest { @Test public void testRuleCreationFromFileReader_withCompositeRules() throws Exception { // given - File rulesDescriptor = new File("src/test/resources/composite-rule.yml"); + File rulesDescriptor = new File("src/test/resources/composite-rules.yml"); // when - Rules rules = MVELRuleFactory.createRulesFrom(new FileReader(rulesDescriptor)); + Rules rules = factory.createRules(new FileReader(rulesDescriptor)); // then assertThat(rules).hasSize(2); @@ -131,7 +134,7 @@ public class MVELRuleFactoryTest { File rulesDescriptor = new File("src/test/resources/composite-rule-invalid-composite-rule-type.yml"); // when - Rules rules = MVELRuleFactory.createRulesFrom(new FileReader(rulesDescriptor)); + Rule rule = factory.createRule(new FileReader(rulesDescriptor)); // then // expected exception @@ -145,7 +148,7 @@ public class MVELRuleFactoryTest { File rulesDescriptor = new File("src/test/resources/composite-rule-invalid-empty-composing-rules.yml"); // when - Rules rules = MVELRuleFactory.createRulesFrom(new FileReader(rulesDescriptor)); + Rule rule = factory.createRule(new FileReader(rulesDescriptor)); // then // expected exception @@ -159,7 +162,7 @@ public class MVELRuleFactoryTest { File rulesDescriptor = new File("src/test/resources/non-composite-rule-with-composing-rules.yml"); // when - Rules rules = MVELRuleFactory.createRulesFrom(new FileReader(rulesDescriptor)); + Rule rule = factory.createRule(new FileReader(rulesDescriptor)); // then // expected exception diff --git a/easy-rules-mvel/src/test/resources/adult-rule-with-default-values.json b/easy-rules-mvel/src/test/resources/adult-rule-with-default-values.json new file mode 100644 index 0000000..1bc28e7 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/adult-rule-with-default-values.json @@ -0,0 +1,8 @@ +[ + { + "condition": "person.age > 18", + "actions": [ + "person.setAdult(true);" + ] + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/adult-rule-without-actions.json b/easy-rules-mvel/src/test/resources/adult-rule-without-actions.json new file mode 100644 index 0000000..0632e18 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/adult-rule-without-actions.json @@ -0,0 +1,5 @@ +[ + { + "condition": "person.age > 18" + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/adult-rule-without-condition.json b/easy-rules-mvel/src/test/resources/adult-rule-without-condition.json new file mode 100644 index 0000000..41c28b1 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/adult-rule-without-condition.json @@ -0,0 +1,7 @@ +[ + { + "actions": [ + "person.setAdult(true);" + ] + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/adult-rule.json b/easy-rules-mvel/src/test/resources/adult-rule.json new file mode 100644 index 0000000..70d9949 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/adult-rule.json @@ -0,0 +1,11 @@ +[ + { + "name": "adult rule", + "description": "when age is greater then 18, then mark as adult", + "priority": 1, + "condition": "person.age > 18", + "actions": [ + "person.setAdult(true);" + ] + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/composite-rule-invalid-composite-rule-type.json b/easy-rules-mvel/src/test/resources/composite-rule-invalid-composite-rule-type.json new file mode 100644 index 0000000..55e3142 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/composite-rule-invalid-composite-rule-type.json @@ -0,0 +1,23 @@ +[ + { + "name": "invalid rule", + "compositeRuleType": "foo", + "priority": 1, + "composingRules": [ + { + "name": "rule1", + "condition": "true", + "actions": [ + "System.out.println();" + ] + }, + { + "name": "rule2", + "condition": "false", + "actions": [ + "System.out.println();" + ] + } + ] + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/composite-rule-invalid-empty-composing-rules.json b/easy-rules-mvel/src/test/resources/composite-rule-invalid-empty-composing-rules.json new file mode 100644 index 0000000..2892b5b --- /dev/null +++ b/easy-rules-mvel/src/test/resources/composite-rule-invalid-empty-composing-rules.json @@ -0,0 +1,8 @@ +[ + { + "name": "invalid rule", + "compositeRuleType": "UnitRuleGroup", + "priority": 1, + "composingRules": [] + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/composite-rules.json b/easy-rules-mvel/src/test/resources/composite-rules.json new file mode 100644 index 0000000..bc51c83 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/composite-rules.json @@ -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": [ + "person.shouldProvideId(true);" + ] + }, + { + "name": "Movie is rated R", + "description": "If the movie is rated R", + "priority": 1, + "condition": "movie.rating == R", + "actions": [ + "person.shouldProvideId(true);" + ] + } + ] + }, + { + "name": "weather rule", + "description": "when it rains, then take an umbrella", + "priority": 1, + "condition": "rain == True", + "actions": [ + "System.out.println(\"It rains, take an umbrella!\");" + ] + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/composite-rule.yml b/easy-rules-mvel/src/test/resources/composite-rules.yml similarity index 100% rename from easy-rules-mvel/src/test/resources/composite-rule.yml rename to easy-rules-mvel/src/test/resources/composite-rules.yml diff --git a/easy-rules-mvel/src/test/resources/non-composite-rule-with-composing-rules.json b/easy-rules-mvel/src/test/resources/non-composite-rule-with-composing-rules.json new file mode 100644 index 0000000..c9d11d3 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/non-composite-rule-with-composing-rules.json @@ -0,0 +1,30 @@ +[ + { + "name": "Movie id rule", + "priority": 1, + "condition": "true", + "actions": [ + "System.out.println();" + ], + "composingRules": [ + { + "name": "Time is evening", + "description": "If it's later than 7pm", + "priority": 1, + "condition": "day.hour > 19", + "actions": [ + "person.shouldProvideId(true);" + ] + }, + { + "name": "Movie is rated R", + "description": "If the movie is rated R", + "priority": 1, + "condition": "movie.rating == R", + "actions": [ + "person.shouldProvideId(true);" + ] + } + ] + } +] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/rules-empty.json b/easy-rules-mvel/src/test/resources/rules-empty.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/rules-empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/easy-rules-mvel/src/test/resources/rules.json b/easy-rules-mvel/src/test/resources/rules.json new file mode 100644 index 0000000..c1131f8 --- /dev/null +++ b/easy-rules-mvel/src/test/resources/rules.json @@ -0,0 +1,20 @@ +[ + { + "name": "adult rule", + "description": "when age is greater then 18, then mark as adult", + "priority": 1, + "condition": "person.age > 18", + "actions": [ + "person.setAdult(true);" + ] + }, + { + "name": "weather rule", + "description": "when it rains, then take an umbrella", + "priority": 2, + "condition": "rain == true", + "actions": [ + "System.out.println(\"It rains, take an umbrella!\");" + ] + } +] diff --git a/easy-rules-tutorials/src/main/java/org/jeasy/rules/tutorials/shop/Launcher.java b/easy-rules-tutorials/src/main/java/org/jeasy/rules/tutorials/shop/Launcher.java index 72e8a96..03f13e9 100644 --- a/easy-rules-tutorials/src/main/java/org/jeasy/rules/tutorials/shop/Launcher.java +++ b/easy-rules-tutorials/src/main/java/org/jeasy/rules/tutorials/shop/Launcher.java @@ -30,13 +30,13 @@ import org.jeasy.rules.api.RulesEngine; import org.jeasy.rules.core.DefaultRulesEngine; import org.jeasy.rules.mvel.MVELRule; import org.jeasy.rules.mvel.MVELRuleFactory; +import org.jeasy.rules.mvel.MVELYamlRuleDefinitionReader; -import java.io.FileNotFoundException; import java.io.FileReader; public class Launcher { - public static void main(String[] args) throws FileNotFoundException { + public static void main(String[] args) throws Exception { //create a person instance (fact) Person tom = new Person("Tom", 14); Facts facts = new Facts(); @@ -49,7 +49,8 @@ public class Launcher { .priority(1) .when("person.age > 18") .then("person.setAdult(true);"); - Rule alcoholRule = MVELRuleFactory.createRuleFrom(new FileReader("src/main/java/org/jeasy/rules/tutorials/shop/alcohol-rule.yml")); + MVELRuleFactory ruleFactory = new MVELRuleFactory(new MVELYamlRuleDefinitionReader()); + Rule alcoholRule = ruleFactory.createRule(new FileReader("src/main/java/org/jeasy/rules/tutorials/shop/alcohol-rule.yml")); // create a rule set Rules rules = new Rules();