Abbasc52/actions for nested levels (#182)
* Added support for nested rule actions * Changed type for Actionspull/147/head v3.3.0
parent
bafbff281d
commit
b763f718bc
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [3.3.0]
|
||||||
|
- Added support for actions in nested rules
|
||||||
|
- Improved serialization support for System.Text.Json for workflow model
|
||||||
|
|
||||||
|
Breaking Change:
|
||||||
|
- Type of Action has been changed from `Dictionary<ActionTriggerType, ActionInfo>` to `RuleActions`
|
||||||
|
- No impact if you are serializing workflow from json
|
||||||
|
- For workflow objects created in code, refer - [link](https://github.com/microsoft/RulesEngine/pull/182/files#diff-a5093dda2dcc1e4958ce3533edb607bb61406e1f0a9071eca4e317bdd987c0d3)
|
||||||
|
|
||||||
## [3.2.0]
|
## [3.2.0]
|
||||||
- Added AddOrUpdateWorkflow method to update workflows atomically (by @AshishPrasad)
|
- Added AddOrUpdateWorkflow method to update workflows atomically (by @AshishPrasad)
|
||||||
- Updated dependencies to latest
|
- Updated dependencies to latest
|
||||||
|
|
|
@ -18,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
CHANGELOG.md = CHANGELOG.md
|
CHANGELOG.md = CHANGELOG.md
|
||||||
global.json = global.json
|
global.json = global.json
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
|
schema\workflowRules-schema.json = schema\workflowRules-schema.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RulesEngineBenchmark", "benchmark\RulesEngineBenchmark\RulesEngineBenchmark.csproj", "{C058809F-C720-4EFC-925D-A486627B238B}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RulesEngineBenchmark", "benchmark\RulesEngineBenchmark\RulesEngineBenchmark.csproj", "{C058809F-C720-4EFC-925D-A486627B238B}"
|
||||||
|
|
|
@ -39,6 +39,12 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Properties": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Actions": {
|
||||||
|
"$ref": "#/definitions/RuleActions"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -53,8 +59,7 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"RuleName",
|
"RuleName",
|
||||||
"Expression",
|
"Expression"
|
||||||
"RuleExpressionType"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"RuleName": {
|
"RuleName": {
|
||||||
|
@ -79,6 +84,35 @@
|
||||||
},
|
},
|
||||||
"SuccessEvent": {
|
"SuccessEvent": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"Properties": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"Actions": {
|
||||||
|
"$ref": "#/definitions/RuleActions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ActionInfo": {
|
||||||
|
"propeties": {
|
||||||
|
"Name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"Context": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"Name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"RuleActions": {
|
||||||
|
"properties": {
|
||||||
|
"OnSuccess": {
|
||||||
|
"$ref": "#/definitions/ActionInfo"
|
||||||
|
},
|
||||||
|
"OnFailure": {
|
||||||
|
"$ref": "#/definitions/ActionInfo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,18 @@ namespace RulesEngine.Actions
|
||||||
foreach (var kv in context)
|
foreach (var kv in context)
|
||||||
{
|
{
|
||||||
string key = kv.Key;
|
string key = kv.Key;
|
||||||
string value = kv.Value is string ? kv.Value.ToString() : JsonConvert.SerializeObject(kv.Value);
|
string value;
|
||||||
|
switch (kv.Value.GetType().Name)
|
||||||
|
{
|
||||||
|
case "String":
|
||||||
|
case "JsonElement":
|
||||||
|
value = kv.Value.ToString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = JsonConvert.SerializeObject(kv.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
_context.Add(key, value);
|
_context.Add(key, value);
|
||||||
}
|
}
|
||||||
_parentResult = parentResult;
|
_parentResult = parentResult;
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
|
|
||||||
namespace RulesEngine.Enums
|
|
||||||
{
|
|
||||||
public enum ActionTriggerType
|
|
||||||
{
|
|
||||||
onSuccess,
|
|
||||||
onFailure
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using RulesEngine.Enums;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
@ -45,7 +44,7 @@ namespace RulesEngine.Models
|
||||||
public IEnumerable<Rule> Rules { get; set; }
|
public IEnumerable<Rule> Rules { get; set; }
|
||||||
public IEnumerable<ScopedParam> LocalParams { get; set; }
|
public IEnumerable<ScopedParam> LocalParams { get; set; }
|
||||||
public string Expression { get; set; }
|
public string Expression { get; set; }
|
||||||
public Dictionary<ActionTriggerType, ActionInfo> Actions { get; set; }
|
public RuleActions Actions { get; set; }
|
||||||
public string SuccessEvent { get; set; }
|
public string SuccessEvent { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace RulesEngine.Models
|
||||||
|
{
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
|
public class RuleActions
|
||||||
|
{
|
||||||
|
public ActionInfo OnSuccess { get; set; }
|
||||||
|
public ActionInfo OnFailure { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,13 +2,11 @@
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using RulesEngine.Actions;
|
using RulesEngine.Actions;
|
||||||
using RulesEngine.Enums;
|
|
||||||
using RulesEngine.Exceptions;
|
using RulesEngine.Exceptions;
|
||||||
using RulesEngine.ExpressionBuilders;
|
using RulesEngine.ExpressionBuilders;
|
||||||
using RulesEngine.Interfaces;
|
using RulesEngine.Interfaces;
|
||||||
|
@ -104,18 +102,26 @@ namespace RulesEngine
|
||||||
public async ValueTask<List<RuleResultTree>> ExecuteAllRulesAsync(string workflowName, params RuleParameter[] ruleParams)
|
public async ValueTask<List<RuleResultTree>> ExecuteAllRulesAsync(string workflowName, params RuleParameter[] ruleParams)
|
||||||
{
|
{
|
||||||
var ruleResultList = ValidateWorkflowAndExecuteRule(workflowName, ruleParams);
|
var ruleResultList = ValidateWorkflowAndExecuteRule(workflowName, ruleParams);
|
||||||
|
await ExecuteActionAsync(ruleResultList);
|
||||||
|
return ruleResultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ValueTask ExecuteActionAsync(IEnumerable<RuleResultTree> ruleResultList)
|
||||||
|
{
|
||||||
foreach (var ruleResult in ruleResultList)
|
foreach (var ruleResult in ruleResultList)
|
||||||
{
|
{
|
||||||
|
if(ruleResult.ChildResults != null)
|
||||||
|
{
|
||||||
|
await ExecuteActionAsync(ruleResult.ChildResults);
|
||||||
|
}
|
||||||
var actionResult = await ExecuteActionForRuleResult(ruleResult, false);
|
var actionResult = await ExecuteActionForRuleResult(ruleResult, false);
|
||||||
ruleResult.ActionResult = new ActionResult {
|
ruleResult.ActionResult = new ActionResult {
|
||||||
Output = actionResult.Output,
|
Output = actionResult.Output,
|
||||||
Exception = actionResult.Exception
|
Exception = actionResult.Exception
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return ruleResultList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async ValueTask<ActionRuleResult> ExecuteActionWorkflowAsync(string workflowName, string ruleName, RuleParameter[] ruleParameters)
|
public async ValueTask<ActionRuleResult> ExecuteActionWorkflowAsync(string workflowName, string ruleName, RuleParameter[] ruleParameters)
|
||||||
{
|
{
|
||||||
var compiledRule = CompileRule(workflowName, ruleName, ruleParameters);
|
var compiledRule = CompileRule(workflowName, ruleName, ruleParameters);
|
||||||
|
@ -125,11 +131,11 @@ namespace RulesEngine
|
||||||
|
|
||||||
private async ValueTask<ActionRuleResult> ExecuteActionForRuleResult(RuleResultTree resultTree, bool includeRuleResults = false)
|
private async ValueTask<ActionRuleResult> ExecuteActionForRuleResult(RuleResultTree resultTree, bool includeRuleResults = false)
|
||||||
{
|
{
|
||||||
var triggerType = resultTree?.IsSuccess == true ? ActionTriggerType.onSuccess : ActionTriggerType.onFailure;
|
var ruleActions = resultTree?.Rule?.Actions;
|
||||||
|
var actionInfo = resultTree?.IsSuccess == true ? ruleActions?.OnSuccess : ruleActions?.OnFailure;
|
||||||
|
|
||||||
if (resultTree?.Rule?.Actions != null && resultTree.Rule.Actions.ContainsKey(triggerType))
|
if (actionInfo != null)
|
||||||
{
|
{
|
||||||
var actionInfo = resultTree.Rule.Actions[triggerType];
|
|
||||||
var action = _actionFactory.Get(actionInfo.Name);
|
var action = _actionFactory.Get(actionInfo.Name);
|
||||||
var ruleParameters = resultTree.Inputs.Select(kv => new RuleParameter(kv.Key, kv.Value)).ToArray();
|
var ruleParameters = resultTree.Inputs.Select(kv => new RuleParameter(kv.Key, kv.Value)).ToArray();
|
||||||
return await action.ExecuteAndReturnResultAsync(new ActionContext(actionInfo.Context, resultTree), ruleParameters, includeRuleResults);
|
return await action.ExecuteAndReturnResultAsync(new ActionContext(actionInfo.Context, resultTree), ruleParameters, includeRuleResults);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Version>3.2.0</Version>
|
<Version>3.3.0</Version>
|
||||||
<Copyright>Copyright (c) Microsoft Corporation.</Copyright>
|
<Copyright>Copyright (c) Microsoft Corporation.</Copyright>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<PackageProjectUrl>https://github.com/microsoft/RulesEngine</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/microsoft/RulesEngine</PackageProjectUrl>
|
||||||
|
@ -27,12 +27,12 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FastExpressionCompiler" Version="3.2.0" />
|
<PackageReference Include="FastExpressionCompiler" Version="3.2.0" />
|
||||||
<PackageReference Include="FluentValidation" Version="10.2.3" />
|
<PackageReference Include="FluentValidation" Version="10.3.0" />
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.6" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="System.Linq" Version="4.3.0" />
|
<PackageReference Include="System.Linq" Version="4.3.0" />
|
||||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.10" />
|
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.11" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.6" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.6" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using RulesEngine.Models;
|
||||||
|
using RulesEngine.UnitTest.ActionTests.MockClass;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace RulesEngine.UnitTest.ActionTests
|
||||||
|
{
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
|
public class CustomActionTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task CustomActionOnRuleMustHaveContextValues()
|
||||||
|
{
|
||||||
|
var workflows = GetWorkflowRules();
|
||||||
|
var re = new RulesEngine(workflows, null, reSettings: new ReSettings {
|
||||||
|
CustomActions = new Dictionary<string, System.Func<Actions.ActionBase>> {
|
||||||
|
|
||||||
|
{ "ReturnContext", () => new ReturnContextAction() }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await re.ExecuteAllRulesAsync("successReturnContextAction", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
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<WorkflowRules[]>(workflowStr,serializationOptions);
|
||||||
|
|
||||||
|
|
||||||
|
var re = new RulesEngine(workflows, null, reSettings: new ReSettings {
|
||||||
|
CustomActions = new Dictionary<string, System.Func<Actions.ActionBase>> {
|
||||||
|
|
||||||
|
{ "ReturnContext", () => new ReturnContextAction() }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var result = await re.ExecuteAllRulesAsync("successReturnContextAction", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorkflowRules[] GetWorkflowRules()
|
||||||
|
{
|
||||||
|
return new WorkflowRules[] {
|
||||||
|
new WorkflowRules {
|
||||||
|
WorkflowName = "successReturnContextAction",
|
||||||
|
Rules = new Rule[] {
|
||||||
|
new Rule {
|
||||||
|
RuleName = "trueRule",
|
||||||
|
Expression = "input1 == true",
|
||||||
|
Actions = new RuleActions() {
|
||||||
|
OnSuccess = new ActionInfo {
|
||||||
|
Name = "ReturnContext",
|
||||||
|
Context = new Dictionary<string, object> {
|
||||||
|
{"stringContext", "hello"},
|
||||||
|
{"intContext",1 },
|
||||||
|
{"objectContext", new { a = "hello", b = 123 } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using RulesEngine.Actions;
|
||||||
|
using RulesEngine.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RulesEngine.UnitTest.ActionTests.MockClass
|
||||||
|
{
|
||||||
|
public class ReturnContextAction : ActionBase
|
||||||
|
{
|
||||||
|
public override ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)
|
||||||
|
{
|
||||||
|
var stringContext = context.GetContext<string>("stringContext");
|
||||||
|
var intContext = context.GetContext<int>("intContext");
|
||||||
|
var objectContext = context.GetContext<object>("objectContext");
|
||||||
|
|
||||||
|
return new ValueTask<object>(new {
|
||||||
|
stringContext,
|
||||||
|
intContext,
|
||||||
|
objectContext
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
using RulesEngine.Enums;
|
|
||||||
using RulesEngine.Models;
|
using RulesEngine.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -81,27 +79,27 @@ namespace RulesEngine.UnitTest
|
||||||
RuleName = "ExpressionOutputRuleTest",
|
RuleName = "ExpressionOutputRuleTest",
|
||||||
RuleExpressionType = RuleExpressionType.LambdaExpression,
|
RuleExpressionType = RuleExpressionType.LambdaExpression,
|
||||||
Expression = "1 == 1",
|
Expression = "1 == 1",
|
||||||
Actions = new Dictionary<ActionTriggerType, ActionInfo>{
|
Actions = new RuleActions{
|
||||||
{ ActionTriggerType.onSuccess, new ActionInfo{
|
OnSuccess = new ActionInfo{
|
||||||
Name = "OutputExpression",
|
Name = "OutputExpression",
|
||||||
Context = new Dictionary<string, object>{
|
Context = new Dictionary<string, object>{
|
||||||
{"expression", "2*2"}
|
{"expression", "2*2"}
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Rule{
|
new Rule{
|
||||||
RuleName = "EvaluateRuleTest",
|
RuleName = "EvaluateRuleTest",
|
||||||
RuleExpressionType = RuleExpressionType.LambdaExpression,
|
RuleExpressionType = RuleExpressionType.LambdaExpression,
|
||||||
Expression = "1 == 1",
|
Expression = "1 == 1",
|
||||||
Actions = new Dictionary<ActionTriggerType, ActionInfo>{
|
Actions = new RuleActions{
|
||||||
{ ActionTriggerType.onSuccess, new ActionInfo{
|
OnSuccess = new ActionInfo{
|
||||||
Name = "EvaluateRule",
|
Name = "EvaluateRule",
|
||||||
Context = new Dictionary<string, object>{
|
Context = new Dictionary<string, object>{
|
||||||
{"workflowName", "ActionWorkflow"},
|
{"workflowName", "ActionWorkflow"},
|
||||||
{"ruleName","ExpressionOutputRuleTest"}
|
{"ruleName","ExpressionOutputRuleTest"}
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -712,7 +712,6 @@ namespace RulesEngine.UnitTest
|
||||||
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\",\"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 re = new RulesEngine(new string[] { workflowStr },null,null);
|
var re = new RulesEngine(new string[] { workflowStr },null,null);
|
||||||
// re.AddWorkflow(workflowStr);
|
|
||||||
|
|
||||||
dynamic input1 = new ExpandoObject();
|
dynamic input1 = new ExpandoObject();
|
||||||
input1.hello = new ExpandoObject();
|
input1.hello = new ExpandoObject();
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
using RulesEngine.Models;
|
using RulesEngine.Models;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -21,8 +23,8 @@ namespace RulesEngine.UnitTest
|
||||||
public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode mode)
|
public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode mode)
|
||||||
{
|
{
|
||||||
var workflows = GetWorkflows();
|
var workflows = GetWorkflows();
|
||||||
var reSettings = new ReSettings { NestedRuleExecutionMode = mode};
|
var reSettings = new ReSettings { NestedRuleExecutionMode = mode };
|
||||||
var rulesEngine = new RulesEngine(workflows, reSettings:reSettings);
|
var rulesEngine = new RulesEngine(workflows, reSettings: reSettings);
|
||||||
dynamic input1 = new ExpandoObject();
|
dynamic input1 = new ExpandoObject();
|
||||||
input1.trueValue = true;
|
input1.trueValue = true;
|
||||||
|
|
||||||
|
@ -32,10 +34,10 @@ namespace RulesEngine.UnitTest
|
||||||
Assert.All(andResults,
|
Assert.All(andResults,
|
||||||
c => Assert.False(c.IsSuccess)
|
c => Assert.False(c.IsSuccess)
|
||||||
);
|
);
|
||||||
Assert.All(orResults,
|
Assert.All(orResults,
|
||||||
c => Assert.True(c.IsSuccess));
|
c => Assert.True(c.IsSuccess));
|
||||||
|
|
||||||
if(mode == NestedRuleExecutionMode.All)
|
if (mode == NestedRuleExecutionMode.All)
|
||||||
{
|
{
|
||||||
Assert.All(andResults,
|
Assert.All(andResults,
|
||||||
c => Assert.Equal(c.Rule.Rules.Count(), c.ChildResults.Count()));
|
c => Assert.Equal(c.Rule.Rules.Count(), c.ChildResults.Count()));
|
||||||
|
@ -60,9 +62,49 @@ namespace RulesEngine.UnitTest
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
private async Task NestedRulesWithNestedActions_ReturnsCorrectResults()
|
||||||
|
{
|
||||||
|
var workflows = GetWorkflows();
|
||||||
|
var reSettings = new ReSettings { };
|
||||||
|
var rulesEngine = new RulesEngine(workflows, reSettings: reSettings);
|
||||||
|
dynamic input1 = new ExpandoObject();
|
||||||
|
input1.trueValue = true;
|
||||||
|
|
||||||
|
List<RuleResultTree> result = await rulesEngine.ExecuteAllRulesAsync("NestedRulesActionsTest", input1);
|
||||||
|
|
||||||
|
Assert.False(result[0].IsSuccess);
|
||||||
|
Assert.Equal(input1.trueValue, result[0].ActionResult.Output);
|
||||||
|
Assert.All(result[0].ChildResults, (childResult) => Assert.Equal(input1.trueValue, childResult.ActionResult.Output));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson_ReturnsCorrectResults()
|
||||||
|
{
|
||||||
|
var workflows = GetWorkflows();
|
||||||
|
var workflowStr = JsonConvert.SerializeObject(workflows);
|
||||||
|
|
||||||
|
var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } };
|
||||||
|
|
||||||
|
|
||||||
|
var workflowsViaTextJson = System.Text.Json.JsonSerializer.Deserialize<WorkflowRules[]>(workflowStr, serializationOptions);
|
||||||
|
|
||||||
|
var reSettings = new ReSettings { };
|
||||||
|
var rulesEngine = new RulesEngine(workflowsViaTextJson, reSettings: reSettings);
|
||||||
|
dynamic input1 = new ExpandoObject();
|
||||||
|
input1.trueValue = true;
|
||||||
|
|
||||||
|
List<RuleResultTree> result = await rulesEngine.ExecuteAllRulesAsync("NestedRulesActionsTest", input1);
|
||||||
|
|
||||||
|
Assert.False(result[0].IsSuccess);
|
||||||
|
Assert.Equal(input1.trueValue, result[0].ActionResult.Output);
|
||||||
|
Assert.All(result[0].ChildResults, (childResult) => Assert.Equal(input1.trueValue, childResult.ActionResult.Output));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,7 +176,50 @@ namespace RulesEngine.UnitTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
new WorkflowRules {
|
||||||
|
WorkflowName = "NestedRulesActionsTest",
|
||||||
|
Rules = new Rule[] {
|
||||||
|
new Rule {
|
||||||
|
RuleName = "AndRuleTrueFalse",
|
||||||
|
Operator = "And",
|
||||||
|
Rules = new Rule[] {
|
||||||
|
new Rule{
|
||||||
|
RuleName = "trueRule1",
|
||||||
|
Expression = "input1.TrueValue == true",
|
||||||
|
Actions = new RuleActions {
|
||||||
|
OnSuccess = new ActionInfo{
|
||||||
|
Name = "OutputExpression",
|
||||||
|
Context = new Dictionary<string, object> {
|
||||||
|
{ "Expression", "input1.TrueValue" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Rule {
|
||||||
|
RuleName = "falseRule1",
|
||||||
|
Expression = "input1.TrueValue == false",
|
||||||
|
Actions = new RuleActions {
|
||||||
|
OnFailure = new ActionInfo{
|
||||||
|
Name = "OutputExpression",
|
||||||
|
Context = new Dictionary<string, object> {
|
||||||
|
{ "Expression", "input1.TrueValue" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Actions = new RuleActions {
|
||||||
|
OnFailure = new ActionInfo{
|
||||||
|
Name = "OutputExpression",
|
||||||
|
Context = new Dictionary<string, object> {
|
||||||
|
{ "Expression", "input1.TrueValue" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace RulesEngine.UnitTest
|
||||||
public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, bool[] expectedRuleResults)
|
public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, bool[] expectedRuleResults)
|
||||||
{
|
{
|
||||||
var workflows = GetWorkflows();
|
var workflows = GetWorkflows();
|
||||||
var rulesEngine = new RulesEngine(workflows);
|
var rulesEngine = new RulesEngine(workflows, reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false });
|
||||||
var input1 = new {
|
var input1 = new {
|
||||||
TrueValue = true
|
TrueValue = true
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,7 @@ namespace RulesEngine.UnitTest
|
||||||
public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, bool[] expectedRuleResults)
|
public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, bool[] expectedRuleResults)
|
||||||
{
|
{
|
||||||
var workflow = GetWorkflows().Single(c => c.WorkflowName == workflowName);
|
var workflow = GetWorkflows().Single(c => c.WorkflowName == workflowName);
|
||||||
var rulesEngine = new RulesEngine();
|
var rulesEngine = new RulesEngine(reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false});
|
||||||
rulesEngine.AddWorkflow(workflow);
|
rulesEngine.AddWorkflow(workflow);
|
||||||
var input1 = new {
|
var input1 = new {
|
||||||
TrueValue = true
|
TrueValue = true
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="Moq" Version="4.16.1" />
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
|
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="coverlet.collector" Version="3.0.3">
|
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|
Loading…
Reference in New Issue