Enhancements (#282)

* * fixed workflowInjection not working
* added optional Inputs filter for EvaluateRuleAction

* * ActionContext now supports optional inputs
* Added support to pass additionalInputs to EvaluateRule Action
pull/283/head
Abbas Cyclewala 2021-11-23 17:47:10 +05:30 committed by GitHub
parent e6624621df
commit 108fa91968
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 131 additions and 12 deletions

View File

@ -21,7 +21,6 @@ namespace RulesEngine.Data
modelBuilder.Entity<Workflow>(entity => {
entity.HasKey(k => k.WorkflowName);
entity.Ignore(b => b.WorkflowRulesToInject);
entity.Ignore(b => b.WorkflowsToInject);
});

View File

@ -40,6 +40,21 @@ namespace RulesEngine.Actions
{
return _parentResult;
}
public bool TryGetContext<T>(string name,out T output)
{
try
{
output = GetContext<T>(name);
return true;
}
catch(ArgumentException)
{
output = default(T);
return false;
}
}
public T GetContext<T>(string name)
{
try

View File

@ -1,8 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.ExpressionBuilders;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RulesEngine.Actions
@ -10,10 +13,12 @@ namespace RulesEngine.Actions
public class EvaluateRuleAction : ActionBase
{
private readonly RulesEngine _ruleEngine;
private readonly RuleExpressionParser _ruleExpressionParser;
public EvaluateRuleAction(RulesEngine ruleEngine)
public EvaluateRuleAction(RulesEngine ruleEngine, RuleExpressionParser ruleExpressionParser)
{
_ruleEngine = ruleEngine;
_ruleExpressionParser = ruleExpressionParser;
}
internal async override ValueTask<ActionRuleResult> ExecuteAndReturnResultAsync(ActionContext context, RuleParameter[] ruleParameters, bool includeRuleResults = false)
@ -23,11 +28,11 @@ namespace RulesEngine.Actions
List<RuleResultTree> resultList = null;
if (includeRuleResults)
{
resultList = new List<RuleResultTree>(output.Results);
resultList = new List<RuleResultTree>(output?.Results ?? new List<RuleResultTree>() { });
resultList.AddRange(innerResult.Results);
}
return new ActionRuleResult {
Output = output.Output,
Output = output?.Output,
Exception = innerResult.Exception,
Results = resultList
};
@ -37,7 +42,22 @@ namespace RulesEngine.Actions
{
var workflowName = context.GetContext<string>("workflowName");
var ruleName = context.GetContext<string>("ruleName");
var ruleResult = await _ruleEngine.ExecuteActionWorkflowAsync(workflowName, ruleName, ruleParameters);
var filteredRuleParameters = new List<RuleParameter>(ruleParameters);
if(context.TryGetContext<List<string>>("inputFilter",out var inputFilter))
{
filteredRuleParameters = ruleParameters.Where(c => inputFilter.Contains(c.Name)).ToList();
}
if (context.TryGetContext<List<ScopedParam>>("additionalInputs", out var additionalInputs))
{
foreach(var additionalInput in additionalInputs)
{
dynamic value = _ruleExpressionParser.Evaluate<object>(additionalInput.Expression, ruleParameters);
filteredRuleParameters.Add(new RuleParameter(additionalInput.Name, value));
}
}
var ruleResult = await _ruleEngine.ExecuteActionWorkflowAsync(workflowName, ruleName, filteredRuleParameters.ToArray());
return ruleResult;
}
}

View File

@ -27,7 +27,6 @@ namespace RulesEngine.Models
/// <value>The workflow rules to inject.</value>
[Obsolete("WorkflowRulesToInject is deprecated. Use WorkflowsToInject instead.")]
public IEnumerable<string> WorkflowRulesToInject {
get { return WorkflowsToInject; }
set { WorkflowsToInject = value; }
}
public IEnumerable<string> WorkflowsToInject { get; set; }

View File

@ -107,7 +107,7 @@ namespace RulesEngine
throw new Exception($"Could not find injected Workflow: {wfname}");
}
workflow.Rules.ToList().AddRange(injectedWorkflow.Rules);
workflow.Rules = workflow.Rules.Concat(injectedWorkflow.Rules).ToList();
}
}

View File

@ -345,7 +345,7 @@ namespace RulesEngine
{
return new Dictionary<string, Func<ActionBase>>{
{"OutputExpression",() => new OutputExpressionAction(_ruleExpressionParser) },
{"EvaluateRule", () => new EvaluateRuleAction(this) }
{"EvaluateRule", () => new EvaluateRuleAction(this,_ruleExpressionParser) }
};
}

View File

@ -53,6 +53,26 @@ namespace RulesEngine.UnitTest
}
[Fact]
public async Task ExecuteActionWorkflowAsync_SelfReferencingAction_NoFilter_ExecutesSuccessfully()
{
var engine = new RulesEngine(GetWorkflowWithActions());
var result = await engine.ExecuteActionWorkflowAsync("WorkflowWithGlobalsAndSelfRefActions", "RuleReferencingSameWorkflow", new RuleParameter[0]);
Assert.NotNull(result);
Assert.Null(result.Output);
}
[Fact]
public async Task ExecuteActionWorkflowAsync_SelfReferencingAction_WithFilter_ExecutesSuccessfully()
{
var engine = new RulesEngine(GetWorkflowWithActions());
var result = await engine.ExecuteActionWorkflowAsync("WorkflowWithGlobalsAndSelfRefActions", "RuleReferencingSameWorkflowWithInputFilter", new RuleParameter[0]);
Assert.NotNull(result);
Assert.Equal(4,result.Output);
}
private Workflow[] GetWorkflowsWithoutActions()
{
var workflow1 = new Workflow {
@ -104,8 +124,74 @@ namespace RulesEngine.UnitTest
}
}
};
return new[] { workflow1 };
var workflow2 = new Workflow {
WorkflowName = "WorkflowWithGlobalsAndSelfRefActions",
GlobalParams = new[] {
new ScopedParam {
Name = "global1",
Expression = "\"Hello\""
}
},
Rules = new[] {
new Rule{
RuleName = "RuleReferencingSameWorkflow",
Expression = "1 == 1",
Actions = new RuleActions {
OnSuccess = new ActionInfo{
Name = "EvaluateRule",
Context = new Dictionary<string, object>{
{"workflowName", "WorkflowWithGlobalsAndSelfRefActions"},
{"ruleName","OtherRule"}
}
}
}
},new Rule{
RuleName = "RuleReferencingSameWorkflowWithInputFilter",
Expression = "1 == 1",
Actions = new RuleActions {
OnSuccess = new ActionInfo{
Name = "EvaluateRule",
Context = new Dictionary<string, object>{
{"workflowName", "WorkflowWithGlobalsAndSelfRefActions"},
{"ruleName","OtherRule"},
{"inputFilter",new string[] { } },
{"additionalInputs", new [] {
new ScopedParam(){
Name = "additionalValue",
Expression = "1"
}
} }
}
}
}
}
, new Rule{
RuleName = "OtherRule",
Expression = "additionalValue == 1",
Actions = new RuleActions {
OnSuccess = new ActionInfo{
Name = "OutputExpression",
Context = new Dictionary<string, object>{
{"expression", "2*2"}
}
}
}
}
}
};
return new[] { workflow1, workflow2 };
}
}
}

View File

@ -35,7 +35,7 @@ namespace RulesEngine.UnitTest
[Theory]
[InlineData("rules2.json")]
public async Task RulesEngine_InjectedRules_ReturnsListOfRuleResultTree(string ruleFileName)
public async Task RulesEngine_InjectedRules_ContainsInjectedRules(string ruleFileName)
{
var re = GetRulesEngine(ruleFileName);
@ -43,9 +43,9 @@ namespace RulesEngine.UnitTest
dynamic input2 = GetInput2();
dynamic input3 = GetInput3();
var result = await re.ExecuteAllRulesAsync("inputWorkflowReference", input1, input2, input3);
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflowReference", input1, input2, input3);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.True(result.Any());
}
[Theory]