From 58130485a3ae3387eefef85b6115563d9a9aeb6c Mon Sep 17 00:00:00 2001 From: "Yolanda M. Davis" Date: Thu, 21 Nov 2019 17:25:28 -0500 Subject: [PATCH] NIFI-6890 Support configuring rules in controller service configuration Signed-off-by: Matthew Burgess This closes #3902 --- .../org/apache/nifi/rules/RulesFactory.java | 49 +++++++------ .../rules/engine/EasyRulesEngineService.java | 55 +++++++++++++-- .../additionalDetails.html | 6 +- .../apache/nifi/rules/TestRulesFactory.java | 68 +++++++++++++++--- .../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 18b6b1c45f..177231d11e 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 createRules(String ruleFile, String ruleFileType, String rulesFileFormat) throws Exception{ + public static List createRulesFromFile(String ruleFile, String ruleFileType, String rulesFileFormat) throws Exception { + InputStream rulesInputStream = new FileInputStream(ruleFile); + return createRules(rulesInputStream, ruleFileType, rulesFileFormat); + } + + public static List createRulesFromString(String rulesBody, String ruleFileType, String rulesFileFormat) throws Exception { + InputStream rulesInputStream = new ByteArrayInputStream(rulesBody.getBytes()); + return createRules(rulesInputStream, ruleFileType, rulesFileFormat); + } + + private static List 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 createRulesFromEasyRulesFormat(String ruleFile, String ruleFileType, String ruleFileFormat) throws Exception{ + private static List 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 ruleDefinitions = reader.read(new FileReader(ruleFile)); + List ruleDefinitions = reader.read(new InputStreamReader(rulesInputStream)); return ruleDefinitions.stream().map(ruleDefinition -> { @@ -81,7 +91,7 @@ public class RulesFactory { List actions = ruleDefinition.getActions().stream().map(ruleAction -> { Action action = new Action(); action.setType("EXPRESSION"); - Map attributes = new HashMap<>(); + Map 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 createRulesFromNiFiFormat(String ruleFile, String ruleFileType) throws Exception{ + private static List 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 yamlToRules(String rulesFile) throws FileNotFoundException { + private static List yamlToRules(InputStream rulesInputStream) throws FileNotFoundException { List 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 jsonToRules(String rulesFile) throws Exception { + private static List jsonToRules(InputStream rulesInputStream) throws Exception { List rules; - InputStreamReader isr = new InputStreamReader(new FileInputStream(rulesFile)); + InputStreamReader isr = new InputStreamReader(rulesInputStream); final ObjectMapper objectMapper = new ObjectMapper(); - rules = objectMapper.readValue(isr, new TypeReference>(){}); + rules = objectMapper.readValue(isr, new TypeReference>() { + }); 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 66f69aeb4c..187f0fe762 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 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,16 +141,44 @@ 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 customValidate(ValidationContext validationContext) { + Set results = new HashSet<>(); + + // Verify that exactly one of "script file" or "script body" is set + Map 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 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 df80687122..48207df932 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 @@

General

-

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 +

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.

- Rules files can be implemented in any of the following formats: + Rules can be implemented in any of the following formats:

  • 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 @@

- 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.

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 ded5ea64ad..a08b805380 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 rules = RulesFactory.createRules(testYamlFile,"YAML", "NIFI"); + List 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 rules = RulesFactory.createRules(testYamlFile,"YAML", "MVEL"); + List 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 rules = RulesFactory.createRules(testYamlFile,"YAML", "SPEL"); + List 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 rules = RulesFactory.createRules(testJsonFile,"JSON", "NIFI"); + List 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 rules = RulesFactory.createRules(testJsonFile,"JSON", "MVEL"); + List 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 rules = RulesFactory.createRules(testJsonFile,"JSON", "SPEL"); + List 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 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 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 0adad8b015..bc7a7859b2 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 @@ -142,6 +142,75 @@ public class TestEasyRulesEngineService { assertEquals(actions.size(), 2); } + @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 facts = new HashMap<>(); + facts.put("predictedQueuedCount",60); + facts.put("predictedTimeToBytesBackpressureMillis",299999); + List 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 facts = new HashMap<>(); + facts.put("predictedQueuedCount",60); + facts.put("predictedTimeToBytesBackpressureMillis",299999); + List actions = service.fireRules(facts); + assertNotNull(actions); + assertEquals(actions.size(), 2); + } + + + @Test public void testIgnoreConditionErrorsFalseNIFI() throws InitializationException, IOException { final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);