From 0b0b79fb14139155c9874c55b43dc62ab8c516f7 Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Mon, 26 Jul 2021 05:18:46 -0700 Subject: [PATCH 01/16] Make consistent singlar/plural names of instances to avoid downstream ambiguous reference issues --- benchmark/RulesEngineBenchmark/Program.cs | 6 +-- .../RulesEngineDemoContext.cs | 10 ++--- demo/DemoApp/BasicDemo.cs | 2 +- demo/DemoApp/EFDemo.cs | 2 +- demo/DemoApp/NestedInputDemo.cs | 2 +- .../LambdaExpressionBuilder.cs | 4 +- .../RuleExpressionParser.cs | 4 +- src/RulesEngine/HelperFunctions/Helpers.cs | 8 ++-- src/RulesEngine/Interfaces/IRulesEngine.cs | 4 +- .../Models/{ReSettings.cs => ReSetting.cs} | 2 +- src/RulesEngine/Models/Rule.cs | 2 +- src/RulesEngine/Models/RuleAction.cs | 2 +- .../{WorkflowRules.cs => WorkflowRule.cs} | 2 +- src/RulesEngine/RuleCompiler.cs | 4 +- .../RuleExpressionBuilderFactory.cs | 4 +- src/RulesEngine/RulesCache.cs | 12 +++--- src/RulesEngine/RulesEngine.cs | 18 ++++---- .../Validators/WorkflowRulesValidator.cs | 2 +- .../ActionTests/CustomActionTest.cs | 14 +++---- .../RulesEngineWithActionsTests.cs | 12 +++--- .../BusinessRuleEngineTest.cs | 42 +++++++++---------- test/RulesEngine.UnitTest/EmptyRulesTest.cs | 16 +++---- .../LambdaExpressionBuilderTest.cs | 2 +- test/RulesEngine.UnitTest/NestedRulesTest.cs | 20 ++++----- test/RulesEngine.UnitTest/RuleCompilerTest.cs | 4 +- .../RuleExpressionBuilderFactoryTest.cs | 2 +- .../RulesEngine.UnitTest/RulesEnabledTests.cs | 10 ++--- test/RulesEngine.UnitTest/ScopedParamsTest.cs | 28 ++++++------- 28 files changed, 120 insertions(+), 120 deletions(-) rename src/RulesEngine/Models/{ReSettings.cs => ReSetting.cs} (98%) rename src/RulesEngine/Models/{WorkflowRules.cs => WorkflowRule.cs} (96%) diff --git a/benchmark/RulesEngineBenchmark/Program.cs b/benchmark/RulesEngineBenchmark/Program.cs index 10ba0320..f1d3f589 100644 --- a/benchmark/RulesEngineBenchmark/Program.cs +++ b/benchmark/RulesEngineBenchmark/Program.cs @@ -16,7 +16,7 @@ public class REBenchmark { private readonly RulesEngine.RulesEngine rulesEngine; private readonly object ruleInput; - private readonly List workflows; + private readonly List workflows; private class ListItem { @@ -34,9 +34,9 @@ public REBenchmark() } var fileData = File.ReadAllText(files[0]); - workflows = JsonConvert.DeserializeObject>(fileData); + workflows = JsonConvert.DeserializeObject>(fileData); - rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null, new ReSettings { + rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null, new ReSetting { EnableFormattedErrorMessage = false, EnableScopedParams = false }); diff --git a/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs b/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs index ed5e6301..89d8d960 100644 --- a/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs +++ b/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs @@ -9,10 +9,10 @@ namespace DemoApp.EFDataExample { public class RulesEngineDemoContext : DbContext { - public DbSet WorkflowRules { get; set; } + public DbSet WorkflowRules { get; set; } public DbSet ActionInfos { get; set; } - public DbSet RuleActions { get; set; } + public DbSet RuleActions { get; set; } public DbSet Rules { get; set; } public DbSet ScopedParams { get; set; } @@ -43,11 +43,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .HasKey(k => k.Name); - modelBuilder.Entity(entity => { + modelBuilder.Entity(entity => { entity.HasKey(k => k.WorkflowName); }); - modelBuilder.Entity(entity => { + modelBuilder.Entity(entity => { entity.HasNoKey(); entity.HasOne(o => o.OnSuccess).WithMany(); entity.HasOne(o => o.OnFailure).WithMany(); @@ -63,7 +63,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Ignore(e => e.Actions); }); - modelBuilder.Entity() + modelBuilder.Entity() .Ignore(b => b.WorkflowRulesToInject); modelBuilder.Entity() diff --git a/demo/DemoApp/BasicDemo.cs b/demo/DemoApp/BasicDemo.cs index 9e70e9f9..9bf6ac63 100644 --- a/demo/DemoApp/BasicDemo.cs +++ b/demo/DemoApp/BasicDemo.cs @@ -39,7 +39,7 @@ public void Run() throw new Exception("Rules not found."); var fileData = File.ReadAllText(files[0]); - var workflowRules = JsonConvert.DeserializeObject>(fileData); + var workflowRules = JsonConvert.DeserializeObject>(fileData); var bre = new RulesEngine.RulesEngine(workflowRules.ToArray(), null); diff --git a/demo/DemoApp/EFDemo.cs b/demo/DemoApp/EFDemo.cs index 7aa90b7e..42e7776a 100644 --- a/demo/DemoApp/EFDemo.cs +++ b/demo/DemoApp/EFDemo.cs @@ -42,7 +42,7 @@ public void Run() throw new Exception("Rules not found."); var fileData = File.ReadAllText(files[0]); - var workflowRules = JsonConvert.DeserializeObject>(fileData); + var workflowRules = JsonConvert.DeserializeObject>(fileData); RulesEngineDemoContext db = new RulesEngineDemoContext(); if (db.Database.EnsureCreated()) diff --git a/demo/DemoApp/NestedInputDemo.cs b/demo/DemoApp/NestedInputDemo.cs index 82f6bb22..ae5a4f75 100644 --- a/demo/DemoApp/NestedInputDemo.cs +++ b/demo/DemoApp/NestedInputDemo.cs @@ -49,7 +49,7 @@ public void Run() } var fileData = File.ReadAllText(files[0]); - var workflowRules = JsonConvert.DeserializeObject>(fileData); + var workflowRules = JsonConvert.DeserializeObject>(fileData); var bre = new RulesEngine.RulesEngine(workflowRules.ToArray(), null); foreach (var workflow in workflowRules) diff --git a/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs b/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs index 2a7cbb58..eb4bd5f7 100644 --- a/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs +++ b/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs @@ -14,10 +14,10 @@ namespace RulesEngine.ExpressionBuilders { internal sealed class LambdaExpressionBuilder : RuleExpressionBuilderBase { - private readonly ReSettings _reSettings; + private readonly ReSetting _reSettings; private readonly RuleExpressionParser _ruleExpressionParser; - internal LambdaExpressionBuilder(ReSettings reSettings, RuleExpressionParser ruleExpressionParser) + internal LambdaExpressionBuilder(ReSetting reSettings, RuleExpressionParser ruleExpressionParser) { _reSettings = reSettings; _ruleExpressionParser = ruleExpressionParser; diff --git a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs index f8566751..ab1ce577 100644 --- a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs +++ b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs @@ -15,11 +15,11 @@ namespace RulesEngine.ExpressionBuilders { public class RuleExpressionParser { - private readonly ReSettings _reSettings; + private readonly ReSetting _reSettings; private static IMemoryCache _memoryCache; private readonly IDictionary _methodInfo; - public RuleExpressionParser(ReSettings reSettings) + public RuleExpressionParser(ReSetting reSettings) { _reSettings = reSettings; _memoryCache = _memoryCache ?? new MemoryCache(new MemoryCacheOptions { diff --git a/src/RulesEngine/HelperFunctions/Helpers.cs b/src/RulesEngine/HelperFunctions/Helpers.cs index f96ff50a..5751c9a2 100644 --- a/src/RulesEngine/HelperFunctions/Helpers.cs +++ b/src/RulesEngine/HelperFunctions/Helpers.cs @@ -15,7 +15,7 @@ namespace RulesEngine.HelperFunctions /// internal static class Helpers { - internal static RuleFunc ToResultTree(ReSettings reSettings, Rule rule, IEnumerable childRuleResults, Func isSuccessFunc, string exceptionMessage = "") + internal static RuleFunc ToResultTree(ReSetting reSettings, Rule rule, IEnumerable childRuleResults, Func isSuccessFunc, string exceptionMessage = "") { return (inputs) => { @@ -45,13 +45,13 @@ internal static RuleFunc ToResultTree(ReSettings reSettings, Rul } - internal static RuleFunc ToRuleExceptionResult(ReSettings reSettings, Rule rule,Exception ex) + internal static RuleFunc ToRuleExceptionResult(ReSetting reSettings, Rule rule,Exception ex) { HandleRuleException(ex, rule, reSettings); return ToResultTree(reSettings, rule, null, (args) => false, ex.Message); } - internal static void HandleRuleException(Exception ex, Rule rule, ReSettings reSettings) + internal static void HandleRuleException(Exception ex, Rule rule, ReSetting reSettings) { ex.Data.Add(nameof(rule.RuleName), rule.RuleName); ex.Data.Add(nameof(rule.Expression), rule.Expression); @@ -70,7 +70,7 @@ internal static void HandleRuleException(Exception ex, Rule rule, ReSettings reS /// /// /// - internal static string GetExceptionMessage(string message,ReSettings reSettings) + internal static string GetExceptionMessage(string message,ReSetting reSettings) { return reSettings.IgnoreException ? "" : message; } diff --git a/src/RulesEngine/Interfaces/IRulesEngine.cs b/src/RulesEngine/Interfaces/IRulesEngine.cs index 438267cf..7ba8d30b 100644 --- a/src/RulesEngine/Interfaces/IRulesEngine.cs +++ b/src/RulesEngine/Interfaces/IRulesEngine.cs @@ -30,7 +30,7 @@ public interface IRulesEngine /// Adds new workflows to RulesEngine /// /// - void AddWorkflow(params WorkflowRules[] workflowRules); + void AddWorkflow(params WorkflowRule[] workflowRules); /// /// Removes all registered workflows from RulesEngine @@ -48,6 +48,6 @@ public interface IRulesEngine /// /// List GetAllRegisteredWorkflowNames(); - void AddOrUpdateWorkflow(params WorkflowRules[] workflowRules); + void AddOrUpdateWorkflow(params WorkflowRule[] workflowRules); } } diff --git a/src/RulesEngine/Models/ReSettings.cs b/src/RulesEngine/Models/ReSetting.cs similarity index 98% rename from src/RulesEngine/Models/ReSettings.cs rename to src/RulesEngine/Models/ReSetting.cs index 33dd7c0c..b1369d5a 100644 --- a/src/RulesEngine/Models/ReSettings.cs +++ b/src/RulesEngine/Models/ReSetting.cs @@ -9,7 +9,7 @@ namespace RulesEngine.Models { [ExcludeFromCodeCoverage] - public class ReSettings + public class ReSetting { /// /// Get/Set the custom types to be used in Rule expressions diff --git a/src/RulesEngine/Models/Rule.cs b/src/RulesEngine/Models/Rule.cs index 611f890b..3111b0dd 100644 --- a/src/RulesEngine/Models/Rule.cs +++ b/src/RulesEngine/Models/Rule.cs @@ -44,7 +44,7 @@ public class Rule public IEnumerable Rules { get; set; } public IEnumerable LocalParams { get; set; } public string Expression { get; set; } - public RuleActions Actions { get; set; } + public RuleAction Actions { get; set; } public string SuccessEvent { get; set; } } diff --git a/src/RulesEngine/Models/RuleAction.cs b/src/RulesEngine/Models/RuleAction.cs index cc5f50ca..f208cb6e 100644 --- a/src/RulesEngine/Models/RuleAction.cs +++ b/src/RulesEngine/Models/RuleAction.cs @@ -6,7 +6,7 @@ namespace RulesEngine.Models { [ExcludeFromCodeCoverage] - public class RuleActions + public class RuleAction { public ActionInfo OnSuccess { get; set; } public ActionInfo OnFailure { get; set; } diff --git a/src/RulesEngine/Models/WorkflowRules.cs b/src/RulesEngine/Models/WorkflowRule.cs similarity index 96% rename from src/RulesEngine/Models/WorkflowRules.cs rename to src/RulesEngine/Models/WorkflowRule.cs index baf07f41..c114b4fc 100644 --- a/src/RulesEngine/Models/WorkflowRules.cs +++ b/src/RulesEngine/Models/WorkflowRule.cs @@ -10,7 +10,7 @@ namespace RulesEngine.Models /// Workflow rules class for deserialization the json config file /// [ExcludeFromCodeCoverage] - public class WorkflowRules + public class WorkflowRule { /// /// Gets the workflow name. diff --git a/src/RulesEngine/RuleCompiler.cs b/src/RulesEngine/RuleCompiler.cs index 907296da..845d1a4e 100644 --- a/src/RulesEngine/RuleCompiler.cs +++ b/src/RulesEngine/RuleCompiler.cs @@ -28,7 +28,7 @@ internal class RuleCompiler /// The expression builder factory /// private readonly RuleExpressionBuilderFactory _expressionBuilderFactory; - private readonly ReSettings _reSettings; + private readonly ReSetting _reSettings; /// /// The logger @@ -40,7 +40,7 @@ internal class RuleCompiler /// /// The expression builder factory. /// expressionBuilderFactory - internal RuleCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ReSettings reSettings, ILogger logger) + internal RuleCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ReSetting reSettings, ILogger logger) { _logger = logger ?? throw new ArgumentNullException($"{nameof(logger)} can't be null."); diff --git a/src/RulesEngine/RuleExpressionBuilderFactory.cs b/src/RulesEngine/RuleExpressionBuilderFactory.cs index 3e88f1c5..3dcdaf5d 100644 --- a/src/RulesEngine/RuleExpressionBuilderFactory.cs +++ b/src/RulesEngine/RuleExpressionBuilderFactory.cs @@ -9,9 +9,9 @@ namespace RulesEngine { internal class RuleExpressionBuilderFactory { - private readonly ReSettings _reSettings; + private readonly ReSetting _reSettings; private readonly LambdaExpressionBuilder _lambdaExpressionBuilder; - public RuleExpressionBuilderFactory(ReSettings reSettings, RuleExpressionParser expressionParser) + public RuleExpressionBuilderFactory(ReSetting reSettings, RuleExpressionParser expressionParser) { _reSettings = reSettings; _lambdaExpressionBuilder = new LambdaExpressionBuilder(_reSettings, expressionParser); diff --git a/src/RulesEngine/RulesCache.cs b/src/RulesEngine/RulesCache.cs index b933f1ac..ee112c4a 100644 --- a/src/RulesEngine/RulesCache.cs +++ b/src/RulesEngine/RulesCache.cs @@ -16,7 +16,7 @@ internal class RulesCache private ConcurrentDictionary>, Int64)> _compileRules = new ConcurrentDictionary>, Int64)>(); /// The workflow rules - private ConcurrentDictionary _workflowRules = new ConcurrentDictionary(); + private ConcurrentDictionary _workflowRules = new ConcurrentDictionary(); /// Determines whether [contains workflow rules] [the specified workflow name]. /// Name of the workflow. @@ -44,7 +44,7 @@ public bool ContainsCompiledRules(string workflowName) /// Adds the or update workflow rules. /// Name of the workflow. /// The rules. - public void AddOrUpdateWorkflowRules(string workflowName, WorkflowRules rules) + public void AddOrUpdateWorkflowRules(string workflowName, WorkflowRule rules) { Int64 ticks = DateTime.UtcNow.Ticks; _workflowRules.AddOrUpdate(workflowName, (rules, ticks), (k, v) => (rules, ticks)); @@ -68,7 +68,7 @@ public bool AreCompiledRulesUpToDate(string compiledRuleKey, string workflowName { if (_compileRules.TryGetValue(compiledRuleKey, out (IDictionary> rules, Int64 tick) compiledRulesObj)) { - if (_workflowRules.TryGetValue(workflowName, out (WorkflowRules rules, Int64 tick) workflowRulesObj)) + if (_workflowRules.TryGetValue(workflowName, out (WorkflowRule rules, Int64 tick) workflowRulesObj)) { return compiledRulesObj.tick >= workflowRulesObj.tick; } @@ -88,9 +88,9 @@ public void Clear() /// Name of the workflow. /// WorkflowRules. /// Could not find injected Workflow: {wfname} - public WorkflowRules GetWorkFlowRules(string workflowName) + public WorkflowRule GetWorkFlowRules(string workflowName) { - if (_workflowRules.TryGetValue(workflowName, out (WorkflowRules rules, Int64 tick) workflowRulesObj)) + if (_workflowRules.TryGetValue(workflowName, out (WorkflowRule rules, Int64 tick) workflowRulesObj)) { var workflowRules = workflowRulesObj.rules; if (workflowRules.WorkflowRulesToInject?.Any() == true) @@ -132,7 +132,7 @@ public IDictionary> GetCompiledRules(string com /// Name of the workflow. public void Remove(string workflowName) { - if (_workflowRules.TryRemove(workflowName, out (WorkflowRules, Int64) workflowObj)) + if (_workflowRules.TryRemove(workflowName, out (WorkflowRule, Int64) workflowObj)) { var compiledKeysToRemove = _compileRules.Keys.Where(key => key.StartsWith(workflowName)); foreach (var key in compiledKeysToRemove) diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index 29029e40..81d2f5d5 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -28,7 +28,7 @@ public class RulesEngine : IRulesEngine { #region Variables private readonly ILogger _logger; - private readonly ReSettings _reSettings; + private readonly ReSetting _reSettings; private readonly RulesCache _rulesCache = new RulesCache(); private readonly RuleExpressionParser _ruleExpressionParser; private readonly RuleCompiler _ruleCompiler; @@ -37,27 +37,27 @@ public class RulesEngine : IRulesEngine #endregion #region Constructor - public RulesEngine(string[] jsonConfig, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) + public RulesEngine(string[] jsonConfig, ILogger logger = null, ReSetting reSettings = null) : this(logger, reSettings) { - var workflowRules = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); + var workflowRules = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); AddWorkflow(workflowRules); } - public RulesEngine(WorkflowRules[] workflowRules, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) + public RulesEngine(WorkflowRule[] workflowRules, ILogger logger = null, ReSetting reSettings = null) : this(logger, reSettings) { AddWorkflow(workflowRules); } - public RulesEngine(ILogger logger = null, ReSettings reSettings = null) + public RulesEngine(ILogger logger = null, ReSetting reSettings = null) { _logger = logger ?? new NullLogger(); - _reSettings = reSettings ?? new ReSettings(); + _reSettings = reSettings ?? new ReSetting(); _ruleExpressionParser = new RuleExpressionParser(_reSettings); _ruleCompiler = new RuleCompiler(new RuleExpressionBuilderFactory(_reSettings, _ruleExpressionParser),_reSettings, _logger); _actionFactory = new ActionFactory(GetActionRegistry(_reSettings)); } - private IDictionary> GetActionRegistry(ReSettings reSettings) + private IDictionary> GetActionRegistry(ReSetting reSettings) { var actionDictionary = GetDefaultActionRegistry(); var customActions = reSettings.CustomActions ?? new Dictionary>(); @@ -159,7 +159,7 @@ private async ValueTask ExecuteActionForRuleResult(RuleResultT /// /// The workflow rules. /// - public void AddWorkflow(params WorkflowRules[] workflowRules) + public void AddWorkflow(params WorkflowRule[] workflowRules) { try { @@ -189,7 +189,7 @@ public void AddWorkflow(params WorkflowRules[] workflowRules) /// /// The workflow rules. /// - public void AddOrUpdateWorkflow(params WorkflowRules[] workflowRules) + public void AddOrUpdateWorkflow(params WorkflowRule[] workflowRules) { try { diff --git a/src/RulesEngine/Validators/WorkflowRulesValidator.cs b/src/RulesEngine/Validators/WorkflowRulesValidator.cs index b175acba..35cc1edb 100644 --- a/src/RulesEngine/Validators/WorkflowRulesValidator.cs +++ b/src/RulesEngine/Validators/WorkflowRulesValidator.cs @@ -8,7 +8,7 @@ namespace RulesEngine.Validators { - internal class WorkflowRulesValidator : AbstractValidator + internal class WorkflowRulesValidator : AbstractValidator { public WorkflowRulesValidator() { diff --git a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs index 71f7ee38..f3a97964 100644 --- a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs +++ b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs @@ -19,7 +19,7 @@ public class CustomActionTest public async Task CustomActionOnRuleMustHaveContextValues() { var workflows = GetWorkflowRules(); - var re = new RulesEngine(workflows, null, reSettings: new ReSettings { + var re = new RulesEngine(workflows, null, reSettings: new ReSetting { CustomActions = new Dictionary> { { "ReturnContext", () => new ReturnContextAction() } @@ -36,10 +36,10 @@ public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues() var workflows = GetWorkflowRules(); var workflowStr = JsonConvert.SerializeObject(workflows); var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }; - var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr,serializationOptions); + var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr,serializationOptions); - var re = new RulesEngine(workflows, null, reSettings: new ReSettings { + var re = new RulesEngine(workflows, null, reSettings: new ReSetting { CustomActions = new Dictionary> { { "ReturnContext", () => new ReturnContextAction() } @@ -51,16 +51,16 @@ public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues() var result = await re.ExecuteAllRulesAsync("successReturnContextAction", true); } - private WorkflowRules[] GetWorkflowRules() + private WorkflowRule[] GetWorkflowRules() { - return new WorkflowRules[] { - new WorkflowRules { + return new WorkflowRule[] { + new WorkflowRule { WorkflowName = "successReturnContextAction", Rules = new Rule[] { new Rule { RuleName = "trueRule", Expression = "input1 == true", - Actions = new RuleActions() { + Actions = new RuleAction() { OnSuccess = new ActionInfo { Name = "ReturnContext", Context = new Dictionary { diff --git a/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs b/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs index 0d0d37d1..86a53539 100644 --- a/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs +++ b/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs @@ -53,9 +53,9 @@ public async Task ExecuteActionWorkflowAsync_CalledWithNoActionsInWorkflow_Execu } - private WorkflowRules[] GetWorkflowRulesWithoutActions() + private WorkflowRule[] GetWorkflowRulesWithoutActions() { - var workflow1 = new WorkflowRules { + var workflow1 = new WorkflowRule { WorkflowName = "NoActionWorkflow", Rules = new List{ new Rule{ @@ -69,17 +69,17 @@ private WorkflowRules[] GetWorkflowRulesWithoutActions() return new[] { workflow1 }; } - private WorkflowRules[] GetWorkflowWithActions() + private WorkflowRule[] GetWorkflowWithActions() { - var workflow1 = new WorkflowRules { + var workflow1 = new WorkflowRule { WorkflowName = "ActionWorkflow", Rules = new List{ new Rule{ RuleName = "ExpressionOutputRuleTest", RuleExpressionType = RuleExpressionType.LambdaExpression, Expression = "1 == 1", - Actions = new RuleActions{ + Actions = new RuleAction{ OnSuccess = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary{ @@ -92,7 +92,7 @@ private WorkflowRules[] GetWorkflowWithActions() RuleName = "EvaluateRuleTest", RuleExpressionType = RuleExpressionType.LambdaExpression, Expression = "1 == 1", - Actions = new RuleActions{ + Actions = new RuleAction{ OnSuccess = new ActionInfo{ Name = "EvaluateRule", Context = new Dictionary{ diff --git a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs index 0d1e01f1..c38aff2e 100644 --- a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs +++ b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs @@ -103,7 +103,7 @@ public async Task ExecuteRule_AddOrUpdateWorkflow_ExecutesUpdatedRules(string pr Assert.Contains(result1, c => c.IsSuccess); // Fetch and update new rules. - WorkflowRules newWorkflowRules = ParseAsWorkflowRules(newWorkflowFile); + WorkflowRule newWorkflowRules = ParseAsWorkflowRules(newWorkflowFile); re.AddOrUpdateWorkflow(newWorkflowRules); // Run new rules. @@ -257,12 +257,12 @@ public async Task ExecuteRule_ReturnsListOfRuleResultTree_ResultMessage(string r public void RulesEngine_New_IncorrectJSON_ThrowsException() { Assert.Throws(() => { - var workflow = new WorkflowRules(); + var workflow = new WorkflowRule(); var re = CreateRulesEngine(workflow); }); Assert.Throws(() => { - var workflow = new WorkflowRules() { WorkflowName = "test" }; + var workflow = new WorkflowRule() { WorkflowName = "test" }; var re = CreateRulesEngine(workflow); }); } @@ -356,7 +356,7 @@ public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Succes } var fileData = File.ReadAllText(files[0]); - var bre = new RulesEngine(JsonConvert.DeserializeObject(fileData), null); + var bre = new RulesEngine(JsonConvert.DeserializeObject(fileData), null); var result = await bre.ExecuteAllRulesAsync("inputWorkflow", ruleParams?.ToArray()); var ruleResult = result?.FirstOrDefault(r => string.Equals(r.Rule.RuleName, "GiveDiscount10", StringComparison.OrdinalIgnoreCase)); Assert.True(ruleResult.IsSuccess); @@ -463,7 +463,7 @@ public async Task ExecuteRule_RuleWithMemberAccessExpression_ReturnsSucess(strin [InlineData("rules9.json")] public async Task ExecuteRule_MissingMethodInExpression_ReturnsException(string ruleFileName) { - var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = false }); + var re = GetRulesEngine(ruleFileName, new ReSetting() { EnableExceptionAsErrorMessage = false }); dynamic input1 = new ExpandoObject(); input1.Data = new { TestProperty = "" }; @@ -480,7 +480,7 @@ await Assert.ThrowsAsync(async () => { [InlineData("rules9.json")] public async Task ExecuteRule_CompilationException_ReturnsAsErrorMessage(string ruleFileName) { - var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = true }); + var re = GetRulesEngine(ruleFileName, new ReSetting() { EnableExceptionAsErrorMessage = true }); dynamic input1 = new ExpandoObject(); input1.Data = new { TestProperty = "" }; @@ -497,7 +497,7 @@ public async Task ExecuteRule_CompilationException_ReturnsAsErrorMessage(string [InlineData("rules9.json")] public async Task ExecuteRuleWithIgnoreException_CompilationException_DoesNotReturnsAsErrorMessage(string ruleFileName) { - var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = true , IgnoreException = true}); + var re = GetRulesEngine(ruleFileName, new ReSetting() { EnableExceptionAsErrorMessage = true , IgnoreException = true}); dynamic input1 = new ExpandoObject(); input1.Data = new { TestProperty = "" }; @@ -515,7 +515,7 @@ public async Task ExecuteRuleWithIgnoreException_CompilationException_DoesNotRet public async Task ExecuteRule_RuntimeError_ShouldReturnAsErrorMessage() { - var workflow = new WorkflowRules { + var workflow = new WorkflowRule { WorkflowName = "TestWorkflow", Rules = new[] { new Rule { @@ -542,7 +542,7 @@ public async Task ExecuteRule_RuntimeError_ShouldReturnAsErrorMessage() public async Task ExecuteRule_RuntimeError_ThrowsException() { - var workflow = new WorkflowRules { + var workflow = new WorkflowRule { WorkflowName = "TestWorkflow", Rules = new[] { new Rule { @@ -552,7 +552,7 @@ public async Task ExecuteRule_RuntimeError_ThrowsException() } }; - var re = new RulesEngine(new[] { workflow }, null, new ReSettings { + var re = new RulesEngine(new[] { workflow }, null, new ReSetting { EnableExceptionAsErrorMessage = false }); var input = new RuleTestClass { @@ -567,7 +567,7 @@ public async Task ExecuteRule_RuntimeError_ThrowsException() public async Task ExecuteRule_RuntimeError_IgnoreException_DoesNotReturnException() { - var workflow = new WorkflowRules { + var workflow = new WorkflowRule { WorkflowName = "TestWorkflow", Rules = new[] { new Rule { @@ -577,7 +577,7 @@ public async Task ExecuteRule_RuntimeError_IgnoreException_DoesNotReturnExceptio } }; - var re = new RulesEngine(new[] { workflow }, null, new ReSettings { + var re = new RulesEngine(new[] { workflow }, null, new ReSetting { IgnoreException = true }); var input = new RuleTestClass { @@ -595,7 +595,7 @@ public async Task ExecuteRule_RuntimeError_IgnoreException_DoesNotReturnExceptio [Fact] public async Task RemoveWorkFlow_ShouldRemoveAllCompiledCache() { - var workflow = new WorkflowRules { + var workflow = new WorkflowRule { WorkflowName = "Test", Rules = new Rule[]{ new Rule { @@ -629,7 +629,7 @@ public async Task RemoveWorkFlow_ShouldRemoveAllCompiledCache() [Fact] public async Task ClearWorkFlow_ShouldRemoveAllCompiledCache() { - var workflow = new WorkflowRules { + var workflow = new WorkflowRule { WorkflowName = "Test", Rules = new Rule[]{ new Rule { @@ -663,7 +663,7 @@ public async Task ClearWorkFlow_ShouldRemoveAllCompiledCache() [Fact] public async Task ExecuteRule_WithNullInput_ShouldNotThrowException() { - var workflow = new WorkflowRules { + var workflow = new WorkflowRule { WorkflowName = "Test", Rules = new Rule[]{ new Rule { @@ -697,7 +697,7 @@ public async Task ExecuteRule_WithNullInput_ShouldNotThrowException() [Fact] public async Task ExecuteRule_SpecialCharInWorkflowName_RunsSuccessfully() { - var workflow = new WorkflowRules { + var workflow = new WorkflowRule { WorkflowName = "Exámple", Rules = new Rule[]{ new Rule { @@ -740,17 +740,17 @@ public void Class_PublicMethods_ArePartOfInterface(Type classType, Type interfac - private RulesEngine CreateRulesEngine(WorkflowRules workflow) + private RulesEngine CreateRulesEngine(WorkflowRule workflow) { var json = JsonConvert.SerializeObject(workflow); return new RulesEngine(new string[] { json }, null); } - private RulesEngine GetRulesEngine(string filename, ReSettings reSettings = null) + private RulesEngine GetRulesEngine(string filename, ReSetting reSettings = null) { var data = GetFileContent(filename); - var injectWorkflow = new WorkflowRules { + var injectWorkflow = new WorkflowRule { WorkflowName = "inputWorkflowReference", WorkflowRulesToInject = new List { "inputWorkflow" } }; @@ -766,10 +766,10 @@ private string GetFileContent(string filename) return File.ReadAllText(filePath); } - private WorkflowRules ParseAsWorkflowRules(string workflowRulesFileName) + private WorkflowRule ParseAsWorkflowRules(string workflowRulesFileName) { string content = GetFileContent(workflowRulesFileName); - return JsonConvert.DeserializeObject(content); + return JsonConvert.DeserializeObject(content); } private dynamic GetInput1() diff --git a/test/RulesEngine.UnitTest/EmptyRulesTest.cs b/test/RulesEngine.UnitTest/EmptyRulesTest.cs index 8bd02b81..74349fcc 100644 --- a/test/RulesEngine.UnitTest/EmptyRulesTest.cs +++ b/test/RulesEngine.UnitTest/EmptyRulesTest.cs @@ -21,7 +21,7 @@ public class EmptyRulesTest private async Task EmptyRules_ReturnsExepectedResults() { var workflows = GetEmptyWorkflows(); - var reSettings = new ReSettings { }; + var reSettings = new ReSetting { }; RulesEngine rulesEngine = new RulesEngine(); Func action = () => { @@ -37,7 +37,7 @@ private async Task EmptyRules_ReturnsExepectedResults() private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults() { var workflows = GetEmptyNestedWorkflows(); - var reSettings = new ReSettings { }; + var reSettings = new ReSetting { }; RulesEngine rulesEngine = new RulesEngine(); Func action = () => { @@ -50,10 +50,10 @@ private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults() Assert.Contains("Atleast one of Rules or WorkflowRulesToInject must be not empty", ex.Message); } - private WorkflowRules[] GetEmptyWorkflows() + private WorkflowRule[] GetEmptyWorkflows() { return new[] { - new WorkflowRules { + new WorkflowRule { WorkflowName = "EmptyRulesTest", Rules = new Rule[] { } @@ -61,10 +61,10 @@ private WorkflowRules[] GetEmptyWorkflows() }; } - private WorkflowRules[] GetEmptyNestedWorkflows() + private WorkflowRule[] GetEmptyNestedWorkflows() { return new[] { - new WorkflowRules { + new WorkflowRule { WorkflowName = "EmptyNestedRulesTest", Rules = new Rule[] { new Rule { @@ -129,7 +129,7 @@ private WorkflowRules[] GetEmptyNestedWorkflows() } } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "EmptyNestedRulesActionsTest", Rules = new Rule[] { new Rule { @@ -138,7 +138,7 @@ private WorkflowRules[] GetEmptyNestedWorkflows() Rules = new Rule[] { }, - Actions = new RuleActions { + Actions = new RuleAction { OnFailure = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { diff --git a/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs b/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs index 3e98ca62..42b7a31c 100644 --- a/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs +++ b/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs @@ -17,7 +17,7 @@ public class LambdaExpressionBuilderTest [Fact] public void BuildExpressionForRuleTest() { - var reSettings = new ReSettings(); + var reSettings = new ReSetting(); var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, new RuleExpressionParser(reSettings)); var builder = objBuilderFactory.RuleGetExpressionBuilder(RuleExpressionType.LambdaExpression); diff --git a/test/RulesEngine.UnitTest/NestedRulesTest.cs b/test/RulesEngine.UnitTest/NestedRulesTest.cs index b11da4bc..8b9ef014 100644 --- a/test/RulesEngine.UnitTest/NestedRulesTest.cs +++ b/test/RulesEngine.UnitTest/NestedRulesTest.cs @@ -23,7 +23,7 @@ public class NestedRulesTest public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode mode) { var workflows = GetWorkflows(); - var reSettings = new ReSettings { NestedRuleExecutionMode = mode }; + var reSettings = new ReSetting { NestedRuleExecutionMode = mode }; var rulesEngine = new RulesEngine(workflows, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -69,7 +69,7 @@ public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode m private async Task NestedRulesWithNestedActions_ReturnsCorrectResults() { var workflows = GetWorkflows(); - var reSettings = new ReSettings { }; + var reSettings = new ReSetting { }; var rulesEngine = new RulesEngine(workflows, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -90,9 +90,9 @@ private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }; - var workflowsViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr, serializationOptions); + var workflowsViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr, serializationOptions); - var reSettings = new ReSettings { }; + var reSettings = new ReSetting { }; var rulesEngine = new RulesEngine(workflowsViaTextJson, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -108,10 +108,10 @@ private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson - private WorkflowRules[] GetWorkflows() + private WorkflowRule[] GetWorkflows() { return new[] { - new WorkflowRules { + new WorkflowRule { WorkflowName = "NestedRulesTest", Rules = new Rule[] { new Rule { @@ -176,7 +176,7 @@ private WorkflowRules[] GetWorkflows() } } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "NestedRulesActionsTest", Rules = new Rule[] { new Rule { @@ -186,7 +186,7 @@ private WorkflowRules[] GetWorkflows() new Rule{ RuleName = "trueRule1", Expression = "input1.TrueValue == true", - Actions = new RuleActions { + Actions = new RuleAction { OnSuccess = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { @@ -198,7 +198,7 @@ private WorkflowRules[] GetWorkflows() new Rule { RuleName = "falseRule1", Expression = "input1.TrueValue == false", - Actions = new RuleActions { + Actions = new RuleAction { OnFailure = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { @@ -208,7 +208,7 @@ private WorkflowRules[] GetWorkflows() } } }, - Actions = new RuleActions { + Actions = new RuleAction { OnFailure = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { diff --git a/test/RulesEngine.UnitTest/RuleCompilerTest.cs b/test/RulesEngine.UnitTest/RuleCompilerTest.cs index 5164b4a0..4fb40e43 100644 --- a/test/RulesEngine.UnitTest/RuleCompilerTest.cs +++ b/test/RulesEngine.UnitTest/RuleCompilerTest.cs @@ -18,7 +18,7 @@ public class RuleCompilerTest public void RuleCompiler_NullCheck() { Assert.Throws(() => new RuleCompiler(null, null,null)); - var reSettings = new ReSettings(); + var reSettings = new ReSetting(); var parser = new RuleExpressionParser(reSettings); Assert.Throws(() => new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser), null,null)); } @@ -26,7 +26,7 @@ public void RuleCompiler_NullCheck() [Fact] public void RuleCompiler_CompileRule_ThrowsException() { - var reSettings = new ReSettings(); + var reSettings = new ReSetting(); var parser = new RuleExpressionParser(reSettings); var compiler = new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser),null, new NullLogger()); Assert.Throws(() => compiler.CompileRule(null, null,null)); diff --git a/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs b/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs index 2ee4891e..6c21a073 100644 --- a/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs +++ b/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs @@ -17,7 +17,7 @@ public class RuleExpressionBuilderFactoryTest [InlineData(RuleExpressionType.LambdaExpression, typeof(LambdaExpressionBuilder))] public void RuleGetExpressionBuilderTest(RuleExpressionType expressionType, Type expectedExpressionBuilderType) { - var reSettings = new ReSettings(); + var reSettings = new ReSetting(); var parser = new RuleExpressionParser(reSettings); var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, parser); var builder = objBuilderFactory.RuleGetExpressionBuilder(expressionType); diff --git a/test/RulesEngine.UnitTest/RulesEnabledTests.cs b/test/RulesEngine.UnitTest/RulesEnabledTests.cs index 8ddc992e..6af5abb6 100644 --- a/test/RulesEngine.UnitTest/RulesEnabledTests.cs +++ b/test/RulesEngine.UnitTest/RulesEnabledTests.cs @@ -23,7 +23,7 @@ public RulesEnabledTests() public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, bool[] expectedRuleResults) { var workflows = GetWorkflows(); - var rulesEngine = new RulesEngine(workflows, reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false }); + var rulesEngine = new RulesEngine(workflows, reSettings: new ReSetting() { EnableExceptionAsErrorMessage = false }); var input1 = new { TrueValue = true }; @@ -45,7 +45,7 @@ public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, bool[] expectedRuleResults) { var workflow = GetWorkflows().Single(c => c.WorkflowName == workflowName); - var rulesEngine = new RulesEngine(reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false}); + var rulesEngine = new RulesEngine(reSettings: new ReSetting() { EnableExceptionAsErrorMessage = false}); rulesEngine.AddWorkflow(workflow); var input1 = new { TrueValue = true @@ -95,10 +95,10 @@ private bool NestedEnabledCheck(IEnumerable ruleResults) return areAllRulesEnabled; } - private WorkflowRules[] GetWorkflows() + private WorkflowRule[] GetWorkflows() { return new[] { - new WorkflowRules { + new WorkflowRule { WorkflowName = "RuleEnabledFeatureTest", Rules = new List { new Rule { @@ -118,7 +118,7 @@ private WorkflowRules[] GetWorkflows() } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "RuleEnabledNestedFeatureTest", Rules = new List { new Rule { diff --git a/test/RulesEngine.UnitTest/ScopedParamsTest.cs b/test/RulesEngine.UnitTest/ScopedParamsTest.cs index e3467fe3..7e4358c4 100644 --- a/test/RulesEngine.UnitTest/ScopedParamsTest.cs +++ b/test/RulesEngine.UnitTest/ScopedParamsTest.cs @@ -77,7 +77,7 @@ public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] { var workflows = GetWorkflowRulesList(); - var engine = new RulesEngine(new string[] { }, null, new ReSettings { + var engine = new RulesEngine(new string[] { }, null, new ReSetting { EnableScopedParams = false }); engine.AddWorkflow(workflows); @@ -176,10 +176,10 @@ private static void CheckInputs(IEnumerable expectedInputs, RuleResultTr } } - private WorkflowRules[] GetWorkflowRulesList() + private WorkflowRule[] GetWorkflowRulesList() { - return new WorkflowRules[] { - new WorkflowRules { + return new WorkflowRule[] { + new WorkflowRule { WorkflowName = "NoLocalAndGlobalParams", Rules = new List { new Rule { @@ -188,7 +188,7 @@ private WorkflowRules[] GetWorkflowRulesList() } } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "LocalParamsOnly", Rules = new List { new Rule { @@ -209,7 +209,7 @@ private WorkflowRules[] GetWorkflowRulesList() }, } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "LocalParamsOnly2", Rules = new List { new Rule { @@ -226,7 +226,7 @@ private WorkflowRules[] GetWorkflowRulesList() } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "GlobalParamsOnly", GlobalParams = new List { new ScopedParam { @@ -241,7 +241,7 @@ private WorkflowRules[] GetWorkflowRulesList() } } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "GlobalAndLocalParams", GlobalParams = new List { new ScopedParam { @@ -263,7 +263,7 @@ private WorkflowRules[] GetWorkflowRulesList() } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "GlobalParamReferencedInLocalParams", GlobalParams = new List { new ScopedParam { @@ -285,7 +285,7 @@ private WorkflowRules[] GetWorkflowRulesList() }, } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "GlobalParamReferencedInNextGlobalParams", GlobalParams = new List { new ScopedParam { @@ -304,7 +304,7 @@ private WorkflowRules[] GetWorkflowRulesList() }, } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "LocalParamReferencedInNextLocalParams", Rules = new List { new Rule { @@ -323,7 +323,7 @@ private WorkflowRules[] GetWorkflowRulesList() }, } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "GlobalParamAndLocalParamsInNestedRules", GlobalParams = new List { new ScopedParam { @@ -362,7 +362,7 @@ private WorkflowRules[] GetWorkflowRulesList() } } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "LocalParamsOnlyWithComplexInput", Rules = new List { new Rule { @@ -378,7 +378,7 @@ private WorkflowRules[] GetWorkflowRulesList() } } }, - new WorkflowRules { + new WorkflowRule { WorkflowName = "GlobalParamsOnlyWithComplexInput", GlobalParams = new List { new ScopedParam { From 43fadcd1d628516c2308330c594c8eddedc4f5ff Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Mon, 26 Jul 2021 05:44:09 -0700 Subject: [PATCH 02/16] Reverted ReSettings pluralization --- benchmark/RulesEngineBenchmark/Program.cs | 2 +- .../ExpressionBuilders/LambdaExpressionBuilder.cs | 4 ++-- .../ExpressionBuilders/RuleExpressionParser.cs | 4 ++-- src/RulesEngine/HelperFunctions/Helpers.cs | 8 ++++---- .../Models/{ReSetting.cs => ReSettings.cs} | 2 +- src/RulesEngine/RuleCompiler.cs | 4 ++-- src/RulesEngine/RuleExpressionBuilderFactory.cs | 4 ++-- src/RulesEngine/RulesEngine.cs | 12 ++++++------ .../ActionTests/CustomActionTest.cs | 4 ++-- test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs | 12 ++++++------ test/RulesEngine.UnitTest/EmptyRulesTest.cs | 4 ++-- .../LambdaExpressionBuilderTest.cs | 2 +- test/RulesEngine.UnitTest/NestedRulesTest.cs | 6 +++--- test/RulesEngine.UnitTest/RuleCompilerTest.cs | 4 ++-- .../RuleExpressionBuilderFactoryTest.cs | 2 +- test/RulesEngine.UnitTest/RulesEnabledTests.cs | 4 ++-- test/RulesEngine.UnitTest/ScopedParamsTest.cs | 2 +- 17 files changed, 40 insertions(+), 40 deletions(-) rename src/RulesEngine/Models/{ReSetting.cs => ReSettings.cs} (98%) diff --git a/benchmark/RulesEngineBenchmark/Program.cs b/benchmark/RulesEngineBenchmark/Program.cs index f1d3f589..430b7f93 100644 --- a/benchmark/RulesEngineBenchmark/Program.cs +++ b/benchmark/RulesEngineBenchmark/Program.cs @@ -36,7 +36,7 @@ public REBenchmark() var fileData = File.ReadAllText(files[0]); workflows = JsonConvert.DeserializeObject>(fileData); - rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null, new ReSetting { + rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null, new ReSettings { EnableFormattedErrorMessage = false, EnableScopedParams = false }); diff --git a/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs b/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs index eb4bd5f7..2a7cbb58 100644 --- a/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs +++ b/src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs @@ -14,10 +14,10 @@ namespace RulesEngine.ExpressionBuilders { internal sealed class LambdaExpressionBuilder : RuleExpressionBuilderBase { - private readonly ReSetting _reSettings; + private readonly ReSettings _reSettings; private readonly RuleExpressionParser _ruleExpressionParser; - internal LambdaExpressionBuilder(ReSetting reSettings, RuleExpressionParser ruleExpressionParser) + internal LambdaExpressionBuilder(ReSettings reSettings, RuleExpressionParser ruleExpressionParser) { _reSettings = reSettings; _ruleExpressionParser = ruleExpressionParser; diff --git a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs index ab1ce577..f8566751 100644 --- a/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs +++ b/src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs @@ -15,11 +15,11 @@ namespace RulesEngine.ExpressionBuilders { public class RuleExpressionParser { - private readonly ReSetting _reSettings; + private readonly ReSettings _reSettings; private static IMemoryCache _memoryCache; private readonly IDictionary _methodInfo; - public RuleExpressionParser(ReSetting reSettings) + public RuleExpressionParser(ReSettings reSettings) { _reSettings = reSettings; _memoryCache = _memoryCache ?? new MemoryCache(new MemoryCacheOptions { diff --git a/src/RulesEngine/HelperFunctions/Helpers.cs b/src/RulesEngine/HelperFunctions/Helpers.cs index 5751c9a2..f96ff50a 100644 --- a/src/RulesEngine/HelperFunctions/Helpers.cs +++ b/src/RulesEngine/HelperFunctions/Helpers.cs @@ -15,7 +15,7 @@ namespace RulesEngine.HelperFunctions /// internal static class Helpers { - internal static RuleFunc ToResultTree(ReSetting reSettings, Rule rule, IEnumerable childRuleResults, Func isSuccessFunc, string exceptionMessage = "") + internal static RuleFunc ToResultTree(ReSettings reSettings, Rule rule, IEnumerable childRuleResults, Func isSuccessFunc, string exceptionMessage = "") { return (inputs) => { @@ -45,13 +45,13 @@ internal static RuleFunc ToResultTree(ReSetting reSettings, Rule } - internal static RuleFunc ToRuleExceptionResult(ReSetting reSettings, Rule rule,Exception ex) + internal static RuleFunc ToRuleExceptionResult(ReSettings reSettings, Rule rule,Exception ex) { HandleRuleException(ex, rule, reSettings); return ToResultTree(reSettings, rule, null, (args) => false, ex.Message); } - internal static void HandleRuleException(Exception ex, Rule rule, ReSetting reSettings) + internal static void HandleRuleException(Exception ex, Rule rule, ReSettings reSettings) { ex.Data.Add(nameof(rule.RuleName), rule.RuleName); ex.Data.Add(nameof(rule.Expression), rule.Expression); @@ -70,7 +70,7 @@ internal static void HandleRuleException(Exception ex, Rule rule, ReSetting reSe /// /// /// - internal static string GetExceptionMessage(string message,ReSetting reSettings) + internal static string GetExceptionMessage(string message,ReSettings reSettings) { return reSettings.IgnoreException ? "" : message; } diff --git a/src/RulesEngine/Models/ReSetting.cs b/src/RulesEngine/Models/ReSettings.cs similarity index 98% rename from src/RulesEngine/Models/ReSetting.cs rename to src/RulesEngine/Models/ReSettings.cs index b1369d5a..33dd7c0c 100644 --- a/src/RulesEngine/Models/ReSetting.cs +++ b/src/RulesEngine/Models/ReSettings.cs @@ -9,7 +9,7 @@ namespace RulesEngine.Models { [ExcludeFromCodeCoverage] - public class ReSetting + public class ReSettings { /// /// Get/Set the custom types to be used in Rule expressions diff --git a/src/RulesEngine/RuleCompiler.cs b/src/RulesEngine/RuleCompiler.cs index 845d1a4e..907296da 100644 --- a/src/RulesEngine/RuleCompiler.cs +++ b/src/RulesEngine/RuleCompiler.cs @@ -28,7 +28,7 @@ internal class RuleCompiler /// The expression builder factory /// private readonly RuleExpressionBuilderFactory _expressionBuilderFactory; - private readonly ReSetting _reSettings; + private readonly ReSettings _reSettings; /// /// The logger @@ -40,7 +40,7 @@ internal class RuleCompiler /// /// The expression builder factory. /// expressionBuilderFactory - internal RuleCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ReSetting reSettings, ILogger logger) + internal RuleCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ReSettings reSettings, ILogger logger) { _logger = logger ?? throw new ArgumentNullException($"{nameof(logger)} can't be null."); diff --git a/src/RulesEngine/RuleExpressionBuilderFactory.cs b/src/RulesEngine/RuleExpressionBuilderFactory.cs index 3dcdaf5d..3e88f1c5 100644 --- a/src/RulesEngine/RuleExpressionBuilderFactory.cs +++ b/src/RulesEngine/RuleExpressionBuilderFactory.cs @@ -9,9 +9,9 @@ namespace RulesEngine { internal class RuleExpressionBuilderFactory { - private readonly ReSetting _reSettings; + private readonly ReSettings _reSettings; private readonly LambdaExpressionBuilder _lambdaExpressionBuilder; - public RuleExpressionBuilderFactory(ReSetting reSettings, RuleExpressionParser expressionParser) + public RuleExpressionBuilderFactory(ReSettings reSettings, RuleExpressionParser expressionParser) { _reSettings = reSettings; _lambdaExpressionBuilder = new LambdaExpressionBuilder(_reSettings, expressionParser); diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index 81d2f5d5..8cac2caa 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -28,7 +28,7 @@ public class RulesEngine : IRulesEngine { #region Variables private readonly ILogger _logger; - private readonly ReSetting _reSettings; + private readonly ReSettings _reSettings; private readonly RulesCache _rulesCache = new RulesCache(); private readonly RuleExpressionParser _ruleExpressionParser; private readonly RuleCompiler _ruleCompiler; @@ -37,27 +37,27 @@ public class RulesEngine : IRulesEngine #endregion #region Constructor - public RulesEngine(string[] jsonConfig, ILogger logger = null, ReSetting reSettings = null) : this(logger, reSettings) + public RulesEngine(string[] jsonConfig, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { var workflowRules = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); AddWorkflow(workflowRules); } - public RulesEngine(WorkflowRule[] workflowRules, ILogger logger = null, ReSetting reSettings = null) : this(logger, reSettings) + public RulesEngine(WorkflowRule[] workflowRules, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { AddWorkflow(workflowRules); } - public RulesEngine(ILogger logger = null, ReSetting reSettings = null) + public RulesEngine(ILogger logger = null, ReSettings reSettings = null) { _logger = logger ?? new NullLogger(); - _reSettings = reSettings ?? new ReSetting(); + _reSettings = reSettings ?? new ReSettings(); _ruleExpressionParser = new RuleExpressionParser(_reSettings); _ruleCompiler = new RuleCompiler(new RuleExpressionBuilderFactory(_reSettings, _ruleExpressionParser),_reSettings, _logger); _actionFactory = new ActionFactory(GetActionRegistry(_reSettings)); } - private IDictionary> GetActionRegistry(ReSetting reSettings) + private IDictionary> GetActionRegistry(ReSettings reSettings) { var actionDictionary = GetDefaultActionRegistry(); var customActions = reSettings.CustomActions ?? new Dictionary>(); diff --git a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs index f3a97964..21720e0c 100644 --- a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs +++ b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs @@ -19,7 +19,7 @@ public class CustomActionTest public async Task CustomActionOnRuleMustHaveContextValues() { var workflows = GetWorkflowRules(); - var re = new RulesEngine(workflows, null, reSettings: new ReSetting { + var re = new RulesEngine(workflows, null, reSettings: new ReSettings { CustomActions = new Dictionary> { { "ReturnContext", () => new ReturnContextAction() } @@ -39,7 +39,7 @@ public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues() var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr,serializationOptions); - var re = new RulesEngine(workflows, null, reSettings: new ReSetting { + var re = new RulesEngine(workflows, null, reSettings: new ReSettings { CustomActions = new Dictionary> { { "ReturnContext", () => new ReturnContextAction() } diff --git a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs index c38aff2e..a3f76adf 100644 --- a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs +++ b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs @@ -463,7 +463,7 @@ public async Task ExecuteRule_RuleWithMemberAccessExpression_ReturnsSucess(strin [InlineData("rules9.json")] public async Task ExecuteRule_MissingMethodInExpression_ReturnsException(string ruleFileName) { - var re = GetRulesEngine(ruleFileName, new ReSetting() { EnableExceptionAsErrorMessage = false }); + var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = false }); dynamic input1 = new ExpandoObject(); input1.Data = new { TestProperty = "" }; @@ -480,7 +480,7 @@ await Assert.ThrowsAsync(async () => { [InlineData("rules9.json")] public async Task ExecuteRule_CompilationException_ReturnsAsErrorMessage(string ruleFileName) { - var re = GetRulesEngine(ruleFileName, new ReSetting() { EnableExceptionAsErrorMessage = true }); + var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = true }); dynamic input1 = new ExpandoObject(); input1.Data = new { TestProperty = "" }; @@ -497,7 +497,7 @@ public async Task ExecuteRule_CompilationException_ReturnsAsErrorMessage(string [InlineData("rules9.json")] public async Task ExecuteRuleWithIgnoreException_CompilationException_DoesNotReturnsAsErrorMessage(string ruleFileName) { - var re = GetRulesEngine(ruleFileName, new ReSetting() { EnableExceptionAsErrorMessage = true , IgnoreException = true}); + var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = true , IgnoreException = true}); dynamic input1 = new ExpandoObject(); input1.Data = new { TestProperty = "" }; @@ -552,7 +552,7 @@ public async Task ExecuteRule_RuntimeError_ThrowsException() } }; - var re = new RulesEngine(new[] { workflow }, null, new ReSetting { + var re = new RulesEngine(new[] { workflow }, null, new ReSettings { EnableExceptionAsErrorMessage = false }); var input = new RuleTestClass { @@ -577,7 +577,7 @@ public async Task ExecuteRule_RuntimeError_IgnoreException_DoesNotReturnExceptio } }; - var re = new RulesEngine(new[] { workflow }, null, new ReSetting { + var re = new RulesEngine(new[] { workflow }, null, new ReSettings { IgnoreException = true }); var input = new RuleTestClass { @@ -746,7 +746,7 @@ private RulesEngine CreateRulesEngine(WorkflowRule workflow) return new RulesEngine(new string[] { json }, null); } - private RulesEngine GetRulesEngine(string filename, ReSetting reSettings = null) + private RulesEngine GetRulesEngine(string filename, ReSettings reSettings = null) { var data = GetFileContent(filename); diff --git a/test/RulesEngine.UnitTest/EmptyRulesTest.cs b/test/RulesEngine.UnitTest/EmptyRulesTest.cs index 74349fcc..6b6d0eee 100644 --- a/test/RulesEngine.UnitTest/EmptyRulesTest.cs +++ b/test/RulesEngine.UnitTest/EmptyRulesTest.cs @@ -21,7 +21,7 @@ public class EmptyRulesTest private async Task EmptyRules_ReturnsExepectedResults() { var workflows = GetEmptyWorkflows(); - var reSettings = new ReSetting { }; + var reSettings = new ReSettings { }; RulesEngine rulesEngine = new RulesEngine(); Func action = () => { @@ -37,7 +37,7 @@ private async Task EmptyRules_ReturnsExepectedResults() private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults() { var workflows = GetEmptyNestedWorkflows(); - var reSettings = new ReSetting { }; + var reSettings = new ReSettings { }; RulesEngine rulesEngine = new RulesEngine(); Func action = () => { diff --git a/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs b/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs index 42b7a31c..3e98ca62 100644 --- a/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs +++ b/test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs @@ -17,7 +17,7 @@ public class LambdaExpressionBuilderTest [Fact] public void BuildExpressionForRuleTest() { - var reSettings = new ReSetting(); + var reSettings = new ReSettings(); var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, new RuleExpressionParser(reSettings)); var builder = objBuilderFactory.RuleGetExpressionBuilder(RuleExpressionType.LambdaExpression); diff --git a/test/RulesEngine.UnitTest/NestedRulesTest.cs b/test/RulesEngine.UnitTest/NestedRulesTest.cs index 8b9ef014..6a9fe909 100644 --- a/test/RulesEngine.UnitTest/NestedRulesTest.cs +++ b/test/RulesEngine.UnitTest/NestedRulesTest.cs @@ -23,7 +23,7 @@ public class NestedRulesTest public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode mode) { var workflows = GetWorkflows(); - var reSettings = new ReSetting { NestedRuleExecutionMode = mode }; + var reSettings = new ReSettings { NestedRuleExecutionMode = mode }; var rulesEngine = new RulesEngine(workflows, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -69,7 +69,7 @@ public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode m private async Task NestedRulesWithNestedActions_ReturnsCorrectResults() { var workflows = GetWorkflows(); - var reSettings = new ReSetting { }; + var reSettings = new ReSettings { }; var rulesEngine = new RulesEngine(workflows, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -92,7 +92,7 @@ private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson var workflowsViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr, serializationOptions); - var reSettings = new ReSetting { }; + var reSettings = new ReSettings { }; var rulesEngine = new RulesEngine(workflowsViaTextJson, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; diff --git a/test/RulesEngine.UnitTest/RuleCompilerTest.cs b/test/RulesEngine.UnitTest/RuleCompilerTest.cs index 4fb40e43..5164b4a0 100644 --- a/test/RulesEngine.UnitTest/RuleCompilerTest.cs +++ b/test/RulesEngine.UnitTest/RuleCompilerTest.cs @@ -18,7 +18,7 @@ public class RuleCompilerTest public void RuleCompiler_NullCheck() { Assert.Throws(() => new RuleCompiler(null, null,null)); - var reSettings = new ReSetting(); + var reSettings = new ReSettings(); var parser = new RuleExpressionParser(reSettings); Assert.Throws(() => new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser), null,null)); } @@ -26,7 +26,7 @@ public void RuleCompiler_NullCheck() [Fact] public void RuleCompiler_CompileRule_ThrowsException() { - var reSettings = new ReSetting(); + var reSettings = new ReSettings(); var parser = new RuleExpressionParser(reSettings); var compiler = new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser),null, new NullLogger()); Assert.Throws(() => compiler.CompileRule(null, null,null)); diff --git a/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs b/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs index 6c21a073..2ee4891e 100644 --- a/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs +++ b/test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs @@ -17,7 +17,7 @@ public class RuleExpressionBuilderFactoryTest [InlineData(RuleExpressionType.LambdaExpression, typeof(LambdaExpressionBuilder))] public void RuleGetExpressionBuilderTest(RuleExpressionType expressionType, Type expectedExpressionBuilderType) { - var reSettings = new ReSetting(); + var reSettings = new ReSettings(); var parser = new RuleExpressionParser(reSettings); var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, parser); var builder = objBuilderFactory.RuleGetExpressionBuilder(expressionType); diff --git a/test/RulesEngine.UnitTest/RulesEnabledTests.cs b/test/RulesEngine.UnitTest/RulesEnabledTests.cs index 6af5abb6..eee9bb8c 100644 --- a/test/RulesEngine.UnitTest/RulesEnabledTests.cs +++ b/test/RulesEngine.UnitTest/RulesEnabledTests.cs @@ -23,7 +23,7 @@ public RulesEnabledTests() public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, bool[] expectedRuleResults) { var workflows = GetWorkflows(); - var rulesEngine = new RulesEngine(workflows, reSettings: new ReSetting() { EnableExceptionAsErrorMessage = false }); + var rulesEngine = new RulesEngine(workflows, reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false }); var input1 = new { TrueValue = true }; @@ -45,7 +45,7 @@ public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, bool[] expectedRuleResults) { var workflow = GetWorkflows().Single(c => c.WorkflowName == workflowName); - var rulesEngine = new RulesEngine(reSettings: new ReSetting() { EnableExceptionAsErrorMessage = false}); + var rulesEngine = new RulesEngine(reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false}); rulesEngine.AddWorkflow(workflow); var input1 = new { TrueValue = true diff --git a/test/RulesEngine.UnitTest/ScopedParamsTest.cs b/test/RulesEngine.UnitTest/ScopedParamsTest.cs index 7e4358c4..dc01231e 100644 --- a/test/RulesEngine.UnitTest/ScopedParamsTest.cs +++ b/test/RulesEngine.UnitTest/ScopedParamsTest.cs @@ -77,7 +77,7 @@ public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] { var workflows = GetWorkflowRulesList(); - var engine = new RulesEngine(new string[] { }, null, new ReSetting { + var engine = new RulesEngine(new string[] { }, null, new ReSettings { EnableScopedParams = false }); engine.AddWorkflow(workflows); From 9b404416b9d6b37632c58965ed9a557417f21d6b Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Wed, 28 Jul 2021 19:07:32 -0700 Subject: [PATCH 03/16] conventional progress --- src/RulesEngine/Models/Rule.cs | 5 +++++ src/RulesEngine/Models/WorkflowRules.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/RulesEngine/Models/Rule.cs b/src/RulesEngine/Models/Rule.cs index 611f890b..cc4ea679 100644 --- a/src/RulesEngine/Models/Rule.cs +++ b/src/RulesEngine/Models/Rule.cs @@ -15,6 +15,11 @@ namespace RulesEngine.Models [ExcludeFromCodeCoverage] public class Rule { + /// + /// Gets the Rule Id. + /// + public int Id { get; set; } + /// /// Rule name for the Rule /// diff --git a/src/RulesEngine/Models/WorkflowRules.cs b/src/RulesEngine/Models/WorkflowRules.cs index baf07f41..1043116b 100644 --- a/src/RulesEngine/Models/WorkflowRules.cs +++ b/src/RulesEngine/Models/WorkflowRules.cs @@ -12,6 +12,11 @@ namespace RulesEngine.Models [ExcludeFromCodeCoverage] public class WorkflowRules { + /// + /// Gets the workflow Id. + /// + public int Id { get; set; } + /// /// Gets the workflow name. /// From 57d49dd77f304fc69a9f257f44f57b1e093549ee Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 29 Jul 2021 11:28:56 -0700 Subject: [PATCH 04/16] Overhaul to simplify, cleanup, deprecate pr --- benchmark/RulesEngineBenchmark/Program.cs | 4 +- .../RulesEngineContext.cs | 52 ++++++++++++++++++ .../RulesEngineDemoContext.cs | 53 +------------------ demo/DemoApp/BasicDemo.cs | 4 +- demo/DemoApp/DemoApp.csproj | 1 - demo/DemoApp/EFDemo.cs | 6 +-- demo/DemoApp/NestedInputDemo.cs | 6 +-- src/RulesEngine/HelperFunctions/Constants.cs | 2 +- src/RulesEngine/Interfaces/IRulesEngine.cs | 6 +-- src/RulesEngine/Models/Rule.cs | 7 ++- .../Models/{WorkflowRule.cs => Workflow.cs} | 15 +++++- src/RulesEngine/RulesCache.cs | 42 +++++++-------- src/RulesEngine/RulesEngine.cs | 46 ++++++++-------- src/RulesEngine/Validators/RuleValidator.cs | 2 +- .../Validators/WorkflowRulesValidator.cs | 6 +-- .../ActionTests/CustomActionTest.cs | 12 ++--- .../RulesEngineWithActionsTests.cs | 10 ++-- .../BusinessRuleEngineTest.cs | 42 +++++++-------- test/RulesEngine.UnitTest/EmptyRulesTest.cs | 14 ++--- test/RulesEngine.UnitTest/NestedRulesTest.cs | 8 +-- .../RulesEngine.UnitTest/RulesEnabledTests.cs | 6 +-- test/RulesEngine.UnitTest/ScopedParamsTest.cs | 40 +++++++------- 22 files changed, 201 insertions(+), 183 deletions(-) create mode 100644 demo/DemoApp.EFDataExample/RulesEngineContext.cs rename src/RulesEngine/Models/{WorkflowRule.cs => Workflow.cs} (69%) diff --git a/benchmark/RulesEngineBenchmark/Program.cs b/benchmark/RulesEngineBenchmark/Program.cs index 430b7f93..d35b442c 100644 --- a/benchmark/RulesEngineBenchmark/Program.cs +++ b/benchmark/RulesEngineBenchmark/Program.cs @@ -16,7 +16,7 @@ public class REBenchmark { private readonly RulesEngine.RulesEngine rulesEngine; private readonly object ruleInput; - private readonly List workflows; + private readonly List workflows; private class ListItem { @@ -34,7 +34,7 @@ public REBenchmark() } var fileData = File.ReadAllText(files[0]); - workflows = JsonConvert.DeserializeObject>(fileData); + workflows = JsonConvert.DeserializeObject>(fileData); rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null, new ReSettings { EnableFormattedErrorMessage = false, diff --git a/demo/DemoApp.EFDataExample/RulesEngineContext.cs b/demo/DemoApp.EFDataExample/RulesEngineContext.cs new file mode 100644 index 00000000..e4375e6b --- /dev/null +++ b/demo/DemoApp.EFDataExample/RulesEngineContext.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using RulesEngine.Models; + +namespace RulesEngine.Data +{ + public class RulesEngineContext : DbContext + { + public DbSet Workflows { get; set; } + public DbSet ActionInfos { get; set; } + + public DbSet RuleActions { get; set; } + public DbSet Rules { get; set; } + public DbSet ScopedParams { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Ignore(); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + + modelBuilder.Entity(entity => { + entity.HasKey(k => k.Id); + entity.Property(p => p.Id).ValueGeneratedOnAdd(); + + entity.Ignore(b => b.WorkflowsToInject); + }); + + modelBuilder.Entity(entity => { + entity.HasKey(k => k.Id); + entity.Property(p => p.Id).ValueGeneratedOnAdd(); + + entity.Property(b => b.Properties) + .HasConversion( + v => JsonSerializer.Serialize(v, null), + v => JsonSerializer.Deserialize>(v, null)); + + entity.Property(p => p.Actions) + .HasConversion( + v => JsonSerializer.Serialize(v, null), + v => JsonSerializer.Deserialize(v, null)); + + entity.Ignore(b => b.WorkflowsToInject); + }); + } + } + +} \ No newline at end of file diff --git a/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs b/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs index 89d8d960..de7ed2bb 100644 --- a/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs +++ b/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs @@ -2,20 +2,13 @@ using System.Collections.Generic; using System.Text.Json; using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; +using RulesEngine.Data; using RulesEngine.Models; namespace DemoApp.EFDataExample { - public class RulesEngineDemoContext : DbContext + public class RulesEngineDemoContext : RulesEngineContext { - public DbSet WorkflowRules { get; set; } - public DbSet ActionInfos { get; set; } - - public DbSet RuleActions { get; set; } - public DbSet Rules { get; set; } - public DbSet ScopedParams { get; set; } - public string DbPath { get; private set; } public RulesEngineDemoContext() @@ -27,48 +20,6 @@ public RulesEngineDemoContext() protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite($"Data Source={DbPath}"); - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity() - .Property(b => b.Context) - .HasConversion( - v => JsonConvert.SerializeObject(v), - v => JsonConvert.DeserializeObject>(v)); - - modelBuilder.Entity() - .HasKey(k => k.Name); - - modelBuilder.Entity() - .HasKey(k => k.Name); - - modelBuilder.Entity(entity => { - entity.HasKey(k => k.WorkflowName); - }); - - modelBuilder.Entity(entity => { - entity.HasNoKey(); - entity.HasOne(o => o.OnSuccess).WithMany(); - entity.HasOne(o => o.OnFailure).WithMany(); - }); - - modelBuilder.Entity(entity => { - entity.HasKey(k => k.RuleName); - - entity.Property(b => b.Properties) - .HasConversion( - v => JsonConvert.SerializeObject(v), - v => JsonConvert.DeserializeObject>(v)); - entity.Ignore(e => e.Actions); - }); - - modelBuilder.Entity() - .Ignore(b => b.WorkflowRulesToInject); - - modelBuilder.Entity() - .Ignore(b => b.WorkflowRulesToInject); - } } } \ No newline at end of file diff --git a/demo/DemoApp/BasicDemo.cs b/demo/DemoApp/BasicDemo.cs index 9bf6ac63..5435b9c9 100644 --- a/demo/DemoApp/BasicDemo.cs +++ b/demo/DemoApp/BasicDemo.cs @@ -39,9 +39,9 @@ public void Run() throw new Exception("Rules not found."); var fileData = File.ReadAllText(files[0]); - var workflowRules = JsonConvert.DeserializeObject>(fileData); + var Workflows = JsonConvert.DeserializeObject>(fileData); - var bre = new RulesEngine.RulesEngine(workflowRules.ToArray(), null); + var bre = new RulesEngine.RulesEngine(Workflows.ToArray(), null); string discountOffered = "No discount offered."; diff --git a/demo/DemoApp/DemoApp.csproj b/demo/DemoApp/DemoApp.csproj index fb6a1495..4e88a3d2 100644 --- a/demo/DemoApp/DemoApp.csproj +++ b/demo/DemoApp/DemoApp.csproj @@ -7,7 +7,6 @@ - diff --git a/demo/DemoApp/EFDemo.cs b/demo/DemoApp/EFDemo.cs index 42e7776a..299aaa60 100644 --- a/demo/DemoApp/EFDemo.cs +++ b/demo/DemoApp/EFDemo.cs @@ -42,16 +42,16 @@ public void Run() throw new Exception("Rules not found."); var fileData = File.ReadAllText(files[0]); - var workflowRules = JsonConvert.DeserializeObject>(fileData); + var Workflows = JsonConvert.DeserializeObject>(fileData); RulesEngineDemoContext db = new RulesEngineDemoContext(); if (db.Database.EnsureCreated()) { - db.WorkflowRules.AddRange(workflowRules); + db.Workflows.AddRange(Workflows); db.SaveChanges(); } - var wfr = db.WorkflowRules.Include(i => i.Rules).ThenInclude(i => i.Rules).ToArray(); + var wfr = db.Workflows.Include(i => i.Rules).ThenInclude(i => i.Rules).ToArray(); var bre = new RulesEngine.RulesEngine(wfr, null); diff --git a/demo/DemoApp/NestedInputDemo.cs b/demo/DemoApp/NestedInputDemo.cs index ae5a4f75..9427af14 100644 --- a/demo/DemoApp/NestedInputDemo.cs +++ b/demo/DemoApp/NestedInputDemo.cs @@ -49,10 +49,10 @@ public void Run() } var fileData = File.ReadAllText(files[0]); - var workflowRules = JsonConvert.DeserializeObject>(fileData); + var Workflows = JsonConvert.DeserializeObject>(fileData); - var bre = new RulesEngine.RulesEngine(workflowRules.ToArray(), null); - foreach (var workflow in workflowRules) + var bre = new RulesEngine.RulesEngine(Workflows.ToArray(), null); + foreach (var workflow in Workflows) { var resultList = bre.ExecuteAllRulesAsync(workflow.WorkflowName, nestedInput).Result; diff --git a/src/RulesEngine/HelperFunctions/Constants.cs b/src/RulesEngine/HelperFunctions/Constants.cs index 1c482a8b..488602aa 100644 --- a/src/RulesEngine/HelperFunctions/Constants.cs +++ b/src/RulesEngine/HelperFunctions/Constants.cs @@ -9,7 +9,7 @@ namespace RulesEngine.HelperFunctions public static class Constants { public const string WORKFLOW_NAME_NULL_ERRMSG = "Workflow name can not be null or empty"; - public const string INJECT_WORKFLOW_RULES_ERRMSG = "Atleast one of Rules or WorkflowRulesToInject must be not empty"; + public const string INJECT_WORKFLOW_RULES_ERRMSG = "Atleast one of Rules or WorkflowsToInject must be not empty"; public const string RULE_CATEGORY_CONFIGURED_ERRMSG = "Rule Category should be configured"; public const string RULE_NULL_ERRMSG = "Rules can not be null or zero"; public const string NESTED_RULE_NULL_ERRMSG = "Nested rules can not be null"; diff --git a/src/RulesEngine/Interfaces/IRulesEngine.cs b/src/RulesEngine/Interfaces/IRulesEngine.cs index 7ba8d30b..4ca8477a 100644 --- a/src/RulesEngine/Interfaces/IRulesEngine.cs +++ b/src/RulesEngine/Interfaces/IRulesEngine.cs @@ -29,8 +29,8 @@ public interface IRulesEngine /// /// Adds new workflows to RulesEngine /// - /// - void AddWorkflow(params WorkflowRule[] workflowRules); + /// + void AddWorkflow(params Workflow[] Workflows); /// /// Removes all registered workflows from RulesEngine @@ -48,6 +48,6 @@ public interface IRulesEngine /// /// List GetAllRegisteredWorkflowNames(); - void AddOrUpdateWorkflow(params WorkflowRule[] workflowRules); + void AddOrUpdateWorkflow(params Workflow[] Workflows); } } diff --git a/src/RulesEngine/Models/Rule.cs b/src/RulesEngine/Models/Rule.cs index 3111b0dd..d8cebf1d 100644 --- a/src/RulesEngine/Models/Rule.cs +++ b/src/RulesEngine/Models/Rule.cs @@ -15,6 +15,11 @@ namespace RulesEngine.Models [ExcludeFromCodeCoverage] public class Rule { + /// + /// Gets the Rule Id. + /// + public int Id { get; set; } + /// /// Rule name for the Rule /// @@ -40,7 +45,7 @@ public class Rule [JsonConverter(typeof(StringEnumConverter))] public RuleExpressionType RuleExpressionType { get; set; } = RuleExpressionType.LambdaExpression; - public IEnumerable WorkflowRulesToInject { get; set; } + public IEnumerable WorkflowsToInject { get; set; } public IEnumerable Rules { get; set; } public IEnumerable LocalParams { get; set; } public string Expression { get; set; } diff --git a/src/RulesEngine/Models/WorkflowRule.cs b/src/RulesEngine/Models/Workflow.cs similarity index 69% rename from src/RulesEngine/Models/WorkflowRule.cs rename to src/RulesEngine/Models/Workflow.cs index c114b4fc..9141fea1 100644 --- a/src/RulesEngine/Models/WorkflowRule.cs +++ b/src/RulesEngine/Models/Workflow.cs @@ -1,17 +1,28 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace RulesEngine.Models { + [Obsolete("Workflow is now Workflow - Workflow will be removed in next major version")] + [ExcludeFromCodeCoverage] + public class WorkflowRule : Workflow { + } + /// /// Workflow rules class for deserialization the json config file /// [ExcludeFromCodeCoverage] - public class WorkflowRule + public class Workflow { + /// + /// Gets the workflow Id. + /// + public int Id { get; set; } + /// /// Gets the workflow name. /// @@ -19,7 +30,7 @@ public class WorkflowRule /// Gets or sets the workflow rules to inject. /// The workflow rules to inject. - public IEnumerable WorkflowRulesToInject { get; set; } + public IEnumerable WorkflowsToInject { get; set; } /// /// Gets or Sets the global params which will be applicable to all rules diff --git a/src/RulesEngine/RulesCache.cs b/src/RulesEngine/RulesCache.cs index ee112c4a..9b7f1e7e 100644 --- a/src/RulesEngine/RulesCache.cs +++ b/src/RulesEngine/RulesCache.cs @@ -16,20 +16,20 @@ internal class RulesCache private ConcurrentDictionary>, Int64)> _compileRules = new ConcurrentDictionary>, Int64)>(); /// The workflow rules - private ConcurrentDictionary _workflowRules = new ConcurrentDictionary(); + private ConcurrentDictionary _Workflows = new ConcurrentDictionary(); /// Determines whether [contains workflow rules] [the specified workflow name]. /// Name of the workflow. /// /// true if [contains workflow rules] [the specified workflow name]; otherwise, false. - public bool ContainsWorkflowRules(string workflowName) + public bool ContainsWorkflows(string workflowName) { - return _workflowRules.ContainsKey(workflowName); + return _Workflows.ContainsKey(workflowName); } public List GetAllWorkflowNames() { - return _workflowRules.Keys.ToList(); + return _Workflows.Keys.ToList(); } /// Determines whether [contains compiled rules] [the specified workflow name]. @@ -44,10 +44,10 @@ public bool ContainsCompiledRules(string workflowName) /// Adds the or update workflow rules. /// Name of the workflow. /// The rules. - public void AddOrUpdateWorkflowRules(string workflowName, WorkflowRule rules) + public void AddOrUpdateWorkflows(string workflowName, Workflow rules) { Int64 ticks = DateTime.UtcNow.Ticks; - _workflowRules.AddOrUpdate(workflowName, (rules, ticks), (k, v) => (rules, ticks)); + _Workflows.AddOrUpdate(workflowName, (rules, ticks), (k, v) => (rules, ticks)); } /// Adds the or update compiled rule. @@ -68,9 +68,9 @@ public bool AreCompiledRulesUpToDate(string compiledRuleKey, string workflowName { if (_compileRules.TryGetValue(compiledRuleKey, out (IDictionary> rules, Int64 tick) compiledRulesObj)) { - if (_workflowRules.TryGetValue(workflowName, out (WorkflowRule rules, Int64 tick) workflowRulesObj)) + if (_Workflows.TryGetValue(workflowName, out (Workflow rules, Int64 tick) WorkflowsObj)) { - return compiledRulesObj.tick >= workflowRulesObj.tick; + return compiledRulesObj.tick >= WorkflowsObj.tick; } } @@ -80,38 +80,38 @@ public bool AreCompiledRulesUpToDate(string compiledRuleKey, string workflowName /// Clears this instance. public void Clear() { - _workflowRules.Clear(); + _Workflows.Clear(); _compileRules.Clear(); } /// Gets the work flow rules. /// Name of the workflow. - /// WorkflowRules. + /// Workflows. /// Could not find injected Workflow: {wfname} - public WorkflowRule GetWorkFlowRules(string workflowName) + public Workflow GetWorkflows(string workflowName) { - if (_workflowRules.TryGetValue(workflowName, out (WorkflowRule rules, Int64 tick) workflowRulesObj)) + if (_Workflows.TryGetValue(workflowName, out (Workflow rules, Int64 tick) WorkflowsObj)) { - var workflowRules = workflowRulesObj.rules; - if (workflowRules.WorkflowRulesToInject?.Any() == true) + var Workflows = WorkflowsObj.rules; + if (Workflows.WorkflowsToInject?.Any() == true) { - if (workflowRules.Rules == null) + if (Workflows.Rules == null) { - workflowRules.Rules = new List(); + Workflows.Rules = new List(); } - foreach (string wfname in workflowRules.WorkflowRulesToInject) + foreach (string wfname in Workflows.WorkflowsToInject) { - var injectedWorkflow = GetWorkFlowRules(wfname); + var injectedWorkflow = GetWorkflows(wfname); if (injectedWorkflow == null) { throw new Exception($"Could not find injected Workflow: {wfname}"); } - workflowRules.Rules.ToList().AddRange(injectedWorkflow.Rules); + Workflows.Rules.ToList().AddRange(injectedWorkflow.Rules); } } - return workflowRules; + return Workflows; } else { @@ -132,7 +132,7 @@ public IDictionary> GetCompiledRules(string com /// Name of the workflow. public void Remove(string workflowName) { - if (_workflowRules.TryRemove(workflowName, out (WorkflowRule, Int64) workflowObj)) + if (_Workflows.TryRemove(workflowName, out (Workflow, Int64) workflowObj)) { var compiledKeysToRemove = _compileRules.Keys.Where(key => key.StartsWith(workflowName)); foreach (var key in compiledKeysToRemove) diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index 8cac2caa..a48195e4 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -39,13 +39,13 @@ public class RulesEngine : IRulesEngine #region Constructor public RulesEngine(string[] jsonConfig, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { - var workflowRules = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); - AddWorkflow(workflowRules); + var Workflows = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); + AddWorkflow(Workflows); } - public RulesEngine(WorkflowRule[] workflowRules, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) + public RulesEngine(Workflow[] Workflows, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { - AddWorkflow(workflowRules); + AddWorkflow(Workflows); } public RulesEngine(ILogger logger = null, ReSettings reSettings = null) @@ -157,23 +157,23 @@ private async ValueTask ExecuteActionForRuleResult(RuleResultT /// /// Adds the workflow if the workflow name is not already added. Ignores the rest. /// - /// The workflow rules. + /// The workflow rules. /// - public void AddWorkflow(params WorkflowRule[] workflowRules) + public void AddWorkflow(params Workflow[] Workflows) { try { - foreach (var workflowRule in workflowRules) + foreach (var Workflow in Workflows) { - var validator = new WorkflowRulesValidator(); - validator.ValidateAndThrow(workflowRule); - if (!_rulesCache.ContainsWorkflowRules(workflowRule.WorkflowName)) + var validator = new WorkflowsValidator(); + validator.ValidateAndThrow(Workflow); + if (!_rulesCache.ContainsWorkflows(Workflow.WorkflowName)) { - _rulesCache.AddOrUpdateWorkflowRules(workflowRule.WorkflowName, workflowRule); + _rulesCache.AddOrUpdateWorkflows(Workflow.WorkflowName, Workflow); } else { - throw new ValidationException($"Cannot add workflow `{workflowRule.WorkflowName}` as it already exists. Use `AddOrUpdateWorkflow` to update existing workflow"); + throw new ValidationException($"Cannot add workflow `{Workflow.WorkflowName}` as it already exists. Use `AddOrUpdateWorkflow` to update existing workflow"); } } } @@ -187,17 +187,17 @@ public void AddWorkflow(params WorkflowRule[] workflowRules) /// Adds new workflow rules if not previously added. /// Or updates the rules for an existing workflow. /// - /// The workflow rules. + /// The workflow rules. /// - public void AddOrUpdateWorkflow(params WorkflowRule[] workflowRules) + public void AddOrUpdateWorkflow(params Workflow[] Workflows) { try { - foreach (var workflowRule in workflowRules) + foreach (var Workflow in Workflows) { - var validator = new WorkflowRulesValidator(); - validator.ValidateAndThrow(workflowRule); - _rulesCache.AddOrUpdateWorkflowRules(workflowRule.WorkflowName, workflowRule); + var validator = new WorkflowsValidator(); + validator.ValidateAndThrow(Workflow); + _rulesCache.AddOrUpdateWorkflows(Workflow.WorkflowName, Workflow); } } catch (ValidationException ex) @@ -271,13 +271,13 @@ private bool RegisterRule(string workflowName, params RuleParameter[] ruleParams return true; } - var workflowRules = _rulesCache.GetWorkFlowRules(workflowName); - if (workflowRules != null) + var Workflows = _rulesCache.GetWorkflows(workflowName); + if (Workflows != null) { var dictFunc = new Dictionary>(); - foreach (var rule in workflowRules.Rules.Where(c => c.Enabled)) + foreach (var rule in Workflows.Rules.Where(c => c.Enabled)) { - dictFunc.Add(rule.RuleName, CompileRule(rule, ruleParams, workflowRules.GlobalParams?.ToArray())); + dictFunc.Add(rule.RuleName, CompileRule(rule, ruleParams, Workflows.GlobalParams?.ToArray())); } _rulesCache.AddOrUpdateCompiledRule(compileRulesKey, dictFunc); @@ -293,7 +293,7 @@ private bool RegisterRule(string workflowName, params RuleParameter[] ruleParams private RuleFunc CompileRule(string workflowName, string ruleName, RuleParameter[] ruleParameters) { - var workflow = _rulesCache.GetWorkFlowRules(workflowName); + var workflow = _rulesCache.GetWorkflows(workflowName); if(workflow == null) { throw new ArgumentException($"Workflow `{workflowName}` is not found"); diff --git a/src/RulesEngine/Validators/RuleValidator.cs b/src/RulesEngine/Validators/RuleValidator.cs index dbb40036..c14156e5 100644 --- a/src/RulesEngine/Validators/RuleValidator.cs +++ b/src/RulesEngine/Validators/RuleValidator.cs @@ -26,7 +26,7 @@ public RuleValidator() .WithMessage(Constants.OPERATOR_INCORRECT_ERRMSG); When(c => c.Rules?.Any() != true, () => { - RuleFor(c => c.WorkflowRulesToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG); + RuleFor(c => c.WorkflowsToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG); }) .Otherwise(() => { RuleFor(c => c.Rules).Must(BeValidRulesList); diff --git a/src/RulesEngine/Validators/WorkflowRulesValidator.cs b/src/RulesEngine/Validators/WorkflowRulesValidator.cs index 35cc1edb..3c46d31b 100644 --- a/src/RulesEngine/Validators/WorkflowRulesValidator.cs +++ b/src/RulesEngine/Validators/WorkflowRulesValidator.cs @@ -8,13 +8,13 @@ namespace RulesEngine.Validators { - internal class WorkflowRulesValidator : AbstractValidator + internal class WorkflowsValidator : AbstractValidator { - public WorkflowRulesValidator() + public WorkflowsValidator() { RuleFor(c => c.WorkflowName).NotEmpty().WithMessage(Constants.WORKFLOW_NAME_NULL_ERRMSG); When(c => c.Rules?.Any() != true, () => { - RuleFor(c => c.WorkflowRulesToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG); + RuleFor(c => c.WorkflowsToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG); }).Otherwise(() => { var ruleValidator = new RuleValidator(); RuleForEach(c => c.Rules).SetValidator(ruleValidator); diff --git a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs index 21720e0c..f4b4c7de 100644 --- a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs +++ b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs @@ -18,7 +18,7 @@ public class CustomActionTest [Fact] public async Task CustomActionOnRuleMustHaveContextValues() { - var workflows = GetWorkflowRules(); + var workflows = GetWorkflows(); var re = new RulesEngine(workflows, null, reSettings: new ReSettings { CustomActions = new Dictionary> { @@ -33,10 +33,10 @@ public async Task CustomActionOnRuleMustHaveContextValues() [Fact] public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues() { - var workflows = GetWorkflowRules(); + var workflows = GetWorkflows(); var workflowStr = JsonConvert.SerializeObject(workflows); var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }; - var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr,serializationOptions); + var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr,serializationOptions); var re = new RulesEngine(workflows, null, reSettings: new ReSettings { @@ -51,10 +51,10 @@ public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues() var result = await re.ExecuteAllRulesAsync("successReturnContextAction", true); } - private WorkflowRule[] GetWorkflowRules() + private Workflow[] GetWorkflows() { - return new WorkflowRule[] { - new WorkflowRule { + return new Workflow[] { + new Workflow { WorkflowName = "successReturnContextAction", Rules = new Rule[] { new Rule { diff --git a/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs b/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs index 86a53539..f931f367 100644 --- a/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs +++ b/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs @@ -46,16 +46,16 @@ public async Task ExecuteActionWorkflowAsync_CalledWithIncorrectWorkflowOrRuleNa public async Task ExecuteActionWorkflowAsync_CalledWithNoActionsInWorkflow_ExecutesSuccessfully() { - var engine = new RulesEngine(GetWorkflowRulesWithoutActions()); + var engine = new RulesEngine(GetWorkflowsWithoutActions()); var result = await engine.ExecuteActionWorkflowAsync("NoActionWorkflow", "NoActionTest", new RuleParameter[0]); Assert.NotNull(result); Assert.Null(result.Output); } - private WorkflowRule[] GetWorkflowRulesWithoutActions() + private Workflow[] GetWorkflowsWithoutActions() { - var workflow1 = new WorkflowRule { + var workflow1 = new Workflow { WorkflowName = "NoActionWorkflow", Rules = new List{ new Rule{ @@ -69,10 +69,10 @@ private WorkflowRule[] GetWorkflowRulesWithoutActions() return new[] { workflow1 }; } - private WorkflowRule[] GetWorkflowWithActions() + private Workflow[] GetWorkflowWithActions() { - var workflow1 = new WorkflowRule { + var workflow1 = new Workflow { WorkflowName = "ActionWorkflow", Rules = new List{ new Rule{ diff --git a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs index a3f76adf..e4fecb44 100644 --- a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs +++ b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs @@ -81,9 +81,9 @@ public async Task ExecuteRule_AddWorkflowWithSameName_ThrowsValidationException( Assert.Contains(result1, c => c.IsSuccess); // Fetch and add new rules. - var newWorkflowRules = ParseAsWorkflowRules(newWorkflowFile); + var newWorkflows = ParseAsWorkflows(newWorkflowFile); - Assert.Throws(() => re.AddWorkflow(newWorkflowRules)); + Assert.Throws(() => re.AddWorkflow(newWorkflows)); } [Theory] @@ -103,8 +103,8 @@ public async Task ExecuteRule_AddOrUpdateWorkflow_ExecutesUpdatedRules(string pr Assert.Contains(result1, c => c.IsSuccess); // Fetch and update new rules. - WorkflowRule newWorkflowRules = ParseAsWorkflowRules(newWorkflowFile); - re.AddOrUpdateWorkflow(newWorkflowRules); + Workflow newWorkflows = ParseAsWorkflows(newWorkflowFile); + re.AddOrUpdateWorkflow(newWorkflows); // Run new rules. List result2 = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3); @@ -257,12 +257,12 @@ public async Task ExecuteRule_ReturnsListOfRuleResultTree_ResultMessage(string r public void RulesEngine_New_IncorrectJSON_ThrowsException() { Assert.Throws(() => { - var workflow = new WorkflowRule(); + var workflow = new Workflow(); var re = CreateRulesEngine(workflow); }); Assert.Throws(() => { - var workflow = new WorkflowRule() { WorkflowName = "test" }; + var workflow = new Workflow() { WorkflowName = "test" }; var re = CreateRulesEngine(workflow); }); } @@ -356,7 +356,7 @@ public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Succes } var fileData = File.ReadAllText(files[0]); - var bre = new RulesEngine(JsonConvert.DeserializeObject(fileData), null); + var bre = new RulesEngine(JsonConvert.DeserializeObject(fileData), null); var result = await bre.ExecuteAllRulesAsync("inputWorkflow", ruleParams?.ToArray()); var ruleResult = result?.FirstOrDefault(r => string.Equals(r.Rule.RuleName, "GiveDiscount10", StringComparison.OrdinalIgnoreCase)); Assert.True(ruleResult.IsSuccess); @@ -515,7 +515,7 @@ public async Task ExecuteRuleWithIgnoreException_CompilationException_DoesNotRet public async Task ExecuteRule_RuntimeError_ShouldReturnAsErrorMessage() { - var workflow = new WorkflowRule { + var workflow = new Workflow { WorkflowName = "TestWorkflow", Rules = new[] { new Rule { @@ -542,7 +542,7 @@ public async Task ExecuteRule_RuntimeError_ShouldReturnAsErrorMessage() public async Task ExecuteRule_RuntimeError_ThrowsException() { - var workflow = new WorkflowRule { + var workflow = new Workflow { WorkflowName = "TestWorkflow", Rules = new[] { new Rule { @@ -567,7 +567,7 @@ public async Task ExecuteRule_RuntimeError_ThrowsException() public async Task ExecuteRule_RuntimeError_IgnoreException_DoesNotReturnException() { - var workflow = new WorkflowRule { + var workflow = new Workflow { WorkflowName = "TestWorkflow", Rules = new[] { new Rule { @@ -595,7 +595,7 @@ public async Task ExecuteRule_RuntimeError_IgnoreException_DoesNotReturnExceptio [Fact] public async Task RemoveWorkFlow_ShouldRemoveAllCompiledCache() { - var workflow = new WorkflowRule { + var workflow = new Workflow { WorkflowName = "Test", Rules = new Rule[]{ new Rule { @@ -629,7 +629,7 @@ public async Task RemoveWorkFlow_ShouldRemoveAllCompiledCache() [Fact] public async Task ClearWorkFlow_ShouldRemoveAllCompiledCache() { - var workflow = new WorkflowRule { + var workflow = new Workflow { WorkflowName = "Test", Rules = new Rule[]{ new Rule { @@ -663,7 +663,7 @@ public async Task ClearWorkFlow_ShouldRemoveAllCompiledCache() [Fact] public async Task ExecuteRule_WithNullInput_ShouldNotThrowException() { - var workflow = new WorkflowRule { + var workflow = new Workflow { WorkflowName = "Test", Rules = new Rule[]{ new Rule { @@ -697,7 +697,7 @@ public async Task ExecuteRule_WithNullInput_ShouldNotThrowException() [Fact] public async Task ExecuteRule_SpecialCharInWorkflowName_RunsSuccessfully() { - var workflow = new WorkflowRule { + var workflow = new Workflow { WorkflowName = "Exámple", Rules = new Rule[]{ new Rule { @@ -709,7 +709,7 @@ public async Task ExecuteRule_SpecialCharInWorkflowName_RunsSuccessfully() } }; - var workflowStr = "{\"WorkflowName\":\"Exámple\",\"WorkflowRulesToInject\":null,\"GlobalParams\":null,\"Rules\":[{\"RuleName\":\"RuleWithLocalParam\",\"Properties\":null,\"Operator\":null,\"ErrorMessage\":null,\"Enabled\":true,\"ErrorType\":\"Warning\",\"RuleExpressionType\":\"LambdaExpression\",\"WorkflowRulesToInject\":null,\"Rules\":null,\"LocalParams\":null,\"Expression\":\"input1 == null || input1.hello.world = \\\"wow\\\"\",\"Actions\":null,\"SuccessEvent\":null}]}"; + var workflowStr = "{\"WorkflowName\":\"Exámple\",\"WorkflowsToInject\":null,\"GlobalParams\":null,\"Rules\":[{\"RuleName\":\"RuleWithLocalParam\",\"Properties\":null,\"Operator\":null,\"ErrorMessage\":null,\"Enabled\":true,\"ErrorType\":\"Warning\",\"RuleExpressionType\":\"LambdaExpression\",\"WorkflowsToInject\":null,\"Rules\":null,\"LocalParams\":null,\"Expression\":\"input1 == null || input1.hello.world = \\\"wow\\\"\",\"Actions\":null,\"SuccessEvent\":null}]}"; var re = new RulesEngine(new string[] { workflowStr },null,null); @@ -740,7 +740,7 @@ public void Class_PublicMethods_ArePartOfInterface(Type classType, Type interfac - private RulesEngine CreateRulesEngine(WorkflowRule workflow) + private RulesEngine CreateRulesEngine(Workflow workflow) { var json = JsonConvert.SerializeObject(workflow); return new RulesEngine(new string[] { json }, null); @@ -750,9 +750,9 @@ private RulesEngine GetRulesEngine(string filename, ReSettings reSettings = null { var data = GetFileContent(filename); - var injectWorkflow = new WorkflowRule { + var injectWorkflow = new Workflow { WorkflowName = "inputWorkflowReference", - WorkflowRulesToInject = new List { "inputWorkflow" } + WorkflowsToInject = new List { "inputWorkflow" } }; var injectWorkflowStr = JsonConvert.SerializeObject(injectWorkflow); @@ -766,10 +766,10 @@ private string GetFileContent(string filename) return File.ReadAllText(filePath); } - private WorkflowRule ParseAsWorkflowRules(string workflowRulesFileName) + private Workflow ParseAsWorkflows(string WorkflowsFileName) { - string content = GetFileContent(workflowRulesFileName); - return JsonConvert.DeserializeObject(content); + string content = GetFileContent(WorkflowsFileName); + return JsonConvert.DeserializeObject(content); } private dynamic GetInput1() diff --git a/test/RulesEngine.UnitTest/EmptyRulesTest.cs b/test/RulesEngine.UnitTest/EmptyRulesTest.cs index 6b6d0eee..fe1e4cca 100644 --- a/test/RulesEngine.UnitTest/EmptyRulesTest.cs +++ b/test/RulesEngine.UnitTest/EmptyRulesTest.cs @@ -31,7 +31,7 @@ private async Task EmptyRules_ReturnsExepectedResults() Exception ex = await Assert.ThrowsAsync(action); - Assert.Contains("Atleast one of Rules or WorkflowRulesToInject must be not empty", ex.Message); + Assert.Contains("Atleast one of Rules or WorkflowsToInject must be not empty", ex.Message); } [Fact] private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults() @@ -47,13 +47,13 @@ private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults() Exception ex = await Assert.ThrowsAsync(action); - Assert.Contains("Atleast one of Rules or WorkflowRulesToInject must be not empty", ex.Message); + Assert.Contains("Atleast one of Rules or WorkflowsToInject must be not empty", ex.Message); } - private WorkflowRule[] GetEmptyWorkflows() + private Workflow[] GetEmptyWorkflows() { return new[] { - new WorkflowRule { + new Workflow { WorkflowName = "EmptyRulesTest", Rules = new Rule[] { } @@ -61,10 +61,10 @@ private WorkflowRule[] GetEmptyWorkflows() }; } - private WorkflowRule[] GetEmptyNestedWorkflows() + private Workflow[] GetEmptyNestedWorkflows() { return new[] { - new WorkflowRule { + new Workflow { WorkflowName = "EmptyNestedRulesTest", Rules = new Rule[] { new Rule { @@ -129,7 +129,7 @@ private WorkflowRule[] GetEmptyNestedWorkflows() } } }, - new WorkflowRule { + new Workflow { WorkflowName = "EmptyNestedRulesActionsTest", Rules = new Rule[] { new Rule { diff --git a/test/RulesEngine.UnitTest/NestedRulesTest.cs b/test/RulesEngine.UnitTest/NestedRulesTest.cs index 6a9fe909..a6dfa5a8 100644 --- a/test/RulesEngine.UnitTest/NestedRulesTest.cs +++ b/test/RulesEngine.UnitTest/NestedRulesTest.cs @@ -90,7 +90,7 @@ private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }; - var workflowsViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr, serializationOptions); + var workflowsViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr, serializationOptions); var reSettings = new ReSettings { }; var rulesEngine = new RulesEngine(workflowsViaTextJson, reSettings: reSettings); @@ -108,10 +108,10 @@ private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson - private WorkflowRule[] GetWorkflows() + private Workflow[] GetWorkflows() { return new[] { - new WorkflowRule { + new Workflow { WorkflowName = "NestedRulesTest", Rules = new Rule[] { new Rule { @@ -176,7 +176,7 @@ private WorkflowRule[] GetWorkflows() } } }, - new WorkflowRule { + new Workflow { WorkflowName = "NestedRulesActionsTest", Rules = new Rule[] { new Rule { diff --git a/test/RulesEngine.UnitTest/RulesEnabledTests.cs b/test/RulesEngine.UnitTest/RulesEnabledTests.cs index eee9bb8c..2bd7d5f0 100644 --- a/test/RulesEngine.UnitTest/RulesEnabledTests.cs +++ b/test/RulesEngine.UnitTest/RulesEnabledTests.cs @@ -95,10 +95,10 @@ private bool NestedEnabledCheck(IEnumerable ruleResults) return areAllRulesEnabled; } - private WorkflowRule[] GetWorkflows() + private Workflow[] GetWorkflows() { return new[] { - new WorkflowRule { + new Workflow { WorkflowName = "RuleEnabledFeatureTest", Rules = new List { new Rule { @@ -118,7 +118,7 @@ private WorkflowRule[] GetWorkflows() } }, - new WorkflowRule { + new Workflow { WorkflowName = "RuleEnabledNestedFeatureTest", Rules = new List { new Rule { diff --git a/test/RulesEngine.UnitTest/ScopedParamsTest.cs b/test/RulesEngine.UnitTest/ScopedParamsTest.cs index dc01231e..5ddcf740 100644 --- a/test/RulesEngine.UnitTest/ScopedParamsTest.cs +++ b/test/RulesEngine.UnitTest/ScopedParamsTest.cs @@ -22,9 +22,9 @@ public class ScopedParamsTest [InlineData("GlobalParamReferencedInNextGlobalParams")] [InlineData("LocalParamReferencedInNextLocalParams")] [InlineData("GlobalParamAndLocalParamsInNestedRules")] - public async Task BasicWorkflowRules_ReturnsTrue(string workflowName) + public async Task BasicWorkflows_ReturnsTrue(string workflowName) { - var workflows = GetWorkflowRulesList(); + var workflows = GetWorkflowsList(); var engine = new RulesEngine(null, null); engine.AddWorkflow(workflows); @@ -45,7 +45,7 @@ public async Task BasicWorkflowRules_ReturnsTrue(string workflowName) [InlineData("GlobalAndLocalParams")] public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName) { - var workflows = GetWorkflowRulesList(); + var workflows = GetWorkflowsList(); var engine = new RulesEngine(null, null); engine.AddWorkflow(workflows); @@ -75,7 +75,7 @@ public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName) [InlineData("GlobalAndLocalParams", new[] { false })] public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] outputs) { - var workflows = GetWorkflowRulesList(); + var workflows = GetWorkflowsList(); var engine = new RulesEngine(new string[] { }, null, new ReSettings { EnableScopedParams = false @@ -103,7 +103,7 @@ public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] [InlineData("LocalParamsOnly2")] public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName) { - var workflows = GetWorkflowRulesList(); + var workflows = GetWorkflowsList(); var engine = new RulesEngine(new string[] { }, null); engine.AddWorkflow(workflows); @@ -123,7 +123,7 @@ public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowN [InlineData("LocalParamsOnlyWithComplexInput")] public async Task RuntimeErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName) { - var workflows = GetWorkflowRulesList(); + var workflows = GetWorkflowsList(); var engine = new RulesEngine(new string[] { }, null); engine.AddWorkflow(workflows); @@ -143,7 +143,7 @@ public async Task RuntimeErrorInScopedParam_ShouldAppearAsErrorMessage(string wo private void CheckResultTreeContainsAllInputs(string workflowName, List result) { - var workflow = GetWorkflowRulesList().Single(c => c.WorkflowName == workflowName); + var workflow = GetWorkflowsList().Single(c => c.WorkflowName == workflowName); var expectedInputs = new List() { "input1" }; expectedInputs.AddRange(workflow.GlobalParams?.Select(c => c.Name) ?? new List()); @@ -176,10 +176,10 @@ private static void CheckInputs(IEnumerable expectedInputs, RuleResultTr } } - private WorkflowRule[] GetWorkflowRulesList() + private Workflow[] GetWorkflowsList() { - return new WorkflowRule[] { - new WorkflowRule { + return new Workflow[] { + new Workflow { WorkflowName = "NoLocalAndGlobalParams", Rules = new List { new Rule { @@ -188,7 +188,7 @@ private WorkflowRule[] GetWorkflowRulesList() } } }, - new WorkflowRule { + new Workflow { WorkflowName = "LocalParamsOnly", Rules = new List { new Rule { @@ -209,7 +209,7 @@ private WorkflowRule[] GetWorkflowRulesList() }, } }, - new WorkflowRule { + new Workflow { WorkflowName = "LocalParamsOnly2", Rules = new List { new Rule { @@ -226,7 +226,7 @@ private WorkflowRule[] GetWorkflowRulesList() } }, - new WorkflowRule { + new Workflow { WorkflowName = "GlobalParamsOnly", GlobalParams = new List { new ScopedParam { @@ -241,7 +241,7 @@ private WorkflowRule[] GetWorkflowRulesList() } } }, - new WorkflowRule { + new Workflow { WorkflowName = "GlobalAndLocalParams", GlobalParams = new List { new ScopedParam { @@ -263,7 +263,7 @@ private WorkflowRule[] GetWorkflowRulesList() } }, - new WorkflowRule { + new Workflow { WorkflowName = "GlobalParamReferencedInLocalParams", GlobalParams = new List { new ScopedParam { @@ -285,7 +285,7 @@ private WorkflowRule[] GetWorkflowRulesList() }, } }, - new WorkflowRule { + new Workflow { WorkflowName = "GlobalParamReferencedInNextGlobalParams", GlobalParams = new List { new ScopedParam { @@ -304,7 +304,7 @@ private WorkflowRule[] GetWorkflowRulesList() }, } }, - new WorkflowRule { + new Workflow { WorkflowName = "LocalParamReferencedInNextLocalParams", Rules = new List { new Rule { @@ -323,7 +323,7 @@ private WorkflowRule[] GetWorkflowRulesList() }, } }, - new WorkflowRule { + new Workflow { WorkflowName = "GlobalParamAndLocalParamsInNestedRules", GlobalParams = new List { new ScopedParam { @@ -362,7 +362,7 @@ private WorkflowRule[] GetWorkflowRulesList() } } }, - new WorkflowRule { + new Workflow { WorkflowName = "LocalParamsOnlyWithComplexInput", Rules = new List { new Rule { @@ -378,7 +378,7 @@ private WorkflowRule[] GetWorkflowRulesList() } } }, - new WorkflowRule { + new Workflow { WorkflowName = "GlobalParamsOnlyWithComplexInput", GlobalParams = new List { new ScopedParam { From 01d6018ec02b4ddc07b5552e42740aa43a31b2e7 Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 29 Jul 2021 11:46:10 -0700 Subject: [PATCH 05/16] Update README.md --- README.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b4d7c92d..9126bfc1 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,9 @@ Rules Engine is a library/NuGet package for abstracting business logic/rules/pol To install this library, please download the latest version of [NuGet Package](https://www.nuget.org/packages/RulesEngine/) from [nuget.org](https://www.nuget.org/) and refer it into your project. ## How to use it +There are several ways to populate workflows for the Rules Engine as listed below. -You need to store the rules based on the [schema definition](https://github.com/microsoft/RulesEngine/blob/main/schema/workflowRules-schema.json) given and they can be stored in any store as deemed appropriate like Azure Blob Storage, Cosmos DB, Azure App Configuration, SQL Servers, file systems etc. The expressions are supposed to be a [lambda expressions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions). +You need to store the rules based on the [schema definition](https://github.com/microsoft/RulesEngine/blob/main/schema/workflowRules-schema.json) given and they can be stored in any store as deemed appropriate like Azure Blob Storage, Cosmos DB, Azure App Configuration, [Entity Framework](https://github.com/microsoft/RulesEngine#entity-framework), SQL Servers, file systems etc. The expressions are supposed to be a [lambda expressions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions). An example rule could be - ```json @@ -52,7 +53,7 @@ Once done, the Rules Engine needs to execute the rules for a given input. It can ```c# List response = await rulesEngine.ExecuteAllRulesAsync(workflowName, input); ``` -Here, *workflowName* is the name of the workflow, which is *Discount* in the above mentioned example. And *input* is the object which needs to be checked against the rules. +Here, *workflowName* is the name of the workflow, which is *Discount* in the above mentioned example. And *input* is the object which needs to be checked against the rules, which itself may consist of a list of class instances. The *response* will contain a list of [*RuleResultTree*](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#ruleresulttree) which gives information if a particular rule passed or failed. @@ -61,6 +62,36 @@ _Note: A detailed example showcasing how to use Rules Engine is explained in [Ge _A demo app for the is available at [this location](https://github.com/microsoft/RulesEngine/tree/main/demo)._ +### Basic +A simple example via code only is as follows: +```c# +List rules = new List(); + +Rule rule = new Rule(); +rule.RuleName = "Test Rule"; +rule.SuccessEvent = "Count is within tolerance."; +rule.ErrorMessage = "Over expected."; +rule.Expression = "count < 3"; +rule.RuleExpressionType = RuleExpressionType.LambdaExpression; + +rules.Add(rule); + +workflowRule.Rules = rules; + +workFlowRules.Add(workflowRule); + +var bre = new RulesEngine.RulesEngine(workFlowRules.ToArray(), null); +``` + + +### Entity Framework +Consuming Entity Framework and populating the Rules Engine is shown in the [EFDemo class](https://github.com/microsoft/RulesEngine/blob/main/demo/DemoApp/EFDemo.cs) with Workflow rules populating the array and passed to the Rules Engine, The Demo App includes an example [RulesEngineDemoContext](https://github.com/microsoft/RulesEngine/blob/main/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs) using SQLite and could be swapped out for another provider. +```c# +var wfr = db.WorkflowRules.Include(i => i.Rules).ThenInclude(i => i.Rules).ToArray(); +var bre = new RulesEngine.RulesEngine(wfr, null); +``` +*Note: For each level of nested rules expected, a ThenInclude query appended will be needed as shown above.* + ## How it works ![](https://github.com/microsoft/RulesEngine/blob/main/assets/BlockDiagram.png) From a863d96ce16bd55181ffb4b6c63863a08f09f3e7 Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 29 Jul 2021 11:50:40 -0700 Subject: [PATCH 06/16] Reverted RuleActions --- demo/DemoApp.EFDataExample/RulesEngineContext.cs | 6 +++--- src/RulesEngine/Models/Rule.cs | 2 +- src/RulesEngine/Models/{RuleAction.cs => RuleActions.cs} | 2 +- test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs | 2 +- .../ActionTests/RulesEngineWithActionsTests.cs | 4 ++-- test/RulesEngine.UnitTest/EmptyRulesTest.cs | 2 +- test/RulesEngine.UnitTest/NestedRulesTest.cs | 6 +++--- 7 files changed, 12 insertions(+), 12 deletions(-) rename src/RulesEngine/Models/{RuleAction.cs => RuleActions.cs} (90%) diff --git a/demo/DemoApp.EFDataExample/RulesEngineContext.cs b/demo/DemoApp.EFDataExample/RulesEngineContext.cs index e4375e6b..1ae7eac4 100644 --- a/demo/DemoApp.EFDataExample/RulesEngineContext.cs +++ b/demo/DemoApp.EFDataExample/RulesEngineContext.cs @@ -11,7 +11,7 @@ public class RulesEngineContext : DbContext public DbSet Workflows { get; set; } public DbSet ActionInfos { get; set; } - public DbSet RuleActions { get; set; } + public DbSet RuleActions { get; set; } public DbSet Rules { get; set; } public DbSet ScopedParams { get; set; } @@ -21,7 +21,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Ignore(); modelBuilder.Ignore(); - modelBuilder.Ignore(); + modelBuilder.Ignore(); modelBuilder.Entity(entity => { entity.HasKey(k => k.Id); @@ -42,7 +42,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(p => p.Actions) .HasConversion( v => JsonSerializer.Serialize(v, null), - v => JsonSerializer.Deserialize(v, null)); + v => JsonSerializer.Deserialize(v, null)); entity.Ignore(b => b.WorkflowsToInject); }); diff --git a/src/RulesEngine/Models/Rule.cs b/src/RulesEngine/Models/Rule.cs index d8cebf1d..e2d4c422 100644 --- a/src/RulesEngine/Models/Rule.cs +++ b/src/RulesEngine/Models/Rule.cs @@ -49,7 +49,7 @@ public class Rule public IEnumerable Rules { get; set; } public IEnumerable LocalParams { get; set; } public string Expression { get; set; } - public RuleAction Actions { get; set; } + public RuleActions Actions { get; set; } public string SuccessEvent { get; set; } } diff --git a/src/RulesEngine/Models/RuleAction.cs b/src/RulesEngine/Models/RuleActions.cs similarity index 90% rename from src/RulesEngine/Models/RuleAction.cs rename to src/RulesEngine/Models/RuleActions.cs index f208cb6e..cc5f50ca 100644 --- a/src/RulesEngine/Models/RuleAction.cs +++ b/src/RulesEngine/Models/RuleActions.cs @@ -6,7 +6,7 @@ namespace RulesEngine.Models { [ExcludeFromCodeCoverage] - public class RuleAction + public class RuleActions { public ActionInfo OnSuccess { get; set; } public ActionInfo OnFailure { get; set; } diff --git a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs index f4b4c7de..371c362c 100644 --- a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs +++ b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs @@ -60,7 +60,7 @@ private Workflow[] GetWorkflows() new Rule { RuleName = "trueRule", Expression = "input1 == true", - Actions = new RuleAction() { + Actions = new RuleActions() { OnSuccess = new ActionInfo { Name = "ReturnContext", Context = new Dictionary { diff --git a/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs b/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs index f931f367..9fa03742 100644 --- a/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs +++ b/test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs @@ -79,7 +79,7 @@ private Workflow[] GetWorkflowWithActions() RuleName = "ExpressionOutputRuleTest", RuleExpressionType = RuleExpressionType.LambdaExpression, Expression = "1 == 1", - Actions = new RuleAction{ + Actions = new RuleActions{ OnSuccess = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary{ @@ -92,7 +92,7 @@ private Workflow[] GetWorkflowWithActions() RuleName = "EvaluateRuleTest", RuleExpressionType = RuleExpressionType.LambdaExpression, Expression = "1 == 1", - Actions = new RuleAction{ + Actions = new RuleActions{ OnSuccess = new ActionInfo{ Name = "EvaluateRule", Context = new Dictionary{ diff --git a/test/RulesEngine.UnitTest/EmptyRulesTest.cs b/test/RulesEngine.UnitTest/EmptyRulesTest.cs index fe1e4cca..e756bbf5 100644 --- a/test/RulesEngine.UnitTest/EmptyRulesTest.cs +++ b/test/RulesEngine.UnitTest/EmptyRulesTest.cs @@ -138,7 +138,7 @@ private Workflow[] GetEmptyNestedWorkflows() Rules = new Rule[] { }, - Actions = new RuleAction { + Actions = new RuleActions { OnFailure = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { diff --git a/test/RulesEngine.UnitTest/NestedRulesTest.cs b/test/RulesEngine.UnitTest/NestedRulesTest.cs index a6dfa5a8..e2c5758d 100644 --- a/test/RulesEngine.UnitTest/NestedRulesTest.cs +++ b/test/RulesEngine.UnitTest/NestedRulesTest.cs @@ -186,7 +186,7 @@ private Workflow[] GetWorkflows() new Rule{ RuleName = "trueRule1", Expression = "input1.TrueValue == true", - Actions = new RuleAction { + Actions = new RuleActions { OnSuccess = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { @@ -198,7 +198,7 @@ private Workflow[] GetWorkflows() new Rule { RuleName = "falseRule1", Expression = "input1.TrueValue == false", - Actions = new RuleAction { + Actions = new RuleActions { OnFailure = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { @@ -208,7 +208,7 @@ private Workflow[] GetWorkflows() } } }, - Actions = new RuleAction { + Actions = new RuleActions { OnFailure = new ActionInfo{ Name = "OutputExpression", Context = new Dictionary { From 4ab6bc1a2b7798829b3e7a60ec6868d420a6e71a Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 29 Jul 2021 12:15:37 -0700 Subject: [PATCH 07/16] Cleanup --- demo/DemoApp/BasicDemo.cs | 57 ++++++++++++++++-------------- src/RulesEngine/Models/Workflow.cs | 2 +- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/demo/DemoApp/BasicDemo.cs b/demo/DemoApp/BasicDemo.cs index 5435b9c9..8389db2f 100644 --- a/demo/DemoApp/BasicDemo.cs +++ b/demo/DemoApp/BasicDemo.cs @@ -1,13 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using RulesEngine.Models; using System; using System.Collections.Generic; using System.Dynamic; -using System.IO; using static RulesEngine.Extensions.ListofRuleResultTreeExtension; namespace DemoApp @@ -17,45 +14,51 @@ public class BasicDemo public void Run() { Console.WriteLine($"Running {nameof(BasicDemo)}...."); - var basicInfo = "{\"name\": \"hello\",\"email\": \"abcy@xyz.com\",\"creditHistory\": \"good\",\"country\": \"canada\",\"loyalityFactor\": 3,\"totalPurchasesToDate\": 10000}"; - var orderInfo = "{\"totalOrders\": 5,\"recurringItems\": 2}"; - var telemetryInfo = "{\"noOfVisitsPerMonth\": 10,\"percentageOfBuyingToVisit\": 15}"; + List Workflow = new List(); + Workflow workflowRule = new Workflow(); + workflowRule.WorkflowName = "Test Workflow Rule 1"; - var converter = new ExpandoObjectConverter(); + List rules = new List(); - dynamic input1 = JsonConvert.DeserializeObject(basicInfo, converter); - dynamic input2 = JsonConvert.DeserializeObject(orderInfo, converter); - dynamic input3 = JsonConvert.DeserializeObject(telemetryInfo, converter); + Rule rule = new Rule(); + rule.RuleName = "Test Rule"; + rule.SuccessEvent = "Count is within tolerance."; + rule.ErrorMessage = "Over expected."; + rule.Expression = "count < 3"; + rule.RuleExpressionType = RuleExpressionType.LambdaExpression; - var inputs = new dynamic[] - { - input1, - input2, - input3 - }; + rules.Add(rule); + + workflowRule.Rules = rules; - var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "Discount.json", SearchOption.AllDirectories); - if (files == null || files.Length == 0) - throw new Exception("Rules not found."); + Workflow.Add(workflowRule); - var fileData = File.ReadAllText(files[0]); - var Workflows = JsonConvert.DeserializeObject>(fileData); + var bre = new RulesEngine.RulesEngine(Workflow.ToArray(), null); + + dynamic datas = new ExpandoObject(); + datas.count = 1; + var inputs = new dynamic[] + { + datas + }; - var bre = new RulesEngine.RulesEngine(Workflows.ToArray(), null); + List resultList = bre.ExecuteAllRulesAsync("Test Workflow Rule 1", inputs).Result; - string discountOffered = "No discount offered."; + bool outcome = false; - List resultList = bre.ExecuteAllRulesAsync("Discount", inputs).Result; + //Different ways to show test results: + outcome = resultList.TrueForAll(r => r.IsSuccess); resultList.OnSuccess((eventName) => { - discountOffered = $"Discount offered is {eventName} % over MRP."; + Console.WriteLine($"Result '{eventName}' is as expected."); + outcome = true; }); resultList.OnFail(() => { - discountOffered = "The user is not eligible for any discount."; + outcome = false; }); - Console.WriteLine(discountOffered); + Console.WriteLine($"Test outcome: {outcome}."); } } } diff --git a/src/RulesEngine/Models/Workflow.cs b/src/RulesEngine/Models/Workflow.cs index 9141fea1..82a4e5ff 100644 --- a/src/RulesEngine/Models/Workflow.cs +++ b/src/RulesEngine/Models/Workflow.cs @@ -9,7 +9,7 @@ namespace RulesEngine.Models { [Obsolete("Workflow is now Workflow - Workflow will be removed in next major version")] [ExcludeFromCodeCoverage] - public class WorkflowRule : Workflow { + public class WorkflowRules : Workflow { } /// From c84725191019eb29c121aa8908f1b0d9931a5d11 Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 29 Jul 2021 12:21:01 -0700 Subject: [PATCH 08/16] Cleanup --- demo/DemoApp/BasicDemo.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/demo/DemoApp/BasicDemo.cs b/demo/DemoApp/BasicDemo.cs index 8389db2f..c2ca31d0 100644 --- a/demo/DemoApp/BasicDemo.cs +++ b/demo/DemoApp/BasicDemo.cs @@ -15,8 +15,8 @@ public void Run() { Console.WriteLine($"Running {nameof(BasicDemo)}...."); List Workflow = new List(); - Workflow workflowRule = new Workflow(); - workflowRule.WorkflowName = "Test Workflow Rule 1"; + Workflow workflow = new Workflow(); + workflow.WorkflowName = "Test Workflow Rule 1"; List rules = new List(); @@ -29,9 +29,9 @@ public void Run() rules.Add(rule); - workflowRule.Rules = rules; + workflow.Rules = rules; - Workflow.Add(workflowRule); + Workflow.Add(workflow); var bre = new RulesEngine.RulesEngine(Workflow.ToArray(), null); From c6fe833af2479e039970f9ea250764e00c2c350b Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 29 Jul 2021 12:22:01 -0700 Subject: [PATCH 09/16] Cleanup --- demo/DemoApp/BasicDemo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/demo/DemoApp/BasicDemo.cs b/demo/DemoApp/BasicDemo.cs index c2ca31d0..b500c72f 100644 --- a/demo/DemoApp/BasicDemo.cs +++ b/demo/DemoApp/BasicDemo.cs @@ -14,7 +14,7 @@ public class BasicDemo public void Run() { Console.WriteLine($"Running {nameof(BasicDemo)}...."); - List Workflow = new List(); + List workflows = new List(); Workflow workflow = new Workflow(); workflow.WorkflowName = "Test Workflow Rule 1"; @@ -31,9 +31,9 @@ public void Run() workflow.Rules = rules; - Workflow.Add(workflow); + workflows.Add(workflow); - var bre = new RulesEngine.RulesEngine(Workflow.ToArray(), null); + var bre = new RulesEngine.RulesEngine(workflows.ToArray(), null); dynamic datas = new ExpandoObject(); datas.count = 1; From b4b352966bf9c6c44ecead3ad650a585c35e050b Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 5 Aug 2021 06:20:10 -0700 Subject: [PATCH 10/16] Cleanup --- CHANGELOG.md | 4 +- benchmark/RulesEngineBenchmark/Program.cs | 8 ++-- demo/DemoApp/EFDemo.cs | 4 +- demo/DemoApp/JSONDemo.cs | 4 +- src/RulesEngine/Interfaces/IRulesEngine.cs | 10 ++--- src/RulesEngine/Models/Rule.cs | 7 ++++ src/RulesEngine/Models/Workflow.cs | 13 ++++-- src/RulesEngine/RulesCache.cs | 34 +++++++-------- src/RulesEngine/RulesEngine.cs | 42 +++++++++---------- .../ActionTests/CustomActionTest.cs | 12 +++--- .../BusinessRuleEngineTest.cs | 42 +++++++++---------- test/RulesEngine.UnitTest/EmptyRulesTest.cs | 10 ++--- test/RulesEngine.UnitTest/NestedRulesTest.cs | 18 ++++---- .../RulesEngine.UnitTest/RulesEnabledTests.cs | 10 ++--- test/RulesEngine.UnitTest/ScopedParamsTest.cs | 30 ++++++------- 15 files changed, 130 insertions(+), 118 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e83c06ac..01a73704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,12 +24,12 @@ Breaking Change: - Enabled localParams support for nested Rules - Made certain fields in Rule model optional allowing users to define workflow with minimal fields - Added option to disable Rule in workflow json -- Added `GetAllRegisteredWorkflow` to RulesEngine to return all registeredWorkflows +- Added `GetAllRegisteredWorkflow` to RulesEngine to return all registered workflows - Runtime errors for expressions will now be logged as errorMessage instead of throwing Exceptions by default - Fixed RuleParameter passed as null ## [3.0.2] -- Fixed LocalParams cache not getting cleaned up when RemoveWorkflow and ClearWorkflows are called +- Fixed LocalParams cache not getting cleaned up when RemoveWorkflows and ClearWorkflows are called ## [3.0.1] - Moved ActionResult and ActionRuleResult under RulesEngine.Models namespace diff --git a/benchmark/RulesEngineBenchmark/Program.cs b/benchmark/RulesEngineBenchmark/Program.cs index d35b442c..6bbfe2f8 100644 --- a/benchmark/RulesEngineBenchmark/Program.cs +++ b/benchmark/RulesEngineBenchmark/Program.cs @@ -16,7 +16,7 @@ public class REBenchmark { private readonly RulesEngine.RulesEngine rulesEngine; private readonly object ruleInput; - private readonly List workflows; + private readonly List workflow; private class ListItem { @@ -34,9 +34,9 @@ public REBenchmark() } var fileData = File.ReadAllText(files[0]); - workflows = JsonConvert.DeserializeObject>(fileData); + workflow = JsonConvert.DeserializeObject>(fileData); - rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null, new ReSettings { + rulesEngine = new RulesEngine.RulesEngine(workflow.ToArray(), null, new ReSettings { EnableFormattedErrorMessage = false, EnableScopedParams = false }); @@ -69,7 +69,7 @@ public REBenchmark() [Benchmark] public void RuleExecutionDefault() { - foreach (var workflow in workflows) + foreach (var workflow in workflow) { _ = rulesEngine.ExecuteAllRulesAsync(workflow.WorkflowName, ruleInput).Result; } diff --git a/demo/DemoApp/EFDemo.cs b/demo/DemoApp/EFDemo.cs index 299aaa60..60c00ef5 100644 --- a/demo/DemoApp/EFDemo.cs +++ b/demo/DemoApp/EFDemo.cs @@ -42,12 +42,12 @@ public void Run() throw new Exception("Rules not found."); var fileData = File.ReadAllText(files[0]); - var Workflows = JsonConvert.DeserializeObject>(fileData); + var workflow = JsonConvert.DeserializeObject>(fileData); RulesEngineDemoContext db = new RulesEngineDemoContext(); if (db.Database.EnsureCreated()) { - db.Workflows.AddRange(Workflows); + db.Workflows.AddRange(workflow); db.SaveChanges(); } diff --git a/demo/DemoApp/JSONDemo.cs b/demo/DemoApp/JSONDemo.cs index 926d391d..042c37f5 100644 --- a/demo/DemoApp/JSONDemo.cs +++ b/demo/DemoApp/JSONDemo.cs @@ -39,9 +39,9 @@ public void Run() throw new Exception("Rules not found."); var fileData = File.ReadAllText(files[0]); - var workflows = JsonConvert.DeserializeObject>(fileData); + var workflow = JsonConvert.DeserializeObject>(fileData); - var bre = new RulesEngine.RulesEngine(workflows.ToArray(), null); + var bre = new RulesEngine.RulesEngine(workflow.ToArray(), null); string discountOffered = "No discount offered."; diff --git a/src/RulesEngine/Interfaces/IRulesEngine.cs b/src/RulesEngine/Interfaces/IRulesEngine.cs index 4ca8477a..65c3c8c0 100644 --- a/src/RulesEngine/Interfaces/IRulesEngine.cs +++ b/src/RulesEngine/Interfaces/IRulesEngine.cs @@ -30,18 +30,18 @@ public interface IRulesEngine /// Adds new workflows to RulesEngine /// /// - void AddWorkflow(params Workflow[] Workflows); + void AddWorkflows(params Workflow[] Workflows); /// - /// Removes all registered workflows from RulesEngine + /// Removes registered workflow from RulesEngine /// - void ClearWorkflows(); + void ClearWorkflow(); /// - /// Removes the workflow from RulesEngine + /// Removes the workflows from RulesEngine /// /// - void RemoveWorkflow(params string[] workflowNames); + void RemoveWorkflows(params string[] workflowNames); /// /// Returns the list of all registered workflow names diff --git a/src/RulesEngine/Models/Rule.cs b/src/RulesEngine/Models/Rule.cs index e2d4c422..19ccdabf 100644 --- a/src/RulesEngine/Models/Rule.cs +++ b/src/RulesEngine/Models/Rule.cs @@ -45,7 +45,14 @@ public class Rule [JsonConverter(typeof(StringEnumConverter))] public RuleExpressionType RuleExpressionType { get; set; } = RuleExpressionType.LambdaExpression; + + [Obsolete("WorkflowRulesToInject is deprecated. Use WorkflowsToInject instead.")] + public IEnumerable WorkflowRulesToInject { + get { return WorkflowsToInject; } + set { WorkflowsToInject = value; } + } public IEnumerable WorkflowsToInject { get; set; } + public IEnumerable Rules { get; set; } public IEnumerable LocalParams { get; set; } public string Expression { get; set; } diff --git a/src/RulesEngine/Models/Workflow.cs b/src/RulesEngine/Models/Workflow.cs index 82a4e5ff..6d553b90 100644 --- a/src/RulesEngine/Models/Workflow.cs +++ b/src/RulesEngine/Models/Workflow.cs @@ -7,7 +7,7 @@ namespace RulesEngine.Models { - [Obsolete("Workflow is now Workflow - Workflow will be removed in next major version")] + [Obsolete("WorkflowRules class is deprecated. Use Workflow class instead.")] [ExcludeFromCodeCoverage] public class WorkflowRules : Workflow { } @@ -28,9 +28,14 @@ public class Workflow /// public string WorkflowName { get; set; } - /// Gets or sets the workflow rules to inject. - /// The workflow rules to inject. - public IEnumerable WorkflowsToInject { get; set; } + /// Gets or sets the workflow rules to inject. + /// The workflow rules to inject. + [Obsolete("WorkflowRulesToInject is deprecated. Use WorkflowsToInject instead.")] + public IEnumerable WorkflowRulesToInject { + get { return WorkflowsToInject; } + set { WorkflowsToInject = value; } + } + public IEnumerable WorkflowsToInject { get; set; } /// /// Gets or Sets the global params which will be applicable to all rules diff --git a/src/RulesEngine/RulesCache.cs b/src/RulesEngine/RulesCache.cs index 9b7f1e7e..256fcf36 100644 --- a/src/RulesEngine/RulesCache.cs +++ b/src/RulesEngine/RulesCache.cs @@ -16,7 +16,7 @@ internal class RulesCache private ConcurrentDictionary>, Int64)> _compileRules = new ConcurrentDictionary>, Int64)>(); /// The workflow rules - private ConcurrentDictionary _Workflows = new ConcurrentDictionary(); + private ConcurrentDictionary _workflow = new ConcurrentDictionary(); /// Determines whether [contains workflow rules] [the specified workflow name]. /// Name of the workflow. @@ -24,12 +24,12 @@ internal class RulesCache /// true if [contains workflow rules] [the specified workflow name]; otherwise, false. public bool ContainsWorkflows(string workflowName) { - return _Workflows.ContainsKey(workflowName); + return _workflow.ContainsKey(workflowName); } public List GetAllWorkflowNames() { - return _Workflows.Keys.ToList(); + return _workflow.Keys.ToList(); } /// Determines whether [contains compiled rules] [the specified workflow name]. @@ -47,7 +47,7 @@ public bool ContainsCompiledRules(string workflowName) public void AddOrUpdateWorkflows(string workflowName, Workflow rules) { Int64 ticks = DateTime.UtcNow.Ticks; - _Workflows.AddOrUpdate(workflowName, (rules, ticks), (k, v) => (rules, ticks)); + _workflow.AddOrUpdate(workflowName, (rules, ticks), (k, v) => (rules, ticks)); } /// Adds the or update compiled rule. @@ -68,7 +68,7 @@ public bool AreCompiledRulesUpToDate(string compiledRuleKey, string workflowName { if (_compileRules.TryGetValue(compiledRuleKey, out (IDictionary> rules, Int64 tick) compiledRulesObj)) { - if (_Workflows.TryGetValue(workflowName, out (Workflow rules, Int64 tick) WorkflowsObj)) + if (_workflow.TryGetValue(workflowName, out (Workflow rules, Int64 tick) WorkflowsObj)) { return compiledRulesObj.tick >= WorkflowsObj.tick; } @@ -80,7 +80,7 @@ public bool AreCompiledRulesUpToDate(string compiledRuleKey, string workflowName /// Clears this instance. public void Clear() { - _Workflows.Clear(); + _workflow.Clear(); _compileRules.Clear(); } @@ -88,30 +88,30 @@ public void Clear() /// Name of the workflow. /// Workflows. /// Could not find injected Workflow: {wfname} - public Workflow GetWorkflows(string workflowName) + public Workflow GetWorkflow(string workflowName) { - if (_Workflows.TryGetValue(workflowName, out (Workflow rules, Int64 tick) WorkflowsObj)) + if (_workflow.TryGetValue(workflowName, out (Workflow rules, Int64 tick) WorkflowsObj)) { - var Workflows = WorkflowsObj.rules; - if (Workflows.WorkflowsToInject?.Any() == true) + var workflow = WorkflowsObj.rules; + if (workflow.WorkflowsToInject?.Any() == true) { - if (Workflows.Rules == null) + if (workflow.Rules == null) { - Workflows.Rules = new List(); + workflow.Rules = new List(); } - foreach (string wfname in Workflows.WorkflowsToInject) + foreach (string wfname in workflow.WorkflowsToInject) { - var injectedWorkflow = GetWorkflows(wfname); + var injectedWorkflow = GetWorkflow(wfname); if (injectedWorkflow == null) { throw new Exception($"Could not find injected Workflow: {wfname}"); } - Workflows.Rules.ToList().AddRange(injectedWorkflow.Rules); + workflow.Rules.ToList().AddRange(injectedWorkflow.Rules); } } - return Workflows; + return workflow; } else { @@ -132,7 +132,7 @@ public IDictionary> GetCompiledRules(string com /// Name of the workflow. public void Remove(string workflowName) { - if (_Workflows.TryRemove(workflowName, out (Workflow, Int64) workflowObj)) + if (_workflow.TryRemove(workflowName, out (Workflow, Int64) workflowObj)) { var compiledKeysToRemove = _compileRules.Keys.Where(key => key.StartsWith(workflowName)); foreach (var key in compiledKeysToRemove) diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index a48195e4..e82c2fbc 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -39,13 +39,13 @@ public class RulesEngine : IRulesEngine #region Constructor public RulesEngine(string[] jsonConfig, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { - var Workflows = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); - AddWorkflow(Workflows); + var workflow = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); + AddWorkflows(workflow); } public RulesEngine(Workflow[] Workflows, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { - AddWorkflow(Workflows); + AddWorkflows(Workflows); } public RulesEngine(ILogger logger = null, ReSettings reSettings = null) @@ -159,21 +159,21 @@ private async ValueTask ExecuteActionForRuleResult(RuleResultT /// /// The workflow rules. /// - public void AddWorkflow(params Workflow[] Workflows) + public void AddWorkflows(params Workflow[] Workflows) { try { - foreach (var Workflow in Workflows) + foreach (var workflow in Workflows) { var validator = new WorkflowsValidator(); - validator.ValidateAndThrow(Workflow); - if (!_rulesCache.ContainsWorkflows(Workflow.WorkflowName)) + validator.ValidateAndThrow(workflow); + if (!_rulesCache.ContainsWorkflows(workflow.WorkflowName)) { - _rulesCache.AddOrUpdateWorkflows(Workflow.WorkflowName, Workflow); + _rulesCache.AddOrUpdateWorkflows(workflow.WorkflowName, workflow); } else { - throw new ValidationException($"Cannot add workflow `{Workflow.WorkflowName}` as it already exists. Use `AddOrUpdateWorkflow` to update existing workflow"); + throw new ValidationException($"Cannot add workflow `{workflow.WorkflowName}` as it already exists. Use `AddOrUpdateWorkflow` to update existing workflow"); } } } @@ -193,11 +193,11 @@ public void AddOrUpdateWorkflow(params Workflow[] Workflows) { try { - foreach (var Workflow in Workflows) + foreach (var workflow in Workflows) { var validator = new WorkflowsValidator(); - validator.ValidateAndThrow(Workflow); - _rulesCache.AddOrUpdateWorkflows(Workflow.WorkflowName, Workflow); + validator.ValidateAndThrow(workflow); + _rulesCache.AddOrUpdateWorkflows(workflow.WorkflowName, workflow); } } catch (ValidationException ex) @@ -212,18 +212,18 @@ public List GetAllRegisteredWorkflowNames() } /// - /// Clears the workflows. + /// Clears the workflow. /// - public void ClearWorkflows() + public void ClearWorkflow() { _rulesCache.Clear(); } /// - /// Removes the workflow. + /// Removes the workflows. /// /// The workflow names. - public void RemoveWorkflow(params string[] workflowNames) + public void RemoveWorkflows(params string[] workflowNames) { foreach (var workflowName in workflowNames) { @@ -271,13 +271,13 @@ private bool RegisterRule(string workflowName, params RuleParameter[] ruleParams return true; } - var Workflows = _rulesCache.GetWorkflows(workflowName); - if (Workflows != null) + var workflow = _rulesCache.GetWorkflow(workflowName); + if (workflow != null) { var dictFunc = new Dictionary>(); - foreach (var rule in Workflows.Rules.Where(c => c.Enabled)) + foreach (var rule in workflow.Rules.Where(c => c.Enabled)) { - dictFunc.Add(rule.RuleName, CompileRule(rule, ruleParams, Workflows.GlobalParams?.ToArray())); + dictFunc.Add(rule.RuleName, CompileRule(rule, ruleParams, workflow.GlobalParams?.ToArray())); } _rulesCache.AddOrUpdateCompiledRule(compileRulesKey, dictFunc); @@ -293,7 +293,7 @@ private bool RegisterRule(string workflowName, params RuleParameter[] ruleParams private RuleFunc CompileRule(string workflowName, string ruleName, RuleParameter[] ruleParameters) { - var workflow = _rulesCache.GetWorkflows(workflowName); + var workflow = _rulesCache.GetWorkflow(workflowName); if(workflow == null) { throw new ArgumentException($"Workflow `{workflowName}` is not found"); diff --git a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs index 371c362c..797ce1ea 100644 --- a/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs +++ b/test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs @@ -18,8 +18,8 @@ public class CustomActionTest [Fact] public async Task CustomActionOnRuleMustHaveContextValues() { - var workflows = GetWorkflows(); - var re = new RulesEngine(workflows, null, reSettings: new ReSettings { + var workflow = GetWorkflow(); + var re = new RulesEngine(workflow, null, reSettings: new ReSettings { CustomActions = new Dictionary> { { "ReturnContext", () => new ReturnContextAction() } @@ -33,13 +33,13 @@ public async Task CustomActionOnRuleMustHaveContextValues() [Fact] public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues() { - var workflows = GetWorkflows(); - var workflowStr = JsonConvert.SerializeObject(workflows); + var workflow = GetWorkflow(); + var workflowStr = JsonConvert.SerializeObject(workflow); var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }; var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr,serializationOptions); - var re = new RulesEngine(workflows, null, reSettings: new ReSettings { + var re = new RulesEngine(workflow, null, reSettings: new ReSettings { CustomActions = new Dictionary> { { "ReturnContext", () => new ReturnContextAction() } @@ -51,7 +51,7 @@ public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues() var result = await re.ExecuteAllRulesAsync("successReturnContextAction", true); } - private Workflow[] GetWorkflows() + private Workflow[] GetWorkflow() { return new Workflow[] { new Workflow { diff --git a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs index e4fecb44..35aaf3f9 100644 --- a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs +++ b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs @@ -81,9 +81,9 @@ public async Task ExecuteRule_AddWorkflowWithSameName_ThrowsValidationException( Assert.Contains(result1, c => c.IsSuccess); // Fetch and add new rules. - var newWorkflows = ParseAsWorkflows(newWorkflowFile); + var newWorkflow = ParseAsWorkflow(newWorkflowFile); - Assert.Throws(() => re.AddWorkflow(newWorkflows)); + Assert.Throws(() => re.AddWorkflows(newWorkflow)); } [Theory] @@ -103,8 +103,8 @@ public async Task ExecuteRule_AddOrUpdateWorkflow_ExecutesUpdatedRules(string pr Assert.Contains(result1, c => c.IsSuccess); // Fetch and update new rules. - Workflow newWorkflows = ParseAsWorkflows(newWorkflowFile); - re.AddOrUpdateWorkflow(newWorkflows); + Workflow newWorkflow = ParseAsWorkflow(newWorkflowFile); + re.AddOrUpdateWorkflow(newWorkflow); // Run new rules. List result2 = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3); @@ -123,21 +123,21 @@ public async Task ExecuteRule_AddOrUpdateWorkflow_ExecutesUpdatedRules(string pr public void GetAllRegisteredWorkflows_ReturnsListOfAllWorkflows(string ruleFileName) { var re = GetRulesEngine(ruleFileName); - var workflows = re.GetAllRegisteredWorkflowNames(); + var workflow = re.GetAllRegisteredWorkflowNames(); - Assert.NotNull(workflows); - Assert.Equal(2, workflows.Count); - Assert.Contains("inputWorkflow", workflows); + Assert.NotNull(workflow); + Assert.Equal(2, workflow.Count); + Assert.Contains("inputWorkflow", workflow); } [Fact] public void GetAllRegisteredWorkflows_NoWorkflow_ReturnsEmptyList() { var re = new RulesEngine(); - var workflows = re.GetAllRegisteredWorkflowNames(); + var workflow = re.GetAllRegisteredWorkflowNames(); - Assert.NotNull(workflows); - Assert.Empty(workflows); + Assert.NotNull(workflow); + Assert.Empty(workflow); } [Theory] @@ -289,7 +289,7 @@ public async Task RemoveWorkflow_RemovesWorkflow(string ruleFileName) var result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3); Assert.NotNull(result); - re.RemoveWorkflow("inputWorkflow"); + re.RemoveWorkflows("inputWorkflow"); await Assert.ThrowsAsync(async () => await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3)); } @@ -300,7 +300,7 @@ public async Task RemoveWorkflow_RemovesWorkflow(string ruleFileName) public async Task ClearWorkflow_RemovesAllWorkflow(string ruleFileName) { var re = GetRulesEngine(ruleFileName); - re.ClearWorkflows(); + re.ClearWorkflow(); dynamic input1 = GetInput1(); dynamic input2 = GetInput2(); @@ -613,15 +613,15 @@ public async Task RemoveWorkFlow_ShouldRemoveAllCompiledCache() }; var re = new RulesEngine(); - re.AddWorkflow(workflow); + re.AddWorkflows(workflow); var result1 = await re.ExecuteAllRulesAsync("Test","hello"); Assert.True(result1.All(c => c.IsSuccess)); - re.RemoveWorkflow("Test"); + re.RemoveWorkflows("Test"); workflow.Rules.First().LocalParams.First().Expression = "false"; - re.AddWorkflow(workflow); + re.AddWorkflows(workflow); var result2 = await re.ExecuteAllRulesAsync("Test", "hello"); Assert.True(result2.All(c => c.IsSuccess == false)); } @@ -647,15 +647,15 @@ public async Task ClearWorkFlow_ShouldRemoveAllCompiledCache() }; var re = new RulesEngine(); - re.AddWorkflow(workflow); + re.AddWorkflows(workflow); var result1 = await re.ExecuteAllRulesAsync("Test", "hello"); Assert.True(result1.All(c => c.IsSuccess)); - re.ClearWorkflows(); + re.ClearWorkflow(); workflow.Rules.First().LocalParams.First().Expression = "false"; - re.AddWorkflow(workflow); + re.AddWorkflows(workflow); var result2 = await re.ExecuteAllRulesAsync("Test", "hello"); Assert.True(result2.All(c => c.IsSuccess == false)); } @@ -676,7 +676,7 @@ public async Task ExecuteRule_WithNullInput_ShouldNotThrowException() }; var re = new RulesEngine(); - re.AddWorkflow(workflow); + re.AddWorkflows(workflow); var result1 = await re.ExecuteAllRulesAsync("Test", new RuleParameter("input1", value:null)); Assert.True(result1.All(c => c.IsSuccess)); @@ -766,7 +766,7 @@ private string GetFileContent(string filename) return File.ReadAllText(filePath); } - private Workflow ParseAsWorkflows(string WorkflowsFileName) + private Workflow ParseAsWorkflow(string WorkflowsFileName) { string content = GetFileContent(WorkflowsFileName); return JsonConvert.DeserializeObject(content); diff --git a/test/RulesEngine.UnitTest/EmptyRulesTest.cs b/test/RulesEngine.UnitTest/EmptyRulesTest.cs index e756bbf5..691603c3 100644 --- a/test/RulesEngine.UnitTest/EmptyRulesTest.cs +++ b/test/RulesEngine.UnitTest/EmptyRulesTest.cs @@ -20,12 +20,12 @@ public class EmptyRulesTest [Fact] private async Task EmptyRules_ReturnsExepectedResults() { - var workflows = GetEmptyWorkflows(); + var workflow = GetEmptyWorkflow(); var reSettings = new ReSettings { }; RulesEngine rulesEngine = new RulesEngine(); Func action = () => { - new RulesEngine(workflows, reSettings: reSettings); + new RulesEngine(workflow, reSettings: reSettings); return Task.CompletedTask; }; @@ -36,12 +36,12 @@ private async Task EmptyRules_ReturnsExepectedResults() [Fact] private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults() { - var workflows = GetEmptyNestedWorkflows(); + var workflow = GetEmptyNestedWorkflows(); var reSettings = new ReSettings { }; RulesEngine rulesEngine = new RulesEngine(); Func action = () => { - new RulesEngine(workflows, reSettings: reSettings); + new RulesEngine(workflow, reSettings: reSettings); return Task.CompletedTask; }; @@ -50,7 +50,7 @@ private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults() Assert.Contains("Atleast one of Rules or WorkflowsToInject must be not empty", ex.Message); } - private Workflow[] GetEmptyWorkflows() + private Workflow[] GetEmptyWorkflow() { return new[] { new Workflow { diff --git a/test/RulesEngine.UnitTest/NestedRulesTest.cs b/test/RulesEngine.UnitTest/NestedRulesTest.cs index e2c5758d..7dff47d8 100644 --- a/test/RulesEngine.UnitTest/NestedRulesTest.cs +++ b/test/RulesEngine.UnitTest/NestedRulesTest.cs @@ -22,9 +22,9 @@ public class NestedRulesTest [InlineData(NestedRuleExecutionMode.Performance)] public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode mode) { - var workflows = GetWorkflows(); + var workflow = GetWorkflow(); var reSettings = new ReSettings { NestedRuleExecutionMode = mode }; - var rulesEngine = new RulesEngine(workflows, reSettings: reSettings); + var rulesEngine = new RulesEngine(workflow, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -68,9 +68,9 @@ public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode m [Fact] private async Task NestedRulesWithNestedActions_ReturnsCorrectResults() { - var workflows = GetWorkflows(); + var workflow = GetWorkflow(); var reSettings = new ReSettings { }; - var rulesEngine = new RulesEngine(workflows, reSettings: reSettings); + var rulesEngine = new RulesEngine(workflow, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -84,16 +84,16 @@ private async Task NestedRulesWithNestedActions_ReturnsCorrectResults() [Fact] private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson_ReturnsCorrectResults() { - var workflows = GetWorkflows(); - var workflowStr = JsonConvert.SerializeObject(workflows); + var workflow = GetWorkflow(); + var workflowStr = JsonConvert.SerializeObject(workflow); var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }; - var workflowsViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr, serializationOptions); + var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize(workflowStr, serializationOptions); var reSettings = new ReSettings { }; - var rulesEngine = new RulesEngine(workflowsViaTextJson, reSettings: reSettings); + var rulesEngine = new RulesEngine(workflowViaTextJson, reSettings: reSettings); dynamic input1 = new ExpandoObject(); input1.trueValue = true; @@ -108,7 +108,7 @@ private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson - private Workflow[] GetWorkflows() + private Workflow[] GetWorkflow() { return new[] { new Workflow { diff --git a/test/RulesEngine.UnitTest/RulesEnabledTests.cs b/test/RulesEngine.UnitTest/RulesEnabledTests.cs index 2bd7d5f0..f78f547f 100644 --- a/test/RulesEngine.UnitTest/RulesEnabledTests.cs +++ b/test/RulesEngine.UnitTest/RulesEnabledTests.cs @@ -22,8 +22,8 @@ public RulesEnabledTests() [InlineData("RuleEnabledNestedFeatureTest", new bool[] { true, true, false })] public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, bool[] expectedRuleResults) { - var workflows = GetWorkflows(); - var rulesEngine = new RulesEngine(workflows, reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false }); + var workflow = GetWorkflows(); + var rulesEngine = new RulesEngine(workflow, reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false }); var input1 = new { TrueValue = true }; @@ -46,7 +46,7 @@ public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, { var workflow = GetWorkflows().Single(c => c.WorkflowName == workflowName); var rulesEngine = new RulesEngine(reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false}); - rulesEngine.AddWorkflow(workflow); + rulesEngine.AddWorkflows(workflow); var input1 = new { TrueValue = true }; @@ -60,12 +60,12 @@ public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, Assert.Equal(expectedRuleResults[i], result[i].IsSuccess); } - rulesEngine.RemoveWorkflow(workflowName); + rulesEngine.RemoveWorkflows(workflowName); var firstRule = workflow.Rules.First(); firstRule.Enabled = false; - rulesEngine.AddWorkflow(workflow); + rulesEngine.AddWorkflows(workflow); var expectedLength = workflow.Rules.Count(c => c.Enabled); diff --git a/test/RulesEngine.UnitTest/ScopedParamsTest.cs b/test/RulesEngine.UnitTest/ScopedParamsTest.cs index 5ddcf740..fbc7f206 100644 --- a/test/RulesEngine.UnitTest/ScopedParamsTest.cs +++ b/test/RulesEngine.UnitTest/ScopedParamsTest.cs @@ -24,10 +24,10 @@ public class ScopedParamsTest [InlineData("GlobalParamAndLocalParamsInNestedRules")] public async Task BasicWorkflows_ReturnsTrue(string workflowName) { - var workflows = GetWorkflowsList(); + var workflow = GetWorkflowList(); var engine = new RulesEngine(null, null); - engine.AddWorkflow(workflows); + engine.AddWorkflows(workflow); var input1 = new { trueValue = true, @@ -45,10 +45,10 @@ public async Task BasicWorkflows_ReturnsTrue(string workflowName) [InlineData("GlobalAndLocalParams")] public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName) { - var workflows = GetWorkflowsList(); + var workflow = GetWorkflowList(); var engine = new RulesEngine(null, null); - engine.AddWorkflow(workflows); + engine.AddWorkflows(workflow); var input1 = new { trueValue = true, @@ -58,10 +58,10 @@ public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName) var result = await engine.ExecuteAllRulesAsync(workflowName, input1); Assert.True(result.All(c => c.IsSuccess)); - var workflowToUpdate = workflows.Single(c => c.WorkflowName == workflowName); - engine.RemoveWorkflow(workflowName); + var workflowToUpdate = workflow.Single(c => c.WorkflowName == workflowName); + engine.RemoveWorkflows(workflowName); workflowToUpdate.GlobalParams.First().Expression = "true == false"; - engine.AddWorkflow(workflowToUpdate); + engine.AddWorkflows(workflowToUpdate); var result2 = await engine.ExecuteAllRulesAsync(workflowName, input1); @@ -75,12 +75,12 @@ public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName) [InlineData("GlobalAndLocalParams", new[] { false })] public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] outputs) { - var workflows = GetWorkflowsList(); + var workflow = GetWorkflowList(); var engine = new RulesEngine(new string[] { }, null, new ReSettings { EnableScopedParams = false }); - engine.AddWorkflow(workflows); + engine.AddWorkflows(workflow); var input1 = new { trueValue = true, @@ -103,10 +103,10 @@ public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] [InlineData("LocalParamsOnly2")] public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName) { - var workflows = GetWorkflowsList(); + var workflow = GetWorkflowList(); var engine = new RulesEngine(new string[] { }, null); - engine.AddWorkflow(workflows); + engine.AddWorkflows(workflow); var input = new { }; var result = await engine.ExecuteAllRulesAsync(workflowName, input); @@ -123,10 +123,10 @@ public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowN [InlineData("LocalParamsOnlyWithComplexInput")] public async Task RuntimeErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName) { - var workflows = GetWorkflowsList(); + var workflow = GetWorkflowList(); var engine = new RulesEngine(new string[] { }, null); - engine.AddWorkflow(workflows); + engine.AddWorkflows(workflow); @@ -143,7 +143,7 @@ public async Task RuntimeErrorInScopedParam_ShouldAppearAsErrorMessage(string wo private void CheckResultTreeContainsAllInputs(string workflowName, List result) { - var workflow = GetWorkflowsList().Single(c => c.WorkflowName == workflowName); + var workflow = GetWorkflowList().Single(c => c.WorkflowName == workflowName); var expectedInputs = new List() { "input1" }; expectedInputs.AddRange(workflow.GlobalParams?.Select(c => c.Name) ?? new List()); @@ -176,7 +176,7 @@ private static void CheckInputs(IEnumerable expectedInputs, RuleResultTr } } - private Workflow[] GetWorkflowsList() + private Workflow[] GetWorkflowList() { return new Workflow[] { new Workflow { From 85ccdaafb166fb43abfa48097c99e96dd42cb9ba Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 5 Aug 2021 06:31:55 -0700 Subject: [PATCH 11/16] Deprecate RuleAction to plural --- src/RulesEngine/Models/RuleActions.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/RulesEngine/Models/RuleActions.cs b/src/RulesEngine/Models/RuleActions.cs index cc5f50ca..be242653 100644 --- a/src/RulesEngine/Models/RuleActions.cs +++ b/src/RulesEngine/Models/RuleActions.cs @@ -1,10 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Diagnostics.CodeAnalysis; namespace RulesEngine.Models { + [Obsolete("RuleAction class is deprecated. Use RuleActions class instead.")] + [ExcludeFromCodeCoverage] + public class RuleAction : RuleActions + { + } + [ExcludeFromCodeCoverage] public class RuleActions { From 508903faa069cc2e783560eac43da749dc72dd9e Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Fri, 6 Aug 2021 04:44:54 -0700 Subject: [PATCH 12/16] Reverted some name changes per @abbasc52 --- src/RulesEngine/Interfaces/IRulesEngine.cs | 12 +++++------ src/RulesEngine/RulesEngine.cs | 10 +++++----- .../BusinessRuleEngineTest.cs | 20 +++++++++---------- .../RulesEngine.UnitTest/RulesEnabledTests.cs | 6 +++--- test/RulesEngine.UnitTest/ScopedParamsTest.cs | 14 ++++++------- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/RulesEngine/Interfaces/IRulesEngine.cs b/src/RulesEngine/Interfaces/IRulesEngine.cs index 65c3c8c0..69f0e655 100644 --- a/src/RulesEngine/Interfaces/IRulesEngine.cs +++ b/src/RulesEngine/Interfaces/IRulesEngine.cs @@ -29,19 +29,19 @@ public interface IRulesEngine /// /// Adds new workflows to RulesEngine /// - /// - void AddWorkflows(params Workflow[] Workflows); + /// + void AddWorkflow(params Workflow[] Workflows); /// - /// Removes registered workflow from RulesEngine + /// Removes all registered workflows from RulesEngine /// - void ClearWorkflow(); + void ClearWorkflows(); /// - /// Removes the workflows from RulesEngine + /// Removes the workflow from RulesEngine /// /// - void RemoveWorkflows(params string[] workflowNames); + void RemoveWorkflow(params string[] workflowNames); /// /// Returns the list of all registered workflow names diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index e82c2fbc..662a8d3f 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -40,12 +40,12 @@ public class RulesEngine : IRulesEngine public RulesEngine(string[] jsonConfig, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { var workflow = jsonConfig.Select(item => JsonConvert.DeserializeObject(item)).ToArray(); - AddWorkflows(workflow); + AddWorkflow(workflow); } public RulesEngine(Workflow[] Workflows, ILogger logger = null, ReSettings reSettings = null) : this(logger, reSettings) { - AddWorkflows(Workflows); + AddWorkflow(Workflows); } public RulesEngine(ILogger logger = null, ReSettings reSettings = null) @@ -159,7 +159,7 @@ private async ValueTask ExecuteActionForRuleResult(RuleResultT /// /// The workflow rules. /// - public void AddWorkflows(params Workflow[] Workflows) + public void AddWorkflow(params Workflow[] Workflows) { try { @@ -214,7 +214,7 @@ public List GetAllRegisteredWorkflowNames() /// /// Clears the workflow. /// - public void ClearWorkflow() + public void ClearWorkflows() { _rulesCache.Clear(); } @@ -223,7 +223,7 @@ public void ClearWorkflow() /// Removes the workflows. /// /// The workflow names. - public void RemoveWorkflows(params string[] workflowNames) + public void RemoveWorkflow(params string[] workflowNames) { foreach (var workflowName in workflowNames) { diff --git a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs index 35aaf3f9..89043e90 100644 --- a/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs +++ b/test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs @@ -83,7 +83,7 @@ public async Task ExecuteRule_AddWorkflowWithSameName_ThrowsValidationException( // Fetch and add new rules. var newWorkflow = ParseAsWorkflow(newWorkflowFile); - Assert.Throws(() => re.AddWorkflows(newWorkflow)); + Assert.Throws(() => re.AddWorkflow(newWorkflow)); } [Theory] @@ -289,7 +289,7 @@ public async Task RemoveWorkflow_RemovesWorkflow(string ruleFileName) var result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3); Assert.NotNull(result); - re.RemoveWorkflows("inputWorkflow"); + re.RemoveWorkflow("inputWorkflow"); await Assert.ThrowsAsync(async () => await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3)); } @@ -300,7 +300,7 @@ public async Task RemoveWorkflow_RemovesWorkflow(string ruleFileName) public async Task ClearWorkflow_RemovesAllWorkflow(string ruleFileName) { var re = GetRulesEngine(ruleFileName); - re.ClearWorkflow(); + re.ClearWorkflows(); dynamic input1 = GetInput1(); dynamic input2 = GetInput2(); @@ -613,15 +613,15 @@ public async Task RemoveWorkFlow_ShouldRemoveAllCompiledCache() }; var re = new RulesEngine(); - re.AddWorkflows(workflow); + re.AddWorkflow(workflow); var result1 = await re.ExecuteAllRulesAsync("Test","hello"); Assert.True(result1.All(c => c.IsSuccess)); - re.RemoveWorkflows("Test"); + re.RemoveWorkflow("Test"); workflow.Rules.First().LocalParams.First().Expression = "false"; - re.AddWorkflows(workflow); + re.AddWorkflow(workflow); var result2 = await re.ExecuteAllRulesAsync("Test", "hello"); Assert.True(result2.All(c => c.IsSuccess == false)); } @@ -647,15 +647,15 @@ public async Task ClearWorkFlow_ShouldRemoveAllCompiledCache() }; var re = new RulesEngine(); - re.AddWorkflows(workflow); + re.AddWorkflow(workflow); var result1 = await re.ExecuteAllRulesAsync("Test", "hello"); Assert.True(result1.All(c => c.IsSuccess)); - re.ClearWorkflow(); + re.ClearWorkflows(); workflow.Rules.First().LocalParams.First().Expression = "false"; - re.AddWorkflows(workflow); + re.AddWorkflow(workflow); var result2 = await re.ExecuteAllRulesAsync("Test", "hello"); Assert.True(result2.All(c => c.IsSuccess == false)); } @@ -676,7 +676,7 @@ public async Task ExecuteRule_WithNullInput_ShouldNotThrowException() }; var re = new RulesEngine(); - re.AddWorkflows(workflow); + re.AddWorkflow(workflow); var result1 = await re.ExecuteAllRulesAsync("Test", new RuleParameter("input1", value:null)); Assert.True(result1.All(c => c.IsSuccess)); diff --git a/test/RulesEngine.UnitTest/RulesEnabledTests.cs b/test/RulesEngine.UnitTest/RulesEnabledTests.cs index f78f547f..a00f438e 100644 --- a/test/RulesEngine.UnitTest/RulesEnabledTests.cs +++ b/test/RulesEngine.UnitTest/RulesEnabledTests.cs @@ -46,7 +46,7 @@ public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, { var workflow = GetWorkflows().Single(c => c.WorkflowName == workflowName); var rulesEngine = new RulesEngine(reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false}); - rulesEngine.AddWorkflows(workflow); + rulesEngine.AddWorkflow(workflow); var input1 = new { TrueValue = true }; @@ -60,12 +60,12 @@ public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, Assert.Equal(expectedRuleResults[i], result[i].IsSuccess); } - rulesEngine.RemoveWorkflows(workflowName); + rulesEngine.RemoveWorkflow(workflowName); var firstRule = workflow.Rules.First(); firstRule.Enabled = false; - rulesEngine.AddWorkflows(workflow); + rulesEngine.AddWorkflow(workflow); var expectedLength = workflow.Rules.Count(c => c.Enabled); diff --git a/test/RulesEngine.UnitTest/ScopedParamsTest.cs b/test/RulesEngine.UnitTest/ScopedParamsTest.cs index fbc7f206..70bf99b7 100644 --- a/test/RulesEngine.UnitTest/ScopedParamsTest.cs +++ b/test/RulesEngine.UnitTest/ScopedParamsTest.cs @@ -27,7 +27,7 @@ public async Task BasicWorkflows_ReturnsTrue(string workflowName) var workflow = GetWorkflowList(); var engine = new RulesEngine(null, null); - engine.AddWorkflows(workflow); + engine.AddWorkflow(workflow); var input1 = new { trueValue = true, @@ -48,7 +48,7 @@ public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName) var workflow = GetWorkflowList(); var engine = new RulesEngine(null, null); - engine.AddWorkflows(workflow); + engine.AddWorkflow(workflow); var input1 = new { trueValue = true, @@ -59,9 +59,9 @@ public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName) Assert.True(result.All(c => c.IsSuccess)); var workflowToUpdate = workflow.Single(c => c.WorkflowName == workflowName); - engine.RemoveWorkflows(workflowName); + engine.RemoveWorkflow(workflowName); workflowToUpdate.GlobalParams.First().Expression = "true == false"; - engine.AddWorkflows(workflowToUpdate); + engine.AddWorkflow(workflowToUpdate); var result2 = await engine.ExecuteAllRulesAsync(workflowName, input1); @@ -80,7 +80,7 @@ public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] var engine = new RulesEngine(new string[] { }, null, new ReSettings { EnableScopedParams = false }); - engine.AddWorkflows(workflow); + engine.AddWorkflow(workflow); var input1 = new { trueValue = true, @@ -106,7 +106,7 @@ public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowN var workflow = GetWorkflowList(); var engine = new RulesEngine(new string[] { }, null); - engine.AddWorkflows(workflow); + engine.AddWorkflow(workflow); var input = new { }; var result = await engine.ExecuteAllRulesAsync(workflowName, input); @@ -126,7 +126,7 @@ public async Task RuntimeErrorInScopedParam_ShouldAppearAsErrorMessage(string wo var workflow = GetWorkflowList(); var engine = new RulesEngine(new string[] { }, null); - engine.AddWorkflows(workflow); + engine.AddWorkflow(workflow); From 43686fe03332a3023d316768daa4528242257717 Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Tue, 10 Aug 2021 04:31:09 -0700 Subject: [PATCH 13/16] Clarity + VS.NET 2019 compilation fix --- demo/DemoApp.EFDataExample/RulesEngineContext.cs | 5 ++--- src/RulesEngine/Models/Rule.cs | 2 +- src/RulesEngine/Models/Workflow.cs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/demo/DemoApp.EFDataExample/RulesEngineContext.cs b/demo/DemoApp.EFDataExample/RulesEngineContext.cs index 1ae7eac4..2ce5450b 100644 --- a/demo/DemoApp.EFDataExample/RulesEngineContext.cs +++ b/demo/DemoApp.EFDataExample/RulesEngineContext.cs @@ -9,11 +9,8 @@ namespace RulesEngine.Data public class RulesEngineContext : DbContext { public DbSet Workflows { get; set; } - public DbSet ActionInfos { get; set; } - public DbSet RuleActions { get; set; } public DbSet Rules { get; set; } - public DbSet ScopedParams { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -27,6 +24,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasKey(k => k.Id); entity.Property(p => p.Id).ValueGeneratedOnAdd(); + entity.Ignore(b => b.WorkflowRulesToInject); entity.Ignore(b => b.WorkflowsToInject); }); @@ -44,6 +42,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) v => JsonSerializer.Serialize(v, null), v => JsonSerializer.Deserialize(v, null)); + entity.Ignore(b => b.WorkflowRulesToInject); entity.Ignore(b => b.WorkflowsToInject); }); } diff --git a/src/RulesEngine/Models/Rule.cs b/src/RulesEngine/Models/Rule.cs index 19ccdabf..da3f5689 100644 --- a/src/RulesEngine/Models/Rule.cs +++ b/src/RulesEngine/Models/Rule.cs @@ -16,7 +16,7 @@ namespace RulesEngine.Models public class Rule { /// - /// Gets the Rule Id. + /// Gets the Rule Id. (OPTIONAL) /// public int Id { get; set; } diff --git a/src/RulesEngine/Models/Workflow.cs b/src/RulesEngine/Models/Workflow.cs index 6d553b90..bec96817 100644 --- a/src/RulesEngine/Models/Workflow.cs +++ b/src/RulesEngine/Models/Workflow.cs @@ -19,7 +19,7 @@ public class WorkflowRules : Workflow { public class Workflow { /// - /// Gets the workflow Id. + /// Gets the workflow Id. (OPTIONAL) /// public int Id { get; set; } From a0587cccbe6fb95451670b363030720d0f3efede Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Tue, 10 Aug 2021 04:36:44 -0700 Subject: [PATCH 14/16] Camelcase fixes --- src/RulesEngine/RulesEngine.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/RulesEngine/RulesEngine.cs b/src/RulesEngine/RulesEngine.cs index 662a8d3f..f0dabdc0 100644 --- a/src/RulesEngine/RulesEngine.cs +++ b/src/RulesEngine/RulesEngine.cs @@ -157,13 +157,13 @@ private async ValueTask ExecuteActionForRuleResult(RuleResultT /// /// Adds the workflow if the workflow name is not already added. Ignores the rest. /// - /// The workflow rules. + /// The workflow rules. /// - public void AddWorkflow(params Workflow[] Workflows) + public void AddWorkflow(params Workflow[] workflows) { try { - foreach (var workflow in Workflows) + foreach (var workflow in workflows) { var validator = new WorkflowsValidator(); validator.ValidateAndThrow(workflow); @@ -187,13 +187,13 @@ public void AddWorkflow(params Workflow[] Workflows) /// Adds new workflow rules if not previously added. /// Or updates the rules for an existing workflow. /// - /// The workflow rules. + /// The workflow rules. /// - public void AddOrUpdateWorkflow(params Workflow[] Workflows) + public void AddOrUpdateWorkflow(params Workflow[] workflows) { try { - foreach (var workflow in Workflows) + foreach (var workflow in workflows) { var validator = new WorkflowsValidator(); validator.ValidateAndThrow(workflow); From e93484962ac91fa9359595003b57534da175256a Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Wed, 11 Aug 2021 06:37:44 -0700 Subject: [PATCH 15/16] Reverted Id properties --- demo/DemoApp.EFDataExample/RulesEngineContext.cs | 12 ++++-------- src/RulesEngine/Models/Rule.cs | 5 ----- src/RulesEngine/Models/Workflow.cs | 5 ----- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/demo/DemoApp.EFDataExample/RulesEngineContext.cs b/demo/DemoApp.EFDataExample/RulesEngineContext.cs index 2ce5450b..6cf4afd8 100644 --- a/demo/DemoApp.EFDataExample/RulesEngineContext.cs +++ b/demo/DemoApp.EFDataExample/RulesEngineContext.cs @@ -16,21 +16,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - modelBuilder.Ignore(); - modelBuilder.Ignore(); - modelBuilder.Ignore(); + modelBuilder.Entity() + .HasKey(k => k.Name); modelBuilder.Entity(entity => { - entity.HasKey(k => k.Id); - entity.Property(p => p.Id).ValueGeneratedOnAdd(); - + entity.HasKey(k => k.WorkflowName); entity.Ignore(b => b.WorkflowRulesToInject); entity.Ignore(b => b.WorkflowsToInject); }); modelBuilder.Entity(entity => { - entity.HasKey(k => k.Id); - entity.Property(p => p.Id).ValueGeneratedOnAdd(); + entity.HasKey(k => k.RuleName); entity.Property(b => b.Properties) .HasConversion( diff --git a/src/RulesEngine/Models/Rule.cs b/src/RulesEngine/Models/Rule.cs index da3f5689..cf080527 100644 --- a/src/RulesEngine/Models/Rule.cs +++ b/src/RulesEngine/Models/Rule.cs @@ -15,11 +15,6 @@ namespace RulesEngine.Models [ExcludeFromCodeCoverage] public class Rule { - /// - /// Gets the Rule Id. (OPTIONAL) - /// - public int Id { get; set; } - /// /// Rule name for the Rule /// diff --git a/src/RulesEngine/Models/Workflow.cs b/src/RulesEngine/Models/Workflow.cs index bec96817..79e163e5 100644 --- a/src/RulesEngine/Models/Workflow.cs +++ b/src/RulesEngine/Models/Workflow.cs @@ -18,11 +18,6 @@ public class WorkflowRules : Workflow { [ExcludeFromCodeCoverage] public class Workflow { - /// - /// Gets the workflow Id. (OPTIONAL) - /// - public int Id { get; set; } - /// /// Gets the workflow name. /// From 79fc87d423d667fac46d33ea0fe98726c6022f02 Mon Sep 17 00:00:00 2001 From: Alex Reich Date: Thu, 12 Aug 2021 14:44:41 -0700 Subject: [PATCH 16/16] Documentation updates --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9126bfc1..49d1b293 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ An example rule could be - You can inject the rules into the Rules Engine by initiating an instance by using the following code - ```c# -var rulesEngine = new RulesEngine(workflowRules, logger); +var rulesEngine = new RulesEngine(workflow, logger); ``` -Here, *workflowRules* is a list of deserialized object based out of the schema explained above and *logger* is a custom logger instance made out of an [ILogger](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#logger) instance. +Here, *workflow* is a list of deserialized object based out of the schema explained above and *logger* is a custom logger instance made out of an [ILogger](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#logger) instance. Once done, the Rules Engine needs to execute the rules for a given input. It can be done by calling the method ExecuteAllRulesAsync as shown below - ```c# @@ -73,21 +73,21 @@ rule.SuccessEvent = "Count is within tolerance."; rule.ErrorMessage = "Over expected."; rule.Expression = "count < 3"; rule.RuleExpressionType = RuleExpressionType.LambdaExpression; - rules.Add(rule); -workflowRule.Rules = rules; - -workFlowRules.Add(workflowRule); +Workflow workflow = new Workflow(); +workflow.WorkflowName = "Example Workflow"; +workflow.Rules = rules; +workflow.Add(workflowRule); -var bre = new RulesEngine.RulesEngine(workFlowRules.ToArray(), null); +var bre = new RulesEngine.RulesEngine(workflow.ToArray(), null); ``` ### Entity Framework Consuming Entity Framework and populating the Rules Engine is shown in the [EFDemo class](https://github.com/microsoft/RulesEngine/blob/main/demo/DemoApp/EFDemo.cs) with Workflow rules populating the array and passed to the Rules Engine, The Demo App includes an example [RulesEngineDemoContext](https://github.com/microsoft/RulesEngine/blob/main/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs) using SQLite and could be swapped out for another provider. ```c# -var wfr = db.WorkflowRules.Include(i => i.Rules).ThenInclude(i => i.Rules).ToArray(); +var wfr = db.Workflows.Include(i => i.Rules).ThenInclude(i => i.Rules).ToArray(); var bre = new RulesEngine.RulesEngine(wfr, null); ``` *Note: For each level of nested rules expected, a ThenInclude query appended will be needed as shown above.*