* fixed namespace issue (#82)

* fixed namespace issue
* added editorconfig
* cleaned up files based on editorconfig
pull/93/head v3.0.1
Abbas Cyclewala 2020-12-23 11:04:10 +05:30 committed by GitHub
parent 2886bca157
commit 9c8b9d484e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 539 additions and 348 deletions

134
.editorconfig Normal file
View File

@ -0,0 +1,134 @@
# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\Users\abcy\source\repos\RulesEngine codebase based on best match to current usage at 22-12-2020
# You can modify the rules from these initially generated values to suit your own policies
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
[*.cs]
#Core editorconfig formatting - indentation
#use soft tabs (spaces) for indentation
indent_style = space
#Formatting - indentation options
#indent switch case contents.
csharp_indent_case_contents = true
#indent switch labels
csharp_indent_switch_labels = true
#Formatting - new line options
#place catch statements on a new line
csharp_new_line_before_catch = true
#place else statements on a new line
csharp_new_line_before_else = true
#require members of anonymous types to be on separate lines
csharp_new_line_before_members_in_anonymous_types = true
#require members of object intializers to be on separate lines
csharp_new_line_before_members_in_object_initializers = true
#require braces to be on a new line for methods, control_blocks, and types (also known as "Allman" style)
csharp_new_line_before_open_brace = methods, control_blocks, types
#Formatting - organize using options
#do not place System.* using directives before other using directives
dotnet_sort_system_directives_first = false
#Formatting - spacing options
#require NO space between a cast and the value
csharp_space_after_cast = false
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_after_colon_in_inheritance_clause = true
#require a space after a keyword in a control flow statement such as a for loop
csharp_space_after_keywords_in_control_flow_statements = true
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_before_colon_in_inheritance_clause = true
#remove space within empty argument list parentheses
csharp_space_between_method_call_empty_parameter_list_parentheses = false
#remove space between method call name and opening parenthesis
csharp_space_between_method_call_name_and_opening_parenthesis = false
#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
csharp_space_between_method_call_parameter_list_parentheses = false
#remove space within empty parameter list parentheses for a method declaration
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
csharp_space_between_method_declaration_parameter_list_parentheses = false
#Formatting - wrapping options
#leave code block on single line
csharp_preserve_single_line_blocks = true
#leave statements and member declarations on the same line
csharp_preserve_single_line_statements = true
#Style - Code block preferences
#prefer curly braces even for one line of code
csharp_prefer_braces = true:suggestion
#Style - expression bodied member options
#prefer block bodies for constructors
csharp_style_expression_bodied_constructors = false:suggestion
#prefer block bodies for methods
csharp_style_expression_bodied_methods = false:suggestion
#Style - expression level options
#prefer out variables to be declared inline in the argument list of a method call when possible
csharp_style_inlined_variable_declaration = true:suggestion
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_member_access = true:suggestion
#Style - Expression-level preferences
#prefer objects to be initialized using object initializers when possible
dotnet_style_object_initializer = true:suggestion
#prefer inferred anonymous type member names
dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
#Style - implicit and explicit types
#prefer var over explicit type in all cases, unless overridden by another code style rule
csharp_style_var_elsewhere = true:suggestion
#prefer var is used to declare variables with built-in system types such as int
csharp_style_var_for_built_in_types = true:suggestion
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
csharp_style_var_when_type_is_apparent = true:suggestion
#Style - language keyword and framework type options
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
#Style - Miscellaneous preferences
#prefer anonymous functions over local functions
csharp_style_pattern_local_over_anonymous_function = false:suggestion
#Style - modifier options
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
#Style - Modifier preferences
#when this rule is set to a list of modifiers, prefer the specified ordering.
csharp_preferred_modifier_order = public,private,internal,readonly,static,async,override,sealed:suggestion
#Style - qualification options
#prefer fields not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_field = false:suggestion
#prefer methods not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_method = false:suggestion
#prefer properties not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_property = false:suggestion
#file header
[*.{cs,vb}]
file_header_template = Copyright (c) Microsoft Corporation.\nLicensed under the MIT License.
file_header_template_style = prepend:error
file_header_template_style = replace:suggestion

View File

@ -2,9 +2,9 @@ name: "Code scanning"
on:
push:
branches: [ master ]
branches: [ main ]
pull_request:
branches: [ master ]
branches: [ main ]
schedule:
- cron: '0 18 * * 1'

View File

@ -2,9 +2,9 @@ name: build
on:
push:
branches: [ master ]
branches: [ main ]
pull_request:
branches: [ master, develop ]
branches: [ main, develop ]
jobs:
build:

View File

@ -1,6 +1,11 @@
# CHANGELOG
All notable changes to this project will be documented in this file.
## [3.0.1]
- Moved ActionResult and ActionRuleResult under RulesEngine.Models namespace
## [3.0.0]
### Major Enhancements
- Added support for Actions. More details on [actions wiki](https://github.com/microsoft/RulesEngine/wiki/Actions)

View File

@ -14,6 +14,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoApp", "demo\DemoApp\Dem
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{019DF693-8442-45B4-88C3-55CB7AFCB42E}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
CHANGELOG.md = CHANGELOG.md
global.json = global.json
README.md = README.md

View File

@ -1,4 +1,7 @@
using BenchmarkDotNet.Attributes;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Newtonsoft.Json;
using RulesEngine.Models;
@ -14,7 +17,8 @@ namespace RulesEngineBenchmark
private readonly RulesEngine.RulesEngine rulesEngine;
private readonly object ruleInput;
private readonly List<WorkflowRules> workflows;
class ListItem
private class ListItem
{
public int Id { get; set; }
public string Value { get; set; }
@ -25,21 +29,21 @@ namespace RulesEngineBenchmark
{
var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "NestedInputDemo.json", SearchOption.AllDirectories);
if (files == null || files.Length == 0)
{
throw new Exception("Rules not found.");
}
var fileData = File.ReadAllText(files[0]);
workflows = JsonConvert.DeserializeObject<List<WorkflowRules>>(fileData);
rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null,new ReSettings {
rulesEngine = new RulesEngine.RulesEngine(workflows.ToArray(), null, new ReSettings {
EnableFormattedErrorMessage = false,
EnableLocalParams = false
});
ruleInput = new
{
ruleInput = new {
SimpleProp = "simpleProp",
NestedProp = new
{
NestedProp = new {
SimpleProp = "nestedSimpleProp",
ListProp = new List<ListItem>
{
@ -67,15 +71,15 @@ namespace RulesEngineBenchmark
{
foreach (var workflow in workflows)
{
List<RuleResultTree> resultList = rulesEngine.ExecuteAllRulesAsync(workflow.WorkflowName, ruleInput).Result;
_ = rulesEngine.ExecuteAllRulesAsync(workflow.WorkflowName, ruleInput).Result;
}
}
}
class Program
public class Program
{
static void Main(string[] args)
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<REBenchmark>();
_ = BenchmarkRunner.Run<REBenchmark>();
}
}
}

View File

@ -1,4 +1,7 @@
using Newtonsoft.Json;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using RulesEngine.Models;
using System;
@ -44,13 +47,11 @@ namespace DemoApp
List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync("Discount", inputs).Result;
resultList.OnSuccess((eventName) =>
{
resultList.OnSuccess((eventName) => {
discountOffered = $"Discount offered is {eventName} % over MRP.";
});
resultList.OnFail(() =>
{
resultList.OnFail(() => {
discountOffered = "The user is not eligible for any discount.";
});

View File

@ -1,14 +1,17 @@
using Newtonsoft.Json;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Newtonsoft.Json;
using RulesEngine.Extensions;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
namespace DemoApp
{
class ListItem {
internal class ListItem
{
public int Id { get; set; }
public string Value { get; set; }
}
@ -18,10 +21,9 @@ namespace DemoApp
public void Run()
{
Console.WriteLine($"Running {nameof(NestedInputDemo)}....");
var nestedInput = new {
var nestedInput = new {
SimpleProp = "simpleProp",
NestedProp = new
{
NestedProp = new {
SimpleProp = "nestedSimpleProp",
ListProp = new List<ListItem>
{
@ -37,29 +39,29 @@ namespace DemoApp
}
}
}
};
var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "NestedInputDemo.json", SearchOption.AllDirectories);
if (files == null || files.Length == 0)
{
throw new Exception("Rules not found.");
}
var fileData = File.ReadAllText(files[0]);
var workflowRules = JsonConvert.DeserializeObject<List<WorkflowRules>>(fileData);
var bre = new RulesEngine.RulesEngine(workflowRules.ToArray(),null);
foreach(var workflow in workflowRules)
var bre = new RulesEngine.RulesEngine(workflowRules.ToArray(), null);
foreach (var workflow in workflowRules)
{
List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync(workflow.WorkflowName, nestedInput).Result;
var resultList = bre.ExecuteAllRulesAsync(workflow.WorkflowName, nestedInput).Result;
resultList.OnSuccess((eventName) =>
{
resultList.OnSuccess((eventName) => {
Console.WriteLine($"{workflow.WorkflowName} evaluation resulted in succees - {eventName}");
}).OnFail(() =>
{
}).OnFail(() => {
Console.WriteLine($"{workflow.WorkflowName} evaluation resulted in failure");
});
}

View File

@ -3,9 +3,9 @@
namespace DemoApp
{
static class Program
public static class Program
{
static void Main(string[] args)
public static void Main(string[] args)
{
new BasicDemo().Run();
new NestedInputDemo().Run();

View File

@ -1,25 +1,30 @@
using System;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using RulesEngine.Models;
namespace RulesEngine.Actions
{
public abstract class ActionBase
{
internal virtual async ValueTask<ActionRuleResult> ExecuteAndReturnResultAsync(ActionContext context, RuleParameter[] ruleParameters,bool includeRuleResults=false){
ActionRuleResult result = new ActionRuleResult();
internal async virtual ValueTask<ActionRuleResult> ExecuteAndReturnResultAsync(ActionContext context, RuleParameter[] ruleParameters, bool includeRuleResults = false)
{
var result = new ActionRuleResult();
try
{
result.Output = await Run(context, ruleParameters);
}
catch(Exception ex)
catch (Exception ex)
{
result.Exception = new Exception($"Exception while executing {this.GetType().Name}: {ex.Message}",ex);
result.Exception = new Exception($"Exception while executing {GetType().Name}: {ex.Message}", ex);
}
finally
{
if(includeRuleResults){
if (includeRuleResults)
{
result.Results = new List<RuleResultTree>()
{
context.GetParentRuleResult()

View File

@ -1,4 +1,7 @@
using Newtonsoft.Json;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Newtonsoft.Json;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
@ -22,7 +25,8 @@ namespace RulesEngine.Actions
_parentResult = parentResult;
}
public RuleResultTree GetParentRuleResult(){
public RuleResultTree GetParentRuleResult()
{
return _parentResult;
}
public T GetContext<T>(string name)

View File

@ -1,4 +1,7 @@
using System;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
namespace RulesEngine.Actions
@ -12,9 +15,10 @@ namespace RulesEngine.Actions
_actionRegistry = new Dictionary<string, Func<ActionBase>>(StringComparer.OrdinalIgnoreCase);
}
internal ActionFactory(IDictionary<string,Func<ActionBase>> actionRegistry): this()
internal ActionFactory(IDictionary<string, Func<ActionBase>> actionRegistry) : this()
{
foreach(var kv in actionRegistry){
foreach (var kv in actionRegistry)
{
_actionRegistry.Add(kv.Key, kv.Value);
}
}

View File

@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
using RulesEngine.ExpressionBuilders;
using RulesEngine.Models;
namespace RulesEngine.Actions
{
@ -14,11 +16,13 @@ namespace RulesEngine.Actions
_ruleEngine = ruleEngine;
}
internal override async ValueTask<ActionRuleResult> ExecuteAndReturnResultAsync(ActionContext context, RuleParameter[] ruleParameters, bool includeRuleResults=false){
var innerResult = await base.ExecuteAndReturnResultAsync(context,ruleParameters,includeRuleResults);
internal async override ValueTask<ActionRuleResult> ExecuteAndReturnResultAsync(ActionContext context, RuleParameter[] ruleParameters, bool includeRuleResults = false)
{
var innerResult = await base.ExecuteAndReturnResultAsync(context, ruleParameters, includeRuleResults);
var output = innerResult.Output as ActionRuleResult;
List<RuleResultTree> resultList = null;
if(includeRuleResults){
if (includeRuleResults)
{
resultList = new List<RuleResultTree>(output.Results);
resultList.AddRange(innerResult.Results);
}
@ -29,11 +33,11 @@ namespace RulesEngine.Actions
};
}
public override async ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)
public async override ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)
{
var workflowName = context.GetContext<string>("workflowName");
var ruleName = context.GetContext<string>("ruleName");
var ruleResult = await _ruleEngine.ExecuteActionWorkflowAsync(workflowName,ruleName,ruleParameters);
var ruleResult = await _ruleEngine.ExecuteActionWorkflowAsync(workflowName, ruleName, ruleParameters);
return ruleResult;
}
}

View File

@ -1,7 +1,9 @@
using System.Collections.Generic;
using System.Threading.Tasks;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.ExpressionBuilders;
using RulesEngine.Models;
using System.Threading.Tasks;
namespace RulesEngine.Actions
{

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace RulesEngine.Enums
{

View File

@ -29,16 +29,16 @@ namespace RulesEngine.ExpressionBuilders
bool func(object[] paramList) => ruleDelegate(paramList);
return Helpers.ToResultTree(rule, null, func);
}
catch (Exception ex)
catch (Exception ex)
{
ex.Data.Add(nameof(rule.RuleName), rule.RuleName);
ex.Data.Add(nameof(rule.Expression), rule.Expression);
if (!_reSettings.EnableExceptionAsErrorMessage) throw;
bool func(object[] param) => false;
var exceptionMessage = $"Exception while parsing expression `{rule?.Expression}` - {ex.Message}";
return Helpers.ToResultTree(rule, null, func, exceptionMessage);
}
}
}
}
}

View File

@ -2,9 +2,6 @@
// Licensed under the MIT License.
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace RulesEngine.ExpressionBuilders
{

View File

@ -1,11 +1,14 @@
using Microsoft.Extensions.Caching.Memory;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using FastExpressionCompiler;
using Microsoft.Extensions.Caching.Memory;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using FastExpressionCompiler;
namespace RulesEngine.ExpressionBuilders
{
@ -17,34 +20,35 @@ namespace RulesEngine.ExpressionBuilders
public RuleExpressionParser(ReSettings reSettings)
{
_reSettings = reSettings;
_memoryCache = new MemoryCache(new MemoryCacheOptions{
_memoryCache = new MemoryCache(new MemoryCacheOptions {
SizeLimit = 1000
});
}
public Func<object[],T> Compile<T>(string expression, RuleParameter[] ruleParams)
public Func<object[], T> Compile<T>(string expression, RuleParameter[] ruleParams)
{
var cacheKey = GetCacheKey(expression,ruleParams,typeof(T));
return _memoryCache.GetOrCreate(cacheKey,(entry) => {
var cacheKey = GetCacheKey(expression, ruleParams, typeof(T));
return _memoryCache.GetOrCreate(cacheKey, (entry) => {
entry.SetSize(1);
var config = new ParsingConfig { CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes) };
var typeParamExpressions = GetParameterExpression(ruleParams).ToArray();
var e = DynamicExpressionParser.ParseLambda(config, true, typeParamExpressions.ToArray(), typeof(T), expression);
var wrappedExpression = WrapExpression<T>(e,typeParamExpressions);
return wrappedExpression.CompileFast<Func<object[],T>>();
var wrappedExpression = WrapExpression<T>(e, typeParamExpressions);
return wrappedExpression.CompileFast<Func<object[], T>>();
});
}
private Expression<Func<object[],T>> WrapExpression<T>(LambdaExpression expression, ParameterExpression[] parameters){
var argExp = Expression.Parameter(typeof(object[]),"args");
IEnumerable<Expression> paramExps = parameters.Select((c, i) => {
private Expression<Func<object[], T>> WrapExpression<T>(LambdaExpression expression, ParameterExpression[] parameters)
{
var argExp = Expression.Parameter(typeof(object[]), "args");
var paramExps = parameters.Select((c, i) => {
var arg = Expression.ArrayAccess(argExp, Expression.Constant(i));
return (Expression)Expression.Assign(c, Expression.Convert(arg, c.Type));
});
var blockExpSteps = paramExps.Concat(new List<Expression> { expression.Body });
var blockExp = Expression.Block(parameters, blockExpSteps);
return Expression.Lambda<Func<object[],T>>(blockExp, argExp);
return Expression.Lambda<Func<object[], T>>(blockExp, argExp);
}
@ -77,9 +81,10 @@ namespace RulesEngine.ExpressionBuilders
}
}
private string GetCacheKey(string expression, RuleParameter[] ruleParameters,Type returnType){
var paramKey = string.Join("|",ruleParameters.Select(c => c.Type.ToString()));
var returnTypeKey = returnType?.ToString() ?? "null";
private string GetCacheKey(string expression, RuleParameter[] ruleParameters, Type returnType)
{
var paramKey = string.Join("|", ruleParameters.Select(c => c.Type.ToString()));
var returnTypeKey = returnType?.ToString() ?? "null";
var combined = $"Expression:{expression}-Params:{paramKey}-ReturnType:{returnTypeKey}";
return combined;
}

View File

@ -5,7 +5,6 @@ using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace RulesEngine.HelperFunctions
{
@ -14,12 +13,11 @@ namespace RulesEngine.HelperFunctions
/// </summary>
internal static class Helpers
{
internal static RuleFunc<RuleResultTree> ToResultTree(Rule rule, IEnumerable<RuleResultTree> childRuleResults, Func<object[],bool> isSuccessFunc, string exceptionMessage = "")
internal static RuleFunc<RuleResultTree> ToResultTree(Rule rule, IEnumerable<RuleResultTree> childRuleResults, Func<object[], bool> isSuccessFunc, string exceptionMessage = "")
{
return (inputs) => new RuleResultTree
{
return (inputs) => new RuleResultTree {
Rule = rule,
Inputs = inputs.ToDictionary(c => c.Name,c => c.Value),
Inputs = inputs.ToDictionary(c => c.Name, c => c.Value),
IsSuccess = isSuccessFunc(inputs.Select(c => c.Value).ToArray()),
ChildResults = childRuleResults,
ExceptionMessage = exceptionMessage

View File

@ -14,7 +14,7 @@ namespace RulesEngine.HelperFunctions
{
public static object GetTypedObject(dynamic input)
{
if(input is ExpandoObject)
if (input is ExpandoObject)
{
Type type = CreateAbstractClassType(input);
return CreateObject(type, input);
@ -28,11 +28,11 @@ namespace RulesEngine.HelperFunctions
{
List<DynamicProperty> props = new List<DynamicProperty>();
if(input == null)
if (input == null)
{
return typeof(object);
}
if(!(input is ExpandoObject))
if (!(input is ExpandoObject))
{
return input.GetType();
}
@ -51,7 +51,7 @@ namespace RulesEngine.HelperFunctions
var internalType = CreateAbstractClassType(((IList)expando.Value)[0]);
value = new List<object>().Cast(internalType).ToList(internalType).GetType();
}
}
else
{
@ -86,7 +86,7 @@ namespace RulesEngine.HelperFunctions
}
else if (expando.Value is IList)
{
var internalType = type.GetProperty(expando.Key).PropertyType.GenericTypeArguments.FirstOrDefault()??typeof(object);
var internalType = type.GetProperty(expando.Key).PropertyType.GenericTypeArguments.FirstOrDefault() ?? typeof(object);
var temp = (IList)expando.Value;
var newList = new List<object>();
for (int i = 0; i < temp.Count; i++)
@ -123,5 +123,5 @@ namespace RulesEngine.HelperFunctions
}
}
}

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace RulesEngine.Interfaces
{
public interface IRulesEngine
{
{
/// <summary>
/// This will execute all the rules of the specified workflow
/// </summary>

View File

@ -1,9 +1,12 @@
using System;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Text;
using System.Diagnostics.CodeAnalysis;
namespace RulesEngine.Models
{
[ExcludeFromCodeCoverage]
public class ActionInfo
{
public string Name { get; set; }

View File

@ -1,6 +1,15 @@
using System;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
public class ActionResult{
public object Output {get; set;}
public Exception Exception { get; set; }
}
using System;
using System.Diagnostics.CodeAnalysis;
namespace RulesEngine.Models
{
[ExcludeFromCodeCoverage]
public class ActionResult
{
public object Output { get; set; }
public Exception Exception { get; set; }
}
}

View File

@ -1,7 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using RulesEngine.Models;
using System.Diagnostics.CodeAnalysis;
public class ActionRuleResult : ActionResult{
public List<RuleResultTree> Results {get; set;}
}
namespace RulesEngine.Models
{
[ExcludeFromCodeCoverage]
public class ActionRuleResult : ActionResult
{
public List<RuleResultTree> Results { get; set; }
}
}

View File

@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace RulesEngine.Models
{
@ -13,10 +14,10 @@ namespace RulesEngine.Models
{
internal string Name { get; set; }
internal Type ReturnType { get; set; }
internal Func<object[],object> Value { get; set; }
internal Func<object[], object> Value { get; set; }
internal RuleParameter AsRuleParameter()
{
return new RuleParameter(Name,ReturnType);
return new RuleParameter(Name, ReturnType);
}
}
}

View File

@ -1,4 +1,7 @@
using Newtonsoft.Json;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Newtonsoft.Json;
using System.Diagnostics.CodeAnalysis;
namespace RulesEngine.Models

View File

@ -14,7 +14,7 @@ namespace RulesEngine.Models
public Type[] CustomTypes { get; set; }
public Dictionary<string, Func<ActionBase>> CustomActions { get; set; }
public bool EnableExceptionAsErrorMessage { get; set; } = true;
public bool EnableFormattedErrorMessage {get; set; } = true;
public bool EnableLocalParams {get;set;} = true;
public bool EnableFormattedErrorMessage { get; set; } = true;
public bool EnableLocalParams { get; set; } = true;
}
}

View File

@ -6,7 +6,6 @@ using Newtonsoft.Json.Converters;
using RulesEngine.Enums;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace RulesEngine.Models
{

View File

@ -1,23 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.HelperFunctions;
using System;
using System.Diagnostics.CodeAnalysis;
using RulesEngine.HelperFunctions;
namespace RulesEngine.Models
{
[ExcludeFromCodeCoverage]
public class RuleParameter
{
public RuleParameter(string name,object value)
public RuleParameter(string name, object value)
{
Value = Utils.GetTypedObject(value);
Type = Value.GetType();
Name = name;
}
internal RuleParameter(string name,Type type){
internal RuleParameter(string name, Type type)
{
Name = name;
Type = type;
}

View File

@ -40,9 +40,9 @@ namespace RulesEngine.Models
/// <summary>
/// Gets or sets the input object
/// </summary>
public Dictionary<string,object> Inputs { get; set; }
public Dictionary<string, object> Inputs { get; set; }
public ActionResult ActionResult {get; set;}
public ActionResult ActionResult { get; set; }
/// <summary>
/// Gets the exception message in case an error is thrown during rules calculation.

View File

@ -1,9 +1,11 @@
using RulesEngine.Models;
// 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 RulesEngine.ExpressionBuilders;
using RulesEngine.HelperFunctions;
namespace RulesEngine
{
@ -32,8 +34,8 @@ namespace RulesEngine
/// </returns>
public IEnumerable<CompiledParam> CompileParamsExpression(Rule rule, IEnumerable<RuleParameter> ruleParams)
{
if(rule.LocalParams == null) return null;
if (rule.LocalParams == null) return null;
var compiledParameters = new List<CompiledParam>();
var evaluatedParameters = new List<RuleParameter>();
@ -54,7 +56,7 @@ namespace RulesEngine
/// <param name="compiledParam">The compiled parameter.</param>
/// <param name="ruleParams">The rule parameters.</param>
/// <returns>RuleParameter.</returns>
public RuleParameter EvaluateCompiledParam(string paramName, Func<object[],object> compiledParam, IEnumerable<RuleParameter> inputs)
public RuleParameter EvaluateCompiledParam(string paramName, Func<object[], object> compiledParam, IEnumerable<RuleParameter> inputs)
{
var result = compiledParam(inputs.Select(c => c.Value).ToArray());
return new RuleParameter(paramName, result);
@ -68,7 +70,7 @@ namespace RulesEngine
/// <param name="typeParameterExpressions">The type parameter expressions.</param>
/// <param name="ruleInputExp">The rule input exp.</param>
/// <returns></returns>
private Func<object[],object> GetDelegateForRuleParam(LocalParam param, RuleParameter[] ruleParameters)
private Func<object[], object> GetDelegateForRuleParam(LocalParam param, RuleParameter[] ruleParameters)
{
return _ruleExpressionParser.Compile<object>(param.Expression, ruleParameters);
}

View File

@ -38,18 +38,8 @@ namespace RulesEngine
/// <exception cref="ArgumentNullException">expressionBuilderFactory</exception>
internal RuleCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ILogger logger)
{
if (expressionBuilderFactory == null)
{
throw new ArgumentNullException($"{nameof(expressionBuilderFactory)} can't be null.");
}
if (logger == null)
{
throw new ArgumentNullException($"{nameof(logger)} can't be null.");
}
_logger = logger;
_expressionBuilderFactory = expressionBuilderFactory;
_logger = logger ?? throw new ArgumentNullException($"{nameof(logger)} can't be null.");
_expressionBuilderFactory = expressionBuilderFactory ?? throw new ArgumentNullException($"{nameof(expressionBuilderFactory)} can't be null.");
}
/// <summary>
@ -60,15 +50,15 @@ namespace RulesEngine
/// <param name="input"></param>
/// <param name="ruleParam"></param>
/// <returns>Compiled func delegate</returns>
internal RuleFunc<RuleResultTree> CompileRule(Rule rule,params RuleParameter[] ruleParams)
internal RuleFunc<RuleResultTree> CompileRule(Rule rule, params RuleParameter[] ruleParams)
{
try
{
if(rule == null)
if (rule == null)
{
throw new ArgumentNullException(nameof(rule));
}
RuleFunc<RuleResultTree> ruleExpression = GetDelegateForRule(rule,ruleParams);
var ruleExpression = GetDelegateForRule(rule, ruleParams);
return ruleExpression;
}
catch (Exception ex)
@ -78,7 +68,7 @@ namespace RulesEngine
}
}
/// <summary>
/// Gets the expression for rule.
@ -89,9 +79,7 @@ namespace RulesEngine
/// <returns></returns>
private RuleFunc<RuleResultTree> GetDelegateForRule(Rule rule, RuleParameter[] ruleParams)
{
ExpressionType nestedOperator;
if (Enum.TryParse(rule.Operator, out nestedOperator) && nestedOperators.Contains(nestedOperator) &&
if (Enum.TryParse(rule.Operator, out ExpressionType nestedOperator) && nestedOperators.Contains(nestedOperator) &&
rule.Rules != null && rule.Rules.Any())
{
return BuildNestedRuleFunc(rule, nestedOperator, ruleParams);
@ -142,13 +130,12 @@ namespace RulesEngine
ruleFuncList.Add(GetDelegateForRule(r, ruleParams));
}
return (paramArray) =>
{
var resultList = ruleFuncList.Select(fn => fn(paramArray));
Func<object[],bool> isSuccess = (p) => ApplyOperation(resultList, operation);
RuleFunc<RuleResultTree> result = Helpers.ToResultTree(parentRule, resultList,isSuccess);
return result(paramArray);
};
return (paramArray) => {
var resultList = ruleFuncList.Select(fn => fn(paramArray));
Func<object[], bool> isSuccess = (p) => ApplyOperation(resultList, operation);
var result = Helpers.ToResultTree(parentRule, resultList, isSuccess);
return result(paramArray);
};
}

View File

@ -13,7 +13,7 @@ namespace RulesEngine
internal class RulesCache
{
/// <summary>The compile rules</summary>
private ConcurrentDictionary<string, IDictionary<string,RuleFunc<RuleResultTree>>> _compileRules = new ConcurrentDictionary<string, IDictionary<string,RuleFunc<RuleResultTree>>>();
private ConcurrentDictionary<string, IDictionary<string, RuleFunc<RuleResultTree>>> _compileRules = new ConcurrentDictionary<string, IDictionary<string, RuleFunc<RuleResultTree>>>();
/// <summary>The workflow rules</summary>
private ConcurrentDictionary<string, WorkflowRules> _workflowRules = new ConcurrentDictionary<string, WorkflowRules>();
@ -47,7 +47,7 @@ namespace RulesEngine
/// <summary>Adds the or update compiled rule.</summary>
/// <param name="compiledRuleKey">The compiled rule key.</param>
/// <param name="compiledRule">The compiled rule.</param>
public void AddOrUpdateCompiledRule(string compiledRuleKey, IDictionary<string,RuleFunc<RuleResultTree>> compiledRule)
public void AddOrUpdateCompiledRule(string compiledRuleKey, IDictionary<string, RuleFunc<RuleResultTree>> compiledRule)
{
_compileRules.AddOrUpdate(compiledRuleKey, compiledRule, (k, v) => compiledRule);
}
@ -100,7 +100,7 @@ namespace RulesEngine
return workflowRules;
}
}
/// <summary>Gets the compiled rules.</summary>
/// <param name="compiledRulesKey">The compiled rules key.</param>
@ -119,7 +119,7 @@ namespace RulesEngine
var compiledKeysToRemove = _compileRules.Keys.Where(key => key.StartsWith(workflowName));
foreach (var key in compiledKeysToRemove)
{
_compileRules.TryRemove(key, out IDictionary<string,RuleFunc<RuleResultTree>> val);
_compileRules.TryRemove(key, out IDictionary<string, RuleFunc<RuleResultTree>> val);
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Threading.Tasks;
using FluentValidation;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
@ -11,6 +10,7 @@ using Newtonsoft.Json.Linq;
using RulesEngine.Actions;
using RulesEngine.Enums;
using RulesEngine.Exceptions;
using RulesEngine.ExpressionBuilders;
using RulesEngine.Interfaces;
using RulesEngine.Models;
using RulesEngine.Validators;
@ -18,7 +18,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using RulesEngine.ExpressionBuilders;
using System.Threading.Tasks;
namespace RulesEngine
{
@ -58,15 +58,16 @@ namespace RulesEngine
_reSettings = reSettings ?? new ReSettings();
_ruleExpressionParser = new RuleExpressionParser(_reSettings);
_ruleParamCompiler = new ParamCompiler(_reSettings, _ruleExpressionParser);
_ruleCompiler = new RuleCompiler(new RuleExpressionBuilderFactory(_reSettings, _ruleExpressionParser),_logger);
_ruleCompiler = new RuleCompiler(new RuleExpressionBuilderFactory(_reSettings, _ruleExpressionParser), _logger);
_actionFactory = new ActionFactory(GetActionRegistry(_reSettings));
}
private IDictionary<string,Func<ActionBase>> GetActionRegistry(ReSettings reSettings)
private IDictionary<string, Func<ActionBase>> GetActionRegistry(ReSettings reSettings)
{
var actionDictionary = GetDefaultActionRegistry();
var customActions = reSettings.CustomActions ?? new Dictionary<string, Func<ActionBase>>();
foreach(var customAction in customActions){
foreach (var customAction in customActions)
{
actionDictionary.Add(customAction);
}
return actionDictionary;
@ -88,7 +89,7 @@ namespace RulesEngine
var ruleParams = new List<RuleParameter>();
for (int i = 0; i < inputs.Length; i++)
for (var i = 0; i < inputs.Length; i++)
{
var input = inputs[i];
ruleParams.Add(new RuleParameter($"input{i + 1}", input));
@ -105,10 +106,11 @@ namespace RulesEngine
/// <returns>List of rule results</returns>
public async ValueTask<List<RuleResultTree>> ExecuteAllRulesAsync(string workflowName, params RuleParameter[] ruleParams)
{
var ruleResultList = ValidateWorkflowAndExecuteRule(workflowName, ruleParams);
foreach(var ruleResult in ruleResultList){
var actionResult = await ExecuteActionForRuleResult(ruleResult,false);
ruleResult.ActionResult = new ActionResult{
var ruleResultList = ValidateWorkflowAndExecuteRule(workflowName, ruleParams);
foreach (var ruleResult in ruleResultList)
{
var actionResult = await ExecuteActionForRuleResult(ruleResult, false);
ruleResult.ActionResult = new ActionResult {
Output = actionResult.Output,
Exception = actionResult.Exception
};
@ -121,27 +123,26 @@ namespace RulesEngine
{
var compiledRule = CompileRule(workflowName, ruleName, ruleParameters);
var resultTree = compiledRule(ruleParameters);
return await ExecuteActionForRuleResult(resultTree,true);
return await ExecuteActionForRuleResult(resultTree, true);
}
private async ValueTask<ActionRuleResult> ExecuteActionForRuleResult(RuleResultTree resultTree, bool includeRuleResults=false)
private async ValueTask<ActionRuleResult> ExecuteActionForRuleResult(RuleResultTree resultTree, bool includeRuleResults = false)
{
ActionTriggerType triggerType = resultTree?.IsSuccess == true ? ActionTriggerType.onSuccess : ActionTriggerType.onFailure;
var triggerType = resultTree?.IsSuccess == true ? ActionTriggerType.onSuccess : ActionTriggerType.onFailure;
if (resultTree?.Rule?.Actions != null && resultTree.Rule.Actions.ContainsKey(triggerType))
{
var actionInfo = resultTree.Rule.Actions[triggerType];
var action = _actionFactory.Get(actionInfo.Name);
var ruleParameters = resultTree.Inputs.Select(kv => new RuleParameter(kv.Key,kv.Value)).ToArray();
return await action.ExecuteAndReturnResultAsync(new ActionContext(actionInfo.Context, resultTree), ruleParameters,includeRuleResults);
var ruleParameters = resultTree.Inputs.Select(kv => new RuleParameter(kv.Key, kv.Value)).ToArray();
return await action.ExecuteAndReturnResultAsync(new ActionContext(actionInfo.Context, resultTree), ruleParameters, includeRuleResults);
}
else
{
//If there is no action,return output as null and return the result for rule
return new ActionRuleResult
{
return new ActionRuleResult {
Output = null,
Results = includeRuleResults ? new List<RuleResultTree>() { resultTree }: null
Results = includeRuleResults ? new List<RuleResultTree>() { resultTree } : null
};
}
}
@ -226,20 +227,22 @@ namespace RulesEngine
/// </returns>
private bool RegisterRule(string workflowName, params RuleParameter[] ruleParams)
{
string compileRulesKey = GetCompiledRulesKey(workflowName,ruleParams);
var compileRulesKey = GetCompiledRulesKey(workflowName, ruleParams);
if (_rulesCache.ContainsCompiledRules(compileRulesKey))
{
return true;
}
var workflowRules = _rulesCache.GetWorkFlowRules(workflowName);
if (workflowRules != null)
{
var dictFunc = new Dictionary<string,RuleFunc<RuleResultTree>>();
var dictFunc = new Dictionary<string, RuleFunc<RuleResultTree>>();
foreach (var rule in workflowRules.Rules)
{
dictFunc.Add(rule.RuleName,CompileRule(workflowName, ruleParams, rule));
dictFunc.Add(rule.RuleName, CompileRule(workflowName, ruleParams, rule));
}
_rulesCache.AddOrUpdateCompiledRule(compileRulesKey,dictFunc);
_rulesCache.AddOrUpdateCompiledRule(compileRulesKey, dictFunc);
_logger.LogTrace($"Rules has been compiled for the {workflowName} workflow and added to dictionary");
return true;
}
@ -250,30 +253,32 @@ namespace RulesEngine
}
private RuleFunc<RuleResultTree> CompileRule(string workflowName,string ruleName,RuleParameter[] ruleParameters){
private RuleFunc<RuleResultTree> CompileRule(string workflowName, string ruleName, RuleParameter[] ruleParameters)
{
var rules = _rulesCache.GetRules(workflowName);
var currentRule = rules?.SingleOrDefault(c => c.RuleName == ruleName);
if(currentRule == null){
if (currentRule == null)
{
throw new ArgumentException($"Workflow `{workflowName}` does not contain any rule named `{ruleName}`");
}
return CompileRule(workflowName,ruleParameters,currentRule);
return CompileRule(workflowName, ruleParameters, currentRule);
}
private RuleFunc<RuleResultTree> CompileRule(string workflowName, RuleParameter[] ruleParams, Rule rule)
{
if(!_reSettings.EnableLocalParams){
return _ruleCompiler.CompileRule(rule,ruleParams);
if (!_reSettings.EnableLocalParams)
{
return _ruleCompiler.CompileRule(rule, ruleParams);
}
var compiledParamsKey = GetCompiledParamsCacheKey(workflowName, rule.RuleName, ruleParams);
IEnumerable<CompiledParam> compiledParamList = _compiledParamsCache.GetOrCreate(compiledParamsKey, (entry) => _ruleParamCompiler.CompileParamsExpression(rule, ruleParams));
var compiledParamList = _compiledParamsCache.GetOrCreate(compiledParamsKey, (entry) => _ruleParamCompiler.CompileParamsExpression(rule, ruleParams));
var compiledRuleParameters = compiledParamList?.Select(c => c.AsRuleParameter()) ?? new List<RuleParameter>();
var updatedRuleParams = ruleParams?.Concat(compiledRuleParameters);
var compiledRule = _ruleCompiler.CompileRule(rule, updatedRuleParams?.ToArray());
RuleFunc<RuleResultTree> updatedRule = (RuleParameter[] paramList) =>
{
RuleFunc<RuleResultTree> updatedRule = (RuleParameter[] paramList) => {
var inputs = paramList.AsEnumerable();
IEnumerable<CompiledParam> localParams = compiledParamList ?? new List<CompiledParam>();
var localParams = compiledParamList ?? new List<CompiledParam>();
var evaluatedParamList = new List<RuleParameter>();
foreach (var localParam in localParams)
{
@ -288,7 +293,7 @@ namespace RulesEngine
return updatedRule;
}
/// <summary>
/// This will execute the compiled rules
@ -300,8 +305,8 @@ namespace RulesEngine
{
_logger.LogTrace($"Compiled rules found for {workflowName} workflow and executed");
List<RuleResultTree> result = new List<RuleResultTree>();
string compiledRulesCacheKey = GetCompiledRulesKey(workflowName,ruleParameters);
var result = new List<RuleResultTree>();
var compiledRulesCacheKey = GetCompiledRulesKey(workflowName, ruleParameters);
foreach (var compiledRule in _rulesCache.GetCompiledRules(compiledRulesCacheKey)?.Values)
{
var resultTree = compiledRule(ruleParameters);
@ -311,20 +316,21 @@ namespace RulesEngine
FormatErrorMessages(result);
return result;
}
private string GetCompiledRulesKey(string workflowName, RuleParameter[] ruleParams)
{
var key = $"{workflowName}-" + String.Join("-", ruleParams.Select(c => c.Type.Name));
var key = $"{workflowName}-" + string.Join("-", ruleParams.Select(c => c.Type.Name));
return key;
}
private string GetCompiledParamsCacheKey(string workflowName,string ruleName,RuleParameter[] ruleParams)
private string GetCompiledParamsCacheKey(string workflowName, string ruleName, RuleParameter[] ruleParams)
{
var key = $"compiledparams-{workflowName}-{ruleName}" + String.Join("-", ruleParams.Select(c => c.Type.Name));
return key;
var key = $"compiledparams-{workflowName}-{ruleName}" + string.Join("-", ruleParams.Select(c => c.Type.Name));
return key;
}
private IDictionary<string,Func<ActionBase>> GetDefaultActionRegistry(){
private IDictionary<string, Func<ActionBase>> GetDefaultActionRegistry()
{
return new Dictionary<string, Func<ActionBase>>{
{"OutputExpression",() => new OutputExpressionAction(_ruleExpressionParser) },
{"EvaluateRule", () => new EvaluateRuleAction(this) }
@ -338,11 +344,13 @@ namespace RulesEngine
/// <returns>Updated error message.</returns>
private IEnumerable<RuleResultTree> FormatErrorMessages(IEnumerable<RuleResultTree> ruleResultList)
{
if(_reSettings.EnableFormattedErrorMessage){
if (_reSettings.EnableFormattedErrorMessage)
{
foreach (var ruleResult in ruleResultList?.Where(r => !r.IsSuccess))
{
var errorMessage = ruleResult?.Rule?.ErrorMessage;
if(errorMessage != null){
if (errorMessage != null)
{
var errorParameters = Regex.Matches(errorMessage, ParamParseRegex);
var inputs = ruleResult.Inputs;
@ -366,7 +374,7 @@ namespace RulesEngine
}
ruleResult.ExceptionMessage = errorMessage;
}
}
}
return ruleResultList;
@ -381,9 +389,9 @@ namespace RulesEngine
/// <param name="typeName">Name of the type.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns>Updated error message.</returns>
private static string UpdateErrorMessage(string errorMessage, IDictionary<string,object> inputs, string property, string typeName, string propertyName)
private static string UpdateErrorMessage(string errorMessage, IDictionary<string, object> inputs, string property, string typeName, string propertyName)
{
var arrParams = inputs?.Select(c => new {Name = c.Key, c.Value });
var arrParams = inputs?.Select(c => new { Name = c.Key, c.Value });
var model = arrParams?.Where(a => string.Equals(a.Name, typeName))?.FirstOrDefault();
if (model != null)
{

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>3.0.0</Version>
<Version>3.0.1</Version>
<Copyright>Copyright (c) Microsoft Corporation.</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://github.com/microsoft/RulesEngine</PackageProjectUrl>

View File

@ -1,13 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using FluentValidation;
using RulesEngine.HelperFunctions;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using FluentValidation;
using RulesEngine.HelperFunctions;
using RulesEngine.Models;
namespace RulesEngine.Validators
{
@ -19,15 +19,13 @@ namespace RulesEngine.Validators
RuleFor(c => c.RuleName).NotEmpty().WithMessage(Constants.RULE_NAME_NULL_ERRMSG);
//Nested expression check
When(c => c.RuleExpressionType == null,() =>
{
When(c => c.RuleExpressionType == null, () => {
RuleFor(c => c.Operator)
.NotNull().WithMessage(Constants.OPERATOR_NULL_ERRMSG)
.Must(op => _nestedOperators.Any(x => x.ToString().Equals(op, StringComparison.OrdinalIgnoreCase)))
.WithMessage(Constants.OPERATOR_INCORRECT_ERRMSG);
When(c => c.Rules?.Any() != true, () =>
{
When(c => c.Rules?.Any() != true, () => {
RuleFor(c => c.WorkflowRulesToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG);
})
.Otherwise(() => {
@ -39,8 +37,7 @@ namespace RulesEngine.Validators
private void RegisterExpressionTypeRules()
{
When(c => c.RuleExpressionType == RuleExpressionType.LambdaExpression, () =>
{
When(c => c.RuleExpressionType == RuleExpressionType.LambdaExpression, () => {
RuleFor(c => c.Expression).NotEmpty().WithMessage(Constants.LAMBDA_EXPRESSION_EXPRESSION_NULL_ERRMSG);
RuleFor(c => c.Operator).Null().WithMessage(Constants.LAMBDA_EXPRESSION_OPERATOR_ERRMSG);
RuleFor(c => c.Rules).Null().WithMessage(Constants.LAMBDA_EXPRESSION_RULES_ERRMSG);
@ -52,7 +49,8 @@ namespace RulesEngine.Validators
if (rules?.Any() != true) return false;
var validator = new RuleValidator();
var isValid = true;
foreach(var rule in rules){
foreach (var rule in rules)
{
isValid &= validator.Validate(rule).IsValid;
if (!isValid) break;
}

View File

@ -1,10 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Linq;
using FluentValidation;
using RulesEngine.HelperFunctions;
using RulesEngine.Models;
using System.Linq;
namespace RulesEngine.Validators
{
@ -13,8 +13,7 @@ namespace RulesEngine.Validators
public WorkflowRulesValidator()
{
RuleFor(c => c.WorkflowName).NotEmpty().WithMessage(Constants.WORKFLOW_NAME_NULL_ERRMSG);
When(c => c.Rules?.Any() != true, () =>
{
When(c => c.Rules?.Any() != true, () => {
RuleFor(c => c.WorkflowRulesToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG);
}).Otherwise(() => {
var ruleValidator = new RuleValidator();

View File

@ -1,5 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using AutoFixture;
using Moq;
using RulesEngine.Actions;
using RulesEngine.Models;
using System;

View File

@ -1,23 +1,28 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.Enums;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using RulesEngine.Enums;
using RulesEngine.Models;
using Xunit;
namespace RulesEngine.UnitTest{
namespace RulesEngine.UnitTest
{
[ExcludeFromCodeCoverage]
public class RulesEngineWithActionsTests{
public class RulesEngineWithActionsTests
{
[Fact]
public async Task WhenExpressionIsSuccess_OutputExpressionAction_ReturnsExpressionEvaluation(){
public async Task WhenExpressionIsSuccess_OutputExpressionAction_ReturnsExpressionEvaluation()
{
var engine = new RulesEngine(GetWorkflowWithActions());
var result = await engine.ExecuteActionWorkflowAsync("ActionWorkflow", "ExpressionOutputRuleTest", new RuleParameter[0]);
Assert.NotNull(result);
Assert.Equal(2*2,result.Output);
Assert.Equal(2 * 2, result.Output);
}
[Fact]
@ -50,8 +55,9 @@ namespace RulesEngine.UnitTest{
}
private WorkflowRules[] GetWorkflowRulesWithoutActions(){
var workflow1 = new WorkflowRules{
private WorkflowRules[] GetWorkflowRulesWithoutActions()
{
var workflow1 = new WorkflowRules {
WorkflowName = "NoActionWorkflow",
Rules = new List<Rule>{
new Rule{
@ -62,12 +68,13 @@ namespace RulesEngine.UnitTest{
}
};
return new []{workflow1};
return new[] { workflow1 };
}
private WorkflowRules[] GetWorkflowWithActions(){
var workflow1 = new WorkflowRules{
private WorkflowRules[] GetWorkflowWithActions()
{
var workflow1 = new WorkflowRules {
WorkflowName = "ActionWorkflow",
Rules = new List<Rule>{
new Rule{
@ -100,7 +107,7 @@ namespace RulesEngine.UnitTest{
}
};
return new []{workflow1};
return new[] { workflow1 };
}
}
}

View File

@ -2,20 +2,20 @@
// Licensed under the MIT License.
using Microsoft.Extensions.Logging;
using RulesEngine.Exceptions;
using RulesEngine.Models;
using Moq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using RulesEngine.Exceptions;
using RulesEngine.HelperFunctions;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.IO;
using System.Linq;
using Xunit;
using Newtonsoft.Json.Converters;
using RulesEngine.HelperFunctions;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Xunit;
namespace RulesEngine.UnitTest
{
@ -41,7 +41,7 @@ namespace RulesEngine.UnitTest
dynamic input2 = GetInput2();
dynamic input3 = GetInput3();
var result = await re.ExecuteAllRulesAsync("inputWorkflowReference",input1, input2, input3);
var result = await re.ExecuteAllRulesAsync("inputWorkflowReference", input1, input2, input3);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
}
@ -59,7 +59,7 @@ namespace RulesEngine.UnitTest
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.Contains(result,c => c.IsSuccess);
Assert.Contains(result, c => c.IsSuccess);
}
[Theory]
@ -79,7 +79,7 @@ namespace RulesEngine.UnitTest
dynamic input7 = GetInput1();
dynamic input8 = GetInput2();
dynamic input9 = GetInput3();
dynamic input10 = GetInput1();
dynamic input11 = GetInput2();
dynamic input12 = GetInput3();
@ -93,7 +93,7 @@ namespace RulesEngine.UnitTest
dynamic input17 = GetInput2();
dynamic input18 = GetInput3();
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3,input4, input5, input6, input7, input8, input9, input10, input11, input12, input13, input14, input15, input16, input17, input18);
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, input11, input12, input13, input14, input15, input16, input17, input18);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.Contains(result, c => c.IsSuccess);
@ -123,8 +123,8 @@ namespace RulesEngine.UnitTest
var expected = result1.Select(c => new { c.Rule.RuleName, c.IsSuccess });
var actual = result2.Select(c => new { c.Rule.RuleName, c.IsSuccess });
Assert.Equal(expected, actual);
}
[Theory]
@ -140,7 +140,7 @@ namespace RulesEngine.UnitTest
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", input1);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.DoesNotContain(result,c => c.IsSuccess);
Assert.DoesNotContain(result, c => c.IsSuccess);
}
[Theory]
@ -168,7 +168,7 @@ namespace RulesEngine.UnitTest
dynamic input2 = GetInput2();
dynamic input3 = GetInput3();
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow",input1, input2, input3);
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3);
Assert.NotNull(result);
Assert.NotNull(result.First().GetMessages());
Assert.NotNull(result.First().GetMessages().WarningMessages);
@ -177,14 +177,12 @@ namespace RulesEngine.UnitTest
[Fact]
public void RulesEngine_New_IncorrectJSON_ThrowsException()
{
Assert.Throws<RuleValidationException>(() =>
{
Assert.Throws<RuleValidationException>(() => {
var workflow = new WorkflowRules();
var re = CreateRulesEngine(workflow);
});
Assert.Throws<RuleValidationException>(() =>
{
Assert.Throws<RuleValidationException>(() => {
var workflow = new WorkflowRules() { WorkflowName = "test" };
var re = CreateRulesEngine(workflow);
});
@ -198,7 +196,7 @@ namespace RulesEngine.UnitTest
var re = GetRulesEngine(ruleFileName);
dynamic input = GetInput1();
await Assert.ThrowsAsync<ArgumentException>(async() => { await re.ExecuteAllRulesAsync("inputWorkflow1", input); });
await Assert.ThrowsAsync<ArgumentException>(async () => { await re.ExecuteAllRulesAsync("inputWorkflow1", input); });
}
[Theory]
@ -214,7 +212,7 @@ namespace RulesEngine.UnitTest
Assert.NotNull(result);
re.RemoveWorkflow("inputWorkflow");
await Assert.ThrowsAsync<ArgumentException>(async() => await re.ExecuteAllRulesAsync("inputWorkflow",input1, input2, input3 ));
await Assert.ThrowsAsync<ArgumentException>(async () => await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3));
}
@ -229,8 +227,8 @@ namespace RulesEngine.UnitTest
dynamic input2 = GetInput2();
dynamic input3 = GetInput3();
await Assert.ThrowsAsync<ArgumentException>(async() => await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3));
await Assert.ThrowsAsync<ArgumentException>(async() => await re.ExecuteAllRulesAsync("inputWorkflowReference", input1, input2, input3));
await Assert.ThrowsAsync<ArgumentException>(async () => await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3));
await Assert.ThrowsAsync<ArgumentException>(async () => await re.ExecuteAllRulesAsync("inputWorkflowReference", input1, input2, input3));
}
@ -248,14 +246,14 @@ namespace RulesEngine.UnitTest
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.Contains(result,c => c.IsSuccess);
Assert.Contains(result, c => c.IsSuccess);
input3.hello = "world";
result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.Contains(result,c => c.IsSuccess);
Assert.Contains(result, c => c.IsSuccess);
}
@ -263,11 +261,11 @@ namespace RulesEngine.UnitTest
[InlineData("rules4.json")]
public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Success(string ruleFileName)
{
dynamic[] inputs = GetInputs4();
var inputs = GetInputs4();
var ruleParams = new List<RuleParameter>();
for (int i = 0; i < inputs.Length; i++)
for (var i = 0; i < inputs.Length; i++)
{
var input = inputs[i];
var obj = Utils.GetTypedObject(input);
@ -276,7 +274,9 @@ namespace RulesEngine.UnitTest
var files = Directory.GetFiles(Directory.GetCurrentDirectory(), ruleFileName, SearchOption.AllDirectories);
if (files == null || files.Length == 0)
{
throw new Exception("Rules not found.");
}
var fileData = File.ReadAllText(files[0]);
var bre = new RulesEngine(JsonConvert.DeserializeObject<WorkflowRules[]>(fileData), null);
@ -291,36 +291,40 @@ namespace RulesEngine.UnitTest
{
var re = GetRulesEngine(ruleFileName);
var input1 = new RuleParameter("customName",GetInput1());
var input2 = new RuleParameter("input2",GetInput2());
var input3 = new RuleParameter("input3",GetInput3());
var input1 = new RuleParameter("customName", GetInput1());
var input2 = new RuleParameter("input2", GetInput2());
var input3 = new RuleParameter("input3", GetInput3());
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", input1,input2, input3);
var result = await re.ExecuteAllRulesAsync("inputWorkflow", input1, input2, input3);
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.Contains(result.First().ChildResults, c => c.ExceptionMessage.Contains("Unknown identifier 'input1'"));
}
[Theory]
[InlineData("rules5.json","hello",true)]
[InlineData("rules5.json",null,false)]
public async Task ExecuteRule_WithInjectedUtils_ReturnsListOfRuleResultTree(string ruleFileName,string propValue,bool expectedResult)
[InlineData("rules5.json", "hello", true)]
[InlineData("rules5.json", null, false)]
public async Task ExecuteRule_WithInjectedUtils_ReturnsListOfRuleResultTree(string ruleFileName, string propValue, bool expectedResult)
{
var re = GetRulesEngine(ruleFileName);
dynamic input1 = new ExpandoObject();
if(propValue != null)
input1.Property1 = propValue;
if (propValue != null)
{
input1.Property1 = propValue;
}
if(propValue == null)
input1.Property1 = null;
if (propValue == null)
{
input1.Property1 = null;
}
var utils = new TestInstanceUtils();
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1",input1),new RuleParameter("utils",utils));
var result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1), new RuleParameter("utils", utils));
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.All(result,c => Assert.Equal(expectedResult,c.IsSuccess));
Assert.All(result, c => Assert.Equal(expectedResult, c.IsSuccess));
}
[Theory]
@ -338,7 +342,7 @@ namespace RulesEngine.UnitTest
var utils = new TestInstanceUtils();
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
var result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.All(result, c => Assert.True(c.IsSuccess));
@ -355,7 +359,7 @@ namespace RulesEngine.UnitTest
var utils = new TestInstanceUtils();
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
var result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.All(result, c => Assert.True(c.IsSuccess));
@ -372,7 +376,7 @@ namespace RulesEngine.UnitTest
var utils = new TestInstanceUtils();
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
var result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.All(result, c => Assert.False(c.IsSuccess));
@ -390,9 +394,8 @@ namespace RulesEngine.UnitTest
var utils = new TestInstanceUtils();
await Assert.ThrowsAsync<System.Linq.Dynamic.Core.Exceptions.ParseException>(async()=>
{
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
await Assert.ThrowsAsync<System.Linq.Dynamic.Core.Exceptions.ParseException>(async () => {
var result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
});
}
@ -408,7 +411,7 @@ namespace RulesEngine.UnitTest
var utils = new TestInstanceUtils();
List<RuleResultTree> result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
var result = await re.ExecuteAllRulesAsync("inputWorkflow", new RuleParameter("input1", input1));
Assert.NotNull(result);
Assert.IsType<List<RuleResultTree>>(result);
Assert.All(result, c => Assert.False(c.IsSuccess));
@ -424,8 +427,7 @@ namespace RulesEngine.UnitTest
var filePath = Path.Combine(Directory.GetCurrentDirectory() as string, "TestData", filename);
var data = File.ReadAllText(filePath);
var injectWorkflow = new WorkflowRules
{
var injectWorkflow = new WorkflowRules {
WorkflowName = "inputWorkflowReference",
WorkflowRulesToInject = new List<string> { "inputWorkflow" }
};
@ -490,10 +492,15 @@ namespace RulesEngine.UnitTest
}
[ExcludeFromCodeCoverage]
private class TestInstanceUtils{
public bool CheckExists(string str){
if(str != null && str.Length > 0)
private class TestInstanceUtils
{
public bool CheckExists(string str)
{
if (str != null && str.Length > 0)
{
return true;
}
return false;
}

View File

@ -1,11 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine;
using Moq;
using System;
using Xunit;
using System.Diagnostics.CodeAnalysis;
using Xunit;
namespace RulesEngine.UnitTest
{
@ -13,15 +12,15 @@ namespace RulesEngine.UnitTest
[ExcludeFromCodeCoverage]
public class CustomTypeProviderTests : IDisposable
{
private MockRepository mockRepository;
private readonly MockRepository _mockRepository;
public CustomTypeProviderTests()
{
this.mockRepository = new MockRepository(MockBehavior.Strict);
_mockRepository = new MockRepository(MockBehavior.Strict);
}
public void Dispose()
{
this.mockRepository.VerifyAll();
_mockRepository.VerifyAll();
}
private CustomTypeProvider CreateProvider()
@ -33,7 +32,7 @@ namespace RulesEngine.UnitTest
public void GetCustomTypes_StateUnderTest_ExpectedBehavior()
{
// Arrange
var unitUnderTest = this.CreateProvider();
var unitUnderTest = CreateProvider();
// Act
var result = unitUnderTest.GetCustomTypes();

View File

@ -1,12 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine;
using RulesEngine.ExpressionBuilders;
using RulesEngine.Models;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using Xunit;
namespace RulesEngine.UnitTest
@ -19,7 +17,7 @@ namespace RulesEngine.UnitTest
public void BuildExpressionForRuleTest()
{
var reSettings = new ReSettings();
var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings,new RuleExpressionParser(reSettings));
var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, new RuleExpressionParser(reSettings));
var builder = objBuilderFactory.RuleGetExpressionBuilder(RuleExpressionType.LambdaExpression);
var ruleParameters = new RuleParameter[] {
@ -29,15 +27,17 @@ namespace RulesEngine.UnitTest
};
Rule mainRule = new Rule();
mainRule.RuleName = "rule1";
mainRule.Operator = "And";
mainRule.Rules = new List<Rule>();
var mainRule = new Rule {
RuleName = "rule1",
Operator = "And",
Rules = new List<Rule>()
};
Rule dummyRule = new Rule();
dummyRule.RuleName = "testRule1";
dummyRule.RuleExpressionType = RuleExpressionType.LambdaExpression;
dummyRule.Expression = "RequestType == \"vod\"";
var dummyRule = new Rule {
RuleName = "testRule1",
RuleExpressionType = RuleExpressionType.LambdaExpression,
Expression = "RequestType == \"vod\""
};
mainRule.Rules.Add(dummyRule);
var func = builder.BuildDelegateForRule(dummyRule, ruleParameters);

View File

@ -1,9 +1,10 @@
using RulesEngine.Extensions;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine.Extensions;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Xunit;
namespace RulesEngine.UnitTest
@ -44,8 +45,7 @@ namespace RulesEngine.UnitTest
var successEventName = string.Empty;
rulesResultTree.OnSuccess((eventName) =>
{
rulesResultTree.OnSuccess((eventName) => {
successEventName = eventName;
});
@ -85,8 +85,7 @@ namespace RulesEngine.UnitTest
var successEventName = string.Empty;
rulesResultTree.OnSuccess((eventName) =>
{
rulesResultTree.OnSuccess((eventName) => {
successEventName = eventName;
});
@ -125,8 +124,7 @@ namespace RulesEngine.UnitTest
var successEventName = string.Empty;
rulesResultTree.OnSuccess((eventName) =>
{
rulesResultTree.OnSuccess((eventName) => {
successEventName = eventName;
});
@ -166,8 +164,7 @@ namespace RulesEngine.UnitTest
var successEventName = true;
rulesResultTree.OnFail(() =>
{
rulesResultTree.OnFail(() => {
successEventName = false;
});
@ -206,8 +203,7 @@ namespace RulesEngine.UnitTest
var successEventName = true;
rulesResultTree.OnFail(() =>
{
rulesResultTree.OnFail(() => {
successEventName = false;
});

View File

@ -10,7 +10,7 @@ using Xunit;
namespace RulesEngine.UnitTest
{
[Trait("Category","Unit")]
[Trait("Category", "Unit")]
[ExcludeFromCodeCoverage]
public class RuleCompilerTest
{
@ -20,7 +20,7 @@ namespace RulesEngine.UnitTest
Assert.Throws<ArgumentNullException>(() => new RuleCompiler(null, null));
var reSettings = new ReSettings();
var parser = new RuleExpressionParser(reSettings);
Assert.Throws<ArgumentNullException>(() => new RuleCompiler(new RuleExpressionBuilderFactory(reSettings,parser), null));
Assert.Throws<ArgumentNullException>(() => new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser), null));
}
[Fact]
@ -28,9 +28,9 @@ namespace RulesEngine.UnitTest
{
var reSettings = new ReSettings();
var parser = new RuleExpressionParser(reSettings);
var compiler = new RuleCompiler(new RuleExpressionBuilderFactory(reSettings,parser), new NullLogger<RuleCompiler>());
var compiler = new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser), new NullLogger<RuleCompiler>());
Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, null));
Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, new RuleParameter[] { null}));
Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, new RuleParameter[] { null }));
}

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using RulesEngine;
using RulesEngine.ExpressionBuilders;
using RulesEngine.Models;
using System;
@ -20,7 +19,7 @@ namespace RulesEngine.UnitTest
{
var reSettings = new ReSettings();
var parser = new RuleExpressionParser(reSettings);
var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings,parser);
var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, parser);
var builder = objBuilderFactory.RuleGetExpressionBuilder(expressionType);
var builderType = builder.GetType();

View File

@ -1,8 +1,8 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Newtonsoft.Json;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace RulesEngine.UnitTest
{

View File

@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Text;
using Xunit;
namespace RulesEngine.UnitTest
@ -14,11 +13,11 @@ namespace RulesEngine.UnitTest
[ExcludeFromCodeCoverage]
public class TestClass
{
public string test { get; set; }
public List<int> testList { get; set; }
public string Test { get; set; }
public List<int> TestList { get; set; }
}
[Trait("Category","Unit")]
[Trait("Category", "Unit")]
[ExcludeFromCodeCoverage]
public class UtilsTests
{
@ -27,27 +26,27 @@ namespace RulesEngine.UnitTest
public void GetTypedObject_dynamicObject()
{
dynamic obj = new ExpandoObject();
obj.test = "hello";
obj.testList = new List<int> { 1, 2, 3 };
obj.Test = "hello";
obj.TestList = new List<int> { 1, 2, 3 };
object typedobj = Utils.GetTypedObject(obj);
Assert.IsNotType<ExpandoObject>(typedobj);
Assert.NotNull(typedobj.GetType().GetProperty("test"));
Assert.NotNull(typedobj.GetType().GetProperty("Test"));
}
[Fact]
public void GetTypedObject_dynamicObject_multipleObjects()
{
dynamic obj = new ExpandoObject();
obj.test = "hello";
obj.testList = new List<int> { 1, 2, 3 };
obj.Test = "hello";
obj.TestList = new List<int> { 1, 2, 3 };
dynamic obj2 = new ExpandoObject();
obj2.test = "world";
obj2.testList = new List<int> { 1, 2, 3 };
obj2.Test = "world";
obj2.TestList = new List<int> { 1, 2, 3 };
object typedobj = Utils.GetTypedObject(obj);
object typedobj2 = Utils.GetTypedObject(obj2);
Assert.IsNotType<ExpandoObject>(typedobj);
Assert.NotNull(typedobj.GetType().GetProperty("test"));
Assert.Equal(typedobj.GetType(),typedobj2.GetType());
Assert.NotNull(typedobj.GetType().GetProperty("Test"));
Assert.Equal(typedobj.GetType(), typedobj2.GetType());
}
@ -55,23 +54,23 @@ namespace RulesEngine.UnitTest
public void GetTypedObject_nonDynamicObject()
{
var obj = new {
test = "hello"
Test = "hello"
};
object typedobj = Utils.GetTypedObject(obj);
var typedobj = Utils.GetTypedObject(obj);
Assert.IsNotType<ExpandoObject>(typedobj);
Assert.NotNull(typedobj.GetType().GetProperty("test"));
Assert.NotNull(typedobj.GetType().GetProperty("Test"));
}
[Fact]
[Fact]
public void CreateObject_dynamicObject()
{
dynamic obj = new ExpandoObject();
obj.test = "test";
obj.testList = new List<int> { 1, 2, 3 };
obj.Test = "test";
obj.TestList = new List<int> { 1, 2, 3 };
object newObj = Utils.CreateObject(typeof(TestClass), obj);
Assert.IsNotType<ExpandoObject>(newObj);
Assert.NotNull(newObj.GetType().GetProperty("test"));
Assert.NotNull(newObj.GetType().GetProperty("Test"));
}
@ -79,13 +78,13 @@ namespace RulesEngine.UnitTest
public void CreateAbstractType_dynamicObject()
{
dynamic obj = new ExpandoObject();
obj.test = "test";
obj.testList = new List<int> { 1, 2, 3 };
obj.Test = "test";
obj.TestList = new List<int> { 1, 2, 3 };
obj.testEmptyList = new List<object>();
Type type = Utils.CreateAbstractClassType( obj);
Type type = Utils.CreateAbstractClassType(obj);
Assert.NotEqual(typeof(ExpandoObject), type);
Assert.NotNull(type.GetProperty("test"));
Assert.NotNull(type.GetProperty("Test"));
}