Abbasc52/null rule param fix (#119)
* Fixed scoped parameter throwing exception on compilation error * Fixed null RuleParameter throwing exception * Replaced thrown Exceptions with RuleExceptionpull/129/head v3.1.0-preview.4
parent
65d2abbd8b
commit
f3ac4316df
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace RulesEngine.Exceptions
|
||||
{
|
||||
public class ExpressionParserException: Exception
|
||||
{
|
||||
public ExpressionParserException(string message, string expression) : base(message)
|
||||
{
|
||||
Data.Add("Expression", expression);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace RulesEngine.Exceptions
|
||||
{
|
||||
public class RuleException : Exception
|
||||
{
|
||||
public RuleException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public RuleException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace RulesEngine.Exceptions
|
||||
{
|
||||
public class ScopedParamException: Exception
|
||||
{
|
||||
public ScopedParamException(string message, Exception innerException, string scopedParamName): base(message,innerException)
|
||||
{
|
||||
Data.Add("ScopedParamName", scopedParamName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using RulesEngine.Exceptions;
|
||||
using RulesEngine.HelperFunctions;
|
||||
using RulesEngine.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core.Exceptions;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace RulesEngine.ExpressionBuilders
|
||||
|
@ -43,7 +45,15 @@ namespace RulesEngine.ExpressionBuilders
|
|||
|
||||
internal override LambdaExpression Parse(string expression, ParameterExpression[] parameters, Type returnType)
|
||||
{
|
||||
return _ruleExpressionParser.Parse(expression, parameters, returnType);
|
||||
try
|
||||
{
|
||||
return _ruleExpressionParser.Parse(expression, parameters, returnType);
|
||||
}
|
||||
catch(ParseException ex)
|
||||
{
|
||||
throw new ExpressionParserException(ex.Message, expression);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal override Func<object[],Dictionary<string,object>> CompileScopedParams(RuleParameter[] ruleParameters, RuleExpressionParameter[] scopedParameters)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using RulesEngine.Exceptions;
|
||||
using RulesEngine.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -27,9 +28,9 @@ namespace RulesEngine.HelperFunctions
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleRuleException(ex, rule, reSettings);
|
||||
isSuccess = false;
|
||||
exceptionMessage = GetExceptionMessage($"Error while executing rule : {rule?.RuleName} - {ex.Message}", reSettings);
|
||||
HandleRuleException(new RuleException(exceptionMessage,ex), rule, reSettings);
|
||||
isSuccess = false;
|
||||
}
|
||||
|
||||
return new RuleResultTree {
|
||||
|
@ -44,6 +45,11 @@ namespace RulesEngine.HelperFunctions
|
|||
|
||||
}
|
||||
|
||||
internal static RuleFunc<RuleResultTree> 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, ReSettings reSettings)
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace RulesEngine.Models
|
|||
public RuleParameter(string name, object value)
|
||||
{
|
||||
Value = Utils.GetTypedObject(value);
|
||||
Init(name, Value.GetType());
|
||||
Init(name, Value?.GetType());
|
||||
}
|
||||
|
||||
internal RuleParameter(string name, Type type)
|
||||
|
@ -29,7 +29,7 @@ namespace RulesEngine.Models
|
|||
private void Init(string name, Type type)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
Type = type ?? typeof(object);
|
||||
ParameterExpression = Expression.Parameter(Type, Name);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RulesEngine.Exceptions;
|
||||
using RulesEngine.ExpressionBuilders;
|
||||
using RulesEngine.HelperFunctions;
|
||||
using RulesEngine.Models;
|
||||
|
@ -57,12 +58,14 @@ namespace RulesEngine
|
|||
/// <returns>Compiled func delegate</returns>
|
||||
internal RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleParameter[] ruleParams, ScopedParam[] globalParams)
|
||||
{
|
||||
if (rule == null)
|
||||
{
|
||||
var ex = new ArgumentNullException(nameof(rule));
|
||||
_logger.LogError(ex.Message);
|
||||
throw ex;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (rule == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(rule));
|
||||
}
|
||||
var globalParamExp = GetRuleExpressionParameters(rule.RuleExpressionType,globalParams, ruleParams);
|
||||
var extendedRuleParams = ruleParams.Concat(globalParamExp.Select(c => new RuleParameter(c.ParameterExpression.Name,c.ParameterExpression.Type)))
|
||||
.ToArray();
|
||||
|
@ -71,8 +74,9 @@ namespace RulesEngine
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
throw;
|
||||
var message = $"Error while compiling rule `{rule.RuleName}`: {ex.Message}";
|
||||
_logger.LogError(message);
|
||||
return Helpers.ToRuleExceptionResult(_reSettings, rule, new RuleException(message, ex));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,13 +129,21 @@ namespace RulesEngine
|
|||
|
||||
foreach (var lp in localParams)
|
||||
{
|
||||
var lpExpression = expressionBuilder.Parse(lp.Expression, parameters.ToArray(), null).Body;
|
||||
var ruleExpParam = new RuleExpressionParameter() {
|
||||
ParameterExpression = Expression.Parameter(lpExpression.Type, lp.Name),
|
||||
ValueExpression = lpExpression
|
||||
};
|
||||
parameters.Add(ruleExpParam.ParameterExpression);
|
||||
ruleExpParams.Add(ruleExpParam);
|
||||
try
|
||||
{
|
||||
var lpExpression = expressionBuilder.Parse(lp.Expression, parameters.ToArray(), null).Body;
|
||||
var ruleExpParam = new RuleExpressionParameter() {
|
||||
ParameterExpression = Expression.Parameter(lpExpression.Type, lp.Name),
|
||||
ValueExpression = lpExpression
|
||||
};
|
||||
parameters.Add(ruleExpParam.ParameterExpression);
|
||||
ruleExpParams.Add(ruleExpParam);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
var message = $"{ex.Message}, in ScopedParam: {lp.Name}";
|
||||
throw new RuleException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ruleExpParams.ToArray();
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace RulesEngine
|
|||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public RulesEngine(string[] jsonConfig, ILogger logger, ReSettings 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<WorkflowRules>(item)).ToArray();
|
||||
AddWorkflow(workflowRules);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>3.1.0-preview.3</Version>
|
||||
<Version>3.1.0-preview.4</Version>
|
||||
<Copyright>Copyright (c) Microsoft Corporation.</Copyright>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageProjectUrl>https://github.com/microsoft/RulesEngine</PackageProjectUrl>
|
||||
|
|
|
@ -279,7 +279,6 @@ namespace RulesEngine.UnitTest
|
|||
Assert.Contains(result, c => c.IsSuccess);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData("rules4.json")]
|
||||
public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Success(string ruleFileName)
|
||||
|
@ -417,7 +416,7 @@ namespace RulesEngine.UnitTest
|
|||
|
||||
var utils = new TestInstanceUtils();
|
||||
|
||||
await Assert.ThrowsAsync<System.Linq.Dynamic.Core.Exceptions.ParseException>(async () => {
|
||||
await Assert.ThrowsAsync<RuleException>(async () => {
|
||||
var result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
|
||||
});
|
||||
}
|
||||
|
@ -505,7 +504,7 @@ namespace RulesEngine.UnitTest
|
|||
Country = null
|
||||
};
|
||||
|
||||
_ = await Assert.ThrowsAsync<ArgumentNullException>(async () => await re.ExecuteAllRulesAsync("TestWorkflow", input));
|
||||
_ = await Assert.ThrowsAsync<RuleException>(async () => await re.ExecuteAllRulesAsync("TestWorkflow", input));
|
||||
|
||||
}
|
||||
|
||||
|
@ -606,6 +605,71 @@ namespace RulesEngine.UnitTest
|
|||
Assert.True(result2.All(c => c.IsSuccess == false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteRule_WithNullInput_ShouldNotThrowException()
|
||||
{
|
||||
var workflow = new WorkflowRules {
|
||||
WorkflowName = "Test",
|
||||
Rules = new Rule[]{
|
||||
new Rule {
|
||||
RuleName = "RuleWithLocalParam",
|
||||
|
||||
RuleExpressionType = RuleExpressionType.LambdaExpression,
|
||||
Expression = "input1 == null || input1.hello.world = \"wow\""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var re = new RulesEngine();
|
||||
re.AddWorkflow(workflow);
|
||||
|
||||
var result1 = await re.ExecuteAllRulesAsync("Test", new RuleParameter("input1", value:null));
|
||||
Assert.True(result1.All(c => c.IsSuccess));
|
||||
|
||||
|
||||
var result2 = await re.ExecuteAllRulesAsync("Test",new object[] { null });
|
||||
Assert.True(result2.All(c => c.IsSuccess));
|
||||
|
||||
dynamic input1 = new ExpandoObject();
|
||||
input1.hello = new ExpandoObject();
|
||||
input1.hello.world = "wow";
|
||||
|
||||
List<RuleResultTree> result3 = await re.ExecuteAllRulesAsync("Test", input1);
|
||||
Assert.True(result3.All(c => c.IsSuccess));
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteRule_SpecialCharInWorkflowName_RunsSuccessfully()
|
||||
{
|
||||
var workflow = new WorkflowRules {
|
||||
WorkflowName = "Exámple",
|
||||
Rules = new Rule[]{
|
||||
new Rule {
|
||||
RuleName = "RuleWithLocalParam",
|
||||
|
||||
RuleExpressionType = RuleExpressionType.LambdaExpression,
|
||||
Expression = "input1 == null || input1.hello.world = \"wow\""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
// re.AddWorkflow(workflowStr);
|
||||
|
||||
dynamic input1 = new ExpandoObject();
|
||||
input1.hello = new ExpandoObject();
|
||||
input1.hello.world = "wow";
|
||||
|
||||
List<RuleResultTree> result3 = await re.ExecuteAllRulesAsync("Exámple", input1);
|
||||
Assert.True(result3.All(c => c.IsSuccess));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private RulesEngine CreateRulesEngine(WorkflowRules workflow)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(workflow);
|
||||
|
|
|
@ -98,6 +98,23 @@ namespace RulesEngine.UnitTest
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GlobalParamsOnly")]
|
||||
[InlineData("LocalParamsOnly")]
|
||||
public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName)
|
||||
{
|
||||
var workflows = GetWorkflowRulesList();
|
||||
|
||||
var engine = new RulesEngine(new string[] { }, null);
|
||||
engine.AddWorkflow(workflows);
|
||||
|
||||
var input = new { };
|
||||
var result = await engine.ExecuteAllRulesAsync(workflowName, input);
|
||||
|
||||
Assert.All(result, c => Assert.False(c.IsSuccess));
|
||||
|
||||
}
|
||||
|
||||
private void CheckResultTreeContainsAllInputs(string workflowName, List<RuleResultTree> result)
|
||||
{
|
||||
var workflow = GetWorkflowRulesList().Single(c => c.WorkflowName == workflowName);
|
||||
|
|
Loading…
Reference in New Issue