Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge docs to main #288

Merged
merged 7 commits into from
Dec 11, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 256 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Description
RulesEngine is a highly extensible library to build rule based system using C# expressions

## Features

**Features**
- Json based rules defination
- Multiple input support
- Dynamic object input support
Expand All @@ -10,6 +10,27 @@ RulesEngine is a highly extensible library to build rule based system using C# e
- Scoped parameters
- Post rule execution actions

**Table Of Content**
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Create a workflow file with rules](#create-a-workflow-file-with-rules)
- [Initialise RulesEngine with the workflow:](#initialise-rulesengine-with-the-workflow)
- [Execute the workflow rules with input:](#execute-the-workflow-rules-with-input)
- [Using custom names for inputs](#using-custom-names-for-inputs)
- [C# Expression support](#c-expression-support)
- [ScopedParams](#scopedparams)
- [GlobalParams](#globalparams)
- [Example](#example)
- [LocalParams](#localparams)
- [Example](#example-1)
- [Referencing ScopedParams in other ScopedParams](#referencing-scopedparams-in-other-scopedparams)
- [Post rule execution actions](#post-rule-execution-actions)
- [Inbuilt Actions](#inbuilt-actions)
- [OutputExpression](#outputexpression)
- [Usage](#usage)
- [Custom Actions](#custom-actions)
- [Steps to use a custom Action](#steps-to-use-a-custom-action)



## Installation
Expand Down Expand Up @@ -92,6 +113,13 @@ var resultList = await re.ExecuteAllRulesAsync("DiscountWithCustomInputNames",r

```

## C# Expression support
The lambda expression allows you to use most of C# constructs and along with some of linq features.

For more details on supported expression language refer - [expression language](https://dynamic-linq.net/expression-language)

For supported linq operations refer - [sequence operators](https://dynamic-linq.net/expression-language#sequence-operators)


## ScopedParams
Sometimes Rules can get very long and complex, scopedParams allow users to replace an expression in rule with an alias making it easier to maintain rule.
Expand All @@ -106,7 +134,7 @@ GlobalParams are defined at workflow level and can be used in any rule.

#### Example

```json
```jsonc
//Rule.json
{
"WorkflowName": "workflowWithGlobalParam",
Expand Down Expand Up @@ -141,3 +169,228 @@ These rules when executed with the below input will return success
```


### LocalParams
LocalParams are defined at rule level and can be used by the rule and its child rules

#### Example

```jsonc
//Rule.json
{
"WorkflowName": "workflowWithLocalParam",

"Rules":[
{
"RuleName": "checkLocalEqualsHello",
"LocalParams":[
{
"Name":"mylocal1",
"Expression":"myInput.hello.ToLower()"
}
],
"Expression":"mylocal1 == \"hello\""
},
{
"RuleName": "checkLocalEqualsInputHelloInNested",
"LocalParams":[
{
"Name":"mylocal1", //redefined here as it is scoped at rule level
"Expression":"myInput.hello.ToLower()"
}
],
"Operator": "And",
"Rules":[
{
"RuleName": "nestedRule",
"Expression":"myInput.hello.ToLower() == mylocal1" //mylocal1 can be used here since it is nested to Rule where mylocal1 is defined
}
]

}
]
}
```

These rules when executed with the below input will return success
```c#
var input = new RuleParameter("myInput",new {
hello = "HELLO"
});

var resultList = await re.ExecuteAllRulesAsync("workflowWithLocalParam",rp);
```

### Referencing ScopedParams in other ScopedParams

Similar to how ScopedParams can be used in expressions, they can also be used in other scoped params that come after them.
This allows us to create multi-step rule which is easier to read and maintain


```jsonc
//Rule.json
{
"WorkflowName": "workflowWithReferencedRule",
"GlobalParams":[
{
"Name":"myglobal1",
"Expression":"myInput.hello"
}
],
"Rules":[
{
"RuleName": "checkGlobalAndLocalEqualsHello",
"LocalParams":[
{
"Name": "mylocal1",
"Expression": "myglobal1.ToLower()"
}
],
"Expression":"mylocal1 == \"hello\""
},
{
"RuleName": "checklocalEqualsInputHello",
"LocalParams":[
{
"Name": "mylocal1",
"Expression": "myglobal1.ToLower()"
},
{
"Name": "mylocal2",
"Expression": "myInput.hello.ToLower() == mylocal1"
}
],
"Expression":"mylocal2 == true"
}
]
}
```

These rules when executed with the below input will return success
```c#
var input = new RuleParameter("myInput",new {
hello = "HELLO"
});

var resultList = await re.ExecuteAllRulesAsync("workflowWithReferencedRule",rp);


```

## Post rule execution actions
As a part of v3, Actions have been introduced to allow custom code execution on rule result. This can be achieved by calling `ExecuteAllRulesAsync` method of RulesEngine

### Inbuilt Actions
RulesEngine provides inbuilt action which cover major scenarios related to rule execution

#### OutputExpression
This action evaluates an expression based on the RuleParameters and returns its value as Output
##### Usage
Define OnSuccess or OnFailure Action for your Rule:
```jsonc
{
"WorkflowName": "inputWorkflow",
"Rules": [
{
"RuleName": "GiveDiscount10Percent",
"SuccessEvent": "10",
"ErrorMessage": "One or more adjust rules failed.",
"ErrorType": "Error",
"RuleExpressionType": "LambdaExpression",
"Expression": "input1.couy == \"india\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input2.noOfVisitsPerMonth > 2",
"Actions": {
"OnSuccess": {
"Name": "OutputExpression", //Name of action you want to call
"Context": { //This is passed to the action as action context
"Expression": "input1.TotalBilled * 0.9"
}
}
}
}
]
}
```
Call `ExecuteAllRulesAsync` with the workflowName, ruleName and ruleParameters
```c#
var ruleResultList = await rulesEngine.ExecuteAllRulesAsync("inputWorkflow",ruleParameters);
foreach(var ruleResult in ruleResultList){
if(ruleResult.ActionResult != null){
Console.WriteLine(ruleResult.ActionResult.Output); //ActionResult.Output contains the evaluated value of the action
}
}

```

### Custom Actions
RulesEngine allows registering custom actions which can be used in the rules workflow.

#### Steps to use a custom Action
1. Create a class which extends `ActionBase` class and implement the run method
```c#
public class MyCustomAction: ActionBase
{

public MyCustomAction(SomeInput someInput)
{
....
}

public override ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)
{
var customInput = context.GetContext<string>("customContextInput");
//Add your custom logic here and return a ValueTask
}
```
Actions can have async code as well
```c#
public class MyCustomAction: ActionBase
{

public MyCustomAction(SomeInput someInput)
{
....
}

public override async ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)
{
var customInput = context.GetContext<string>("customContextInput");
//Add your custom logic here
return await MyCustomLogicAsync();
}
```
2. Register them in ReSettings and pass it to RulesEngine
```c#
var reSettings = new ReSettings{
CustomActions = new Dictionary<string, Func<ActionBase>>{
{"MyCustomAction", () => new MyCustomAction(someInput) }
}
};

var re = new RulesEngine(workflowRules,logger,reSettings);
```
3. You can now use the name you registered in the Rules json in success or failure actions
```jsonc
{
"WorkflowName": "inputWorkflow",
"Rules": [
{
"RuleName": "GiveDiscount10Percent",
"SuccessEvent": "10",
"ErrorMessage": "One or more adjust rules failed.",
"ErrorType": "Error",
"RuleExpressionType": "LambdaExpression",
"Expression": "input1.couy == \"india\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input2.noOfVisitsPerMonth > 2",
"Actions": {
"OnSuccess": {
"Name": "MyCustomAction", //Name context
"Context": { //This is passed to the action as action context
"customContextInput": "input1.TotalBilled * 0.9"
}
}
}
}
]
}
```


_For more details please check out [Rules Engine Wiki](https://github.com/microsoft/RulesEngine/wiki)._