You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2019/11/22 16:19:02 UTC

[nifi] branch master updated: NIFI-6890 Support configuring rules in controller service configuration

This is an automated email from the ASF dual-hosted git repository.

mattyb149 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/master by this push:
     new 5813048  NIFI-6890 Support configuring rules in controller service configuration
5813048 is described below

commit 58130485a3ae3387eefef85b6115563d9a9aeb6c
Author: Yolanda M. Davis <yo...@gmail.com>
AuthorDate: Thu Nov 21 17:25:28 2019 -0500

    NIFI-6890 Support configuring rules in controller service configuration
    
    Signed-off-by: Matthew Burgess <ma...@apache.org>
    
    This closes #3902
---
 .../java/org/apache/nifi/rules/RulesFactory.java   | 49 ++++++++-------
 .../nifi/rules/engine/EasyRulesEngineService.java  | 55 +++++++++++++++--
 .../additionalDetails.html                         |  6 +-
 .../org/apache/nifi/rules/TestRulesFactory.java    | 68 ++++++++++++++++++---
 .../rules/engine/TestEasyRulesEngineService.java   | 69 ++++++++++++++++++++++
 5 files changed, 211 insertions(+), 36 deletions(-)

diff --git a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/RulesFactory.java b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/RulesFactory.java
index 18b6b1c..177231d 100644
--- a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/RulesFactory.java
+++ b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/RulesFactory.java
@@ -25,10 +25,9 @@ import org.jeasy.rules.support.YamlRuleDefinitionReader;
 import org.yaml.snakeyaml.Yaml;
 import org.yaml.snakeyaml.constructor.Constructor;
 
-import java.io.File;
+import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
@@ -51,25 +50,36 @@ public class RulesFactory {
         NIFI, MVEL, SPEL;
     }
 
-    public static List<Rule> createRules(String ruleFile, String ruleFileType, String rulesFileFormat) throws Exception{
+    public static List<Rule> createRulesFromFile(String ruleFile, String ruleFileType, String rulesFileFormat) throws Exception {
+        InputStream rulesInputStream = new FileInputStream(ruleFile);
+        return createRules(rulesInputStream, ruleFileType, rulesFileFormat);
+    }
+
+    public static List<Rule> createRulesFromString(String rulesBody, String ruleFileType, String rulesFileFormat) throws Exception {
+        InputStream rulesInputStream = new ByteArrayInputStream(rulesBody.getBytes());
+        return createRules(rulesInputStream, ruleFileType, rulesFileFormat);
+    }
+
+    private static List<Rule> createRules(InputStream rulesInputStream, String ruleFileType, String rulesFileFormat) throws Exception {
         FileFormat fileFormat = FileFormat.valueOf(rulesFileFormat);
-        switch (fileFormat){
+        switch (fileFormat) {
             case NIFI:
-                return createRulesFromNiFiFormat(ruleFile, ruleFileType);
+                return createRulesFromNiFiFormat(rulesInputStream, ruleFileType);
             case MVEL:
             case SPEL:
-                return createRulesFromEasyRulesFormat(ruleFile, ruleFileType, rulesFileFormat);
+                return createRulesFromEasyRulesFormat(rulesInputStream, ruleFileType, rulesFileFormat);
             default:
                 return null;
         }
+
     }
 
-    private static List<Rule> createRulesFromEasyRulesFormat(String ruleFile, String ruleFileType, String ruleFileFormat) throws Exception{
+    private static List<Rule> createRulesFromEasyRulesFormat(InputStream rulesInputStream, String ruleFileType, String ruleFileFormat) throws Exception {
 
         RuleDefinitionReader reader = FileType.valueOf(ruleFileType).equals(FileType.YAML)
-                                      ? new YamlRuleDefinitionReader() : new JsonRuleDefinitionReader();
+                ? new YamlRuleDefinitionReader() : new JsonRuleDefinitionReader();
 
-        List<RuleDefinition> ruleDefinitions = reader.read(new FileReader(ruleFile));
+        List<RuleDefinition> ruleDefinitions = reader.read(new InputStreamReader(rulesInputStream));
 
         return ruleDefinitions.stream().map(ruleDefinition -> {
 
@@ -81,7 +91,7 @@ public class RulesFactory {
             List<Action> actions = ruleDefinition.getActions().stream().map(ruleAction -> {
                 Action action = new Action();
                 action.setType("EXPRESSION");
-                Map<String,String> attributes = new HashMap<>();
+                Map<String, String> attributes = new HashMap<>();
                 attributes.put("command", ruleAction);
                 attributes.put("type", ruleFileFormat);
                 action.setAttributes(attributes);
@@ -93,23 +103,21 @@ public class RulesFactory {
         }).collect(Collectors.toList());
     }
 
-    private static List<Rule> createRulesFromNiFiFormat(String ruleFile, String ruleFileType) throws Exception{
+    private static List<Rule> createRulesFromNiFiFormat(InputStream rulesInputStream, String ruleFileType) throws Exception {
         FileType type = FileType.valueOf(ruleFileType.toUpperCase());
         if (type.equals(FileType.YAML)) {
-            return yamlToRules(ruleFile);
+            return yamlToRules(rulesInputStream);
         } else if (type.equals(FileType.JSON)) {
-            return jsonToRules(ruleFile);
+            return jsonToRules(rulesInputStream);
         } else {
             return null;
         }
     }
 
-    private static List<Rule> yamlToRules(String rulesFile) throws FileNotFoundException {
+    private static List<Rule> yamlToRules(InputStream rulesInputStream) throws FileNotFoundException {
         List<Rule> rules = new ArrayList<>();
         Yaml yaml = new Yaml(new Constructor(Rule.class));
-        File yamlFile = new File(rulesFile);
-        InputStream inputStream = new FileInputStream(yamlFile);
-        for (Object object : yaml.loadAll(inputStream)) {
+        for (Object object : yaml.loadAll(rulesInputStream)) {
             if (object instanceof Rule) {
                 rules.add((Rule) object);
             }
@@ -117,11 +125,12 @@ public class RulesFactory {
         return rules;
     }
 
-    private static List<Rule> jsonToRules(String rulesFile) throws Exception {
+    private static List<Rule> jsonToRules(InputStream rulesInputStream) throws Exception {
         List<Rule> rules;
-        InputStreamReader isr = new InputStreamReader(new FileInputStream(rulesFile));
+        InputStreamReader isr = new InputStreamReader(rulesInputStream);
         final ObjectMapper objectMapper = new ObjectMapper();
-        rules = objectMapper.readValue(isr, new TypeReference<List<Rule>>(){});
+        rules = objectMapper.readValue(isr, new TypeReference<List<Rule>>() {
+        });
         return rules;
     }
 }
diff --git a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/engine/EasyRulesEngineService.java b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/engine/EasyRulesEngineService.java
index 66f69ae..187f0fe 100644
--- a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/engine/EasyRulesEngineService.java
+++ b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/java/org/apache/nifi/rules/engine/EasyRulesEngineService.java
@@ -21,6 +21,9 @@ import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
 import org.apache.nifi.controller.AbstractControllerService;
 import org.apache.nifi.controller.ConfigurationContext;
 import org.apache.nifi.controller.ControllerServiceInitializationContext;
@@ -33,6 +36,7 @@ import org.apache.nifi.rules.Rule;
 import org.apache.nifi.rules.RulesFactory;
 import org.apache.nifi.rules.RulesMVELCondition;
 import org.apache.nifi.rules.RulesSPELCondition;
+import org.apache.nifi.util.StringUtils;
 import org.jeasy.rules.api.Condition;
 import org.jeasy.rules.api.Facts;
 import org.jeasy.rules.api.RuleListener;
@@ -41,9 +45,12 @@ import org.jeasy.rules.core.DefaultRulesEngine;
 import org.jeasy.rules.core.RuleBuilder;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Implementation of RulesEngineService interface
@@ -64,16 +71,25 @@ public class EasyRulesEngineService  extends AbstractControllerService implement
     static final PropertyDescriptor RULES_FILE_PATH = new PropertyDescriptor.Builder()
             .name("rules-file-path")
             .displayName("Rules File Path")
-            .description("Path to location of rules file.")
-            .required(true)
+            .description("Path to location of rules file. Only one of Rules File or Rules Body may be used")
+            .required(false)
             .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR)
             .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
             .build();
 
+    static final PropertyDescriptor RULES_BODY = new PropertyDescriptor.Builder()
+            .name("rules-body")
+            .displayName("Rules Body")
+            .description("Body of rules file to execute. Only one of Rules File or Rules Body may be used")
+            .required(false)
+            .addValidator(Validator.VALID)
+            .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .build();
+
     static final PropertyDescriptor RULES_FILE_TYPE = new PropertyDescriptor.Builder()
             .name("rules-file-type")
             .displayName("Rules File Type")
-            .description("File type for rules definition. Supported file types are YAML and JSON")
+            .description("File or Body type for rules definition. Supported types are YAML and JSON")
             .required(true)
             .allowableValues(JSON,YAML)
             .defaultValue(JSON.getValue())
@@ -82,7 +98,7 @@ public class EasyRulesEngineService  extends AbstractControllerService implement
     static final PropertyDescriptor RULES_FILE_FORMAT = new PropertyDescriptor.Builder()
             .name("rules-file-format")
             .displayName("Rules File Format")
-            .description("File format for rules. Supported formats are NiFi Rules, Easy Rules files with MVEL Expression Language" +
+            .description("Format for rules. Supported formats are NiFi Rules, Easy Rules files with MVEL Expression Language" +
                     " and Easy Rules files with Spring Expression Language.")
             .required(true)
             .allowableValues(NIFI,MVEL,SPEL)
@@ -111,6 +127,7 @@ public class EasyRulesEngineService  extends AbstractControllerService implement
         final List<PropertyDescriptor> properties = new ArrayList<>();
         properties.add(RULES_FILE_TYPE);
         properties.add(RULES_FILE_PATH);
+        properties.add(RULES_BODY);
         properties.add(RULES_FILE_FORMAT);
         properties.add(IGNORE_CONDITION_ERRORS);
         this.properties = Collections.unmodifiableList(properties);
@@ -124,17 +141,45 @@ public class EasyRulesEngineService  extends AbstractControllerService implement
     @OnEnabled
     public void onEnabled(final ConfigurationContext context) throws InitializationException {
         final String rulesFile = context.getProperty(RULES_FILE_PATH).getValue();
+        final String rulesBody = context.getProperty(RULES_BODY).getValue();
         final String rulesFileType = context.getProperty(RULES_FILE_TYPE).getValue();
         rulesFileFormat = context.getProperty(RULES_FILE_FORMAT).getValue();
         ignoreConditionErrors = context.getProperty(IGNORE_CONDITION_ERRORS).asBoolean();
         try{
-            rules = RulesFactory.createRules(rulesFile, rulesFileType, rulesFileFormat);
+            if(StringUtils.isEmpty(rulesFile)){
+                rules = RulesFactory.createRulesFromString(rulesBody, rulesFileType, rulesFileFormat);
+            }else{
+                rules = RulesFactory.createRulesFromFile(rulesFile, rulesFileType, rulesFileFormat);
+            }
         } catch (Exception fex){
             throw new InitializationException(fex);
         }
     }
 
     /**
+     * Custom validation for ensuring exactly one of Script File or Script Body is populated
+     *
+     * @param validationContext provides a mechanism for obtaining externally
+     *                          managed values, such as property values and supplies convenience methods
+     *                          for operating on those values
+     * @return A collection of validation results
+     */
+    @Override
+    public Collection<ValidationResult> customValidate(ValidationContext validationContext) {
+        Set<ValidationResult> results = new HashSet<>();
+
+        // Verify that exactly one of "script file" or "script body" is set
+        Map<PropertyDescriptor, String> propertyMap = validationContext.getProperties();
+        if (StringUtils.isEmpty(propertyMap.get(RULES_FILE_PATH)) == StringUtils.isEmpty(propertyMap.get(RULES_BODY))) {
+            results.add(new ValidationResult.Builder().subject("Rules Body or Rules File").valid(false).explanation(
+                    "exactly one of Rules File or Rules Body must be set").build());
+        }
+
+        return results;
+    }
+
+
+    /**
      * Return the list of actions what should be executed for a given set of facts
      * @param facts a Map of key and facts values, as objects, that should be evaluated by the rules engine
      * @return
diff --git a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/resources/docs/org.apache.nifi.rules.engine.EasyRulesEngineService/additionalDetails.html b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/resources/docs/org.apache.nifi.rules.engine.EasyRulesEngineService/additionalDetails.html
index df80687..48207df 100644
--- a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/resources/docs/org.apache.nifi.rules.engine.EasyRulesEngineService/additionalDetails.html
+++ b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/main/resources/docs/org.apache.nifi.rules.engine.EasyRulesEngineService/additionalDetails.html
@@ -21,11 +21,11 @@
 </head>
 <body>
 <h2>General</h2>
-<p>The Easy Rules Engine Service supports execution of a centralized set of rules (stored as files) against a provided set of data called facts.  Facts sent to the service are processed against
+<p>The Easy Rules Engine Service supports execution of a centralized set of rules (stored as files or provided within the service configuration) against a provided set of data called facts.  Facts sent to the service are processed against
     the rules engine to determine what, if any, actions should be executed based on the conditions defined within the rules. The list of actions are returned to the caller to handle as needed.
 </p>
 <p>
-    Rules files can be implemented in any of the following formats:
+    Rules can be implemented in any of the following formats:
 </p>
 <ul>
     <li> NiFi Rules Format - This is a rules file which follows the NiFi style for rules definition, which supports MVEL (MVFLEX) Expression language for conditions (default format).
@@ -36,7 +36,7 @@
     </li>
 </ul>
 <p>
-    All rules formats can be implemented as JSON or YAML files (with JSON serving as default file type).
+    All rules formats can be structured as JSON or YAML (with JSON serving as default type). Rules can be stored as a file or provided in the Rules Body setting in the service's configuration settings.
 </p>
 
 <p>
diff --git a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/TestRulesFactory.java b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/TestRulesFactory.java
index ded5ea6..a08b805 100644
--- a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/TestRulesFactory.java
+++ b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/TestRulesFactory.java
@@ -29,7 +29,7 @@ public class TestRulesFactory {
     public void testCreateRulesFromNiFiYaml(){
         try {
             String testYamlFile = "src/test/resources/test_nifi_rules.yml";
-            List<Rule> rules = RulesFactory.createRules(testYamlFile,"YAML", "NIFI");
+            List<Rule> rules = RulesFactory.createRulesFromFile(testYamlFile,"YAML", "NIFI");
             assertEquals(2, rules.size());
             assert confirmEntries(rules);
         }catch (Exception ex){
@@ -41,7 +41,7 @@ public class TestRulesFactory {
     public void testCreateRulesFromMvelYaml(){
         try {
             String testYamlFile = "src/test/resources/test_mvel_rules.yml";
-            List<Rule> rules = RulesFactory.createRules(testYamlFile,"YAML", "MVEL");
+            List<Rule> rules = RulesFactory.createRulesFromFile(testYamlFile,"YAML", "MVEL");
             assertEquals(2, rules.size());
             assert confirmEntries(rules);
             assertSame("EXPRESSION", rules.get(0).getActions().get(0).getType());
@@ -54,7 +54,7 @@ public class TestRulesFactory {
     public void testCreateRulesFromSpelYaml(){
         try {
             String testYamlFile = "src/test/resources/test_spel_rules.yml";
-            List<Rule> rules = RulesFactory.createRules(testYamlFile,"YAML", "SPEL");
+            List<Rule> rules = RulesFactory.createRulesFromFile(testYamlFile,"YAML", "SPEL");
             assertEquals(2, rules.size());
             assertSame("EXPRESSION", rules.get(0).getActions().get(0).getType());
         }catch (Exception ex){
@@ -66,7 +66,7 @@ public class TestRulesFactory {
     public void testCreateRulesFromNiFiJson(){
         try {
             String testJsonFile = "src/test/resources/test_nifi_rules.json";
-            List<Rule> rules = RulesFactory.createRules(testJsonFile,"JSON", "NIFI");
+            List<Rule> rules = RulesFactory.createRulesFromFile(testJsonFile,"JSON", "NIFI");
             assertEquals(2, rules.size());
             assert confirmEntries(rules);
         }catch (Exception ex){
@@ -78,7 +78,7 @@ public class TestRulesFactory {
     public void testCreateRulesFromMvelJson(){
         try {
             String testJsonFile = "src/test/resources/test_mvel_rules.json";
-            List<Rule> rules = RulesFactory.createRules(testJsonFile,"JSON", "MVEL");
+            List<Rule> rules = RulesFactory.createRulesFromFile(testJsonFile,"JSON", "MVEL");
             assertEquals(2, rules.size());
             assertSame("EXPRESSION", rules.get(0).getActions().get(0).getType());
             assert confirmEntries(rules);
@@ -91,7 +91,59 @@ public class TestRulesFactory {
     public void testCreateRulesFromSpelJson(){
         try {
             String testJsonFile = "src/test/resources/test_spel_rules.json";
-            List<Rule> rules = RulesFactory.createRules(testJsonFile,"JSON", "SPEL");
+            List<Rule> rules = RulesFactory.createRulesFromFile(testJsonFile,"JSON", "SPEL");
+            assertEquals(2, rules.size());
+            assertSame("EXPRESSION", rules.get(0).getActions().get(0).getType());
+        }catch (Exception ex){
+            fail("Unexpected exception occurred: "+ex.getMessage());
+        }
+    }
+
+    @Test
+    public void testCreateRulesFromStringSpelJson(){
+        try {
+            String testJson = "[\n" +
+                    "  {\n" +
+                    "    \"name\": \"Queue Size\",\n" +
+                    "    \"description\": \"Queue size check greater than 50\",\n" +
+                    "    \"priority\": 1,\n" +
+                    "    \"condition\": \"#predictedQueuedCount > 50\",\n" +
+                    "    \"actions\": [\"#predictedQueuedCount + 'is large'\"]\n" +
+                    "  },\n" +
+                    "  {\n" +
+                    "    \"name\": \"Time To Back Pressure\",\n" +
+                    "    \"description\": \"Back pressure time less than 5 minutes\",\n" +
+                    "    \"priority\": 2,\n" +
+                    "    \"condition\": \"#predictedTimeToBytesBackpressureMillis < 300000 && #predictedTimeToBytesBackpressureMillis >= 0\",\n" +
+                    "    \"actions\": [\"'System is approaching backpressure! Predicted time left: ' + #predictedTimeToBytesBackpressureMillis\"]\n" +
+                    "  }\n" +
+                    "]";
+            List<Rule> rules = RulesFactory.createRulesFromString(testJson,"JSON", "SPEL");
+            assertEquals(2, rules.size());
+            assertSame("EXPRESSION", rules.get(0).getActions().get(0).getType());
+        }catch (Exception ex){
+            fail("Unexpected exception occurred: "+ex.getMessage());
+        }
+    }
+
+    @Test
+    public void testCreateRulesFromStringSpelYaml(){
+        try {
+            String testYaml = "---\n" +
+                    "name: \"Queue Size\"\n" +
+                    "description: \"Queue size check greater than 50\"\n" +
+                    "priority: 1\n" +
+                    "condition: \"#predictedQueuedCount > 50\"\n" +
+                    "actions:\n" +
+                    "  - \"System.out.println(\\\"Queue Size Over 50 is detected!\\\")\"\n" +
+                    "---\n" +
+                    "name: \"Time To Back Pressure\"\n" +
+                    "description: \"Back pressure time less than 5 minutes\"\n" +
+                    "priority: 2\n" +
+                    "condition: \"#predictedTimeToBytesBackpressureMillis < 300000 && #predictedTimeToBytesBackpressureMillis >= 0\"\n" +
+                    "actions:\n" +
+                    "  - \"System.out.println(\\\"Back Pressure prediction less than 5 minutes!\\\")\"";
+            List<Rule> rules = RulesFactory.createRulesFromString(testYaml,"YAML", "SPEL");
             assertEquals(2, rules.size());
             assertSame("EXPRESSION", rules.get(0).getActions().get(0).getType());
         }catch (Exception ex){
@@ -102,7 +154,7 @@ public class TestRulesFactory {
     @Test
     public void testFakeTypeNotSupported(){
         try {
-            RulesFactory.createRules("FAKEFILE", "FAKE", "NIFI");
+            RulesFactory.createRulesFromFile("FAKEFILE", "FAKE", "NIFI");
         }catch (Exception ex){
             return;
         }
@@ -112,7 +164,7 @@ public class TestRulesFactory {
     @Test
     public void testFakeFormatNotSupported(){
         try {
-            RulesFactory.createRules("FAKEFILE", "JSON", "FAKE");
+            RulesFactory.createRulesFromFile("FAKEFILE", "JSON", "FAKE");
         }catch (Exception ex){
             return;
         }
diff --git a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/engine/TestEasyRulesEngineService.java b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/engine/TestEasyRulesEngineService.java
index 0adad8b..bc7a785 100644
--- a/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/engine/TestEasyRulesEngineService.java
+++ b/nifi-nar-bundles/nifi-easyrules-bundle/nifi-easyrules-service/src/test/java/org/apache/nifi/rules/engine/TestEasyRulesEngineService.java
@@ -143,6 +143,75 @@ public class TestEasyRulesEngineService {
     }
 
     @Test
+    public void testJsonSpelRulesAsString() throws InitializationException, IOException {
+        final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
+        final RulesEngineService service = new MockEasyRulesEngineService();
+        runner.addControllerService("easy-rules-engine-service-test",service);
+        String testRules = "[\n" +
+                "  {\n" +
+                "    \"name\": \"Queue Size\",\n" +
+                "    \"description\": \"Queue size check greater than 50\",\n" +
+                "    \"priority\": 1,\n" +
+                "    \"condition\": \"#predictedQueuedCount > 50\",\n" +
+                "    \"actions\": [\"#predictedQueuedCount + 'is large'\"]\n" +
+                "  },\n" +
+                "  {\n" +
+                "    \"name\": \"Time To Back Pressure\",\n" +
+                "    \"description\": \"Back pressure time less than 5 minutes\",\n" +
+                "    \"priority\": 2,\n" +
+                "    \"condition\": \"#predictedTimeToBytesBackpressureMillis < 300000 && #predictedTimeToBytesBackpressureMillis >= 0\",\n" +
+                "    \"actions\": [\"'System is approaching backpressure! Predicted time left: ' + #predictedTimeToBytesBackpressureMillis\"]\n" +
+                "  }\n" +
+                "]";
+        runner.setProperty(service, EasyRulesEngineService.RULES_BODY, testRules);
+        runner.setProperty(service,EasyRulesEngineService.RULES_FILE_TYPE, "JSON");
+        runner.setProperty(service,EasyRulesEngineService.RULES_FILE_FORMAT, "SPEL");
+        runner.enableControllerService(service);
+        runner.assertValid(service);
+        Map<String, Object> facts = new HashMap<>();
+        facts.put("predictedQueuedCount",60);
+        facts.put("predictedTimeToBytesBackpressureMillis",299999);
+        List<Action> actions = service.fireRules(facts);
+        assertNotNull(actions);
+        assertEquals(actions.size(), 2);
+    }
+
+    @Test
+    public void testYamlMvelRulesAsString() throws InitializationException, IOException {
+        final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
+        final RulesEngineService service = new MockEasyRulesEngineService();
+        runner.addControllerService("easy-rules-engine-service-test",service);
+        String testYaml = "---\n" +
+                "name: \"Queue Size\"\n" +
+                "description: \"Queue size check greater than 50\"\n" +
+                "priority: 1\n" +
+                "condition: \"predictedQueuedCount > 50\"\n" +
+                "actions:\n" +
+                "  - \"System.out.println(\\\"Queue Size Over 50 is detected!\\\")\"\n" +
+                "---\n" +
+                "name: \"Time To Back Pressure\"\n" +
+                "description: \"Back pressure time less than 5 minutes\"\n" +
+                "priority: 2\n" +
+                "condition: \"predictedTimeToBytesBackpressureMillis < 300000 && predictedTimeToBytesBackpressureMillis >= 0\"\n" +
+                "actions:\n" +
+                "  - \"System.out.println(\\\"Back Pressure prediction less than 5 minutes!\\\")\"";
+
+        runner.setProperty(service, EasyRulesEngineService.RULES_BODY, testYaml);
+        runner.setProperty(service,EasyRulesEngineService.RULES_FILE_TYPE, "YAML");
+        runner.setProperty(service,EasyRulesEngineService.RULES_FILE_FORMAT, "MVEL");
+        runner.enableControllerService(service);
+        runner.assertValid(service);
+        Map<String, Object> facts = new HashMap<>();
+        facts.put("predictedQueuedCount",60);
+        facts.put("predictedTimeToBytesBackpressureMillis",299999);
+        List<Action> actions = service.fireRules(facts);
+        assertNotNull(actions);
+        assertEquals(actions.size(), 2);
+    }
+
+
+
+    @Test
     public void testIgnoreConditionErrorsFalseNIFI() throws InitializationException, IOException {
         final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
         final RulesEngineService service = new MockEasyRulesEngineService();