[
  {
    "path": ".config/dotnet-tools.json",
    "content": "{\n  \"version\": 1,\n  \"isRoot\": true,\n  \"tools\": {\n    \"dotnet-reportgenerator-globaltool\": {\n      \"version\": \"5.1.26\",\n      \"commands\": [\n        \"reportgenerator\"\n      ]\n    }\n  }\n}"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\r\n\t\"name\": \"RulesEngine Codespace\",\r\n\t\"image\": \"mcr.microsoft.com/vscode/devcontainers/dotnet:0-6.0\",\r\n\t\"settings\": {\r\n\t\t\"terminal.integrated.defaultProfile.linux\": \"bash\"\r\n\t},\r\n\t\"extensions\": [\r\n\t\t\"eamodio.gitlens\",\r\n\t\t\"ms-dotnettools.csharp\",\r\n\t\t\"VisualStudioExptTeam.vscodeintellicode\",\r\n\t\t\"ms-vscode.powershell\",\r\n\t\t\"cschleiden.vscode-github-actions\",\r\n\t\t\"redhat.vscode-yaml\",\r\n\t\t\"bierner.markdown-preview-github-styles\",\r\n\t\t\"coenraads.bracket-pair-colorizer\",\r\n\t\t\"vscode-icons-team.vscode-icons\",\r\n\t\t\"editorconfig.editorconfig\",\r\n\t\t\"aliasadidev.nugetpackagemanagergui\",\r\n\t\t\"formulahendry.dotnet-test-explorer\"\r\n\t],\r\n\t\"postCreateCommand\": \"dotnet restore RulesEngine.sln && dotnet build RulesEngine.sln --configuration Release --no-restore && dotnet test RulesEngine.sln --configuration Release --no-build --verbosity minimal\",\r\n\t\"features\": {\r\n\t\t\"powershell\": \"7.1\"\r\n\t},\r\n}\r\n// Built with ❤ by [Pipeline Foundation](https://pipeline.foundation)\r\n"
  },
  {
    "path": ".editorconfig",
    "content": "# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\\Users\\purunjaybhal\\source\\repos\\RulesEngine codebase based on best match to current usage at 22-12-2020\n# You can modify the rules from these initially generated values to suit your own policies\n# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference\n[*.cs]\n\n\n#Core editorconfig formatting - indentation\n\n#use soft tabs (spaces) for indentation\nindent_style = space\n\n#Formatting - indentation options\n\n#indent switch case contents.\ncsharp_indent_case_contents = true\n#indent switch labels\ncsharp_indent_switch_labels = true\n\n#Formatting - new line options\n\n#place catch statements on a new line\ncsharp_new_line_before_catch = true\n#place else statements on a new line\ncsharp_new_line_before_else = true\n#require members of anonymous types to be on separate lines\ncsharp_new_line_before_members_in_anonymous_types = true\n#require members of object intializers to be on separate lines\ncsharp_new_line_before_members_in_object_initializers = true\n#require braces to be on a new line for methods, control_blocks, and types (also known as \"Allman\" style)\ncsharp_new_line_before_open_brace = methods, control_blocks, types\n\n#Formatting - organize using options\n\n#do not place System.* using directives before other using directives\ndotnet_sort_system_directives_first = false\n\n#Formatting - spacing options\n\n#require NO space between a cast and the value\ncsharp_space_after_cast = false\n#require a space before the colon for bases or interfaces in a type declaration\ncsharp_space_after_colon_in_inheritance_clause = true\n#require a space after a keyword in a control flow statement such as a for loop\ncsharp_space_after_keywords_in_control_flow_statements = true\n#require a space before the colon for bases or interfaces in a type declaration\ncsharp_space_before_colon_in_inheritance_clause = true\n#remove space within empty argument list parentheses\ncsharp_space_between_method_call_empty_parameter_list_parentheses = false\n#remove space between method call name and opening parenthesis\ncsharp_space_between_method_call_name_and_opening_parenthesis = false\n#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call\ncsharp_space_between_method_call_parameter_list_parentheses = false\n#remove space within empty parameter list parentheses for a method declaration\ncsharp_space_between_method_declaration_empty_parameter_list_parentheses = false\n#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.\ncsharp_space_between_method_declaration_parameter_list_parentheses = false\n\n#Formatting - wrapping options\n\n#leave code block on single line\ncsharp_preserve_single_line_blocks = true\n#leave statements and member declarations on the same line\ncsharp_preserve_single_line_statements = true\n\n#Style - Code block preferences\n\n#prefer curly braces even for one line of code\ncsharp_prefer_braces = true:suggestion\n\n#Style - expression bodied member options\n\n#prefer block bodies for constructors\ncsharp_style_expression_bodied_constructors = false:suggestion\n#prefer block bodies for methods\ncsharp_style_expression_bodied_methods = false:suggestion\n\n#Style - expression level options\n\n#prefer out variables to be declared inline in the argument list of a method call when possible\ncsharp_style_inlined_variable_declaration = true:suggestion\n#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them\ndotnet_style_predefined_type_for_member_access = true:suggestion\n\n#Style - Expression-level  preferences\n\n#prefer objects to be initialized using object initializers when possible\ndotnet_style_object_initializer = true:suggestion\n#prefer inferred anonymous type member names\ndotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion\n\n#Style - implicit and explicit types\n\n#prefer var over explicit type in all cases, unless overridden by another code style rule\ncsharp_style_var_elsewhere = true:suggestion\n#prefer var is used to declare variables with built-in system types such as int\ncsharp_style_var_for_built_in_types = true:suggestion\n#prefer var when the type is already mentioned on the right-hand side of a declaration expression\ncsharp_style_var_when_type_is_apparent = true:suggestion\n\n#Style - language keyword and framework type options\n\n#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\ndotnet_style_predefined_type_for_locals_parameters_members = true:suggestion\n\n#Style - Miscellaneous preferences\n\n#prefer anonymous functions over local functions\ncsharp_style_pattern_local_over_anonymous_function = false:suggestion\n\n#Style - modifier options\n\n#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.\ndotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion\n\n#Style - Modifier preferences\n\n#when this rule is set to a list of modifiers, prefer the specified ordering.\ncsharp_preferred_modifier_order = public,private,internal,readonly,static,async,override,sealed:suggestion\n\n#Style - qualification options\n\n#prefer fields not to be prefaced with this. or Me. in Visual Basic\ndotnet_style_qualification_for_field = false:suggestion\n#prefer methods not to be prefaced with this. or Me. in Visual Basic\ndotnet_style_qualification_for_method = false:suggestion\n#prefer properties not to be prefaced with this. or Me. in Visual Basic\ndotnet_style_qualification_for_property = false:suggestion\n\n\n#file header\n[*.{cs,vb}]\nfile_header_template = Copyright (c) Microsoft Corporation.\\nLicensed under the MIT License.\nfile_header_template_style = prepend:error\nfile_header_template_style = replace:suggestion\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    # default location of `.github/workflows`\n    directory: \"/\"\n    open-pull-requests-limit: 3\n    schedule:\n      interval: \"weekly\"\n    # assignees:\n    #   - assignee_one\n    # reviewers:\n    #   - reviewer_one\n  - package-ecosystem: \"nuget\"\n    # location of package manifests\n    directory: \"/\"\n    open-pull-requests-limit: 3\n    schedule:\n      interval: \"weekly\"\n    ignore:\n      - dependency-name: \"*\"\n        update-types: [\"version-update:semver-minor\"]\n    # assignees:\n    #   - assignee_one\n    # reviewers:\n    #   - reviewer_one\n  - package-ecosystem: dotnet-sdk\n    directory: /\n    schedule:\n      interval: weekly\n      day: wednesday\n    ignore:\n      - dependency-name: '*'\n        update-types:\n          - version-update:semver-major\n          - version-update:semver-minor\n# Built with ❤ by [Pipeline Foundation](https://pipeline.foundation)\n"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ main, develop ]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [ main ]\n  schedule:\n    - cron: '22 15 * * 4'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'csharp' ]\n        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]\n        # Learn more:\n        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v4\n\n    - name: Setup .NET Core\n      uses: actions/setup-dotnet@v4\n      with:\n        dotnet-version: |\n          6.0.x\n          8.0.x\n          9.0.x\n\n    # Initializes the CodeQL tools for scanning.\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: ${{ matrix.language }}\n        # If you wish to specify custom queries, you can do so here or in a config file.\n        # By default, queries listed here will override any specified in a config file.\n        # Prefix the list here with \"+\" to use these queries and those in the config file.\n        # queries: ./path/to/local/query, your-org/your-repo/queries@main\n\n    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n    # If this step fails, then you should remove it and run the build manually (see below)\n    - name: Autobuild\n      uses: github/codeql-action/autobuild@v3\n\n    # ℹ️ Command-line programs to run using the OS shell.\n    # 📚 https://git.io/JvXDl\n\n    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines\n    #    and modify them (or add more) to build your code if your project\n    #    uses a compiled language\n\n    #- run: |\n    #   make bootstrap\n    #   make release\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n    \n"
  },
  {
    "path": ".github/workflows/dotnetcore-build.yml",
    "content": "name: build\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main, develop ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n    - name: Setup .NET Core\n      uses: actions/setup-dotnet@v4\n      with:\n        dotnet-version: |\n          6.0.x\n          8.0.x\n          9.0.x\n    \n    - name: Install dependencies\n      run: dotnet restore RulesEngine.sln\n    \n    - name: Build\n      run: dotnet build RulesEngine.sln --configuration Release --no-restore\n    \n    - name: Test\n      run: dotnet test RulesEngine.sln --collect:\"XPlat Code Coverage\" --no-build --configuration Release --verbosity m \n\n    - name: Generate Report\n      shell: pwsh\n      run: ./scripts/generate-coverage-report.ps1\n\n    - name: Check Coverage\n      shell: pwsh\n      run: ./scripts/check-coverage.ps1 -reportPath coveragereport/Cobertura.xml -threshold 94\n    \n    - name: Coveralls GitHub Action\n      uses: coverallsapp/github-action@v2.3.6\n      if: ${{ github.event_name == 'push' }}\n      with:\n        github-token: ${{ secrets.GITHUB_TOKEN }}\n        path-to-lcov: ./coveragereport/lcov.info\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n**/Properties/launchSettings.json\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk \n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush\n.cr/\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output \nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder \n.mfractor/\n/src/RulesEngine/RulesEngine.sln.licenseheader\n/assets/RulesEnginePackageFile.xml\ncoveragereport/\n\nsrc/**/*.snk\n\ndist"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n   // Use IntelliSense to find out which attributes exist for C# debugging\n   // Use hover for the description of the existing attributes\n   // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md\n   \"version\": \"0.2.0\",\n   \"configurations\": [\n        {\n            \"name\": \".NET Core Launch (console)\",\n            \"type\": \"coreclr\",\n            \"request\": \"launch\",\n            \"preLaunchTask\": \"build\",\n            // If you have changed target frameworks, make sure to update the program path.\n            \"program\": \"${workspaceFolder}/demo/DemoApp/bin/Debug/netcoreapp3.1/DemoApp.dll\",\n            \"args\": [],\n            \"cwd\": \"${workspaceFolder}/demo/DemoApp\",\n            // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console\n            \"console\": \"internalConsole\",\n            \"stopAtEntry\": false\n        },\n        {\n            \"name\": \".NET Core Attach\",\n            \"type\": \"coreclr\",\n            \"request\": \"attach\",\n            \"processId\": \"${command:pickProcess}\"\n        }\n    ]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"dotnetCoreExplorer.searchpatterns\": \"test/**/bin/Debug/netcoreapp*/*.{dll,exe,json}\",\n    \"coverage-gutters.coverageBaseDir\": \"coveragereport\",\n    \"coverage-gutters.coverageReportFileName\": \"coveragereport/**/index.html\"\n}"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "{\n    \"version\": \"2.0.0\",\n    \"tasks\": [\n        {\n            \"label\": \"build\",\n            \"command\": \"dotnet\",\n            \"type\": \"process\",\n            \"args\": [\n                \"build\",\n                \"${workspaceFolder}/demo/DemoApp/DemoApp.csproj\",\n                \"/property:GenerateFullPaths=true\",\n                \"/consoleloggerparameters:NoSummary\"\n            ],\n            \"problemMatcher\": \"$msCompile\"\n        },\n        {\n            \"label\": \"publish\",\n            \"command\": \"dotnet\",\n            \"type\": \"process\",\n            \"args\": [\n                \"publish\",\n                \"${workspaceFolder}/demo/DemoApp/DemoApp.csproj\",\n                \"/property:GenerateFullPaths=true\",\n                \"/consoleloggerparameters:NoSummary\"\n            ],\n            \"problemMatcher\": \"$msCompile\"\n        },\n        {\n            \"label\": \"watch\",\n            \"command\": \"dotnet\",\n            \"type\": \"process\",\n            \"args\": [\n                \"watch\",\n                \"run\",\n                \"${workspaceFolder}/demo/DemoApp/DemoApp.csproj\",\n                \"/property:GenerateFullPaths=true\",\n                \"/consoleloggerparameters:NoSummary\"\n            ],\n            \"problemMatcher\": \"$msCompile\"\n        }\n    ]\n}"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# CHANGELOG\n\nAll notable changes to this project will be documented in this file.\n\n## [5.0.3]\n- Updated dependencies to latest\n- Fixed RulesEngine throwing exception when type name is same as input name\n- Added config to disable FastCompile for expressions\n- Added RuleParameter.Create method for better handling on types when value is null\n\n## [5.0.2]\n- Fixed Scoped Params returning incorrect results in some corner case scenarios\n\n## [5.0.1]\n- Added option to disable automatic type registry for input parameters in reSettings\n- Added option to make expression case sensitive in reSettings\n\n## [5.0.0]\n- Fixed security bug related to System.Dynamic.Linq.Core\n\n### Breaking Changes\n- As a part of security bug fix, method call for only registered types via reSettings will be allowed. This only impacts strongly typed inputs and nested types\n\n\n## [4.0.0]\n- RulesEngine is now available in both dotnet 6 and netstandard 2.0\n- Dependency on ILogger, MemoryCache have been removed \n- Obsolete Properties and Methods have been removed\n- Fixed name of RuleParameter is ignored if the type is recognized (by @peeveen)\n### Breaking Changes\n- ILogger has been removed from RulesEngine and all its constructors\n```diff\n- RulesEngine(string[] jsonConfig, ILogger logger = null, ReSettings reSettings = null)\n+ RulesEngine(string[] jsonConfig, ReSettings reSettings = null)\n\n- RulesEngine(Workflow[] Workflows, ILogger logger = null, ReSettings reSettings = null)\n+ RulesEngine(Workflow[] Workflows, ReSettings reSettings = null)\n\n- RulesEngine(ILogger logger = null, ReSettings reSettings = null)\n+ RulesEngine(ReSettings reSettings = null)\n```\n- Obsolete methods and properties have been removed, from the follow models:-\n\t- RuleResultTree\n\t\t- `ToResultTreeMessages()` has been removed from `RuleResultTree` model\n\t\t- `GetMessages()` has been removed from `RuleResultTree` model\n\t\t- `RuleEvaluatedParams` has been removed from `RuleResultTree` model, Please use `Inputs` instead\n\n\t- Workflow\n\t\t- `WorkflowRulesToInject` has been removed, Please use `WorkflowsToInject` instead\n\t\t- `ErrorType` has been removed from `Rule`\n\n\t- Resettings\n\t\t- `EnableLocalParams` has been removed from `ReSettings`, Please use `EnableScopedParams` instead\n\t\n\n## [3.5.0]\n- `EvaluateRule` action now support custom inputs and filtered inputs\n- Added `ContainsWorkflow` method in RulesEngine (by @okolobaxa)\n- Fixed minor bugs (#258 & #259)\n\n## [3.4.0]\n- Made RulesEngine Strong Name and Authenticode signed\n- Renamed few models to streamline names (by @alexrich)\n\t- `WorkflowRules` is renamed to `Workflow`\n\t- `WorkflowRulesToInject` is renamed to `WorkflowsToInject`\n\t- `RuleAction` is renamed to `RuleActions`\n\t\n\t**Note**: The old models are still supported but will be removed with version 4.0.0\n\n\n## [3.3.0]\n- Added support for actions in nested rules\n- Improved serialization support for System.Text.Json for workflow model\n  \nBreaking Change:\n  - Type of Action has been changed from `Dictionary<ActionTriggerType, ActionInfo>` to `RuleActions`\n    - No impact if you are serializing workflow from json\n    - For workflow objects created in code, refer - [link](https://github.com/microsoft/RulesEngine/pull/182/files#diff-a5093dda2dcc1e4958ce3533edb607bb61406e1f0a9071eca4e317bdd987c0d3)\n\n## [3.2.0]\n- Added AddOrUpdateWorkflow method to update workflows atomically (by @AshishPrasad)\n- Updated dependencies to latest\n\nBreaking Change:\n  - `AddWorkflow` now throws exception if you try to add a workflow which already exists.\n  Use `AddOrUpdateWorkflow` to update existing workflow\n\n## [3.1.0]\n- Added globalParams feature which can be applied to all rules\n- Enabled localParams support for nested Rules\n- Made certain fields in Rule model optional allowing users to define workflow with minimal fields\n- Added option to disable Rule in workflow json\n- Added `GetAllRegisteredWorkflow` to RulesEngine to return all registered workflows\n- Runtime errors for expressions will now be logged as errorMessage instead of throwing Exceptions by default\n- Fixed RuleParameter passed as null\n\n## [3.0.2]\n- Fixed LocalParams cache not getting cleaned up when RemoveWorkflows and ClearWorkflows are called\n\n## [3.0.1]\n- Moved ActionResult and ActionRuleResult under RulesEngine.Models namespace\n\n\n## [3.0.0]\n### Major Enhancements\n- Added support for Actions. More details on [actions wiki](https://github.com/microsoft/RulesEngine/wiki/Actions)\n- Major performance improvement\n\t- 25% improvement from previous version\n\t- Upto 35% improvement by disabling optional features\n- RulesEngine now virtually supports unlimited inputs (Previous limitation was 16 inputs)\n- RuleExpressionParser is now available to use expression evaluation outside RulesEngine\n\n### Breaking Changes\n- `ExecuteRule` method has been renamed to `ExecuteAllRulesAsync`\n- `Input` field in RuleResultTree has been changed to `Inputs` which returns all the the inputs as Dictionary of name and value pair\n\n## [2.1.5] - 02-11-2020\n- Added `Properties` field to Rule to allow custom fields to Rule\n\n## [2.1.4] - 15-10-2020\n- Added exception data properties to identify RuleName.\n\n## [2.1.3] - 12-10-2020\n- Optional parameter for rethrow exception on failure of expression compilation.\n\n## [2.1.2] - 02-10-2020\n- Fixed binary expression requirement. Now any expression will work as long as it evalutes to boolean.\n\n## [2.1.1] - 01-09-2020\n- Fixed exception thrown when errormessage field is null\n- Added better messaging when identifier is not found in expression\n- Fixed other minor bugs\n\n## [2.1.0] - 18-05-2020\n- Adding local param support to make expression authroing more intuitive.\n\n## [2.0.0] - 18-05-2020\n### Changed\n- Interface simplified by removing redundant parameters in the IRulesEngine.\n- Custom Logger replaced with Microsoft Logger.\n\n## [1.0.2] - 16-01-2020\n### Added\n- Cache system added so that rules compilation is stored and thus made more efficient.\n\n### Fix\n- Concurrency issue which arose by dictionary was resolved.\n\n## [1.0.1] - 24-09-2019\n### Added\n- Exceptions handling scenario in the case a rule execution throws an exception \n\n## [1.0.0] - 20-08-2019\n\n### Added\n- The first version of the NuGet\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nResources:\n\n- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\n- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns\n"
  },
  {
    "path": "LICENSE",
    "content": "    MIT License\n\n    Copyright (c) Microsoft Corporation.\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE\n"
  },
  {
    "path": "README.md",
    "content": "# Rules Engine\n![build](https://github.com/microsoft/RulesEngine/workflows/build/badge.svg?branch=main)\n[![Coverage Status](https://coveralls.io/repos/github/microsoft/RulesEngine/badge.svg?branch=main)](https://coveralls.io/github/microsoft/RulesEngine?branch=main)\n[![Nuget download][download-image]][download-url]\n\n[download-image]: https://img.shields.io/nuget/dt/RulesEngine\n[download-url]: https://www.nuget.org/packages/RulesEngine/\n\n## Overview\n\nRules Engine is a library/NuGet package for abstracting business logic/rules/policies out of a system. It provides a simple way of giving you the ability to put your rules in a store outside the core logic of the system, thus ensuring that any change in rules don't affect the core system.\n\n## Installation\n\nTo install this library, download the latest version of [NuGet Package](https://www.nuget.org/packages/RulesEngine/) from [nuget.org](https://www.nuget.org/) and refer it into your project.  \n\n## How to use it\n\nThere are several ways to populate workflows for the Rules Engine as listed below.\n\nYou need to store the rules based on the [schema definition](https://github.com/microsoft/RulesEngine/blob/main/schema/workflow-schema.json) given and they can be stored in any store as deemed appropriate like Azure Blob Storage, Cosmos DB, Azure App Configuration, [Entity Framework](https://github.com/microsoft/RulesEngine#entity-framework), SQL Servers, file systems etc. For RuleExpressionType `LambdaExpression`, the rule is written as a [lambda expressions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions).\n\nAn example rule:\n\n```json\n[\n  {\n    \"WorkflowName\": \"Discount\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"SuccessEvent\": \"10\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor <= 2 AND input1.totalPurchasesToDate >= 5000\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount20\",\n        \"SuccessEvent\": \"20\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor >= 3 AND input1.totalPurchasesToDate >= 10000\"\n      }\n    ]\n  }\n]\n```\n\nYou can inject the rules into the Rules Engine by initiating an instance by using the following code - \n\n```c#\nvar rulesEngine = new RulesEngine(workflow);\n```\nHere, *workflow* is a list of deserialized objects based on the schema explained above\nOnce initialised, the Rules Engine needs to execute the rules for a given input. This can be done by calling the method `ExecuteAllRulesAsync`: \n\n```c#\nList<RuleResultTree> response = await rulesEngine.ExecuteAllRulesAsync(workflowName, input);\n```\n\nHere, *workflowName* is the name of the workflow, which is *Discount* in the above mentioned example. And *input* is the object which needs to be checked against the rules,  which itself may consist of a list of class instances.\n\nThe *response* will contain a list of [*RuleResultTree*](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#ruleresulttree) which gives information if a particular rule passed or failed. \n\n_Note: A detailed example showcasing how to use Rules Engine is explained in [Getting Started page](https://github.com/microsoft/RulesEngine/wiki/Getting-Started) of [Rules Engine Wiki](https://github.com/microsoft/RulesEngine/wiki)._\n\n_A demo app for the is available at [this location](https://github.com/microsoft/RulesEngine/tree/main/demo)._\n\n### Basic\n\nA simple example via code only is as follows:\n\n```c#\nList<Rule> rules = new List<Rule>();\n\nRule rule = new Rule();\nrule.RuleName = \"Test Rule\";\nrule.SuccessEvent = \"Count is within tolerance.\";\nrule.ErrorMessage = \"Over expected.\";\nrule.Expression = \"count < 3\";\nrule.RuleExpressionType = RuleExpressionType.LambdaExpression;\nrules.Add(rule);\n\nvar workflows = new List<Workflow>();\n\nWorkflow exampleWorkflow = new Workflow();\nexampleWorkflow.WorkflowName = \"Example Workflow\";\nexampleWorkflow.Rules = rules;\n\nworkflows.Add(exampleWorkflow);\n\nvar bre = new RulesEngine.RulesEngine(workflows.ToArray());\n```\n### Entity Framework\n\nConsuming Entity Framework and populating the Rules Engine is shown in the [EFDemo class](https://github.com/microsoft/RulesEngine/blob/main/demo/DemoApp/EFDemo.cs) with Workflow rules populating the array and passed to the Rules Engine, The Demo App includes an example [RulesEngineDemoContext](https://github.com/microsoft/RulesEngine/blob/main/demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs) using SQLite and could be swapped out for another provider.\n\n```c#\nvar wfr = db.Workflows.Include(i => i.Rules).ThenInclude(i => i.Rules).ToArray();\nvar bre = new RulesEngine.RulesEngine(wfr, null);\n```\n\n*Note: For each level of nested rules expected, a ThenInclude query appended will be needed as shown above.*\n\n## How it works\n\n![](https://github.com/microsoft/RulesEngine/blob/main/assets/BlockDiagram.png)\n\nThe rules can be stored in any store and be fed to the system in a structure which adheres to the [schema](https://github.com/microsoft/RulesEngine/blob/main/schema/workflow-schema.json) of WorkFlow model.\n\nA wrapper needs to be created over the Rules Engine package, which will get the rules and input message(s) from any store that your system dictates and put it into the Engine. The wrapper then handles the output using appropriate means.\n\n_Note: To know in detail of the workings of Rules Engine, please visit [How it works section](https://github.com/microsoft/RulesEngine/wiki/Introduction#how-it-works) in [Rules Engine Wiki](https://github.com/microsoft/RulesEngine/wiki)._\n\n## 3rd Party Tools\n\n### RulesEngine Editor\nThere is an editor library with it's own [NuGet Package](https://www.nuget.org/packages/RulesEngineEditor/) written in Blazor, more information is in it's repo https://github.com/alexreich/RulesEngineEditor. \n\n#### Live Demo\nhttps://alexreich.github.io/RulesEngineEditor  \n> This can also be installed as a standalone PWA and used offline.\n\n#### With Sample Data\nhttps://alexreich.github.io/RulesEngineEditor/demo\n\n## Contributing\n\nThis project welcomes contributions and suggestions.  Most contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n\n---\n\n_For more details please check out [Rules Engine Wiki](https://github.com/microsoft/RulesEngine/wiki)._\n"
  },
  {
    "path": "RulesEngine.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.0.31717.71\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"RulesEngine\", \"src\\RulesEngine\\RulesEngine.csproj\", \"{CD4DFE6A-083B-478E-8377-77F474833E30}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"RulesEngine.UnitTest\", \"test\\RulesEngine.UnitTest\\RulesEngine.UnitTest.csproj\", \"{50E0C2A5-E2C8-4B12-8C0E-B69F698A82BF}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"DemoApp\", \"demo\\DemoApp\\DemoApp.csproj\", \"{57BB8C07-799A-4F87-A7CC-D3D3F694DD02}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{CD4DFE6A-083B-478E-8377-77F474833E30} = {CD4DFE6A-083B-478E-8377-77F474833E30}\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{019DF693-8442-45B4-88C3-55CB7AFCB42E}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.editorconfig = .editorconfig\n\t\tCHANGELOG.md = CHANGELOG.md\n\t\tglobal.json = global.json\n\t\tREADME.md = README.md\n\t\tschema\\workflow-list-schema.json = schema\\workflow-list-schema.json\n\t\tschema\\workflow-schema.json = schema\\workflow-schema.json\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"RulesEngineBenchmark\", \"benchmark\\RulesEngineBenchmark\\RulesEngineBenchmark.csproj\", \"{C058809F-C720-4EFC-925D-A486627B238B}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{CD4DFE6A-083B-478E-8377-77F474833E30} = {CD4DFE6A-083B-478E-8377-77F474833E30}\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"DemoApp.EFDataExample\", \"demo\\DemoApp.EFDataExample\\DemoApp.EFDataExample.csproj\", \"{E376D3E6-6890-4C09-9EA0-3EFD9C1E036D}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{CD4DFE6A-083B-478E-8377-77F474833E30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CD4DFE6A-083B-478E-8377-77F474833E30}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CD4DFE6A-083B-478E-8377-77F474833E30}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CD4DFE6A-083B-478E-8377-77F474833E30}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{50E0C2A5-E2C8-4B12-8C0E-B69F698A82BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{50E0C2A5-E2C8-4B12-8C0E-B69F698A82BF}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{50E0C2A5-E2C8-4B12-8C0E-B69F698A82BF}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{50E0C2A5-E2C8-4B12-8C0E-B69F698A82BF}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{57BB8C07-799A-4F87-A7CC-D3D3F694DD02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{57BB8C07-799A-4F87-A7CC-D3D3F694DD02}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{57BB8C07-799A-4F87-A7CC-D3D3F694DD02}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{57BB8C07-799A-4F87-A7CC-D3D3F694DD02}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{C058809F-C720-4EFC-925D-A486627B238B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{C058809F-C720-4EFC-925D-A486627B238B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{C058809F-C720-4EFC-925D-A486627B238B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{C058809F-C720-4EFC-925D-A486627B238B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E376D3E6-6890-4C09-9EA0-3EFD9C1E036D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E376D3E6-6890-4C09-9EA0-3EFD9C1E036D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E376D3E6-6890-4C09-9EA0-3EFD9C1E036D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E376D3E6-6890-4C09-9EA0-3EFD9C1E036D}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {E1F2EC8E-4005-4DFE-90ED-296D4592867A}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).\n\nIf you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).\n\nIf you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).\n\nYou should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). \n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n  * Full paths of source file(s) related to the manifestation of the issue\n  * The location of the affected source code (tag/branch/commit or direct URL)\n  * Any special configuration required to reproduce the issue\n  * Step-by-step instructions to reproduce the issue\n  * Proof-of-concept or exploit code (if possible)\n  * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nIf you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n\n## Policy\n\nMicrosoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).\n\n<!-- END MICROSOFT SECURITY.MD BLOCK -->"
  },
  {
    "path": "benchmark/RulesEngineBenchmark/Program.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Running;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing BenchmarkDotNet.Jobs;\nusing System.Text.Json;\n\nnamespace RulesEngineBenchmark\n{\n\n    [MemoryDiagnoser]\n    [SimpleJob(RuntimeMoniker.Net60)]\n    [SimpleJob(RuntimeMoniker.Net80)]\n    [SimpleJob(RuntimeMoniker.Net90)]\n    public class REBenchmark\n    {\n        private readonly RulesEngine.RulesEngine rulesEngine;\n        private readonly object ruleInput;\n        private readonly List<Workflow> workflow;\n\n        private class ListItem\n        {\n            public int Id { get; set; }\n            public string Value { get; set; }\n        }\n\n\n        public REBenchmark()\n        {\n            var files = Directory.GetFiles(Directory.GetCurrentDirectory(), \"NestedInputDemo.json\", SearchOption.AllDirectories);\n            if (files == null || files.Length == 0)\n            {\n                throw new Exception(\"Rules not found.\");\n            }\n\n            var fileData = File.ReadAllText(files[0]);\n            workflow = JsonSerializer.Deserialize<List<Workflow>>(fileData);\n\n            rulesEngine = new RulesEngine.RulesEngine(workflow.ToArray(), new ReSettings {\n                EnableFormattedErrorMessage = false,\n                EnableScopedParams = false\n            });\n\n            ruleInput = new {\n                SimpleProp = \"simpleProp\",\n                NestedProp = new {\n                    SimpleProp = \"nestedSimpleProp\",\n                    ListProp = new List<ListItem>\n                    {\n                        new ListItem\n                        {\n                            Id = 1,\n                            Value = \"first\"\n                        },\n                        new ListItem\n                        {\n                            Id = 2,\n                            Value = \"second\"\n                        }\n                    }\n                }\n\n            };\n        }\n\n        [Params(1000, 10000)]\n        public int N;\n\n        [Benchmark]\n        public void RuleExecutionDefault()\n        {\n            foreach (var workflow in workflow)\n            {\n                _ = rulesEngine.ExecuteAllRulesAsync(workflow.WorkflowName, ruleInput).Result;\n            }\n        }\n    }\n    public class Program\n    {\n        public static void Main(string[] args)\n        {\n            _ = BenchmarkRunner.Run<REBenchmark>();\n        }\n    }\n}\n"
  },
  {
    "path": "benchmark/RulesEngineBenchmark/RulesEngineBenchmark.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"BenchmarkDotNet\" Version=\"0.14.0\" />\n    <!--<PackageReference Include=\"RulesEngine\" Version=\"3.0.2\" />-->\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\RulesEngine\\RulesEngine.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Update=\"Workflows\\Discount.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"Workflows\\NestedInputDemo.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "benchmark/RulesEngineBenchmark/Workflows/Discount.json",
    "content": "[\n  {\n    \"WorkflowName\": \"Discount\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"SuccessEvent\": \"10\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount20\",\n        \"SuccessEvent\": \"20\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount25\",\n        \"SuccessEvent\": \"25\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country != \\\"india\\\" AND input1.loyaltyFactor >= 2 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 5\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount30\",\n        \"SuccessEvent\": \"30\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.loyaltyFactor > 3 AND input1.totalPurchasesToDate >= 50000 AND input1.totalPurchasesToDate <= 100000 AND input2.totalOrders > 5 AND input3.noOfVisitsPerMonth > 15\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount30NestedOrExample\",\n        \"SuccessEvent\": \"30\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"Operator\": \"OrElse\",\n        \"Rules\":[\n          {\n            \"RuleName\": \"IsLoyalAndHasGoodSpend\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.loyaltyFactor > 3 AND input1.totalPurchasesToDate >= 50000 AND input1.totalPurchasesToDate <= 100000\"\n          },\n          {\n            \"RuleName\": \"OrHasHighNumberOfTotalOrders\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input2.totalOrders > 15\"\n          }\n        ]\n      },\n      {\n        \"RuleName\": \"GiveDiscount35NestedAndExample\",\n        \"SuccessEvent\": \"35\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"Operator\": \"AndAlso\",\n        \"Rules\": [\n          {\n            \"RuleName\": \"IsLoyal\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.loyaltyFactor > 3\"\n          },\n          {\n            \"RuleName\": \"AndHasTotalPurchased100000\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.totalPurchasesToDate >= 100000\"\n          },\n          {\n            \"RuleName\": \"AndOtherConditions\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input2.totalOrders > 15 AND input3.noOfVisitsPerMonth > 25\"\n          }\n        ]\n      }\n    ]\n  }\n]"
  },
  {
    "path": "benchmark/RulesEngineBenchmark/Workflows/NestedInputDemo.json",
    "content": "﻿[\n  {\n    \"WorkflowName\": \"NestedInputDemoWorkflow1\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"CheckNestedSimpleProp\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.NestedProp.SimpleProp == \\\"nestedSimpleProp\\\"\"\n      }\n    ]\n  },\n  {\n    \"WorkflowName\": \"NestedInputDemoWorkflow2\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"CheckNestedListProp\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.NestedProp.ListProp[0].Id == 1 && input1.NestedProp.ListProp[1].Value == \\\"second\\\"\"\n      }\n    ]\n  },\n\n  {\n    \"WorkflowName\": \"NestedInputDemoWorkflow3\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"CheckNestedListPropFunctions\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.NestedProp.ListProp[1].Value.ToUpper() = \\\"SECOND\\\"\"\n      }\n    ]\n  }\n]"
  },
  {
    "path": "demo/DemoApp/BasicDemo.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing static RulesEngine.Extensions.ListofRuleResultTreeExtension;\n\nnamespace DemoApp\n{\n    public class BasicDemo\n    {\n        public void Run()\n        {\n            Console.WriteLine($\"Running {nameof(BasicDemo)}....\");\n            List<Workflow> workflows = new List<Workflow>();\n            Workflow workflow = new Workflow();\n            workflow.WorkflowName = \"Test Workflow Rule 1\";\n\n            List<Rule> rules = new List<Rule>();\n\n            Rule rule = new Rule();\n            rule.RuleName = \"Test Rule\";\n            rule.SuccessEvent = \"Count is within tolerance.\";\n            rule.ErrorMessage = \"Over expected.\";\n            rule.Expression = \"count < 3\";\n            rule.RuleExpressionType = RuleExpressionType.LambdaExpression;\n\n            rules.Add(rule);\n\n            workflow.Rules = rules;\n\n            workflows.Add(workflow);\n\n            var bre = new RulesEngine.RulesEngine(workflows.ToArray(), null);\n\n            dynamic datas = new ExpandoObject();\n            datas.count = 1;\n            var inputs = new dynamic[]\n              {\n                    datas\n              };\n\n            List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync(\"Test Workflow Rule 1\", inputs).Result;\n\n            bool outcome = false;\n\n            //Different ways to show test results:\n            outcome = resultList.TrueForAll(r => r.IsSuccess);\n\n            resultList.OnSuccess((eventName) => {\n                Console.WriteLine($\"Result '{eventName}' is as expected.\");\n                outcome = true;\n            });\n\n            resultList.OnFail(() => {\n                outcome = false;\n            });\n\n            Console.WriteLine($\"Test outcome: {outcome}.\");\n        }\n    }\n}\n"
  },
  {
    "path": "demo/DemoApp/DemoApp.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>\n    <StartupObject>DemoApp.Program</StartupObject>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"../../src/RulesEngine/RulesEngine.csproj\" />\n    <ProjectReference Include=\"..\\DemoApp.EFDataExample\\DemoApp.EFDataExample.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Update=\"Workflows\\Discount.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"Workflows\\NestedInputDemo.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/DemoApp/EFDemo.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing DemoApp.EFDataExample;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing System.IO;\nusing System.Linq;\nusing static RulesEngine.Extensions.ListofRuleResultTreeExtension;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace DemoApp\n{\n    using System.Text.Json;\n\n    public class EFDemo\n    {\n        public void Run()\n        {\n            Console.WriteLine($\"Running {nameof(EFDemo)}....\");\n            var basicInfo = \"{\\\"name\\\": \\\"hello\\\",\\\"email\\\": \\\"abcy@xyz.com\\\",\\\"creditHistory\\\": \\\"good\\\",\\\"country\\\": \\\"canada\\\",\\\"loyaltyFactor\\\": 3,\\\"totalPurchasesToDate\\\": 10000}\";\n            var orderInfo = \"{\\\"totalOrders\\\": 5,\\\"recurringItems\\\": 2}\";\n            var telemetryInfo = \"{\\\"noOfVisitsPerMonth\\\": 10,\\\"percentageOfBuyingToVisit\\\": 15}\";\n\n           dynamic input1 = JsonSerializer.Deserialize<ExpandoObject>(basicInfo);\n            dynamic input2 = JsonSerializer.Deserialize<ExpandoObject>(orderInfo);\n            dynamic input3 = JsonSerializer.Deserialize<ExpandoObject>(telemetryInfo);\n\n            var inputs = new dynamic[]\n                {\n                    input1,\n                    input2,\n                    input3\n                };\n\n            var files = Directory.GetFiles(Directory.GetCurrentDirectory(), \"Discount.json\", SearchOption.AllDirectories);\n            if (files == null || files.Length == 0)\n                throw new Exception(\"Rules not found.\");\n\n            var fileData = File.ReadAllText(files[0]);\n            var workflow = JsonSerializer.Deserialize<List<Workflow>>(fileData);\n\n            RulesEngineDemoContext db = new RulesEngineDemoContext();\n            if (db.Database.EnsureCreated())\n            {\n                db.Workflows.AddRange(workflow);\n                db.SaveChanges();\n            }\n\n            var wfr = db.Workflows.Include(i => i.Rules).ThenInclude(i => i.Rules).ToArray();\n\n            var bre = new RulesEngine.RulesEngine(wfr, null);\n\n            string discountOffered = \"No discount offered.\";\n\n            List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync(\"Discount\", inputs).Result;\n\n            resultList.OnSuccess((eventName) => {\n                discountOffered = $\"Discount offered is {eventName} % over MRP.\";\n            });\n\n            resultList.OnFail(() => {\n                discountOffered = \"The user is not eligible for any discount.\";\n            });\n\n            Console.WriteLine(discountOffered);\n        }\n    }\n}\n"
  },
  {
    "path": "demo/DemoApp/JSONDemo.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing System.IO;\nusing static RulesEngine.Extensions.ListofRuleResultTreeExtension;\n\nnamespace DemoApp\n{\n    using System.Text.Json;\n\n    public class JSONDemo\n    {\n        public void Run()\n        {\n            Console.WriteLine($\"Running {nameof(JSONDemo)}....\");\n            var basicInfo = \"{\\\"name\\\": \\\"hello\\\",\\\"email\\\": \\\"abcy@xyz.com\\\",\\\"creditHistory\\\": \\\"good\\\",\\\"country\\\": \\\"canada\\\",\\\"loyaltyFactor\\\": 3,\\\"totalPurchasesToDate\\\": 10000}\";\n            var orderInfo = \"{\\\"totalOrders\\\": 5,\\\"recurringItems\\\": 2}\";\n            var telemetryInfo = \"{\\\"noOfVisitsPerMonth\\\": 10,\\\"percentageOfBuyingToVisit\\\": 15}\";\n\n\n\n            dynamic input1 = JsonSerializer.Deserialize<ExpandoObject>(basicInfo);\n            dynamic input2 = JsonSerializer.Deserialize<ExpandoObject>(orderInfo);\n            dynamic input3 = JsonSerializer.Deserialize<ExpandoObject>(telemetryInfo);\n\n            var inputs = new dynamic[]\n                {\n                    input1,\n                    input2,\n                    input3\n                };\n\n            var files = Directory.GetFiles(Directory.GetCurrentDirectory(), \"Discount.json\", SearchOption.AllDirectories);\n            if (files == null || files.Length == 0)\n                throw new Exception(\"Rules not found.\");\n\n            var fileData = File.ReadAllText(files[0]);\n            var workflow = JsonSerializer.Deserialize<List<Workflow>>(fileData);\n\n            var bre = new RulesEngine.RulesEngine(workflow.ToArray(), null);\n\n            string discountOffered = \"No discount offered.\";\n\n            List<RuleResultTree> resultList = bre.ExecuteAllRulesAsync(\"Discount\", inputs).Result;\n\n            resultList.OnSuccess((eventName) => {\n                discountOffered = $\"Discount offered is {eventName} % over MRP.\";\n            });\n\n            resultList.OnFail(() => {\n                discountOffered = \"The user is not eligible for any discount.\";\n            });\n\n            Console.WriteLine(discountOffered);\n        }\n    }\n}\n"
  },
  {
    "path": "demo/DemoApp/NestedInputDemo.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.Extensions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\n\nnamespace DemoApp\n{\n    using System.Text.Json;\n\n    internal class ListItem\n    {\n        public int Id { get; set; }\n        public string Value { get; set; }\n    }\n\n    public class NestedInputDemo\n    {\n        public void Run()\n        {\n            Console.WriteLine($\"Running {nameof(NestedInputDemo)}....\");\n            var nestedInput = new {\n                SimpleProp = \"simpleProp\",\n                NestedProp = new {\n                    SimpleProp = \"nestedSimpleProp\",\n                    ListProp = new List<ListItem>\n                    {\n                        new ListItem\n                        {\n                            Id = 1,\n                            Value = \"first\"\n                        },\n                        new ListItem\n                        {\n                            Id = 2,\n                            Value = \"second\"\n                        }\n                    }\n                }\n\n            };\n\n            var files = Directory.GetFiles(Directory.GetCurrentDirectory(), \"NestedInputDemo.json\", SearchOption.AllDirectories);\n            if (files == null || files.Length == 0)\n            {\n                throw new Exception(\"Rules not found.\");\n            }\n\n            var fileData = File.ReadAllText(files[0]);\n            var Workflows = JsonSerializer.Deserialize<List<Workflow>>(fileData);\n\n            var bre = new RulesEngine.RulesEngine(Workflows.ToArray(), null);\n            foreach (var workflow in Workflows)\n            {\n                var resultList = bre.ExecuteAllRulesAsync(workflow.WorkflowName, nestedInput).Result;\n\n                resultList.OnSuccess((eventName) => {\n                    Console.WriteLine($\"{workflow.WorkflowName} evaluation resulted in success - {eventName}\");\n                }).OnFail(() => {\n                    Console.WriteLine($\"{workflow.WorkflowName} evaluation resulted in failure\");\n                });\n\n            }\n\n\n        }\n    }\n}"
  },
  {
    "path": "demo/DemoApp/Program.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT License.\r\n\r\nnamespace DemoApp\r\n{\r\n    public static class Program\r\n    {\r\n        public static void Main(string[] args)\r\n        {\r\n            new BasicDemo().Run();\r\n            new JSONDemo().Run();\r\n            new NestedInputDemo().Run();\r\n            new EFDemo().Run();\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "demo/DemoApp/Workflows/Discount.json",
    "content": "[\n  {\n    \"WorkflowName\": \"Discount\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"SuccessEvent\": \"10\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount20\",\n        \"SuccessEvent\": \"20\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount25\",\n        \"SuccessEvent\": \"25\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country != \\\"india\\\" AND input1.loyaltyFactor >= 2 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 5\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount30\",\n        \"SuccessEvent\": \"30\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.loyaltyFactor > 3 AND input1.totalPurchasesToDate >= 50000 AND input1.totalPurchasesToDate <= 100000 AND input2.totalOrders > 5 AND input3.noOfVisitsPerMonth > 15\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount30NestedOrExample\",\n        \"SuccessEvent\": \"30\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"Operator\": \"OrElse\",\n        \"Rules\":[\n          {\n            \"RuleName\": \"IsLoyalAndHasGoodSpend\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.loyaltyFactor > 3 AND input1.totalPurchasesToDate >= 50000 AND input1.totalPurchasesToDate <= 100000\"\n          },\n          {\n            \"RuleName\": \"OrHasHighNumberOfTotalOrders\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input2.totalOrders > 15\"\n          }\n        ]\n      },\n      {\n        \"RuleName\": \"GiveDiscount35NestedAndExample\",\n        \"SuccessEvent\": \"35\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"Operator\": \"AndAlso\",\n        \"Rules\": [\n          {\n            \"RuleName\": \"IsLoyal\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.loyaltyFactor > 3\"\n          },\n          {\n            \"RuleName\": \"AndHasTotalPurchased100000\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.totalPurchasesToDate >= 100000\"\n          },\n          {\n            \"RuleName\": \"AndOtherConditions\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input2.totalOrders > 15 AND input3.noOfVisitsPerMonth > 25\"\n          }\n        ]\n      }\n    ]\n  }\n]"
  },
  {
    "path": "demo/DemoApp/Workflows/NestedInputDemo.json",
    "content": "﻿[\n  {\n    \"WorkflowName\": \"NestedInputDemoWorkflow1\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"CheckNestedSimpleProp\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.NestedProp.SimpleProp == \\\"nestedSimpleProp\\\"\"\n      }\n    ]\n  },\n  {\n    \"WorkflowName\": \"NestedInputDemoWorkflow2\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"CheckNestedListProp\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.NestedProp.ListProp[0].Id == 1 && input1.NestedProp.ListProp[1].Value == \\\"second\\\"\"\n      }\n    ]\n  },\n\n  {\n    \"WorkflowName\": \"NestedInputDemoWorkflow3\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"CheckNestedListPropFunctions\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.NestedProp.ListProp[1].Value.ToUpper() = \\\"SECOND\\\"\"\n      }\n    ]\n  }\n]"
  },
  {
    "path": "demo/DemoApp.EFDataExample/DemoApp.EFDataExample.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>\n    <RootNamespace>DemoApp.EFDataExample</RootNamespace>\n    <AssemblyName>DemoApp.EFDataExample</AssemblyName>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.EntityFrameworkCore\" Version=\"9.0.1\" />\n    <PackageReference Include=\"Microsoft.EntityFrameworkCore.Sqlite\" Version=\"9.0.1\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\RulesEngine\\RulesEngine.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/DemoApp.EFDataExample/RulesEngineContext.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text.Json;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.ChangeTracking;\nusing Microsoft.EntityFrameworkCore.Metadata.Builders;\nusing RulesEngine.Models;\n\nnamespace RulesEngine.Data\n{\n    public class RulesEngineContext : DbContext\n    {\n        public DbSet<Workflow> Workflows { get; set; }\n\n        public DbSet<Rule> Rules { get; set; }\n\n        protected override void OnModelCreating(ModelBuilder modelBuilder)\n        {\n            base.OnModelCreating(modelBuilder);\n\n            modelBuilder.Entity<ScopedParam>()\n              .HasKey(k => k.Name);\n\n            modelBuilder.Entity<Workflow>(entity => {\n                entity.HasKey(k => k.WorkflowName);\n                entity.Ignore(b => b.WorkflowsToInject);\n            });\n\n            modelBuilder.Entity<Rule>().HasOne<Rule>().WithMany(r => r.Rules).HasForeignKey(\"RuleNameFK\");\n\n            var serializationOptions = new JsonSerializerOptions(JsonSerializerDefaults.General);\n\n            modelBuilder.Entity<Rule>(entity => {\n                entity.HasKey(k => k.RuleName);\n\n                var valueComparer = new ValueComparer<Dictionary<string, object>>(\n                    (c1, c2) => c1.SequenceEqual(c2),\n                    c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),\n                    c => c);\n\n                entity.Property(b => b.Properties)\n                .HasConversion(\n                    v => JsonSerializer.Serialize(v, serializationOptions),\n                    v => JsonSerializer.Deserialize<Dictionary<string, object>>(v, serializationOptions))\n                    .Metadata\n                    .SetValueComparer(valueComparer);\n\n                entity.Property(p => p.Actions)\n                .HasConversion(\n                    v => JsonSerializer.Serialize(v, serializationOptions),\n                   v => JsonSerializer.Deserialize<RuleActions>(v, serializationOptions));\n\n                entity.Ignore(b => b.WorkflowsToInject);\n            });\n        }\n    }\n\n}\n"
  },
  {
    "path": "demo/DemoApp.EFDataExample/RulesEngineDemoContext.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text.Json;\nusing Microsoft.EntityFrameworkCore;\nusing RulesEngine.Data;\nusing RulesEngine.Models;\n\nnamespace DemoApp.EFDataExample\n{\n    public class RulesEngineDemoContext : RulesEngineContext\n    {\n        public string DbPath { get; private set; }\n\n        public RulesEngineDemoContext()\n        {\n            var folder = Environment.SpecialFolder.LocalApplicationData;\n            var path = Environment.GetFolderPath(folder);\n            DbPath = $\"{path}{System.IO.Path.DirectorySeparatorChar}RulesEngineDemo.db\";\n        }\n        protected override void OnConfiguring(DbContextOptionsBuilder options)\n          => options.UseSqlite($\"Data Source={DbPath}\");\n\n    }\n\n}"
  },
  {
    "path": "deployment/build-signed.ps1",
    "content": "param(\n    [Parameter(Mandatory)]\n    [string] $csprojFilePath,\n    [Parameter(Mandatory)]\n    [string] $signingKey\n)\n\n# sign and build the project\n$directory = Split-Path $csprojFilePath;\n$signKeyFile = Join-Path $directory \"signKey.snk\";\n\n$bytes = [Convert]::FromBase64String($signingKey)\n[IO.File]::WriteAllBytes($signKeyFile, $bytes)\n\ndotnet build $csprojFilePath -c Release -p:ContinuousIntegrationBuild=true -p:DelaySign=false -p:AssemblyOriginatorKeyFile=$signKeyFile "
  },
  {
    "path": "docs/Getting-Started.md",
    "content": "## Getting Started with Rules Engine\nRulesEngine is a library/NuGet package for abstracting rules and running the Rules Engine.\n\n### Publicly accessible interfaces, models, methods and schemas\nAs with any library/package there are public interfaces with which we interact with that library/packages. There are a few public interfaces in this package as well. The interface which will be used to access this package is [IRulesEngine](#irulesengine), with four overloaded methods for executing rules. To understand the methods, we need to go through some of the models/schemas first. \n\n#### Rules\nThe rules used in this system is mostly comprising of [lambda expressions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions). Anything that can be defined in a lambda expression can be used as a rule in this library.\n\n#### Rules Schema\nRules schema is available in the [schema file](https://github.com/microsoft/RulesEngine/blob/main/schema/workflow-schema.json). The workflow rules are how we store the rules in the system. In our system, the name of the model typed in the library is [Workflow](https://github.com/microsoft/RulesEngine/blob/main/src/RulesEngine/Models/Workflow.cs). An example json would be – \n\n```json\n[\n  {\n    \"WorkflowName\": \"Discount\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"SuccessEvent\": \"10\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount20\",\n        \"SuccessEvent\": \"20\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyalityFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount25\",\n        \"SuccessEvent\": \"25\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.country != \\\"india\\\" AND input1.loyalityFactor >= 2 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 5\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount30\",\n        \"SuccessEvent\": \"30\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.loyalityFactor > 3 AND input1.totalPurchasesToDate >= 50000 AND input1.totalPurchasesToDate <= 100000 AND input2.totalOrders > 5 AND input3.noOfVisitsPerMonth > 15\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount35\",\n        \"SuccessEvent\": \"35\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.loyalityFactor > 3 AND input1.totalPurchasesToDate >= 100000 AND input2.totalOrders > 15 AND input3.noOfVisitsPerMonth > 25\"\n      }\n    ]\n  }\n]\n```\n\nThis workflow rules showcased in the above json is of a sample [Use Case](https://github.com/microsoft/RulesEngine/wiki/Use-Case) which is going to be used to explain the library. \nDemo App for the given use case is available at [this location](https://github.com/microsoft/RulesEngine/tree/main/demo).\n#### Logger\nAnother public interface for custom logging is ILogger. This interface is not implemented and looks for custom implementation of the user who wants to use it. The methods available for this interface are – \n```c# \nvoid LogTrace(string msg);\nvoid LogError(Exception ex);\n```\nThese methods can be implemented in any logging mechanism that is expected and its instance can be injected into the Rules Engine as shown in [Initiating the Rules Engine](#initiating-the-rules-engine).\n#### ReSettings\nThis model is a list of custom types. \nWhile, lambda expressions are quite powerful, there is a limit to what they can do because of the fact that the methods that a lambda expression can do are limited to [System namespace](https://docs.microsoft.com/en-us/dotnet/api/system) of [.Net framework](https://docs.microsoft.com/en-us/dotnet/framework/). \n\nTo use more complex and custom classes and have some logics which are way too complex to be written into a lambda expression, these settings come into picture. If the user wants to create a custom method to run for the rule to be used, these settings help them. \n\nExample – \n\nYou can create a public class called Utils and include a method in it which check contains in a list.\n```c#\nusing System;\nusing System.Linq;\n\nnamespace RE.HelperFunctions\n{\n    public static class Utils\n    {\n        public static bool CheckContains(string check, string valList)\n        {\n            if (String.IsNullOrEmpty(check) || String.IsNullOrEmpty(valList))\n                return false;\n\n            var list = valList.Split(',').ToList();\n            return list.Contains(check);\n        }\n    }\n}\n```\nAnd this can be then used in lambda expression in a very simple manner like this – \n```json\n\"Expression\": \"Utils.CheckContains(input1.country, \\\"india,usa,canada,France\\\") == true\"\n```\n\nTo use the custom class when evaluating the rules:\n\n1. Register the class\n2. Then pass settings through rules engine\n```csharp\nvar reSettingsWithCustomTypes = new ReSettings { CustomTypes = new Type[] { typeof(Utils) } };\nnew RulesEngine.RulesEngine(workflowRules.ToArray(), null, reSettingsWithCustomTypes);\n```\n\n#### RuleParameter\nThis is a model class for custom inputs which can be seen in the [RuleParameter Class](https://github.com/microsoft/RulesEngine/blob/main/src/RulesEngine/Models/RuleParameter.cs). This type is present to add another layer of customization to the rules. \n\nFor example, the rules present in the example mentioned in the [Rules Schema](#rules-schema) section are using 3 different inputs for each run. The inputs are of different types as mentioned in the [Use Case]((https://github.com/microsoft/RulesEngine/wiki/Use-Case)) and is coming from different sources. Now, in rules we had to use input1, input2 and input3 to target data coming from the basic info, order info and telemetry info, respectively. \n\n\nWith RuleParameter class, we can give context specific names to the rules in the list of rules instead of input1, input2 and input3. \n\n#### LocalParams\n\nRules Engine has a param (like ‘var’ in c#) feature support now, it makes authoring and troubleshooting of issues very easy. Now you can breakdown your bigger statements into smaller logical expressions as parameters within a rule definition.\n\nBelow is an example of a complex rule which can be authored easily using logical intermediate parameters and can be used to write the final rule expression to return a binary value. Sample rule requirement here is to provide access to a user only when user has completed some mandatory trainings or the user is accessing the site it from a secure domain. \n\n```\n{\n        \"name\": \"allow_access_if_all_mandatory_trainings_are_done_or_access_isSecure\",\n        \"errorMessage\": \"Please complete all your training(s) to get access to this content or access it from a secure domain/location.\",\n        \"errorType\": \"Error\",\n        \"localParams\": [\n          {\n            \"name\": \"completedSecurityTrainings\",\n            \"expression\": \"MasterSecurityComplainceTrainings.Where(Status.Equals(\\\"Completed\\\", StringComparison.InvariantCultureIgnoreCase))\"\n          },\n          {\n            \"name\": \"completedProjectTrainings\",\n            \"expression\": \"MasterProjectComplainceTrainings.Where(Status.Equals(\\\"Completed\\\", StringComparison.InvariantCultureIgnoreCase))\"\n          },\n          {\n            \"name\": \"isRequestAccessSecured\",\n            \"expression\": \"UserRequestDetails.Location.Country == \\\"India\\\" ? ((UserRequestDetails.Location.City == \\\"Bangalore\\\" && UserRequestDetails.Domain=\\\"xxxx\\\")? true : false):false\"\n          }\n        ],\n        \"expression\": \"(completedSecurityTrainings.Any() && completedProjectTrainings.Any()) || isRequestAccessSecured \"\n      }\n ```\n\n\n#### RuleResultTree\n[This model](https://github.com/microsoft/RulesEngine/blob/main/src/RulesEngine/Models/RuleResultTree.cs) is the output of the Rules Engine. Once the execution of the Rules Engine is completed and the Engine has gone through all the rules, a list of this type is returned. What this model include is – \n##### Rule\nThis is the rule that is currently being referred. It is of a custom model type and has information of that rule which ran on the input. \n##### IsSuccess\nThis is a Boolean value showcasing whether the given rule passed or not.\n##### ChildResults\nIn the case, the rule has child rules, this variable gets initialized else it is null. This is a nested list of RuleResultTree type to showcase the response of the children rules.\n##### Input\nThis is the input that was being checked upon while the rules were being verified on this object. In case of multiple inputs, it takes up the first input.\n\n\n#### IRulesEngine\nIRulesEngine is the main interface which is used to handle all the executions. This interface has four overloaded methods to execute rules – \n```c#\nList<RuleResultTree> ExecuteRule(string workflowName, IEnumerable<dynamic> input, object[] otherInputs);\nList<RuleResultTree> ExecuteRule(string workflowName, object[] inputs);\nList<RuleResultTree> ExecuteRule(string workflowName, object input);\nList<RuleResultTree> ExecuteRule(string workflowName, RuleParameter[] ruleParams);\n```\nOne Rules Engine can take in multiple workflows and the workflows can be distributed based on the logic dictated by the system you are building.\n\n\nIn the first definition – \n* workflowName is the name of the workflow you want to take up.\n* input is a list of dynamic inputs which is being entered. \n* otherInputs is an array of other auxiliary inputs which complement the inputs based on the rules present.\n\n\nIn the second definition – \n* workflowName is the name of the workflow you want to take up.\n* input is an array of dynamic inputs which is being entered. \n\n\nIn the third definition – \n* workflowName is the name of the workflow you want to take up.\n* input is a single dynamic input which is being entered.\n\n\nIn the fourth definition – \n* workflowName is the name of the workflow you want to take up.\n* ruleParams is an array of RuleParameters as explained in [RuleParameter](#ruleparameter). \n\n#### Initiating the Rules Engine\nTo initiate the Rules Engine instance to be used for executing rules, the workflow rules need to be injected into the library. The two different definitions of constructors are – \n```c#\npublic RulesEngine(string[] jsonConfig, ILogger logger, ReSettings reSettings = null) \npublic RulesEngine(WorkflowRules[] workflowRules, ILogger logger, ReSettings reSettings = null)\n```\nHere, \n* jsonConfig is the list of serialized json strings following the schema mentioned in [Rules Schema](#rules-schema).\n* logger is an instance of the logger created by you, following the information given in [Logger](#logger).\n* reSettings is list of custom types as mention in [ReSettings](#resettings).\n* workflowRules is a list of objects of type WorkflowRules which is mentioned in the [Rules Schema](#rules-schema).\n\n#### Success/Failure\nFor the rules to make sense, there are always success and failure scenarios. This library gives the user an inbuilt scenario where in success and failure scenario an event can be created.\n##### Success\nIn case of success, there could be one or more than one rules which passed based on the given input(s). The success event will be triggered and will be run based on the first rule which was true and give you the success event which was initialized as SuccessEvent in the RulesSchema section.\n\n\nExample – \n```c#\nList<RuleResultTree> resultList = bre.ExecuteRule(\"Discount\", inputs);\n\nresultList.OnSuccess((eventName) =>\n{\ndiscountOffered = $\"Discount offered is {eventName} % over MRP.\";\n});\n```\n##### Failure\nIn case, none of the rules succeeded the failure event gets triggered. \n\nExample – \n```c#\nList<RuleResultTree> resultList = bre.ExecuteRule(\"Discount\", inputs);\nresultList.OnFail(() =>\n{\ndiscountOffered = \"The user is not eligible for any discount.\";\n});\n```\n\n### How to use Rules Engine\n1.\tTo install this library, please download the latest version of  [NuGet Package](https://www.nuget.org/packages/RulesEngine/) from [nuget.org](https://www.nuget.org/) and refer it into your project. \n2.\tInitiate the instance of Rules Engine as mentioned in [Initiating the Rules Engine](#initiating-the-rules-engine).\n3.\tOnce done, the rules can be executed using any of the overloaded methods as explained in [IRulesEngine](#irulesengine) section. It returns the list of [RuleResultTree](#ruleresulttree) which can be used in any way the user wants to. \n4.\tThe success or failure events can be defined as explained in the [Success/Failure](#successfailure) section. \n    * Based on the rules and input the success or failure event can be triggered. \n"
  },
  {
    "path": "docs/Home.md",
    "content": "## Welcome\nWelcome to the RulesEngine Wiki!\n\nThe pages here are primarily intended for those who wish to contribute to the Rules Engine Project by suggesting new features or building extensions or submitting pull requests. \n\nThis Wiki also includes a demo along with the explanation of different features of the project so that the using of the application can be easily understood. \n\n## About\nDeep dive into the project code and the Wiki to find different features and workings of the project. \n\nSearch for the solution or file a new issue in [GitHub](https://github.com/microsoft/RulesEngine/issues) if you find something broken in the code.\n\n## Contributing\nThis project welcomes contributions and suggestions.  Most contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\n\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n"
  },
  {
    "path": "docs/Introduction.md",
    "content": "## What is the Rules Engine\nWhile building any application, the crux or the core part of it is always business logic or business rules. And as with any application, there always comes a time when some or a lot of the rules or policies change in the system. But with that change, comes a lot of rework like changing design or creating a new module altogether to code in the changes in the rules, regression testing, performance testing etc. The rework along with debugging if required amounts to a lot of unnecessary work which can otherwise be utilized for other work, thus reducing the engineering cycle by drastic amounts.  \n\nIn this library, we have abstracted the rules so that the core logic is always maintained while the rules change can happen in an easy way without changing the code base. Also, the input to the system is dynamic in nature so the model need not be defined in the system. It can be sent as an expando object or any other typed object and the system will be able to handle it. \n\nThese all features make this library highly configurable and extensible as shown in [Getting Started with Rules Engine](https://github.com/microsoft/RulesEngine/wiki/Getting-Started).\n\n\n### How it works\n\n![](https://github.com/microsoft/RulesEngine/blob/main/assets/BlockDiagram.png)\n\nHere. there are multiple actors/component involved.\n##### Rules Engine\nThis component is the Rules Engine library/NuGet package being referenced by the developer.\n##### Rules Store\nAs shown in [Rules Schema](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#rules-schema), we need rules in a particular format for the library to work. While the rules structure is rigid, the data itself can be stored in any component and can be accessed in any form the developer chooses. It can be stored in the form of json files in file structure or blob, or as documents in cosmos db, or any database, azure app configuration or any other place the developer thinks is going to be appropriate based on the project requirements. \n##### Input\nThe input(s) for the system can be taken from any place as well like user input, blobs, databases, service bus or any other system. \n##### Wrapper\nThis library sits as a black box outside the project as a referenced project or NuGet package. Then the user can create a wrapper around the library, which will get the rules from the rules store and convert it into the WorkFlowRules structure and send it to the RulesEngine along with the input(s). The RulesEngine then computes and give the information to the wrapper and the wrapper can then do whatever the logic demands with the output information.\n\n"
  },
  {
    "path": "docs/Use-Case.md",
    "content": "## Use Case\nThe use case for demo purposes used here is explained as follows. The system we are designing is an e-commerce discount calculation system. \n\n### Rules\nThe rules for the discount calculation are –\n\n1.\tGive the user a discount of 10% over MRP if the following conditions are followed – \n    * The user’s country is India.\n    * The user’s loyalty factor is less than or equal to 2.\n    * All the orders purchased by the user so far should amount to more than 5,000.\n    * User should have at least made more than two successful orders. \n    * The user should have visited the site more than two times every month.\n2.\tGive the user a discount of 20% over MRP if the following conditions are followed – \n    * The user’s country is India.\n    * The user’s loyalty factor is equal to 3.\n    * All the orders purchased by the user so far should amount to more than 10,000.\n    * User should have at least made more than two successful orders. \n    * The user should have visited the site more than two times every month.\n3.\tGive the user a discount of 25% over MRP if the following conditions are followed – \n    * The user’s country is not India.\n    * The user’s loyalty factor is greater than or equal to 2.\n    * All the orders purchased by the user so far should amount to more than 10,000.\n    * User should have at least made more than two successful orders. \n    * The user should have visited the site more than five times every month.\n4.\tGive the user a discount of 30% over MRP if the following conditions are followed – \n    * The user’s loyalty factor is greater than 3.\n    * All the orders purchased by the user so far should amount to more than 50,000 but less than 100,000.\n    * User should have at least made more than five successful orders. \n    * The user should have visited the site more than fifteen times every month.\n5.\tGive the user a discount of 30% over MRP if the following conditions are followed – \n    * The user’s loyalty factor is greater than 3.\n    * All the orders purchased by the user so far should amount to more than 100,000.\n    * User should have at least made more than fifteen successful orders. \n    * The user should have visited the site more than 25 times every month.\n6.\tGive 0% discount in any other case.\n\n### Inputs\nHere the inputs will be of three different types as they are coming from three different data sources/APIs. \n#### User Basic Info\nThis input has information like –\n* Name\n* Country\n* Email\n* Credit history\n* Loyalty factor\n* Sum of the purchases made by the user till date.\n\n#### Users Order Information\nThis input is a summarization of the orders made by the user so far. This input has information like – \n* Total number of orders\n* Recurring items in those orders if any\n\n#### Users Telemetry Information\nThis input is a summarization of the telemetry information collected based on the user’s visit to the site. This input has information like – \n* Number of visits to the site per month\n* Percentage of the number of times the user purchased something to the number of times the user visited\n"
  },
  {
    "path": "docs/_Sidebar.md",
    "content": "[Home](https://github.com/microsoft/RulesEngine/wiki)\n* [Welcome](https://github.com/microsoft/RulesEngine/wiki#welcome)\n* [About](https://github.com/microsoft/RulesEngine/wiki#about)\n* [Contributing](https://github.com/microsoft/RulesEngine/wiki#contributing)\n\n[Introduction](https://github.com/microsoft/RulesEngine/wiki/Introduction)\n* [What is the Rules Engine](https://github.com/microsoft/RulesEngine/wiki/Introduction#what-is-the-rules-engine)\n  * [How it works](https://github.com/microsoft/RulesEngine/wiki/Introduction#how-it-works) \n    * [Rules Engine](https://github.com/microsoft/RulesEngine/wiki/Introduction#rules-engine)\n    * [Rules Store](https://github.com/microsoft/RulesEngine/wiki/Introduction#rules-store)\n    * [Input](https://github.com/microsoft/RulesEngine/wiki/Introduction#input)\n    * [Wrapper](https://github.com/microsoft/RulesEngine/wiki/Introduction#wrapper)\n\n[Getting Started](https://github.com/microsoft/RulesEngine/wiki/Getting-Started) \n* [Getting Started with Rules Engine](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#getting-started-with-rules-engine)\n  * [Publicly accessible interfaces, models, methods and schemas](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#publicly-accessible-interfaces-models-methods-and-schemas) \n    * [Rules](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#rules)\n    * [Rules Schema](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#rules-schema)\n    * [Logger](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#logger)\n    * [ReSettings](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#resettings)\n    * [LocalParams](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#localparams)\n    * [RuleParameter](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#ruleparameter)\n    * [RuleResultTree](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#ruleresulttree)\n    * [IRulesEngine](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#irulesengine)\n    * [Initiating the Rules Engine](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#initiating-the-rules-engine)\n    * [Success/Failure](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#successfailure)\n  * [How to use Rules Engine](https://github.com/microsoft/RulesEngine/wiki/Getting-Started#how-to-use-rules-engine) \n\n[Use Case](https://github.com/microsoft/RulesEngine/wiki/Use-Case)\n* [Use Case](https://github.com/microsoft/RulesEngine/wiki/Use-Case#use-case)\n  * [Rules](https://github.com/microsoft/RulesEngine/wiki/Use-Case#rules)\n  * [Inputs](https://github.com/microsoft/RulesEngine/wiki/Use-Case#inputs)\n    * [User Basic Info](https://github.com/microsoft/RulesEngine/wiki/Use-Case#user-basic-info)\n    * [Users Order Information](https://github.com/microsoft/RulesEngine/wiki/Use-Case#users-order-information)\n    * [Users Telemetry Information](https://github.com/microsoft/RulesEngine/wiki/Use-Case#users-telemetry-information)"
  },
  {
    "path": "docs/_config.yml",
    "content": "theme: jekyll-theme-cayman"
  },
  {
    "path": "docs/index.md",
    "content": "RulesEngine is a highly extensible library to build rule based system using C# expressions\n\n\n**Features**\n- Json based rules definition\n- Multiple input support\n- Dynamic object input support\n- C# Expression support\n- Extending expression via custom class/type injection\n- Scoped parameters\n- Post rule execution actions\n- Standalone expression evaluator\n\n**Table Of Content**\n- [Installation](#installation)\n- [Basic Usage](#basic-usage)\n  - [Create a workflow file with rules](#create-a-workflow-file-with-rules)\n  - [Initialise RulesEngine with the workflow:](#initialise-rulesengine-with-the-workflow)\n  - [Execute the workflow rules with input:](#execute-the-workflow-rules-with-input)\n  - [Using custom names for inputs](#using-custom-names-for-inputs)\n- [C# Expression support](#c-expression-support)\n- [Extending expression via custom class/type injection](#extending-expression-via-custom-classtype-injection)\n  - [Example](#example)\n- [ScopedParams](#scopedparams)\n  - [GlobalParams](#globalparams)\n    - [Example](#example-1)\n  - [LocalParams](#localparams)\n    - [Example](#example-2)\n  - [Referencing ScopedParams in other ScopedParams](#referencing-scopedparams-in-other-scopedparams)\n- [Post rule execution actions](#post-rule-execution-actions)\n  - [Inbuilt Actions](#inbuilt-actions)\n    - [OutputExpression](#outputexpression)\n      - [Usage](#usage)\n    - [EvaluateRule](#evaluaterule)\n      - [Usage](#usage-1)\n  - [Custom Actions](#custom-actions)\n    - [Steps to use a custom Action](#steps-to-use-a-custom-action)\n- [Standalone Expression Evaluator](#standalone-expression-evaluator)\n  - [Usage](#usage-2)\n- [Settings](#settings)\n  - [NestedRuleExecutionMode](#nestedruleexecutionmode)\n\n\n\n## Installation\nNuget package: [![nuget](https://img.shields.io/nuget/dt/RulesEngine)](https://www.nuget.org/packages/RulesEngine/)\n\n## Basic Usage\n### Create a workflow file with rules\n```json\n[\n  {\n    \"WorkflowName\": \"Discount\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount20\",\n        \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyalityFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n      }\n    ]\n  }\n] \n```\n\n### Initialise RulesEngine with the workflow:\n```c#\nvar workflowRules = //Get list of workflow rules declared in the json\nvar re = new RulesEngine.RulesEngine(workflowRules);\n```\n\n### Execute the workflow rules with input:\n```c#\n// Declare input1,input2,input3 \nvar resultList  = await re.ExecuteAllRulesAsync(\"Discount\", input1,input2,input3);\n\n//Check success for rule\nforeach(var result in resultList){\n  Console.WriteLine($\"Rule - {result.Rule.RuleName}, IsSuccess - {result.IsSuccess}\");\n}\n```\nThis will execute all the rules under `Discount` workflow and return ruleResultTree for all rules\n\n**Note: input passed to rulesEngine can be of a concrete type, an anonymous type or dynamic(Expandobject). In case of dynamic object, RulesEngine will internally convert to an anonymous type**\n\n### Using custom names for inputs\nBy Default, RulesEngine will name the inputs as input1, input2, input3... respectively.\nIt is possible to use a custom name in rules by passing input as `RuleParameter`\n```json\n[\n  {\n    \"WorkflowName\": \"DiscountWithCustomInputNames\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"Expression\": \"basicInfo.country == \\\"india\\\" AND basicInfo.loyalityFactor <= 2 AND basicInfo.totalPurchasesToDate >= 5000 AND orderInfo.totalOrders > 2 AND telemetryInfo.noOfVisitsPerMonth > 2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount20\",\n        \"Expression\": \"basicInfo.country == \\\"india\\\" AND basicInfo.loyalityFactor == 3 AND basicInfo.totalPurchasesToDate >= 10000 AND orderInfo.totalOrders > 2 AND telemetryInfo.noOfVisitsPerMonth > 2\"\n      }\n    ]\n  }\n] \n\n```\nNow we can call rulesEngine with the custom names:\n```c#\nvar workflowRules = //Get list of workflow rules declared in the json\nvar re = new RulesEngine.RulesEngine(workflowRules);\n\n\n// Declare input1,input2,input3 \n\nvar rp1 = new RuleParameter(\"basicInfo\",input1);\nvar rp2 = new RuleParameter(\"orderInfo\", input2);\nvar rp3 = new RuleParameter(\"telemetryInfo\",input3);\n\nvar resultList  = await re.ExecuteAllRulesAsync(\"DiscountWithCustomInputNames\",rp1,rp2,rp3);\n\n```\n\n## C# Expression support\nThe lambda expression allows you to use most of C# constructs and along with some of linq features.\n\nFor more details on supported expression language refer - [expression language](https://dynamic-linq.net/expression-language)\n\nFor supported linq operations refer - [sequence operators](https://dynamic-linq.net/expression-language#sequence-operators)\n\n\n## Extending expression via custom class/type injection\nAlthough RulesEngine supports C# expressions, you may need to perform more complex operation.\n\nRulesEngine supports injecting custom classes/types via `ReSettings` which can allow you to call properties and methods of your custom class in expressions\n\n### Example\nCreate a custom static class\n```c#\nusing System;\nusing System.Linq;\n\nnamespace RE.HelperFunctions\n{\n    public static class Utils\n    {\n        public static bool CheckContains(string check, string valList)\n        {\n            if (String.IsNullOrEmpty(check) || String.IsNullOrEmpty(valList))\n                return false;\n\n            var list = valList.Split(',').ToList();\n            return list.Contains(check);\n        }\n    }\n}\n```\n\nAdd it in your ReSettings and pass in RulesEngine constructor\n\n```c#\n  var reSettings = new ReSettings{\n      CustomTypes = new Type[] { typeof(Utils) }\n  };\n\n  var rulesEngine = new RulesEngine.RulesEngine(workflowRules,reSettings);\n```\n\nWith this you can call Utils class in your Rules\n\n```json\n{\n    \"WorkflowName\": \"DiscountWithCustomInputNames\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"Expression\": \"Utils.CheckContains(input1.country, \\\"india,usa,canada,France\\\") == true\"\n      }\n    ]\n}\n\n```\n\n\n## ScopedParams\nSometimes 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.\n\nRulesEngine supports two type of ScopedParams:\n- GlobalParams\n- LocalParams\n\n\n### GlobalParams\nGlobalParams are defined at workflow level and can be used in any rule.\n\n#### Example\n\n```jsonc\n//Rule.json\n{\n  \"WorkflowName\": \"workflowWithGlobalParam\",\n  \"GlobalParams\":[\n    {\n      \"Name\":\"myglobal1\",\n      \"Expression\":\"myInput.hello.ToLower()\"\n    }\n  ],\n  \"Rules\":[\n    {\n      \"RuleName\": \"checkGlobalEqualsHello\",\n      \"Expression\":\"myglobal1 == \\\"hello\\\"\"\n    },\n    {\n      \"RuleName\": \"checkGlobalEqualsInputHello\",\n      \"Expression\":\"myInput.hello.ToLower() == myglobal1\"\n    }\n  ]\n}\n```\n\nThese rules when executed with the below input will return success\n```c#\n  var input = new RuleParameter(\"myInput\",new {\n    hello = \"HELLO\"\n  });\n\n  var resultList  = await re.ExecuteAllRulesAsync(\"workflowWithGlobalParam\",rp);\n\n\n```\n\n\n### LocalParams\nLocalParams are defined at rule level and can be used by the rule and its child rules\n\n#### Example\n\n```jsonc\n//Rule.json\n{\n  \"WorkflowName\": \"workflowWithLocalParam\",\n  \n  \"Rules\":[\n    {\n      \"RuleName\": \"checkLocalEqualsHello\",\n      \"LocalParams\":[\n        {\n          \"Name\":\"mylocal1\",\n          \"Expression\":\"myInput.hello.ToLower()\"\n        }\n      ],\n      \"Expression\":\"mylocal1 == \\\"hello\\\"\"\n    },\n    {\n      \"RuleName\": \"checkLocalEqualsInputHelloInNested\",\n      \"LocalParams\":[\n        {\n          \"Name\":\"mylocal1\", //redefined here as it is scoped at rule level\n          \"Expression\":\"myInput.hello.ToLower()\"\n        }\n      ],\n      \"Operator\": \"And\",\n      \"Rules\":[\n        {\n          \"RuleName\": \"nestedRule\",\n          \"Expression\":\"myInput.hello.ToLower() == mylocal1\" //mylocal1 can be used here since it is nested to Rule where mylocal1 is defined\n        }\n      ]\n      \n    }\n  ]\n}\n```\n\nThese rules when executed with the below input will return success\n```c#\n  var rp = new RuleParameter(\"myInput\",new {\n    hello = \"HELLO\"\n  });\n\n  var resultList  = await re.ExecuteAllRulesAsync(\"workflowWithLocalParam\",rp);\n```\n\n### Referencing ScopedParams in other ScopedParams\n\nSimilar to how ScopedParams can be used in expressions, they can also be used in other scoped params that come after them.\nThis allows us to create multi-step rule which is easier to read and maintain\n\n\n```jsonc\n//Rule.json\n{\n  \"WorkflowName\": \"workflowWithReferencedRule\",\n  \"GlobalParams\":[\n    {\n      \"Name\":\"myglobal1\",\n      \"Expression\":\"myInput.hello\"\n    }\n  ],\n  \"Rules\":[\n    {\n      \"RuleName\": \"checkGlobalAndLocalEqualsHello\",\n      \"LocalParams\":[\n        {\n          \"Name\": \"mylocal1\",\n          \"Expression\": \"myglobal1.ToLower()\"\n        }\n      ],\n      \"Expression\":\"mylocal1 == \\\"hello\\\"\"\n    },\n    {\n      \"RuleName\": \"checklocalEqualsInputHello\",\n       \"LocalParams\":[\n        {\n          \"Name\": \"mylocal1\",\n          \"Expression\": \"myglobal1.ToLower()\"\n        },\n        {\n          \"Name\": \"mylocal2\",\n          \"Expression\": \"myInput.hello.ToLower() == mylocal1\"\n        }\n      ],\n      \"Expression\":\"mylocal2 == true\"\n    }\n  ]\n}\n```\n\nThese rules when executed with the below input will return success\n```c#\n  var input = new RuleParameter(\"myInput\",new {\n    hello = \"HELLO\"\n  });\n\n  var resultList  = await re.ExecuteAllRulesAsync(\"workflowWithReferencedRule\",rp);\n\n\n```\n\n## Post rule execution actions\nAs 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\n\n### Inbuilt Actions\nRulesEngine provides inbuilt action which cover major scenarios related to rule execution\n\n#### OutputExpression\nThis action evaluates an expression based on the RuleParameters and returns its value as Output\n##### Usage\nDefine OnSuccess or OnFailure Action for your Rule:\n```jsonc\n{\n  \"WorkflowName\": \"inputWorkflow\",\n  \"Rules\": [\n    {\n      \"RuleName\": \"GiveDiscount10Percent\",\n      \"SuccessEvent\": \"10\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"RuleExpressionType\": \"LambdaExpression\",\n      \"Expression\": \"input1.couy == \\\"india\\\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input2.noOfVisitsPerMonth > 2\",\n      \"Actions\": {\n         \"OnSuccess\": {\n            \"Name\": \"OutputExpression\",  //Name of action you want to call\n            \"Context\": {  //This is passed to the action as action context\n               \"Expression\": \"input1.TotalBilled * 0.9\"\n            }\n         }\n      }\n    }\n  ]\n}\n```\nCall `ExecuteAllRulesAsync` with the workflowName, ruleName and ruleParameters\n```c#\n   var ruleResultList = await rulesEngine.ExecuteAllRulesAsync(\"inputWorkflow\",ruleParameters);\n   foreach(var ruleResult in ruleResultList){\n      if(ruleResult.ActionResult != null){\n          Console.WriteLine(ruleResult.ActionResult.Output); //ActionResult.Output contains the evaluated value of the action\n      }\n   }\n   \n```\n\n#### EvaluateRule\nThis action allows chaining of rules along with their actions. It also supports filtering inputs provided to chained rule as well as providing custom inputs\n\n##### Usage\nDefine OnSuccess or OnFailure Action for your Rule:\n```jsonc\n{\n  \"WorkflowName\": \"inputWorkflow\",\n  \"Rules\": [\n    {\n        \"RuleName\": \"GiveDiscount20Percent\",\n        \"Expression\": \"input1.couy == \\\"india\\\" AND input1.loyalityFactor <= 5 AND input1.totalPurchasesToDate >= 20000\",\n        \"Actions\": {\n           \"OnSuccess\": {\n              \"Name\": \"OutputExpression\",  //Name of action you want to call\n              \"Context\": {  //This is passed to the action as action context\n                 \"Expression\": \"input1.TotalBilled * 0.8\"\n              }\n           },\n           \"OnFailure\": { // This will execute if the Rule evaluates to failure\n               \"Name\": \"EvaluateRule\",\n               \"Context\": {\n                   \"workflowName\": \"inputWorkflow\",\n                   \"ruleName\": \"GiveDiscount10Percent\"\n               }\n           }\n        }\n    },\n    {\n      \"RuleName\": \"GiveDiscount10Percent\",\n      \"SuccessEvent\": \"10\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"RuleExpressionType\": \"LambdaExpression\",\n      \"Expression\": \"input1.couy == \\\"india\\\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input2.noOfVisitsPerMonth > 2\",\n      \"Actions\": {\n         \"OnSuccess\": {\n            \"Name\": \"OutputExpression\",  //Name of action you want to call\n            \"Context\": {  //This is passed to the action as action context\n               \"Expression\": \"input1.TotalBilled * 0.9\"\n            }\n         }\n      }\n    }\n  ]\n}\n```\nCall `ExecuteActionWorkflowAsync` with the workflowName, ruleName and ruleParameters\n```c#\n   var result = await rulesEngine.ExecuteActionWorkflowAsync(\"inputWorkflow\",\"GiveDiscount20Percent\",ruleParameters);\n   Console.WriteLine(result.Output); //result.Output contains the evaluated value of the action\n```\n\nIn the above scenario if `GiveDiscount20Percent` succeeds, it will return 20 percent discount in output. If it fails, `EvaluateRule` action will call `GiveDiscount10Percent` internally and if it succeeds, it will return 10 percent discount in output.\n\nEvaluateRule also supports passing filtered inputs and computed inputs to chained rule\n```jsonc\n \"Actions\": {\n         \"OnSuccess\": {\n            \"Name\": \"EvaluateRule\",\n               \"Context\": {\n                   \"workflowName\": \"inputWorkflow\",\n                   \"ruleName\": \"GiveDiscount10Percent\",\n                   \"inputFilter\": [\"input2\"], //will only pass input2 from existing inputs,scopedparams to the chained rule\n                   \"additionalInputs\":[ // will pass a new input named currentDiscount with the result of the expression to the chained rule\n                     {\n                       \"Name\": \"currentDiscount\",\n                       \"Expression\": \"input1.TotalBilled * 0.9\"\n                     }\n                   ]\n               }\n         }\n      }\n\n```\n\n\n### Custom Actions\nRulesEngine allows registering custom actions which can be used in the rules workflow.\n\n#### Steps to use a custom Action\n1. Create a class which extends `ActionBase` class and implement the run method\n```c#\n public class MyCustomAction: ActionBase\n    {\n     \n        public MyCustomAction(SomeInput someInput)\n        {\n            ....\n        }\n\n        public override ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)\n        {\n            var customInput = context.GetContext<string>(\"customContextInput\");\n            //Add your custom logic here and return a ValueTask\n        }\n```\nActions can have async code as well\n```c#\n public class MyCustomAction: ActionBase\n    {\n     \n        public MyCustomAction(SomeInput someInput)\n        {\n            ....\n        }\n\n        public override async ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)\n        {\n            var customInput = context.GetContext<string>(\"customContextInput\");\n            //Add your custom logic here\n            return await MyCustomLogicAsync();\n        }\n```\n2. Register them in ReSettings and pass it to RulesEngine\n```c#\n   var reSettings = new ReSettings{\n                        CustomActions = new Dictionary<string, Func<ActionBase>>{\n                                             {\"MyCustomAction\", () => new MyCustomAction(someInput) }\n                                         }\n                     };\n\n   var re = new RulesEngine(workflowRules,reSettings);\n```\n3. You can now use the name you registered in the Rules json in success or failure actions\n```jsonc\n{\n  \"WorkflowName\": \"inputWorkflow\",\n  \"Rules\": [\n    {\n      \"RuleName\": \"GiveDiscount10Percent\",\n      \"SuccessEvent\": \"10\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"RuleExpressionType\": \"LambdaExpression\",\n      \"Expression\": \"input1.couy == \\\"india\\\" AND input1.loyalityFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input2.noOfVisitsPerMonth > 2\",\n      \"Actions\": {\n         \"OnSuccess\": {\n            \"Name\": \"MyCustomAction\",  //Name context\n            \"Context\": {  //This is passed to the action as action context\n               \"customContextInput\": \"input1.TotalBilled * 0.9\"\n            }\n         }\n      }\n    }\n  ]\n}\n```\n\n## Standalone Expression Evaluator\nIf you are not looking for a full fledged RulesEngine and need only an expression evaluator. RulesEngine offers `RuleExpressionParser` which handles expression parsing and evaluation.\n\n### Usage\n```c#\nusing System;\nusing RulesEngine.Models;\nusing RulesEngine.ExpressionBuilders;\n\t\t\t\t\t\npublic class Program\n{\n\tpublic static void Main()\n\t{\n\t\tvar reParser = new RuleExpressionParser(new ReSettings());\n\t\tvar result = reParser.Evaluate<string>(\"a+b\", new RuleParameter[]{\n\t\t\tnew RuleParameter(\"a\",\"Hello \"),\n\t\t\tnew RuleParameter(\"b\",\"World\")\n\t\t});\n\t\tConsole.WriteLine(result);\n\t}\n}\n```\nThis will output \"Hello World\"\n\nFor more advanced usage, refer - https://dotnetfiddle.net/KSX8i0\n\n## Settings\nRulesEngine allows you to pass optional `ReSettings` in constructor to specify certain configuration for RulesEngine.\n\nHere are the all the options available:-\n\n\n| Property | Type | Default Value | Description |\n| --- | --- | --- | --- |\n| `CustomTypes` | `Type[]` | N/A | Custom types to be used in rule expressions. |\n| `CustomActions` | `Dictionary<string, Func<ActionBase>>` | N/A | Custom actions that can be used in the rules. |\n| `EnableExceptionAsErrorMessage` | `bool` | `true` | If `true`, returns any exception occurred while rule execution as an error message. Otherwise, throws an exception. This setting is only applicable if `IgnoreException` is set to `false`. |\n| `IgnoreException` | `bool` | `false` | If `true`, it will ignore any exception thrown with rule compilation/execution. |\n| `EnableFormattedErrorMessage` | `bool` | `true` | Enables error message formatting. |\n| `EnableScopedParams` | `bool` | `true` | Enables global parameters and local parameters for rules. |\n| `IsExpressionCaseSensitive` | `bool` | `false` | Sets whether expressions are case sensitive. |\n| `AutoRegisterInputType` | `bool` | `true` | Auto registers input type in custom type to allow calling method on type. |\n| `NestedRuleExecutionMode` | `NestedRuleExecutionMode` | `All` | Sets the mode for nested rule execution. |\n| `CacheConfig` | `MemCacheConfig` | N/A | Configures the memory cache. |\n| `UseFastExpressionCompiler` | `bool` | `true` | Whether to use FastExpressionCompiler for rule compilation. |\n\n\n### NestedRuleExecutionMode \n\n| Value | Description |\n| --- | --- |\n| `All` | Executes all nested rules. |\n| `Performance` | Skips nested rules whose execution does not impact parent rule's result. |\n"
  },
  {
    "path": "global.json",
    "content": "{\n  \"sdk\": {\n    \"version\": \"9.0.301\",\n    \"rollForward\": \"latestFeature\",\n    \"allowPrerelease\": false\n  }\n}"
  },
  {
    "path": "schema/workflow-list-schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"array\",\n  \"items\": {\n    \"$ref\": \"https://raw.githubusercontent.com/microsoft/RulesEngine/main/schema/workflow-schema.json\"\n  }\n}"
  },
  {
    "path": "schema/workflow-schema.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"definitions\": {\n    \"ScopedParam\": {\n      \"type\": \"object\",\n      \"properties\": {\n        \"Name\": { \"type\": \"string\" },\n        \"Expression\": { \"type\": \"string\" }\n      },\n      \"required\": [ \"Name\", \"Expression\" ]\n    },\n    \"Rule\": {\n      \"title\": \"Rule\",\n      \"properties\": {\n        \"RuleName\": {\n          \"type\": \"string\"\n        },\n        \"LocalParams\": {\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/definitions/ScopedParam\" }\n        },\n        \"Operator\": {\n          \"enum\": [\n            \"And\",\n            \"AndAlso\",\n            \"Or\",\n            \"OrElse\"\n          ]\n        },\n        \"ErrorMessage\": {\n          \"type\": \"string\"\n        },\n        \"SuccessEvent\": {\n          \"type\": \"string\"\n        },\n        \"Rules\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"anyOf\": [\n              {\n                \"$ref\": \"#/definitions/LeafRule\"\n              },\n              {\n                \"$ref\": \"#/definitions/Rule\"\n              }\n            ]\n          }\n        },\n        \"Properties\": {\n          \"type\": \"object\"\n        },\n        \"Actions\": {\n          \"$ref\": \"#/definitions/RuleActions\"\n        },\n        \"Enabled\": {\n          \"type\": \"boolean\",\n          \"default\": true\n        }\n      },\n      \"required\": [\n        \"RuleName\",\n        \"Operator\",\n        \"Rules\"\n      ],\n      \"type\": \"object\"\n    },\n    \"LeafRule\": {\n      \"title\": \"Leaf Rule\",\n      \"type\": \"object\",\n      \"required\": [\n        \"RuleName\",\n        \"Expression\"\n      ],\n      \"properties\": {\n        \"RuleName\": {\n          \"type\": \"string\"\n        },\n        \"LocalParams\": {\n          \"type\": \"array\",\n          \"items\": { \"$ref\": \"#/definitions/ScopedParam\" }\n        },\n        \"Expression\": {\n          \"type\": \"string\"\n        },\n        \"RuleExpressionType\": {\n          \"enum\": [\n            \"LambdaExpression\"\n          ]\n        },\n        \"ErrorMessage\": {\n          \"type\": \"string\"\n        },\n        \"SuccessEvent\": {\n          \"type\": \"string\"\n        },\n        \"Properties\": {\n          \"type\": \"object\"\n        },\n        \"Actions\": {\n          \"$ref\": \"#/definitions/RuleActions\"\n        },\n        \"Enabled\": {\n          \"type\": \"boolean\",\n          \"default\": true\n        }\n      }\n    },\n    \"ActionInfo\": {\n      \"properties\": {\n        \"Name\": {\n          \"type\": \"string\"\n        },\n        \"Context\": {\n          \"type\": \"object\"\n        }\n      },\n      \"required\": [\n        \"Name\"\n      ]\n    },\n    \"RuleActions\": {\n      \"properties\": {\n        \"OnSuccess\": {\n          \"$ref\": \"#/definitions/ActionInfo\"\n        },\n        \"OnFailure\": {\n          \"$ref\": \"#/definitions/ActionInfo\"\n        }\n      }\n    }\n\n  },\n  \"properties\": {\n    \"WorkflowName\": {\n      \"type\": \"string\"\n    },\n    \"WorkflowsToInject\": {\n      \"type\": \"array\",\n      \"items\": { \"type\": \"string\" }\n    },\n    \"GlobalParams\": {\n      \"type\": \"array\",\n      \"items\": { \"$ref\": \"#/definitions/ScopedParam\" }\n    },\n    \"Rules\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"anyOf\": [\n          {\n            \"$ref\": \"#/definitions/LeafRule\"\n          },\n          {\n            \"$ref\": \"#/definitions/Rule\"\n          }\n        ]\n      }\n    }\n  },\n  \"required\": [\n    \"WorkflowName\",\n    \"Rules\"\n  ],\n  \"type\": \"object\"\n}\n"
  },
  {
    "path": "scripts/check-coverage.ps1",
    "content": "param(\n    [Parameter(Mandatory=$true)][string] $reportPath,\n    [Parameter(Mandatory=$true)][decimal] $threshold\n)\n\n\n[XML]$report = Get-Content $reportPath;\n[decimal]$coverage = [decimal]$report.coverage.'line-rate' * 100;\n\nif ($coverage -lt $threshold) {\n  Write-Error \"Coverage($coverage) is less than $threshold percent\"\n  exit 1\n}\nelse{\n    Write-Host \"Coverage($coverage) is more than $threshold percent\"\n}\n"
  },
  {
    "path": "scripts/generate-coverage-report.ps1",
    "content": "dotnet tool restore\ndotnet reportgenerator \"-reports:**/coverage.cobertura.xml\" \"-targetdir:coveragereport\" -reporttypes:\"Html;lcov;Cobertura\""
  },
  {
    "path": "src/RulesEngine/Actions/ActionBase.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace RulesEngine.Actions\n{\n    public abstract class ActionBase\n    {\n        internal async virtual ValueTask<ActionRuleResult> ExecuteAndReturnResultAsync(ActionContext context, RuleParameter[] ruleParameters, bool includeRuleResults = false)\n        {\n            var result = new ActionRuleResult();\n            try\n            {\n                result.Output = await Run(context, ruleParameters);\n            }\n            catch (Exception ex)\n            {\n                result.Exception = new Exception($\"Exception while executing {GetType().Name}: {ex.Message}\", ex);\n            }\n            finally\n            {\n                if (includeRuleResults)\n                {\n                    result.Results = new List<RuleResultTree>()\n                    {\n                        context.GetParentRuleResult()\n                    };\n                }\n            }\n            return result;\n        }\n        public abstract ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters);\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Actions/ActionContext.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\n\nnamespace RulesEngine.Actions\n{\n    using System.Text.Json;\n\n    public class ActionContext\n    {\n        private readonly IDictionary<string, string> _context;\n        private readonly RuleResultTree _parentResult;\n\n        public ActionContext(IDictionary<string, object> context, RuleResultTree parentResult)\n        {\n            _context = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);\n            foreach (var kv in context)\n            {\n                string key = kv.Key;\n                string value;\n                switch (kv.Value.GetType().Name)\n                {\n                    case \"String\":\n                    case \"JsonElement\":\n                        value =  kv.Value.ToString();\n                        break;\n                    default:\n                        value = JsonSerializer.Serialize(kv.Value);\n                        break;\n\n                }\n                _context.Add(key, value);\n            }\n            _parentResult = parentResult;\n        }\n\n        public RuleResultTree GetParentRuleResult()\n        {\n            return _parentResult;\n        }\n\n        public bool TryGetContext<T>(string name,out T output)\n        {\n            try\n            {\n                //key not found return\n                //Returning a KeyNotFoundException has a significant impact on performance.\n                if (!_context.ContainsKey(name))\n                {\n                    output = default(T);\n                    return false;\n                }\n                output =  GetContext<T>(name);\n                return true;\n            }\n            catch(ArgumentException)\n            {\n                output = default(T);\n                return false;\n            }\n        }\n\n        public T GetContext<T>(string name)\n        {\n            try\n            {\n                if (typeof(T) == typeof(string))\n                {\n                    return (T)Convert.ChangeType(_context[name], typeof(T));\n                }\n                return JsonSerializer.Deserialize<T>(_context[name]);\n            }\n            catch (KeyNotFoundException)\n            {\n                throw new ArgumentException($\"Argument `{name}` was not found in the action context\");\n            }\n            catch (JsonException)\n            {\n                throw new ArgumentException($\"Failed to convert argument `{name}` to type `{typeof(T).Name}` in the action context\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Actions/ActionFactory.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace RulesEngine.Actions\n{\n    internal class ActionFactory\n    {\n        private readonly IDictionary<string, Func<ActionBase>> _actionRegistry;\n\n        internal ActionFactory()\n        {\n            _actionRegistry = new Dictionary<string, Func<ActionBase>>(StringComparer.OrdinalIgnoreCase);\n        }\n        internal ActionFactory(IDictionary<string, Func<ActionBase>> actionRegistry) : this()\n        {\n            foreach (var kv in actionRegistry)\n            {\n                _actionRegistry.Add(kv.Key, kv.Value);\n            }\n        }\n\n        internal ActionBase Get(string name)\n        {\n            if (_actionRegistry.ContainsKey(name))\n            {\n                return _actionRegistry[name]();\n            }\n            throw new KeyNotFoundException($\"Action with name: {name} does not exist\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Actions/EvaluateRuleAction.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nnamespace RulesEngine.Actions\n{\n    public class EvaluateRuleAction : ActionBase\n    {\n        private readonly RulesEngine _ruleEngine;\n        private readonly RuleExpressionParser _ruleExpressionParser;\n\n        public EvaluateRuleAction(RulesEngine ruleEngine, RuleExpressionParser ruleExpressionParser)\n        {\n            _ruleEngine = ruleEngine;\n            _ruleExpressionParser = ruleExpressionParser;\n        }\n\n        internal async override ValueTask<ActionRuleResult> ExecuteAndReturnResultAsync(ActionContext context, RuleParameter[] ruleParameters, bool includeRuleResults = false)\n        {\n            var innerResult = await base.ExecuteAndReturnResultAsync(context, ruleParameters, includeRuleResults);\n            var output = innerResult.Output as ActionRuleResult;\n            List<RuleResultTree> resultList = null;\n            if (includeRuleResults)\n            {\n                resultList = new List<RuleResultTree>(output?.Results ?? new List<RuleResultTree>() { });\n                resultList.AddRange(innerResult.Results);\n            }\n            return new ActionRuleResult {\n                Output = output?.Output,\n                Exception = innerResult.Exception,\n                Results = resultList\n            };\n        }\n\n        public async override ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)\n        {\n            var workflowName = context.GetContext<string>(\"workflowName\");\n            var ruleName = context.GetContext<string>(\"ruleName\");\n            var filteredRuleParameters = new List<RuleParameter>(ruleParameters);\n            if(context.TryGetContext<List<string>>(\"inputFilter\",out var inputFilter))\n            {\n                filteredRuleParameters = ruleParameters.Where(c => inputFilter.Contains(c.Name)).ToList();\n            }\n            if (context.TryGetContext<List<ScopedParam>>(\"additionalInputs\", out var additionalInputs))\n            {\n                foreach(var additionalInput in additionalInputs)\n                {\n                    dynamic value = _ruleExpressionParser.Evaluate<object>(additionalInput.Expression, ruleParameters);\n                    filteredRuleParameters.Add(new RuleParameter(additionalInput.Name, value));\n                    \n                }\n            }\n   \n            var ruleResult = await _ruleEngine.ExecuteActionWorkflowAsync(workflowName, ruleName, filteredRuleParameters.ToArray());\n            return ruleResult;\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Actions/ExpressionOutputAction.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.Models;\nusing System.Threading.Tasks;\n\nnamespace RulesEngine.Actions\n{\n    public class OutputExpressionAction : ActionBase\n    {\n        private readonly RuleExpressionParser _ruleExpressionParser;\n\n        public OutputExpressionAction(RuleExpressionParser ruleExpressionParser)\n        {\n            _ruleExpressionParser = ruleExpressionParser;\n        }\n\n        public override ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)\n        {\n            var expression = context.GetContext<string>(\"expression\");\n            return new ValueTask<object>(_ruleExpressionParser.Evaluate<object>(expression, ruleParameters));\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/CustomTypeProvider.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.HelperFunctions;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Dynamic.Core;\nusing System.Linq.Dynamic.Core.CustomTypeProviders;\n\nnamespace RulesEngine\n{\n    public class CustomTypeProvider : DefaultDynamicLinqCustomTypeProvider\n    {\n        private readonly HashSet<Type> _types;\n\n        public CustomTypeProvider(Type[] types) : base(ParsingConfig.Default)\n        {\n            _types = new HashSet<Type>(types ?? Array.Empty<Type>());\n\n            _types.Add(typeof(ExpressionUtils));\n\n            _types.Add(typeof(Enumerable));\n\n            var queue = new Queue<Type>(_types);\n            while (queue.Count > 0)\n            {\n                var t = queue.Dequeue();\n\n                var baseType = t.BaseType;\n                if (baseType != null && _types.Add(baseType))\n                    queue.Enqueue(baseType);\n\n                foreach (var interfaceType in t.GetInterfaces())\n                {\n                    if (_types.Add(interfaceType))\n                        queue.Enqueue(interfaceType);\n                }\n            }\n\n            _types.Add(typeof(IEnumerable));\n        }\n\n        public override HashSet<Type> GetCustomTypes()\n        {\n            var all = new HashSet<Type>(base.GetCustomTypes());\n            all.UnionWith(_types);\n            return all;\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Exceptions/ExpressionParserException.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace RulesEngine.Exceptions\n{\n    public class ExpressionParserException: Exception\n    {\n        public ExpressionParserException(string message, string expression) : base(message)\n        {\n            Data.Add(\"Expression\", expression);\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Exceptions/RuleException.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace RulesEngine.Exceptions\n{\n    public class RuleException : Exception\n    {\n        public RuleException(string message) : base(message)\n        {\n        }\n\n        public RuleException(string message, Exception innerException) : base(message, innerException)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Exceptions/RuleValidationException.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing FluentValidation;\nusing FluentValidation.Results;\nusing System.Collections.Generic;\n\nnamespace RulesEngine.Exceptions\n{\n    public class RuleValidationException : ValidationException\n    {\n        public RuleValidationException(string message, IEnumerable<ValidationFailure> errors) : base(message, errors)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Exceptions/ScopedParamException.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace RulesEngine.Exceptions\n{\n    public class ScopedParamException: Exception\n    {\n        public ScopedParamException(string message, Exception innerException, string scopedParamName): base(message,innerException)\n        {\n            Data.Add(\"ScopedParamName\", scopedParamName);\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/ExpressionBuilders/LambdaExpressionBuilder.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Exceptions;\nusing RulesEngine.HelperFunctions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Dynamic.Core.Exceptions;\nusing System.Linq.Expressions;\n\nnamespace RulesEngine.ExpressionBuilders\n{\n    internal sealed class LambdaExpressionBuilder : RuleExpressionBuilderBase\n    {\n        private readonly ReSettings _reSettings;\n        private readonly RuleExpressionParser _ruleExpressionParser;\n\n        internal LambdaExpressionBuilder(ReSettings reSettings, RuleExpressionParser ruleExpressionParser)\n        {\n            _reSettings = reSettings;\n            _ruleExpressionParser = ruleExpressionParser;\n        }\n\n        internal override RuleFunc<RuleResultTree> BuildDelegateForRule(Rule rule, RuleParameter[] ruleParams)\n        {\n            try\n            {\n                var ruleDelegate = _ruleExpressionParser.Compile<bool>(rule.Expression, ruleParams);\n                return Helpers.ToResultTree(_reSettings, rule, null, ruleDelegate);\n            }\n            catch (Exception ex)\n            {\n                Helpers.HandleRuleException(ex,rule,_reSettings);\n\n                var exceptionMessage = Helpers.GetExceptionMessage($\"Exception while parsing expression `{rule?.Expression}` - {ex.Message}\",\n                                                                    _reSettings);\n\n                bool func(object[] param) => false;\n                \n                return Helpers.ToResultTree(_reSettings, rule, null,func, exceptionMessage);\n            }\n        }\n\n        internal override Expression Parse(string expression, ParameterExpression[] parameters, Type returnType)\n        {\n            try\n            {\n                return _ruleExpressionParser.Parse(expression, parameters, returnType);\n            }\n            catch(ParseException ex)\n            {\n                throw new ExpressionParserException(ex.Message, expression);\n            }\n            \n        }\n\n        internal override Func<object[],Dictionary<string,object>> CompileScopedParams(RuleParameter[] ruleParameters, RuleExpressionParameter[] scopedParameters)\n        {\n            return _ruleExpressionParser.CompileRuleExpressionParameters(ruleParameters, scopedParameters);\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/ExpressionBuilders/RuleExpressionBuilderBase.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq.Expressions;\n\nnamespace RulesEngine.ExpressionBuilders\n{\n    /// <summary>\n    /// Base class for expression builders\n    /// </summary>\n    internal abstract class RuleExpressionBuilderBase\n    {\n        /// <summary>\n        /// Builds the expression for rule.\n        /// </summary>\n        /// <param name=\"rule\">The rule.</param>\n        /// <param name=\"typeParamExpressions\">The type parameter expressions.</param>\n        /// <param name=\"ruleInputExp\">The rule input exp.</param>\n        /// <returns>Expression type</returns>\n        internal abstract RuleFunc<RuleResultTree> BuildDelegateForRule(Rule rule, RuleParameter[] ruleParams);\n\n        internal abstract Expression Parse(string expression, ParameterExpression[] parameters, Type returnType);\n\n        internal abstract Func<object[], Dictionary<string, object>> CompileScopedParams(RuleParameter[] ruleParameters, RuleExpressionParameter[] scopedParameters);\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/ExpressionBuilders/RuleExpressionParser.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\r\n//  Licensed under the MIT License.\r\n\r\nusing FastExpressionCompiler;\r\nusing RulesEngine.HelperFunctions;\r\nusing RulesEngine.Models;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Linq.Dynamic.Core;\r\nusing System.Linq.Dynamic.Core.Exceptions;\r\nusing System.Linq.Dynamic.Core.Parser;\r\nusing System.Linq.Expressions;\r\nusing System.Reflection;\r\nusing System.Text.RegularExpressions;\r\n\r\nnamespace RulesEngine.ExpressionBuilders\r\n{\r\n    public class RuleExpressionParser\r\n    {\r\n        private readonly ReSettings _reSettings;\r\n        private readonly IDictionary<string, MethodInfo> _methodInfo;\r\n\r\n        public RuleExpressionParser(ReSettings reSettings = null)\r\n        {\r\n            _reSettings = reSettings ?? new ReSettings();\r\n            _methodInfo = new Dictionary<string, MethodInfo>();\r\n            PopulateMethodInfo();\r\n        }\r\n\r\n        private void PopulateMethodInfo()\r\n        {\r\n            var dict_add = typeof(Dictionary<string, object>).GetMethod(\"Add\", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string), typeof(object) }, null);\r\n            _methodInfo.Add(\"dict_add\", dict_add);\r\n        }\r\n\r\n        public Expression Parse(string expression, ParameterExpression[] parameters, Type returnType)\r\n        {\r\n            var config = new ParsingConfig {\r\n                CustomTypeProvider = new CustomTypeProvider(_reSettings.CustomTypes),\r\n                IsCaseSensitive = _reSettings.IsExpressionCaseSensitive\r\n            };\r\n\r\n            // Instead of immediately returning default values, allow for expression parsing to handle dynamic evaluation.\r\n            try\r\n            {\r\n                return new ExpressionParser(parameters, expression, Array.Empty<object>(), config).Parse(returnType);\r\n            }\r\n            catch (ParseException)\r\n            {\r\n                if (_reSettings.EnableExceptionAsErrorMessageForRuleExpressionParsing)\r\n                {\r\n                    throw;\r\n                }\r\n                return Expression.Constant(GetDefaultValueForType(returnType));\r\n            }\r\n            catch (Exception)\r\n            {\r\n                throw;\r\n            }\r\n        }\r\n\r\n        private object GetDefaultValueForType(Type type)\r\n        {\r\n            if (type == typeof(bool))\r\n                return false;\r\n            if (type == typeof(int) || type == typeof(float) || type == typeof(double))\r\n                return int.MinValue;\r\n            return null;\r\n        }\r\n\r\n        public Func<object[], T> Compile<T>(string expression, RuleParameter[] ruleParams)\r\n        {\r\n            var rtype = typeof(T);\r\n            if (rtype == typeof(object))\r\n            {\r\n                rtype = null;\r\n            }\r\n            var parameterExpressions = GetParameterExpression(ruleParams).ToArray();\r\n\r\n            var e = Parse(expression, parameterExpressions, rtype);\r\n            if (rtype == null)\r\n            {\r\n                e = Expression.Convert(e, typeof(T));\r\n            }\r\n            var expressionBody = new List<Expression>() { e };\r\n            var wrappedExpression = WrapExpression<T>(expressionBody, parameterExpressions, new ParameterExpression[] { });\r\n            return CompileExpression(wrappedExpression);\r\n\r\n        }\r\n\r\n        private Func<object[], T> CompileExpression<T>(Expression<Func<object[], T>> expression)\r\n        {\r\n            if (_reSettings.UseFastExpressionCompiler)\r\n            {\r\n                return expression.CompileFast();\r\n            }\r\n            return expression.Compile();\r\n        }\r\n\r\n        private Expression<Func<object[], T>> WrapExpression<T>(List<Expression> expressionList, ParameterExpression[] parameters, ParameterExpression[] variables)\r\n        {\r\n            var argExp = Expression.Parameter(typeof(object[]), \"args\");\r\n            var paramExps = parameters.Select((c, i) => {\r\n                var arg = Expression.ArrayAccess(argExp, Expression.Constant(i));\r\n                return (Expression)Expression.Assign(c, Expression.Convert(arg, c.Type));\r\n            });\r\n            var blockExpSteps = paramExps.Concat(expressionList);\r\n            var blockExp = Expression.Block(parameters.Concat(variables), blockExpSteps);\r\n            return Expression.Lambda<Func<object[], T>>(blockExp, argExp);\r\n        }\r\n\r\n        internal Func<object[], Dictionary<string, object>> CompileRuleExpressionParameters(RuleParameter[] ruleParams, RuleExpressionParameter[] ruleExpParams = null)\r\n        {\r\n            ruleExpParams = ruleExpParams ?? new RuleExpressionParameter[] { };\r\n            var expression = CreateDictionaryExpression(ruleParams, ruleExpParams);\r\n            return CompileExpression(expression);\r\n        }\r\n\r\n        public T Evaluate<T>(string expression, RuleParameter[] ruleParams)\r\n        {\r\n            var func = Compile<T>(expression, ruleParams);\r\n            return func(ruleParams.Select(c => c.Value).ToArray());\r\n        }\r\n\r\n        private IEnumerable<Expression> CreateAssignedParameterExpression(RuleExpressionParameter[] ruleExpParams)\r\n        {\r\n            return ruleExpParams.Select((c, i) => {\r\n                return Expression.Assign(c.ParameterExpression, c.ValueExpression);\r\n            });\r\n        }\r\n\r\n        // <summary>\r\n        /// Gets the parameter expression.\r\n        /// </summary>\r\n        /// <param name=\"ruleParams\">The types.</param>\r\n        /// <returns></returns>\r\n        /// <exception cref=\"ArgumentException\">\r\n        /// types\r\n        /// or\r\n        /// type\r\n        /// </exception>\r\n        private IEnumerable<ParameterExpression> GetParameterExpression(RuleParameter[] ruleParams)\r\n        {\r\n            foreach (var ruleParam in ruleParams)\r\n            {\r\n                if (ruleParam == null)\r\n                {\r\n                    throw new ArgumentException($\"{nameof(ruleParam)} can't be null.\");\r\n                }\r\n\r\n                yield return ruleParam.ParameterExpression;\r\n            }\r\n        }\r\n\r\n        private Expression<Func<object[], Dictionary<string, object>>> CreateDictionaryExpression(RuleParameter[] ruleParams, RuleExpressionParameter[] ruleExpParams)\r\n        {\r\n            var body = new List<Expression>();\r\n            var paramExp = new List<ParameterExpression>();\r\n            var variableExp = new List<ParameterExpression>();\r\n\r\n\r\n            var variableExpressions = CreateAssignedParameterExpression(ruleExpParams);\r\n\r\n            body.AddRange(variableExpressions);\r\n\r\n            var dict = Expression.Variable(typeof(Dictionary<string, object>));\r\n            var add = _methodInfo[\"dict_add\"];\r\n\r\n            body.Add(Expression.Assign(dict, Expression.New(typeof(Dictionary<string, object>))));\r\n            variableExp.Add(dict);\r\n\r\n            for (var i = 0; i < ruleParams.Length; i++)\r\n            {\r\n                paramExp.Add(ruleParams[i].ParameterExpression);\r\n            }\r\n            for (var i = 0; i < ruleExpParams.Length; i++)\r\n            {\r\n                var key = Expression.Constant(ruleExpParams[i].ParameterExpression.Name);\r\n                var value = Expression.Convert(ruleExpParams[i].ParameterExpression, typeof(object));\r\n                variableExp.Add(ruleExpParams[i].ParameterExpression);\r\n                body.Add(Expression.Call(dict, add, key, value));\r\n\r\n            }\r\n            // Return value\r\n            body.Add(dict);\r\n\r\n            return WrapExpression<Dictionary<string, object>>(body, paramExp.ToArray(), variableExp.ToArray());\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "src/RulesEngine/Extensions/EnumerableExtensions.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace RulesEngine.Extensions\n{\n    internal static class EnumerableExtensions\n    {\n        public static IEnumerable<T> Safe<T>(this IEnumerable<T> enumerable)\n        {\n            return enumerable ?? Enumerable.Empty<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Extensions/ListofRuleResultTreeExtension.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System.Collections.Generic;\nusing System.Linq;\n\n\nnamespace RulesEngine.Extensions\n{\n    public static class ListofRuleResultTreeExtension\n    {\n        public delegate void OnSuccessFunc(string eventName);\n        public delegate void OnFailureFunc();\n\n\n        /// <summary>\n        /// Calls the Success Func for the first rule which succeeded among the ruleResults\n        /// </summary>\n        /// <param name=\"ruleResultTrees\"></param>\n        /// <param name=\"onSuccessFunc\"></param>\n        /// <returns></returns>\n        public static List<RuleResultTree> OnSuccess(this List<RuleResultTree> ruleResultTrees, OnSuccessFunc onSuccessFunc)\n        {\n            var successfulRuleResult = ruleResultTrees.FirstOrDefault(ruleResult => ruleResult.IsSuccess == true);\n            if (successfulRuleResult != null)\n            {\n                var eventName = successfulRuleResult.Rule.SuccessEvent ?? successfulRuleResult.Rule.RuleName;\n                onSuccessFunc(eventName);\n            }\n\n            return ruleResultTrees;\n        }\n\n        /// <summary>\n        /// Calls the Failure Func if all rules failed in the ruleReults\n        /// </summary>\n        /// <param name=\"ruleResultTrees\"></param>\n        /// <param name=\"onFailureFunc\"></param>\n        /// <returns></returns>\n        public static List<RuleResultTree> OnFail(this List<RuleResultTree> ruleResultTrees, OnFailureFunc onFailureFunc)\n        {\n            bool allFailure = ruleResultTrees.All(ruleResult => ruleResult.IsSuccess == false);\n            if (allFailure)\n                onFailureFunc();\n            return ruleResultTrees;\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/HelperFunctions/Constants.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nnamespace RulesEngine.HelperFunctions\n{\n    /// <summary>\n    /// Constants\n    /// </summary>\n    public static class Constants\n    {\n        public const string WORKFLOW_NAME_NULL_ERRMSG = \"Workflow name can not be null or empty\";\n        public const string INJECT_WORKFLOW_RULES_ERRMSG = \"Atleast one of Rules or WorkflowsToInject must be not empty\";\n        public const string RULE_CATEGORY_CONFIGURED_ERRMSG = \"Rule Category should be configured\";\n        public const string RULE_NULL_ERRMSG = \"Rules can not be null or zero\";\n        public const string NESTED_RULE_NULL_ERRMSG = \"Nested rules can not be null\";\n        public const string NESTED_RULE_CONFIGURED_ERRMSG = \"Nested rules can not be configured\";\n        public const string OPERATOR_NULL_ERRMSG = \"Operator can not be null\";\n        public const string OPERATOR_INCORRECT_ERRMSG = \"Operator {PropertyValue} is not allowed\";\n        public const string RULE_NAME_NULL_ERRMSG = \"Rule Name can not be null\";\n        public const string OPERATOR_RULES_ERRMSG = \"Cannot use Rules field when Operator is null\";\n        public const string LAMBDA_EXPRESSION_EXPRESSION_NULL_ERRMSG = \"Expression cannot be null or empty when RuleExpressionType is LambdaExpression\";\n        public const string LAMBDA_EXPRESSION_OPERATOR_ERRMSG = \"Cannot use Operator field when RuleExpressionType is LambdaExpression\";\n        public const string LAMBDA_EXPRESSION_RULES_ERRMSG = \"Cannot use Rules field when RuleExpressionType is LambdaExpression\";\n\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/HelperFunctions/ExpressionUtils.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Linq;\n\nnamespace RulesEngine.HelperFunctions\n{\n    public static class ExpressionUtils\n    {\n        public static bool CheckContains(string check, string valList)\n        {\n            if (string.IsNullOrEmpty(check) || string.IsNullOrEmpty(valList))\n                return false;\n\n            var list = valList.Split(',').ToList();\n            return list.Contains(check);\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/HelperFunctions/Helpers.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Exceptions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace RulesEngine.HelperFunctions\n{\n    /// <summary>\n    /// Helpers\n    /// </summary>\n    internal static class Helpers\n    {\n        internal static RuleFunc<RuleResultTree> ToResultTree(ReSettings reSettings, Rule rule, IEnumerable<RuleResultTree> childRuleResults, Func<object[], bool> isSuccessFunc, string exceptionMessage = \"\")\n        {\n            return (inputs) => {\n                var isSuccess = false;\n                var inputsDict = new Dictionary<string, object>();\n                string finalMessage = exceptionMessage;\n                try\n                {\n                    inputsDict = inputs.ToDictionary(c => c.Name, c => c.Value);\n                    isSuccess = isSuccessFunc(inputs.Select(c => c.Value).ToArray());\n                }\n                catch (Exception ex)\n                {\n                    finalMessage = GetExceptionMessage($\"Error while executing rule : {rule?.RuleName} - {ex.Message}\", reSettings);\n                    HandleRuleException(new RuleException(exceptionMessage,ex), rule, reSettings);\n                    isSuccess = false;\n                }\n\n                return new RuleResultTree {\n                    Rule = rule,\n                    Inputs = inputsDict,\n                    IsSuccess = isSuccess,\n                    ChildResults = childRuleResults,\n                    ExceptionMessage = finalMessage\n                };\n\n            };\n        }\n\n        internal static RuleFunc<RuleResultTree> ToRuleExceptionResult(ReSettings reSettings, Rule rule,Exception ex)\n        {\n            HandleRuleException(ex, rule, reSettings);\n            return ToResultTree(reSettings, rule, null, (args) => false, ex.Message);\n        }\n\n        internal static void HandleRuleException(Exception ex, Rule rule, ReSettings reSettings)\n        {\n            ex.Data.Add(nameof(rule.RuleName), rule.RuleName);\n            ex.Data.Add(nameof(rule.Expression), rule.Expression);\n\n            if (!reSettings.EnableExceptionAsErrorMessage)\n            {\n                throw ex;\n            }\n        }\n\n        /// <summary>\n        /// \n        /// </summary>\n        /// <param name=\"ex\"></param>\n        /// <param name=\"message\"></param>\n        /// <param name=\"rule\"></param>\n        /// <param name=\"reSettings\"></param>\n        /// <returns></returns>\n        internal static string GetExceptionMessage(string message,ReSettings reSettings)\n        {\n            return reSettings.IgnoreException ? \"\" : message;\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/HelperFunctions/MemCache.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Collections;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\n\nnamespace RulesEngine.HelperFunctions\n{\n    public class MemCacheConfig {\n        public int SizeLimit { get; set; } = 1000;\n    }\n\n\n    internal class MemCache\n    {\n        private readonly MemCacheConfig _config;\n        private ConcurrentDictionary<string, (object value, DateTimeOffset expiry)> _cacheDictionary;\n        private ConcurrentQueue<(string key, DateTimeOffset expiry)> _cacheEvictionQueue;\n\n        public MemCache(MemCacheConfig config)\n        {\n            if(config == null)\n            {\n                config = new MemCacheConfig();\n            }\n            _config = config;\n            _cacheDictionary = new ConcurrentDictionary<string, (object value, DateTimeOffset expiry)>();\n            _cacheEvictionQueue = new ConcurrentQueue<(string key, DateTimeOffset expiry)>();\n        }\n\n        public bool TryGetValue<T>(string key,out T value)\n        {\n            value = default;\n            if (_cacheDictionary.TryGetValue(key, out var cacheItem))\n            {\n                if(cacheItem.expiry < DateTimeOffset.UtcNow)\n                {\n                    _cacheDictionary.TryRemove(key, out _);\n                    return false;\n                }\n                else\n                {\n                    value = (T)cacheItem.value;\n                    return true;\n                }   \n            }\n            return false;\n           \n        }\n\n\n        public T Get<T>(string key)\n        {\n            TryGetValue<T>(key, out var value);\n            return value;\n        }\n\n\n        /// <summary>\n        /// Returns all known keys. May return keys for expired data as well\n        /// </summary>\n        /// <returns></returns>\n        public IEnumerable<string> GetKeys()\n        {\n            return _cacheDictionary.Keys;\n        }\n\n        public T GetOrCreate<T>(string key, Func<T> createFn, DateTimeOffset? expiry = null)\n        {\n            if(!TryGetValue<T>(key,out var value))\n            {\n                value = createFn();\n                return Set<T>(key,value,expiry);\n            }\n            return value;\n        }\n\n        public T Set<T>(string key, T value, DateTimeOffset? expiry = null)\n        {\n            var fixedExpiry = expiry ?? DateTimeOffset.MaxValue;\n\n            while (_cacheDictionary.Count > _config.SizeLimit)\n            {\n                if (_cacheEvictionQueue.IsEmpty)\n                {\n                    _cacheDictionary.Clear();\n                }\n                if(_cacheEvictionQueue.TryDequeue(out var result)\n                    && _cacheDictionary.TryGetValue(result.key,out var dictionaryValue)\n                    &&  dictionaryValue.expiry == result.expiry)\n                {   \n                    _cacheDictionary.TryRemove(result.key, out _);\n                }\n                \n            }\n\n            _cacheDictionary.AddOrUpdate(key, (value, fixedExpiry), (k, v) => (value, fixedExpiry));\n            _cacheEvictionQueue.Enqueue((key, fixedExpiry));\n            return value;\n        }\n\n        public void Remove(string key)\n        {\n            _cacheDictionary.TryRemove(key, out _);\n        }\n\n        public void Clear()\n        {\n            _cacheDictionary.Clear();\n            _cacheEvictionQueue =  new ConcurrentQueue<(string key, DateTimeOffset expiry)>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/HelperFunctions/Utils.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Dynamic;\nusing System.Linq;\nusing System.Linq.Dynamic.Core;\n\nnamespace RulesEngine.HelperFunctions\n{\n    public static class Utils\n    {\n        public static object GetTypedObject(dynamic input)\n        {\n            if (input is ExpandoObject)\n            {\n                Type type = CreateAbstractClassType(input);\n                return CreateObject(type, input);\n            }\n            else\n            {\n                return input;\n            }\n        }\n        public static Type CreateAbstractClassType(dynamic input)\n        {\n            List<DynamicProperty> props = [];\n\n            if (input is System.Text.Json.JsonElement jsonElement)\n            {\n                if (jsonElement.ValueKind == System.Text.Json.JsonValueKind.Null)\n                {\n                    return typeof(object);\n                }\n            }\n            else if (input == null)\n            {\n                return typeof(object);\n            }\n\n            if (input is not ExpandoObject expandoObject)\n            {\n                return input.GetType();\n            }\n\n            foreach (var expando in expandoObject)\n            {\n                Type value;\n                if (expando.Value is IList list)\n                {\n                    if (list.Count == 0)\n                    {\n                        value = typeof(List<object>);\n                    }\n                    else\n                    {\n                        var internalType = CreateAbstractClassType(list[0]);\n                        value = new List<object>().Cast(internalType).ToList(internalType).GetType();\n                    }\n\n                }\n                else\n                {\n                    value = CreateAbstractClassType(expando.Value);\n                }\n                props.Add(new DynamicProperty(expando.Key, value));\n            }\n\n            var type = DynamicClassFactory.CreateType(props);\n            return type;\n        }\n\n        public static object CreateObject(Type type, dynamic input)\n        {\n            if (input is not ExpandoObject expandoObject)\n            {\n                return Convert.ChangeType(input, type);\n            }\n            var obj = Activator.CreateInstance(type);\n\n            var typeProps = type.GetProperties().ToDictionary(c => c.Name);\n\n            foreach (var expando in expandoObject)\n            {\n                if (typeProps.ContainsKey(expando.Key) &&\n                    expando.Value != null && (expando.Value.GetType().Name != \"DBNull\" || expando.Value != DBNull.Value))\n                {\n                    object val;\n                    var propInfo = typeProps[expando.Key];\n                    if (expando.Value is ExpandoObject)\n                    {\n                        var propType = propInfo.PropertyType;\n                        val = CreateObject(propType, expando.Value);\n                    }\n                    else if (expando.Value is IList temp)\n                    {\n                        var internalType = propInfo.PropertyType.GenericTypeArguments.FirstOrDefault() ?? typeof(object);\n                        var newList = new List<object>().Cast(internalType).ToList(internalType);\n                        foreach (var t in temp)\n                        {\n                            var child = CreateObject(internalType, t);\n                            newList.Add(child);\n                        };\n                        val = newList;\n                    }\n                    else\n                    {\n                        val = expando.Value;\n                    }\n                    propInfo.SetValue(obj, val, null);\n                }\n            }\n\n            return obj;\n        }\n\n        private static IEnumerable Cast(this IEnumerable self, Type innerType)\n        {\n            var methodInfo = typeof(Enumerable).GetMethod(\"Cast\");\n            var genericMethod = methodInfo.MakeGenericMethod(innerType);\n            return genericMethod.Invoke(null, new[] { self }) as IEnumerable;\n        }\n\n        private static IList ToList(this IEnumerable self, Type innerType)\n        {\n            var methodInfo = typeof(Enumerable).GetMethod(\"ToList\");\n            var genericMethod = methodInfo.MakeGenericMethod(innerType);\n            return genericMethod.Invoke(null, new[] { self }) as IList;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/RulesEngine/Interfaces/IRulesEngine.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace RulesEngine.Interfaces\n{\n    public interface IRulesEngine\n    {\n        /// <summary>\n        /// This will execute all the rules of the specified workflow\n        /// </summary>\n        /// <param name=\"workflowName\">The name of the workflow with rules to execute against the inputs</param>\n        /// <param name=\"inputs\">A variable number of inputs</param>\n        /// <returns>List of rule results</returns>\n        ValueTask<List<RuleResultTree>> ExecuteAllRulesAsync(string workflowName, params object[] inputs);\n\n        /// <summary>\n        /// This will execute all the rules of the specified workflow\n        /// </summary>\n        /// <param name=\"workflowName\">The name of the workflow with rules to execute against the inputs</param>\n        /// <param name=\"ruleParams\">A variable number of rule parameters</param>\n        /// <returns>List of rule results</returns>\n        ValueTask<List<RuleResultTree>> ExecuteAllRulesAsync(string workflowName, params RuleParameter[] ruleParams);\n        ValueTask<ActionRuleResult> ExecuteActionWorkflowAsync(string workflowName, string ruleName, RuleParameter[] ruleParameters);\n\n        /// <summary>\n        /// Adds new workflows to RulesEngine\n        /// </summary>\n        /// <param name=\"workflow\"></param>\n        void AddWorkflow(params Workflow[] Workflows);\n\n        /// <summary>\n        /// Removes all registered workflows from RulesEngine\n        /// </summary>\n        void ClearWorkflows();\n\n        /// <summary>\n        /// Removes the workflow from RulesEngine\n        /// </summary>\n        /// <param name=\"workflowNames\"></param>\n        void RemoveWorkflow(params string[] workflowNames);\r\n\r\n        /// <summary>\n        /// Checks is workflow exist.\n        /// </summary>\n        /// <param name=\"workflowName\">The workflow name.</param>\n        /// <returns> <c>true</c> if contains the specified workflow name; otherwise, <c>false</c>.</returns>\n        bool ContainsWorkflow(string workflowName);\n\n        /// <summary>\n        /// Returns the list of all registered workflow names\n        /// </summary>\n        /// <returns></returns>\n        List<string> GetAllRegisteredWorkflowNames();\n        void AddOrUpdateWorkflow(params Workflow[] Workflows);\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/ActionInfo.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    [ExcludeFromCodeCoverage]\n    public class ActionInfo\n    {\n        public string Name { get; set; }\n        public Dictionary<string, object> Context { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/ActionResult.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    [ExcludeFromCodeCoverage]\n    public class ActionResult\n    {\n        public object Output { get; set; }\n        public Exception Exception { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/ActionRuleResult.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    [ExcludeFromCodeCoverage]\n    public class ActionRuleResult : ActionResult\n    {\n        public List<RuleResultTree> Results { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/ReSettings.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Actions;\nusing RulesEngine.HelperFunctions;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    [ExcludeFromCodeCoverage]\n    public class ReSettings\n    {\n        public ReSettings() { }\n\n        // create a copy of settings\n        internal ReSettings(ReSettings reSettings)\n        {\n            CustomTypes = reSettings.CustomTypes;\n            CustomActions = reSettings.CustomActions;\n            EnableExceptionAsErrorMessage = reSettings.EnableExceptionAsErrorMessage;\n            IgnoreException = reSettings.IgnoreException;\n            EnableFormattedErrorMessage = reSettings.EnableFormattedErrorMessage;\n            EnableScopedParams = reSettings.EnableScopedParams;\n            NestedRuleExecutionMode = reSettings.NestedRuleExecutionMode;\n            CacheConfig = reSettings.CacheConfig;\n            IsExpressionCaseSensitive = reSettings.IsExpressionCaseSensitive;\n            AutoRegisterInputType = reSettings.AutoRegisterInputType;\n            UseFastExpressionCompiler = reSettings.UseFastExpressionCompiler;\n            EnableExceptionAsErrorMessageForRuleExpressionParsing = reSettings.EnableExceptionAsErrorMessageForRuleExpressionParsing;\n        }\n\n\n        /// <summary>\n        /// Get/Set the custom types to be used in Rule expressions\n        /// </summary>\n        public Type[] CustomTypes { get; set; }\n\n        /// <summary>\n        /// Get/Set the custom actions that can be used in the Rules\n        /// </summary>\n        public Dictionary<string, Func<ActionBase>> CustomActions { get; set; }\n\n        /// <summary>\n        /// When set to true, returns any exception occurred \n        /// while rule execution as ErrorMessage \n        /// otherwise throws an exception\n        /// </summary>\n        /// <remarks>This setting is only applicable if IgnoreException is set to false</remarks>\n        public bool EnableExceptionAsErrorMessage { get; set; } = true;\n\n        /// <summary>\n        /// When set to true, it will ignore any exception thrown with rule compilation/execution\n        /// </summary>\n        public bool IgnoreException { get; set; } = false;\n\n        /// <summary>\n        /// Enables ErrorMessage Formatting \n        /// </summary>\n        public bool EnableFormattedErrorMessage { get; set; } = true;\n\n        /// <summary>\n        /// Enables Global params and local params for rules\n        /// </summary>\n        public bool EnableScopedParams { get; set; } = true;\n\n        /// <summary>\n        /// Sets whether expression are case sensitive\n        /// </summary>\n        public bool IsExpressionCaseSensitive { get; set; } = false;\n\n        /// <summary>\n        /// Auto Registers input type in Custom Type to allow calling method on type.\n        /// Default : true\n        /// </summary>\n        public bool AutoRegisterInputType { get; set; } = true;\n\n        /// <summary>\n        /// Sets the mode for Nested rule execution, Default: All\n        /// </summary>\n        public NestedRuleExecutionMode NestedRuleExecutionMode { get; set; } = NestedRuleExecutionMode.All;\n        public MemCacheConfig CacheConfig { get; set; }\n        /// <summary>\n        /// Whether to use FastExpressionCompiler for rule compilation\n        /// </summary>\n        public bool UseFastExpressionCompiler { get; set; } = false;\n        /// <summary>\n        /// Sets the mode for ParsingException to cascade to child elements and result in a expression parser\n        /// Default: true\n        /// </summary>\n        public bool EnableExceptionAsErrorMessageForRuleExpressionParsing { get; set; } = true;\n    }\n\n    public enum NestedRuleExecutionMode\n    {\n        /// <summary>\n        /// Executes all nested rules\n        /// </summary>\n        All,\n        /// <summary>\n        /// Skips nested rules whose execution does not impact parent rule's result\n        /// </summary>\n        Performance\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/Rule.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    using System.Text.Json.Serialization;\n\n    /// <summary>\n    /// Rule class\n    /// </summary>\n    [ExcludeFromCodeCoverage]\n    public class Rule\n    {\n        /// <summary>\n        /// Rule name for the Rule\n        /// </summary>\n        public string RuleName { get; set; }\n        /// <summary>\t\n        /// Gets or sets the custom property or tags of the rule.\t\n        /// </summary>\t\n        /// <value>\t\n        /// The properties of the rule.\t\n        /// </value>\t\n        public Dictionary<string, object> Properties { get; set; }\n        public string Operator { get; set; }\n        public string ErrorMessage { get; set; }\n\n        /// <summary>\n        /// Gets or sets whether the rule is enabled.\n        /// </summary>\n        public bool Enabled { get; set; } = true;\n\n        [JsonConverter (typeof(JsonStringEnumConverter))]\n        public RuleExpressionType RuleExpressionType { get; set; } = RuleExpressionType.LambdaExpression;\n        public IEnumerable<string> WorkflowsToInject { get; set; }\n        public IEnumerable<Rule> Rules { get; set; }\n        public IEnumerable<ScopedParam> LocalParams { get; set; }\n        public string Expression { get; set; }\n        public RuleActions Actions { get; set; }\n        public string SuccessEvent { get; set; }\n\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/RuleActions.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    [Obsolete(\"RuleAction class is deprecated. Use RuleActions class instead.\")]\n    [ExcludeFromCodeCoverage]\n    public class RuleAction : RuleActions\n    {\n    }\n  \n    [ExcludeFromCodeCoverage]\n    public class RuleActions\n    {\n        public ActionInfo OnSuccess { get; set; }\n        public ActionInfo OnFailure { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/RuleDelegate.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nnamespace RulesEngine.Models\n{\n    public delegate T RuleFunc<T>(params RuleParameter[] ruleParameters);\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/RuleErrorType.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nnamespace RulesEngine.Models\n{\n    /// <summary>\n    /// This is error type of rules which will use in rule config files\n    /// </summary>\n    public enum ErrorType\n    {\n        Warning = 0,\n        Error = 1,\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/RuleExpressionParameter.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\nnamespace RulesEngine.Models\n{\n    /// <summary>\n    /// CompiledParam class.\n    /// </summary>\n    [ExcludeFromCodeCoverage]\n    public class RuleExpressionParameter\n    {\n        public ParameterExpression ParameterExpression { get; set; }\n        \n        public Expression ValueExpression { get; set; }\n\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/RuleExpressionType.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nnamespace RulesEngine.Models\n{\n    /// <summary>\n    /// This is rule expression type which will use in rule config files \n    /// </summary>\n    public enum RuleExpressionType\n    {\n        LambdaExpression = 0\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/RuleParameter.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.HelperFunctions;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq.Expressions;\n\nnamespace RulesEngine.Models\n{\n    [ExcludeFromCodeCoverage]\n    public class RuleParameter\n    {\n        public RuleParameter(string name, object value)\n        {\n            Value = Utils.GetTypedObject(value);\n            Init(name, Value?.GetType());\n        }\n\n       \n        internal RuleParameter(string name, Type type,object value = null)\n        {\n            Value = Utils.GetTypedObject(value);\n            Init(name, type);\n        }\n\n        public Type Type { get; private set; }\n        public string Name { get; private set; }\n        public object Value { get; private set; }\n        public ParameterExpression ParameterExpression { get; private set; }\n\n        private void Init(string name, Type type)\n        {\n            Name = name;\n            Type = type ?? typeof(object);\n            ParameterExpression = Expression.Parameter(Type, Name);\n        }\n\n        public static RuleParameter Create(string name, Type type)\n        {\n            return new RuleParameter(name, type);\n        }\n      \n        public static RuleParameter Create<T>(string name, T value)\n        {\n            var typedValue = Utils.GetTypedObject(value);\n            var type = typedValue?.GetType() ?? typeof(T);\n            return new RuleParameter(name,type,value);\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/RuleResultTree.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.HelperFunctions;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    /// <summary>\n    /// Rule result class with child result heirarchy\n    /// </summary>\n    [ExcludeFromCodeCoverage]\n    public class RuleResultTree\n    {\n        /// <summary>\n        /// Gets or sets the rule.\n        /// </summary>\n        /// <value>\n        /// The rule.\n        /// </value>\n        public Rule Rule { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether this instance is success.\n        /// </summary>\n        /// <value>\n        ///   <c>true</c> if this instance is success; otherwise, <c>false</c>.\n        /// </value>\n        public bool IsSuccess { get; set; }\n\n        /// <summary>\n        /// Gets or sets the child result.\n        /// </summary>\n        /// <value>\n        /// The child result.\n        /// </value>\n        public IEnumerable<RuleResultTree> ChildResults { get; set; }\n\n        /// <summary>\n        /// Gets or sets the input object\n        /// </summary>\n        public Dictionary<string, object> Inputs { get; set; }\n\n        public ActionResult ActionResult { get; set; }\n\n        /// <summary>\n        /// Gets the exception message in case an error is thrown during rules calculation.\n        /// </summary>\n        public string ExceptionMessage { get; set; }\n\n    }\n\n    /// <summary>\n    /// This class will hold the error messages\n    /// </summary>\n    [ExcludeFromCodeCoverage]\n    public class RuleResultMessage\n    {\n        /// <summary>\n        /// Constructor will initialize the List \n        /// </summary>\n        public RuleResultMessage()\n        {\n            ErrorMessages = new List<string>();\n            WarningMessages = new List<string>();\n        }\n\n        /// <summary>\n        /// This will hold the list of error messages\n        /// </summary>\n        public List<string> ErrorMessages { get; set; }\n\n        /// <summary>\n        /// This will hold the list of warning messages\n        /// </summary>\n        public List<string> WarningMessages { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/ScopedParam.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    /// <summary>Class LocalParam.\n    /// </summary>\n    [ExcludeFromCodeCoverage]\n    public class ScopedParam\n    {\n\n        /// <summary>\n        /// Gets or sets the name of the param.\n        /// </summary>\n        /// <value>\n        /// The name of the rule.\n        /// </value>]\n        public string Name { get; set; }\n\n        /// <summary>\n        /// Gets or Sets the lambda expression which can be reference in Rule. \n        /// </summary>\n        public string Expression { get; set; }\n    }\n\n    [ExcludeFromCodeCoverage]\n    public class LocalParam : ScopedParam { }\n}\n"
  },
  {
    "path": "src/RulesEngine/Models/Workflow.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.Models\n{\n    [Obsolete(\"WorkflowRules class is deprecated. Use Workflow class instead.\")]\n    [ExcludeFromCodeCoverage]\n    public class WorkflowRules : Workflow {\n    }\n\n    /// <summary>\n    /// Workflow rules class for deserialization  the json config file\n    /// </summary>\n    [ExcludeFromCodeCoverage]\n    public class Workflow\n    {\n        /// <summary>\n        /// Gets the workflow name.\n        /// </summary>\n        public string WorkflowName { get; set; }\n\n        /// <summary>Gets or sets the workflow rules to inject.</summary>\n        /// <value>The workflow rules to inject.</value>\n        [Obsolete(\"WorkflowRulesToInject is deprecated. Use WorkflowsToInject instead.\")]\n        public IEnumerable<string> WorkflowRulesToInject {\n          set { WorkflowsToInject = value; }\n        }\n        public IEnumerable<string> WorkflowsToInject { get; set; }\n\n        public RuleExpressionType RuleExpressionType { get; set; } = RuleExpressionType.LambdaExpression;\n\n        /// <summary>\n        /// Gets or Sets the global params which will be applicable to all rules\n        /// </summary>\n        public IEnumerable<ScopedParam> GlobalParams { get; set; }\n\n        /// <summary>\n        /// list of rules.\n        /// </summary>\n        public IEnumerable<Rule> Rules { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Properties/AssemblyInfo.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n[assembly: InternalsVisibleTo(\"RulesEngine.UnitTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c15956b2ac0945c55b69a185f5c3e02276693b0a5e42c8a1f08cb24e03dd87d91f9fa09f79b6b7b3aac4df46f2ea4ce4bfa31920bb0aad9f02793ab29de9fbf40f5ba9e347aa8569128459f31da1f6357eabe6e1308ac7c16b87a4d61e8d1785746a57ec67956d2e2454b3c98502a5d5c4a4168133bfaa431207c108efae03aa\")]\n"
  },
  {
    "path": "src/RulesEngine/RuleCompiler.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Exceptions;\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.HelperFunctions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace RulesEngine\n{\n    /// <summary>\n    /// Rule compilers\n    /// </summary>\n    internal class RuleCompiler\n    {\n        /// <summary>\n        /// The nested operators\n        /// </summary>\n        private readonly ExpressionType[] nestedOperators = new ExpressionType[] { ExpressionType.And, ExpressionType.AndAlso, ExpressionType.Or, ExpressionType.OrElse };\n\n        /// <summary>\n        /// The expression builder factory\n        /// </summary>\n        private readonly RuleExpressionBuilderFactory _expressionBuilderFactory;\n        private readonly ReSettings _reSettings;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"RuleCompiler\"/> class.\n        /// </summary>\n        /// <param name=\"expressionBuilderFactory\">The expression builder factory.</param>\n        /// <exception cref=\"ArgumentNullException\">expressionBuilderFactory</exception>\n        internal RuleCompiler(RuleExpressionBuilderFactory expressionBuilderFactory, ReSettings reSettings)\n        {\n            _expressionBuilderFactory = expressionBuilderFactory ?? throw new ArgumentNullException($\"{nameof(expressionBuilderFactory)} can't be null.\");\n            _reSettings = reSettings;\n        }\n\n        /// <summary>\n        /// Compiles the rule\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"rule\"></param>\n        /// <param name=\"input\"></param>\n        /// <param name=\"ruleParam\"></param>\n        /// <returns>Compiled func delegate</returns>\n        internal RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleExpressionType ruleExpressionType, RuleParameter[] ruleParams, Lazy<RuleExpressionParameter[]> globalParams)\n        {\n            if (rule == null)\n            {\n                var ex =  new ArgumentNullException(nameof(rule));\n                throw ex;\n            }\n            try\n            {\n                var globalParamExp = globalParams.Value;\n                var extendedRuleParams = ruleParams.Concat(globalParamExp.Select(c => new RuleParameter(c.ParameterExpression.Name,c.ParameterExpression.Type)))\n                                                   .ToArray();\n                var ruleExpression = GetDelegateForRule(rule, extendedRuleParams);\n                \n\n                return GetWrappedRuleFunc(rule,ruleExpression,ruleParams,globalParamExp);\n            }\n            catch (Exception ex)\n            {\n                var message = $\"Error while compiling rule `{rule.RuleName}`: {ex.Message}\";\n                return Helpers.ToRuleExceptionResult(_reSettings, rule, new RuleException(message, ex));\n            }\n        }\n\n\n\n        /// <summary>\n        /// Gets the expression for rule.\n        /// </summary>\n        /// <param name=\"rule\">The rule.</param>\n        /// <param name=\"typeParameterExpressions\">The type parameter expressions.</param>\n        /// <param name=\"ruleInputExp\">The rule input exp.</param>\n        /// <returns></returns>\n        private RuleFunc<RuleResultTree> GetDelegateForRule(Rule rule, RuleParameter[] ruleParams)\n        {\n            var scopedParamList = GetRuleExpressionParameters(rule.RuleExpressionType, rule?.LocalParams, ruleParams);\n\n            var extendedRuleParams = ruleParams.Concat(scopedParamList.Select(c => new RuleParameter(c.ParameterExpression.Name, c.ParameterExpression.Type)))\n                                               .ToArray();\n\n            RuleFunc<RuleResultTree> ruleFn;\n            \n            if (Enum.TryParse(rule.Operator, out ExpressionType nestedOperator) && nestedOperators.Contains(nestedOperator) &&\n                rule.Rules != null && rule.Rules.Any())\n            {\n                ruleFn = BuildNestedRuleFunc(rule, nestedOperator, extendedRuleParams);\n            }\n            else\n            {\n                ruleFn = BuildRuleFunc(rule, extendedRuleParams);\n            }\n\n            return GetWrappedRuleFunc(rule, ruleFn, ruleParams, scopedParamList);\n        }\n\n        internal RuleExpressionParameter[] GetRuleExpressionParameters(RuleExpressionType ruleExpressionType,IEnumerable<ScopedParam> localParams, RuleParameter[] ruleParams)\n        {\n            if(!_reSettings.EnableScopedParams)\n            {\n                return new RuleExpressionParameter[] { };\n            }\n            var ruleExpParams = new List<RuleExpressionParameter>();\n\n            if (localParams?.Any() == true)\n            {\n\n                var parameters = ruleParams.Select(c => c.ParameterExpression)\n                                            .ToList();\n\n                var expressionBuilder = GetExpressionBuilder(ruleExpressionType);\n\n                foreach (var lp in localParams)\n                {\n                    try\n                    {\n                        var lpExpression = expressionBuilder.Parse(lp.Expression, parameters.ToArray(), null);\n                        var ruleExpParam = new RuleExpressionParameter() {\n                            ParameterExpression = Expression.Parameter(lpExpression.Type, lp.Name),\n                            ValueExpression = lpExpression\n                        };\n                        parameters.Add(ruleExpParam.ParameterExpression);\n                        ruleExpParams.Add(ruleExpParam);\n                    }\n                    catch(Exception ex)\n                    {\n                        var message = $\"{ex.Message}, in ScopedParam: {lp.Name}\";\n                        throw new RuleException(message);\n                    }\n                }\n            }\n            return ruleExpParams.ToArray();\n\n        }\n\n        /// <summary>\n        /// Builds the expression.\n        /// </summary>\n        /// <param name=\"rule\">The rule.</param>\n        /// <param name=\"typeParameterExpressions\">The type parameter expressions.</param>\n        /// <param name=\"ruleInputExp\">The rule input exp.</param>\n        /// <returns></returns>\n        /// <exception cref=\"InvalidOperationException\"></exception>\n        private RuleFunc<RuleResultTree> BuildRuleFunc(Rule rule, RuleParameter[] ruleParams)\n        {\n            var ruleExpressionBuilder = GetExpressionBuilder(rule.RuleExpressionType);\n\n            var ruleFunc = ruleExpressionBuilder.BuildDelegateForRule(rule, ruleParams);\n\n            return ruleFunc;\n        }\n\n        /// <summary>\n        /// Builds the nested expression.\n        /// </summary>\n        /// <param name=\"parentRule\">The parent rule.</param>\n        /// <param name=\"childRules\">The child rules.</param>\n        /// <param name=\"operation\">The operation.</param>\n        /// <param name=\"typeParameterExpressions\">The type parameter expressions.</param>\n        /// <param name=\"ruleInputExp\">The rule input exp.</param>\n        /// <returns>Expression of func delegate</returns>\n        /// <exception cref=\"InvalidCastException\"></exception>\n        private RuleFunc<RuleResultTree> BuildNestedRuleFunc(Rule parentRule, ExpressionType operation, RuleParameter[] ruleParams)\n        {\n            var ruleFuncList = new List<RuleFunc<RuleResultTree>>();\n            foreach (var r in parentRule.Rules.Where(c => c.Enabled))\n            {\n                ruleFuncList.Add(GetDelegateForRule(r, ruleParams));\n            }\n\n            return (paramArray) => {\n                var (isSuccess, resultList) = ApplyOperation(paramArray, ruleFuncList, operation);\n                bool isSuccessFn(object[] p) => isSuccess;\n                var result = Helpers.ToResultTree(_reSettings, parentRule, resultList, isSuccessFn);\n                return result(paramArray);\n            };\n        }\n\n\n        private (bool isSuccess ,IEnumerable<RuleResultTree> result) ApplyOperation(RuleParameter[] paramArray,IEnumerable<RuleFunc<RuleResultTree>> ruleFuncList, ExpressionType operation)\n        {\n            if (ruleFuncList?.Any() != true)\n            {\n                return (false,new List<RuleResultTree>());\n            }\n\n            var resultList = new List<RuleResultTree>();\n            var isSuccess = false;\n\n            if(operation == ExpressionType.And || operation == ExpressionType.AndAlso)\n            {\n                isSuccess = true;\n            }\n\n            foreach(var ruleFunc in ruleFuncList)\n            {\n                var ruleResult = ruleFunc(paramArray);\n                resultList.Add(ruleResult);\n                switch (operation)\n                {\n                    case ExpressionType.And:\n                    case ExpressionType.AndAlso:\n                        isSuccess = isSuccess && ruleResult.IsSuccess;\n                        if(_reSettings.NestedRuleExecutionMode ==  NestedRuleExecutionMode.Performance && isSuccess == false)\n                        {\n                            return (isSuccess, resultList);\n                        }\n                        break;\n\n                    case ExpressionType.Or:\n                    case ExpressionType.OrElse:\n                        isSuccess = isSuccess || ruleResult.IsSuccess;\n                        if (_reSettings.NestedRuleExecutionMode == NestedRuleExecutionMode.Performance && isSuccess == true)\n                        {\n                            return (isSuccess, resultList);\n                        }\n                        break;\n                }\n                \n            }\n            return (isSuccess, resultList);\n        }\n\n        internal Func<object[],Dictionary<string,object>> CompileScopedParams(RuleExpressionType ruleExpressionType, RuleParameter[] ruleParameters,RuleExpressionParameter[] ruleExpParams)\n        {\n            return GetExpressionBuilder(ruleExpressionType).CompileScopedParams(ruleParameters, ruleExpParams);\n\n        }\n\n        private RuleFunc<RuleResultTree> GetWrappedRuleFunc(Rule rule, RuleFunc<RuleResultTree> ruleFunc,RuleParameter[] ruleParameters,RuleExpressionParameter[] ruleExpParams)\n        {\n            if(ruleExpParams.Length == 0)\n            {\n                return ruleFunc;\n            }\n            var paramDelegate = CompileScopedParams(rule.RuleExpressionType,ruleParameters, ruleExpParams);\n\n            return (ruleParams) => {\n                var inputs = ruleParams.Select(c => c.Value).ToArray();\n                IEnumerable<RuleParameter> scopedParams;\n                try\n                {\n                    var scopedParamsDict = paramDelegate(inputs);\n                    scopedParams = scopedParamsDict.Select(c => new RuleParameter(c.Key, c.Value));\n                }\n                catch(Exception ex)\n                {\n                    var message = $\"Error while executing scoped params for rule `{rule.RuleName}` - {ex}\";\n                    var resultFn = Helpers.ToRuleExceptionResult(_reSettings, rule, new RuleException(message, ex));\n                    return resultFn(ruleParams);\n                }\n               \n                var extendedInputs = ruleParams.Concat(scopedParams);\n                var result = ruleFunc(extendedInputs.ToArray());\n                return result;\n            };\n        }\n\n        private RuleExpressionBuilderBase GetExpressionBuilder(RuleExpressionType expressionType)\n        {\n            return _expressionBuilderFactory.RuleGetExpressionBuilder(expressionType);\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/RuleExpressionBuilderFactory.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.Models;\nusing System;\n\nnamespace RulesEngine\n{\n    internal class RuleExpressionBuilderFactory\n    {\n        private readonly ReSettings _reSettings;\n        private readonly LambdaExpressionBuilder _lambdaExpressionBuilder;\n        public RuleExpressionBuilderFactory(ReSettings reSettings, RuleExpressionParser expressionParser)\n        {\n            _reSettings = reSettings;\n            _lambdaExpressionBuilder = new LambdaExpressionBuilder(_reSettings, expressionParser);\n        }\n        public RuleExpressionBuilderBase RuleGetExpressionBuilder(RuleExpressionType ruleExpressionType)\n        {\n            switch (ruleExpressionType)\n            {\n                case RuleExpressionType.LambdaExpression:\n                    return _lambdaExpressionBuilder;\n                default:\n                    throw new InvalidOperationException($\"{nameof(ruleExpressionType)} has not been supported yet.\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/RulesCache.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.HelperFunctions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace RulesEngine\n{\n    /// <summary>Class RulesCache.</summary>\n    internal class RulesCache\n    {\n        /// <summary>The compile rules</summary>\n        private readonly MemCache _compileRules;\n\n        /// <summary>The workflow rules</summary>\n        private readonly ConcurrentDictionary<string, (Workflow, long)> _workflow = new ConcurrentDictionary<string, (Workflow, long)>();\n\n\n        public RulesCache(ReSettings reSettings)\n        {\n            _compileRules = new MemCache(reSettings.CacheConfig);\n        }\n\n\n        /// <summary>Determines whether [contains workflow rules] [the specified workflow name].</summary>\n        /// <param name=\"workflowName\">Name of the workflow.</param>\n        /// <returns>\n        ///   <c>true</c> if [contains workflow rules] [the specified workflow name]; otherwise, <c>false</c>.</returns>\n        public bool ContainsWorkflows(string workflowName)\n        {\n            return _workflow.ContainsKey(workflowName);\n        }\n\n        public List<string> GetAllWorkflowNames()\n        {\n            return _workflow.Keys.ToList();\n        }\n\n        /// <summary>Adds the or update workflow rules.</summary>\n        /// <param name=\"workflowName\">Name of the workflow.</param>\n        /// <param name=\"rules\">The rules.</param>\n        public void AddOrUpdateWorkflows(string workflowName, Workflow rules)\n        {\n            long ticks = DateTime.UtcNow.Ticks;\n            _workflow.AddOrUpdate(workflowName, (rules, ticks), (k, v) => (rules, ticks));\n        }\n\n        /// <summary>Adds the or update compiled rule.</summary>\n        /// <param name=\"compiledRuleKey\">The compiled rule key.</param>\n        /// <param name=\"compiledRule\">The compiled rule.</param>\n        public void AddOrUpdateCompiledRule(string compiledRuleKey, IDictionary<string, RuleFunc<RuleResultTree>> compiledRule)\n        {\n            long ticks = DateTime.UtcNow.Ticks;\n            _compileRules.Set(compiledRuleKey,(compiledRule, ticks));\n        }\n\n        /// <summary>Checks if the compiled rules are up-to-date.</summary>\n        /// <param name=\"compiledRuleKey\">The compiled rule key.</param>\n        /// <param name=\"workflowName\">The workflow name.</param>\n         /// <returns>\n        ///   <c>true</c> if [compiled rules] is newer than the [workflow rules]; otherwise, <c>false</c>.</returns>\n        public bool AreCompiledRulesUpToDate(string compiledRuleKey, string workflowName)\n        {\n            if (_compileRules.TryGetValue(compiledRuleKey, out (IDictionary<string, RuleFunc<RuleResultTree>> rules, long tick) compiledRulesObj))\n            {\n                if (_workflow.TryGetValue(workflowName, out (Workflow rules, long tick) WorkflowsObj))\n                {\n                    return compiledRulesObj.tick >= WorkflowsObj.tick;\n                }\n            }\n\n            return false;\n        }\n\n        /// <summary>Clears this instance.</summary>\n        public void Clear()\n        {\n            _workflow.Clear();\n            _compileRules.Clear();\n        }\n\n        /// <summary>Gets the work flow rules.</summary>\n        /// <param name=\"workflowName\">Name of the workflow.</param>\n        /// <returns>Workflows.</returns>\n        /// <exception cref=\"Exception\">Could not find injected Workflow: {wfname}</exception>\n        public Workflow GetWorkflow(string workflowName)\n        {\n            if (_workflow.TryGetValue(workflowName, out (Workflow rules, long tick) WorkflowsObj))\n            {\n                var workflow = WorkflowsObj.rules;\n                if (workflow.WorkflowsToInject?.Any() == true)\n                {\n                    if (workflow.Rules == null)\n                    {\n                        workflow.Rules = new List<Rule>();\n                    }\n                    foreach (string wfname in workflow.WorkflowsToInject)\n                    {\n                        var injectedWorkflow = GetWorkflow(wfname);\n                        if (injectedWorkflow == null)\n                        {\n                            throw new Exception($\"Could not find injected Workflow: {wfname}\");\n                        }\n\n                        workflow.Rules = workflow.Rules.Concat(injectedWorkflow.Rules).ToList();\n                    }\n                }\n\n                return workflow;\n            }\n            else\n            {\n                return null;\n            }\n        }\n\n\n        /// <summary>Gets the compiled rules.</summary>\n        /// <param name=\"compiledRulesKey\">The compiled rules key.</param>\n        /// <returns>CompiledRule.</returns>\n        public IDictionary<string, RuleFunc<RuleResultTree>> GetCompiledRules(string compiledRulesKey)\n        {\n            return _compileRules.Get<(IDictionary<string, RuleFunc<RuleResultTree>> rules, long tick)>(compiledRulesKey).rules;\n        }\n\n        /// <summary>Removes the specified workflow name.</summary>\n        /// <param name=\"workflowName\">Name of the workflow.</param>\n        public void Remove(string workflowName)\n        {\n            if (_workflow.TryRemove(workflowName, out var workflowObj))\n            {\n                var compiledKeysToRemove = _compileRules.GetKeys().Where(key => key.StartsWith(workflowName));\n                foreach (var key in compiledKeysToRemove)\n                {\n                    _compileRules.Remove(key);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/RulesEngine.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT License.\r\n\r\nusing FluentValidation;\r\nusing RulesEngine.Actions;\r\nusing RulesEngine.Exceptions;\r\nusing RulesEngine.ExpressionBuilders;\r\nusing RulesEngine.Extensions;\r\nusing RulesEngine.HelperFunctions;\r\nusing RulesEngine.Interfaces;\r\nusing RulesEngine.Models;\r\nusing RulesEngine.Validators;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text.RegularExpressions;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace RulesEngine\r\n{\r\n    using System.Text.Json;\r\n    using System.Text.Json.Nodes;\r\n\r\n    /// <summary>\r\n    /// \r\n    /// </summary>\r\n    /// <seealso cref=\"IRulesEngine\" />\r\n    public class RulesEngine : IRulesEngine\r\n    {\r\n        #region Variables\r\n        private readonly ReSettings _reSettings;\r\n        private readonly RulesCache _rulesCache;\r\n        private readonly RuleExpressionParser _ruleExpressionParser;\r\n        private readonly RuleCompiler _ruleCompiler;\r\n        private readonly ActionFactory _actionFactory;\r\n        private const string ParamParseRegex = \"(\\\\$\\\\(.*?\\\\))\";\r\n        #endregion\r\n\r\n        #region Constructor\r\n        public RulesEngine(string[] jsonConfig, ReSettings reSettings = null) : this(reSettings)\r\n        {\r\n            var workflow = jsonConfig.Select(item => JsonSerializer.Deserialize<Workflow>(item)).ToArray();\r\n            AddWorkflow(workflow);\r\n        }\r\n\r\n        public RulesEngine(Workflow[] Workflows, ReSettings reSettings = null) : this(reSettings)\r\n        {\r\n            AddWorkflow(Workflows);\r\n        }\r\n\r\n        public RulesEngine(ReSettings reSettings = null)\r\n        {\r\n            _reSettings = reSettings == null ? new ReSettings(): new ReSettings(reSettings);\r\n            if(_reSettings.CacheConfig == null)\r\n            {\r\n                _reSettings.CacheConfig = new MemCacheConfig();         \r\n            }\r\n            _rulesCache = new RulesCache(_reSettings);\r\n            _ruleExpressionParser = new RuleExpressionParser(_reSettings);\r\n            _ruleCompiler = new RuleCompiler(new RuleExpressionBuilderFactory(_reSettings, _ruleExpressionParser),_reSettings);\r\n            _actionFactory = new ActionFactory(GetActionRegistry(_reSettings));\r\n        }\r\n\r\n        private IDictionary<string, Func<ActionBase>> GetActionRegistry(ReSettings reSettings)\r\n        {\r\n            var actionDictionary = GetDefaultActionRegistry();\r\n            var customActions = reSettings.CustomActions ?? new Dictionary<string, Func<ActionBase>>();\r\n            foreach (var customAction in customActions)\r\n            {\r\n                actionDictionary.Add(customAction);\r\n            }\r\n            return actionDictionary;\r\n\r\n        }\r\n        #endregion\r\n\r\n        #region Public Methods\r\n\r\n        /// <summary>\r\n        /// This will execute all the rules of the specified workflow\r\n        /// </summary>\r\n        /// <param name=\"workflowName\">The name of the workflow with rules to execute against the inputs</param>\r\n        /// <param name=\"inputs\">A variable number of inputs</param>\r\n        /// <returns>List of rule results</returns>\r\n        public async ValueTask<List<RuleResultTree>> ExecuteAllRulesAsync(string workflowName, params object[] inputs)\r\n        {\r\n            var ruleParams = new List<RuleParameter>();\r\n\r\n            for (var i = 0; i < inputs.Length; i++)\r\n            {\r\n                var input = inputs[i];\r\n                ruleParams.Add(new RuleParameter($\"input{i + 1}\", input));\r\n            }\r\n\r\n            return await ExecuteAllRulesAsync(workflowName, ruleParams.ToArray());\r\n        }\r\n\r\n        /// <summary>\r\n        /// This will execute all the rules of the specified workflow\r\n        /// </summary>\r\n        /// <param name=\"workflowName\">The name of the workflow with rules to execute against the inputs</param>\r\n        /// <param name=\"ruleParams\">A variable number of rule parameters</param>\r\n        /// <returns>List of rule results</returns>\r\n        public async ValueTask<List<RuleResultTree>> ExecuteAllRulesAsync(string workflowName, params RuleParameter[] ruleParams)\r\n        {\r\n            var sortedRuleParams = ruleParams.ToList();\r\n            sortedRuleParams.Sort((RuleParameter a, RuleParameter b) => string.Compare(a.Name, b.Name));\r\n            var ruleResultList = ValidateWorkflowAndExecuteRule(workflowName, sortedRuleParams.ToArray());\r\n            await ExecuteActionAsync(ruleResultList);\r\n            return ruleResultList;\r\n        }\r\n\r\n        private async ValueTask ExecuteActionAsync(IEnumerable<RuleResultTree> ruleResultList)\r\n        {\r\n            foreach (var ruleResult in ruleResultList)\r\n            {\r\n                if(ruleResult.ChildResults !=  null)\r\n                {\r\n                    await ExecuteActionAsync(ruleResult.ChildResults);\r\n                }\r\n                var actionResult = await ExecuteActionForRuleResult(ruleResult, false);\r\n                ruleResult.ActionResult = new ActionResult {\r\n                    Output = actionResult.Output,\r\n                    Exception = actionResult.Exception\r\n                };\r\n            }\r\n        }\r\n\r\n        public async ValueTask<ActionRuleResult> ExecuteActionWorkflowAsync(string workflowName, string ruleName, RuleParameter[] ruleParameters)\r\n        {\r\n            var compiledRule = CompileRule(workflowName, ruleName, ruleParameters);\r\n            var resultTree = compiledRule(ruleParameters);\r\n            return await ExecuteActionForRuleResult(resultTree, true);\r\n        }\r\n\r\n        private async ValueTask<ActionRuleResult> ExecuteActionForRuleResult(RuleResultTree resultTree, bool includeRuleResults = false)\r\n        {\r\n            var ruleActions = resultTree?.Rule?.Actions;\r\n            var actionInfo = resultTree?.IsSuccess == true ? ruleActions?.OnSuccess : ruleActions?.OnFailure;\r\n\r\n            if (actionInfo != null)\r\n            {\r\n                var action = _actionFactory.Get(actionInfo.Name);\r\n                var ruleParameters = resultTree.Inputs.Select(kv => new RuleParameter(kv.Key, kv.Value)).ToArray();\r\n                return await action.ExecuteAndReturnResultAsync(new ActionContext(actionInfo.Context, resultTree), ruleParameters, includeRuleResults);\r\n            }\r\n            else\r\n            {\r\n                //If there is no action,return output as null and return the result for rule\r\n                return new ActionRuleResult {\r\n                    Output = null,\r\n                    Results = includeRuleResults ? new List<RuleResultTree>() { resultTree } : null\r\n                };\r\n            }\r\n        }\r\n\r\n        #endregion\r\n\r\n        #region Private Methods\r\n\r\n        /// <summary>\r\n        /// Adds the workflow if the workflow name is not already added. Ignores the rest.\r\n        /// </summary>\r\n        /// <param name=\"workflows\">The workflow rules.</param>\r\n        /// <exception cref=\"RuleValidationException\"></exception>\r\n        public void AddWorkflow(params Workflow[] workflows)\r\n        {\r\n            try\r\n            {\r\n                foreach (var workflow in workflows)\r\n                {                    \r\n                    var validator = new WorkflowsValidator();\r\n                    validator.ValidateAndThrow(workflow);\r\n                    if (!_rulesCache.ContainsWorkflows(workflow.WorkflowName))\r\n                    {\r\n                        _rulesCache.AddOrUpdateWorkflows(workflow.WorkflowName, workflow);\r\n                    }\r\n                    else\r\n                    {\r\n                        throw new ValidationException($\"Cannot add workflow `{workflow.WorkflowName}` as it already exists. Use `AddOrUpdateWorkflow` to update existing workflow\");\r\n                    }\r\n                }\r\n            }\r\n            catch (ValidationException ex)\r\n            {\r\n                throw new RuleValidationException(ex.Message, ex.Errors);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Adds new workflow rules if not previously added.\r\n        /// Or updates the rules for an existing workflow.\r\n        /// </summary>\r\n        /// <param name=\"workflows\">The workflow rules.</param>\r\n        /// <exception cref=\"RuleValidationException\"></exception>\r\n        public void AddOrUpdateWorkflow(params Workflow[] workflows)\r\n        {\r\n            try\r\n            {\r\n                foreach (var workflow in workflows)\r\n                {\r\n                    var validator = new WorkflowsValidator();\r\n                    validator.ValidateAndThrow(workflow);\r\n                    _rulesCache.AddOrUpdateWorkflows(workflow.WorkflowName, workflow);\r\n                }\r\n            }\r\n            catch (ValidationException ex)\r\n            {\r\n                throw new RuleValidationException(ex.Message, ex.Errors);\r\n            }\r\n        }\r\n\r\n        public List<string> GetAllRegisteredWorkflowNames()\r\n        {\r\n            return _rulesCache.GetAllWorkflowNames();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Checks is workflow exist.\r\n        /// </summary>\r\n        /// <param name=\"workflowName\">The workflow name.</param>\r\n        /// <returns> <c>true</c> if contains the specified workflow name; otherwise, <c>false</c>.</returns>\r\n        public bool ContainsWorkflow(string workflowName)\r\n        {\r\n            return _rulesCache.ContainsWorkflows(workflowName);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Clears the workflow.\r\n        /// </summary>\r\n        public void ClearWorkflows()\r\n        {\r\n            _rulesCache.Clear();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Removes the workflows.\r\n        /// </summary>\r\n        /// <param name=\"workflowNames\">The workflow names.</param>\r\n        public void RemoveWorkflow(params string[] workflowNames)\r\n        {\r\n            foreach (var workflowName in workflowNames)\r\n            {\r\n                _rulesCache.Remove(workflowName);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// This will validate workflow rules then call execute method\r\n        /// </summary>\r\n        /// <typeparam name=\"T\">type of entity</typeparam>\r\n        /// <param name=\"input\">input</param>\r\n        /// <param name=\"workflowName\">workflow name</param>\r\n        /// <returns>list of rule result set</returns>\r\n        private List<RuleResultTree> ValidateWorkflowAndExecuteRule(string workflowName, RuleParameter[] ruleParams)\r\n        {\r\n            List<RuleResultTree> result;\r\n\r\n            if (RegisterRule(workflowName, ruleParams))\r\n            {\r\n                result = ExecuteAllRuleByWorkflow(workflowName, ruleParams);\r\n            }\r\n            else\r\n            {\r\n                // if rules are not registered with Rules Engine\r\n                throw new ArgumentException($\"Rule config file is not present for the {workflowName} workflow\");\r\n            }\r\n            return result;\r\n        }\r\n\r\n        /// <summary>\r\n        /// This will compile the rules and store them to dictionary\r\n        /// </summary>\r\n        /// <param name=\"workflowName\">workflow name</param>\r\n        /// <param name=\"ruleParams\">The rule parameters.</param>\r\n        /// <returns>\r\n        /// bool result\r\n        /// </returns>\r\n        private bool RegisterRule(string workflowName, params RuleParameter[] ruleParams)\r\n        {\r\n            var compileRulesKey = GetCompiledRulesKey(workflowName, ruleParams);\r\n            if (_rulesCache.AreCompiledRulesUpToDate(compileRulesKey, workflowName))\r\n            {\r\n                return true;\r\n            }\r\n\r\n            var workflow = _rulesCache.GetWorkflow(workflowName);\r\n            if (workflow != null)\r\n            {\r\n                var dictFunc = new Dictionary<string, RuleFunc<RuleResultTree>>();\r\n                if (_reSettings.AutoRegisterInputType)\r\n                {\r\n                    var collector = new HashSet<Type>(_reSettings.CustomTypes.Safe());\r\n\r\n                    foreach (var rp in ruleParams)\r\n                    {\r\n                        CollectAllElementTypes(rp.Type, collector);\r\n                    }\r\n\r\n                    _reSettings.CustomTypes = collector.ToArray();\r\n                }\r\n\r\n                // add separate compilation for global params\r\n\r\n                var globalParamExp = new Lazy<RuleExpressionParameter[]>(\r\n                    () => _ruleCompiler.GetRuleExpressionParameters(workflow.RuleExpressionType, workflow.GlobalParams, ruleParams)\r\n                );\r\n\r\n                foreach (var rule in workflow.Rules.Where(c => c.Enabled))\r\n                {\r\n                    dictFunc.Add(rule.RuleName, CompileRule(rule,workflow.RuleExpressionType, ruleParams, globalParamExp));\r\n                }\r\n\r\n                _rulesCache.AddOrUpdateCompiledRule(compileRulesKey, dictFunc);\r\n                return true;\r\n            }\r\n            else\r\n            {\r\n                return false;\r\n            }\r\n        }\r\n\r\n\r\n        private RuleFunc<RuleResultTree> CompileRule(string workflowName, string ruleName, RuleParameter[] ruleParameters)\r\n        {\r\n            var workflow = _rulesCache.GetWorkflow(workflowName);\r\n            if(workflow == null)\r\n            {\r\n                throw new ArgumentException($\"Workflow `{workflowName}` is not found\");\r\n            }\r\n            var currentRule = workflow.Rules?.SingleOrDefault(c => c.RuleName == ruleName && c.Enabled);\r\n            if (currentRule == null)\r\n            {\r\n                throw new ArgumentException($\"Workflow `{workflowName}` does not contain any rule named `{ruleName}`\");\r\n            }\r\n            var globalParamExp = new Lazy<RuleExpressionParameter[]>(\r\n                  () => _ruleCompiler.GetRuleExpressionParameters(workflow.RuleExpressionType, workflow.GlobalParams, ruleParameters)\r\n              );\r\n            return CompileRule(currentRule,workflow.RuleExpressionType, ruleParameters, globalParamExp);\r\n        }\r\n\r\n        private RuleFunc<RuleResultTree> CompileRule(Rule rule, RuleExpressionType ruleExpressionType, RuleParameter[] ruleParams, Lazy<RuleExpressionParameter[]> scopedParams)\r\n        {\r\n            return _ruleCompiler.CompileRule(rule, ruleExpressionType, ruleParams, scopedParams);\r\n        }\r\n\r\n        private static void CollectAllElementTypes(Type t, ISet<Type> collector)\r\n        {\r\n            if (t == null || collector.Contains(t))\r\n                return;\r\n\r\n            collector.Add(t);\r\n\r\n            if (t.IsGenericType)\r\n            {\r\n                foreach (var ga in t.GetGenericArguments())\r\n                    CollectAllElementTypes(ga, collector);\r\n            }\r\n\r\n            if (t.IsArray)\r\n            {\r\n                CollectAllElementTypes(t.GetElementType(), collector);\r\n            }\r\n\r\n            if (Nullable.GetUnderlyingType(t) is Type underly && !collector.Contains(underly))\r\n            {\r\n                CollectAllElementTypes(underly, collector);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// This will execute the compiled rules \r\n        /// </summary>\r\n        /// <param name=\"workflowName\"></param>\r\n        /// <param name=\"ruleParams\"></param>\r\n        /// <returns>list of rule result set</returns>\r\n        private List<RuleResultTree> ExecuteAllRuleByWorkflow(string workflowName, RuleParameter[] ruleParameters)\r\n        {\r\n            var result = new List<RuleResultTree>();\r\n            var compiledRulesCacheKey = GetCompiledRulesKey(workflowName, ruleParameters);\r\n            foreach (var compiledRule in _rulesCache.GetCompiledRules(compiledRulesCacheKey)?.Values)\r\n            {\r\n                var resultTree = compiledRule(ruleParameters);\r\n                result.Add(resultTree);\r\n            }\r\n\r\n            FormatErrorMessages(result);\r\n            return result;\r\n        }\r\n\r\n        private string GetCompiledRulesKey(string workflowName, RuleParameter[] ruleParams)\r\n        {\r\n            var ruleParamsKey = string.Join(\"-\", ruleParams.Select(c => $\"{c.Name}_{c.Type.Name}\"));\r\n            var key = $\"{workflowName}-\" + ruleParamsKey;\r\n            return key;\r\n        }\r\n\r\n        private IDictionary<string, Func<ActionBase>> GetDefaultActionRegistry()\r\n        {\r\n            return new Dictionary<string, Func<ActionBase>>{\r\n                {\"OutputExpression\",() => new OutputExpressionAction(_ruleExpressionParser) },\r\n                {\"EvaluateRule\", () => new EvaluateRuleAction(this,_ruleExpressionParser) }\r\n            };\r\n        }\r\n\r\n        /// <summary>\r\n        /// The result\r\n        /// </summary>\r\n        /// <param name=\"ruleResultList\">The result.</param>\r\n        /// <returns>Updated error message.</returns>\r\n        private IEnumerable<RuleResultTree> FormatErrorMessages(IEnumerable<RuleResultTree> ruleResultList)\r\n        {\r\n            if (_reSettings.EnableFormattedErrorMessage)\r\n            {\r\n                foreach (var ruleResult in ruleResultList?.Where(r => !r.IsSuccess))\r\n                {\r\n                    var errorMessage = ruleResult?.Rule?.ErrorMessage;\r\n                    if (string.IsNullOrWhiteSpace(ruleResult.ExceptionMessage) && errorMessage != null)\r\n                    {\r\n                        var errorParameters = Regex.Matches(errorMessage, ParamParseRegex);\r\n\r\n                        var inputs = ruleResult.Inputs;\r\n                        foreach (var param in errorParameters)\r\n                        {\r\n                            var paramVal = param?.ToString();\r\n                            var property = paramVal?.Substring(2, paramVal.Length - 3);\r\n                            if (property?.Split('.')?.Count() > 1)\r\n                            {\r\n                                var typeName = property?.Split('.')?[0];\r\n                                var propertyName = property?.Split('.')?[1];\r\n                                errorMessage = UpdateErrorMessage(errorMessage, inputs, property, typeName, propertyName);\r\n                            }\r\n                            else\r\n                            {\r\n                                var arrParams = inputs?.Select(c => new { Name = c.Key, c.Value });\r\n                                var model = arrParams?.Where(a => string.Equals(a.Name, property))?.FirstOrDefault();\r\n                                var value = model?.Value != null ? JsonSerializer.Serialize(model?.Value) : null;\r\n                                errorMessage = errorMessage?.Replace($\"$({property})\", value ?? $\"$({property})\");\r\n                            }\r\n                        }\r\n                        ruleResult.ExceptionMessage = errorMessage;\r\n                    }\r\n\r\n                }\r\n            }\r\n            return ruleResultList;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Updates the error message.\r\n        /// </summary>\r\n        /// <param name=\"errorMessage\">The error message.</param>\r\n        /// <param name=\"evaluatedParams\">The evaluated parameters.</param>\r\n        /// <param name=\"property\">The property.</param>\r\n        /// <param name=\"typeName\">Name of the type.</param>\r\n        /// <param name=\"propertyName\">Name of the property.</param>\r\n        /// <returns>Updated error message.</returns>\r\n        private static string UpdateErrorMessage(string errorMessage, IDictionary<string, object> inputs, string property, string typeName, string propertyName)\r\n        {\r\n            var arrParams = inputs?.Select(c => new { Name = c.Key, c.Value });\r\n            var model = arrParams?.Where(a => string.Equals(a.Name, typeName))?.FirstOrDefault();\r\n            if (model != null)\r\n            {\r\n                var modelJson = JsonSerializer.Serialize(model?.Value);\r\n                var jObj = JsonObject.Parse(modelJson).AsObject();\r\n                JsonNode jToken = null;\r\n                var val = jObj?.TryGetPropertyValue(propertyName, out jToken);\r\n                errorMessage = errorMessage.Replace($\"$({property})\", jToken != null ? jToken?.ToString() : $\"({property})\");\r\n            }\r\n\r\n            return errorMessage;\r\n        }\r\n        #endregion\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/RulesEngine/RulesEngine.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>net6.0;net8.0;net9.0;netstandard2.0</TargetFrameworks>\n    <LangVersion>13.0</LangVersion>\n    <Version>6.0.0</Version>\n    <Copyright>Copyright (c) Microsoft Corporation.</Copyright>\n    <PackageLicenseFile>LICENSE</PackageLicenseFile>\n    <PackageProjectUrl>https://github.com/microsoft/RulesEngine</PackageProjectUrl>\n    <Authors>Purunjay Bhal</Authors>\n    <Description>Rules Engine is a package for abstracting business logic/rules/policies out of the system. This works in a very simple way by giving you an ability to put your rules in a store outside the core logic of the system thus ensuring that any change in rules doesn't affect the core system.</Description>\n    <PackageReleaseNotes>https://github.com/microsoft/RulesEngine/blob/main/CHANGELOG.md</PackageReleaseNotes>\n    <PackageTags>BRE, Rules Engine, Abstraction</PackageTags>\n    <PackageReadmeFile>README.md</PackageReadmeFile>\n  </PropertyGroup>\n\n  <PropertyGroup Condition=\" '$(Configuration)' == 'Release' \">\n    <Optimize>true</Optimize>\n  </PropertyGroup>\n  <PropertyGroup Label=\"SourceLink\">\n    <PublishRepositoryUrl>true</PublishRepositoryUrl>\n    <IncludeSymbols>true</IncludeSymbols>\n    <SymbolPackageFormat>snupkg</SymbolPackageFormat>\n    <SignAssembly>True</SignAssembly>\n    <AssemblyOriginatorKeyFile>..\\..\\signing\\RulesEngine-publicKey.snk</AssemblyOriginatorKeyFile>\n    <DelaySign>True</DelaySign>\n    <Deterministic>true</Deterministic>\n    <EmbedUntrackedSources>true</EmbedUntrackedSources>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"..\\..\\LICENSE\" Pack=\"true\" PackagePath=\"\\\" />\n    <None Include=\"..\\..\\README.md\" Pack=\"true\" PackagePath=\"\\\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"FastExpressionCompiler\" Version=\"5.0.2\" />\n    <PackageReference Include=\"FluentValidation\" Version=\"11.11.0\" />\n    <PackageReference Include=\"System.Linq.Dynamic.Core\" Version=\"1.6.2\" />\n    <PackageReference Include=\"Microsoft.SourceLink.GitHub\" Version=\"8.0.0\" PrivateAssets=\"All\" />\n  </ItemGroup>\n\n  <Choose>\n    <When Condition=\"'$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'netstandard2.0'\">\n      <ItemGroup>\n        <PackageReference Include=\"System.Text.Json\" Version=\"6.0.11\" />\n      </ItemGroup>\n    </When>\n    <Otherwise>\n      <ItemGroup>\n        <PackageReference Include=\"System.Text.Json\" Version=\"9.0.1\" />\n      </ItemGroup>\n    </Otherwise>\n  </Choose>\n\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'netstandard2.0'\">\n    <PackageReference Include=\"Microsoft.CSharp\" Version=\"4.7.0\"/>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/RulesEngine/Validators/RuleValidator.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing FluentValidation;\nusing RulesEngine.HelperFunctions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace RulesEngine.Validators\n{\n    internal class RuleValidator : AbstractValidator<Rule>\n    {\n        private readonly List<ExpressionType> _nestedOperators = new List<ExpressionType> { ExpressionType.And, ExpressionType.AndAlso, ExpressionType.Or, ExpressionType.OrElse };\n        public RuleValidator()\n        {\n            RuleFor(c => c.RuleName).NotEmpty().WithMessage(Constants.RULE_NAME_NULL_ERRMSG);\n\n            //Nested expression check\n            When(c => c.Operator != null, () => {\n                RuleFor(c => c.Operator)\n                   .NotNull().WithMessage(Constants.OPERATOR_NULL_ERRMSG)\n                   .Must(op => _nestedOperators.Any(x => x.ToString().Equals(op, StringComparison.OrdinalIgnoreCase)))\n                   .WithMessage(Constants.OPERATOR_INCORRECT_ERRMSG);\n\n                When(c => c.Rules?.Any() != true, () => {\n                    RuleFor(c => c.WorkflowsToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG);\n                })\n                .Otherwise(() => {\n                    RuleFor(c => c.Rules).Must(BeValidRulesList);\n                });\n            });\n            RegisterExpressionTypeRules();\n        }\n\n        private void RegisterExpressionTypeRules()\n        {\n            When(c => c.Operator == null && c.RuleExpressionType == RuleExpressionType.LambdaExpression, () => {\n                RuleFor(c => c.Expression).NotEmpty().WithMessage(Constants.LAMBDA_EXPRESSION_EXPRESSION_NULL_ERRMSG);\n                RuleFor(c => c.Rules).Empty().WithMessage(Constants.OPERATOR_RULES_ERRMSG);\n            });\n        }\n\n        private bool BeValidRulesList(IEnumerable<Rule> rules)\n        {\n            if (rules?.Any() != true) return false;\n            var validator = new RuleValidator();\n            var isValid = true;\n            foreach (var rule in rules)\n            {\n                isValid &= validator.Validate(rule).IsValid;\n                if (!isValid) break;\n            }\n            return isValid;\n        }\n    }\n}\n"
  },
  {
    "path": "src/RulesEngine/Validators/WorkflowRulesValidator.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing FluentValidation;\nusing RulesEngine.HelperFunctions;\nusing RulesEngine.Models;\nusing System.Linq;\n\nnamespace RulesEngine.Validators\n{\n    internal class WorkflowsValidator : AbstractValidator<Workflow>\n    {\n        public WorkflowsValidator()\n        {\n            RuleFor(c => c.WorkflowName).NotEmpty().WithMessage(Constants.WORKFLOW_NAME_NULL_ERRMSG);\n            When(c => c.Rules?.Any() != true, () => {\n                RuleFor(c => c.WorkflowsToInject).NotEmpty().WithMessage(Constants.INJECT_WORKFLOW_RULES_ERRMSG);\n            }).Otherwise(() => {\n                var ruleValidator = new RuleValidator();\n                RuleForEach(c => c.Rules).SetValidator(ruleValidator);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/ActionTests/ActionContextTests.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing AutoFixture;\nusing RulesEngine.Actions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class ActionContextTests\n    {\n        [Fact]\n        public void GetParentRuleResult_ReturnsParentRule()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var contextInput = fixture.Create<string>();\n            var context = new Dictionary<string, object> {\n                { nameof(contextInput), contextInput }\n            };\n            var parentRuleResult = new RuleResultTree();\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n\n            // Act\n            var result = actionContext.GetParentRuleResult();\n\n            // Assert\n            Assert.NotNull(result);\n            Assert.Equal(parentRuleResult, result);\n        }\n\n        [Fact]\n        public void GetContext_ValidName_ReturnsContext()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var contextInput = fixture.Create<string>();\n            var context = new Dictionary<string, object> {\n                { nameof(contextInput), contextInput }\n            };\n            var parentRuleResult = new RuleResultTree();\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n            string name = nameof(contextInput);\n\n            // Act\n            var result = actionContext.GetContext<string>(name);\n\n            // Assert\n            Assert.Equal(contextInput, result);\n        }\n\n        [Fact]\n        public void GetContext_ObjectContext_ReturnsTypedContext()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var contextInput = fixture.CreateMany<string>();\n            var context = new Dictionary<string, object> {\n                { nameof(contextInput), contextInput }\n            };\n            var parentRuleResult = new RuleResultTree();\n\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n            string name = nameof(contextInput);\n\n            // Act\n            var result = actionContext.GetContext<List<string>>(name);\n\n            // Assert\n            Assert.Equal(contextInput, result);\n        }\n\n        [Fact]\n        public void GetContext_ValidNameWithStringCaseDiffernce_ReturnsContext()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var contextInput = fixture.Create<string>();\n            var context = new Dictionary<string, object> {\n                { nameof(contextInput), contextInput }\n            };\n            var parentRuleResult = new RuleResultTree();\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n            string name = nameof(contextInput).ToUpper();\n\n            // Act\n            var result = actionContext.GetContext<string>(name);\n\n            // Assert\n            Assert.Equal(contextInput, result);\n        }\n\n        [Fact]\n        public void GetContext_InvalidName_ThrowsArgumentException()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var contextInput = fixture.Create<string>();\n            var context = new Dictionary<string, object> {\n                { nameof(contextInput), contextInput }\n            };\n            var parentRuleResult = new RuleResultTree();\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n            string name = fixture.Create<string>();\n\n            // Act\n            Assert.Throws<ArgumentException>(() => actionContext.GetContext<string>(name));\n        }\n\n        [Fact]\n        public void GetContext_PrimitiveInputs_ReturnsResult()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var intInput = fixture.Create<int>();\n            var strInput = fixture.Create<string>();\n            var floatInput = fixture.Create<float>();\n\n            var context = new Dictionary<string, object> {\n                { nameof(intInput), intInput },\n                { nameof(strInput), strInput },\n                { nameof(floatInput), floatInput },\n            };\n            var parentRuleResult = new RuleResultTree();\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n\n            // Act\n            var intResult = actionContext.GetContext<int>(nameof(intInput));\n            var strResult = actionContext.GetContext<string>(nameof(strInput));\n            var floatResult = actionContext.GetContext<float>(nameof(floatInput));\n\n            // Assert\n            Assert.Equal(intInput, intResult);\n            Assert.Equal(strInput, strResult);\n            Assert.Equal(floatInput, floatResult);\n        }\n\n        [Fact]\n        public void GetContext_InvalidNameListContext_ThrowsArgumentException()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var contextInput = fixture.CreateMany<string>();\n            var context = new Dictionary<string, object> {\n                { nameof(contextInput), contextInput }\n            };\n            var parentRuleResult = new RuleResultTree();\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n            string name = fixture.Create<string>();\n\n            // Act\n            Assert.Throws<ArgumentException>(() => actionContext.GetContext<List<string>>(name));\n        }\n\n        [Fact]\n        public void GetContext_InvalidTypeConversion_ThrowsArgumentException()\n        {\n            // Arrange\n            var fixture = new Fixture();\n            var contextInput = fixture.CreateMany<string>();\n            var context = new Dictionary<string, object> {\n                { nameof(contextInput), contextInput }\n            };\n            var parentRuleResult = new RuleResultTree();\n\n            var actionContext = new ActionContext(context, parentRuleResult);\n            string name = nameof(contextInput);\n\n            // Act\n            Assert.Throws<ArgumentException>(() => actionContext.GetContext<RuleResultTree>(name));\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/ActionTests/CustomActionTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing Newtonsoft.Json;\nusing RulesEngine.Models;\nusing RulesEngine.UnitTest.ActionTests.MockClass;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text.Json.Serialization;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest.ActionTests\n{\n    [ExcludeFromCodeCoverage]\n    public class CustomActionTest\n    {\n        [Fact]\n        public async Task CustomActionOnRuleMustHaveContextValues()\n        {\n            var workflow = GetWorkflow();\n            var re = new RulesEngine(workflow, reSettings: new ReSettings {\n                CustomActions = new Dictionary<string, System.Func<Actions.ActionBase>> {\n\n                    { \"ReturnContext\", () => new ReturnContextAction() }\n                }\n            });\n\n            var result = await re.ExecuteAllRulesAsync(\"successReturnContextAction\", true);\n        }\n\n\n        [Fact]\n        public async Task CustomAction_WithSystemTextJsobOnRuleMustHaveContextValues()\n        {\n            var workflow = GetWorkflow();\n            var workflowStr = JsonConvert.SerializeObject(workflow);\n            var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } };\n            var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize<Workflow[]>(workflowStr,serializationOptions);\n\n\n            var re = new RulesEngine(workflow, reSettings: new ReSettings {\n                CustomActions = new Dictionary<string, System.Func<Actions.ActionBase>> {\n\n                    { \"ReturnContext\", () => new ReturnContextAction() }\n                }\n            });\n\n\n\n            var result = await re.ExecuteAllRulesAsync(\"successReturnContextAction\", true);\n        }\n\n        private Workflow[] GetWorkflow()\n        {\n            return new Workflow[] {\n                new Workflow {\n                    WorkflowName = \"successReturnContextAction\",\n                    Rules = new Rule[] {\n                        new Rule {\n                            RuleName = \"trueRule\",\n                            Expression = \"input1 == true\",\n                            Actions = new RuleActions() {\n                                OnSuccess = new ActionInfo {\n                                    Name = \"ReturnContext\",\n                                    Context =  new Dictionary<string, object> {\n                                        {\"stringContext\", \"hello\"},\n                                        {\"intContext\",1 },\n                                        {\"objectContext\", new { a = \"hello\", b = 123 } }\n                                    }\n                                }\n\n                            }\n\n                        },\n                        \n\n                    }\n                }\n\n            };\n        }\n\n\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/ActionTests/MockClass/ReturnContextAction.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Actions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace RulesEngine.UnitTest.ActionTests.MockClass\n{\n    [ExcludeFromCodeCoverage]\n    public class ReturnContextAction : ActionBase\n    {\n        public override ValueTask<object> Run(ActionContext context, RuleParameter[] ruleParameters)\n        {\n            var stringContext = context.GetContext<string>(\"stringContext\");\n            var intContext = context.GetContext<int>(\"intContext\");\n            var objectContext = context.GetContext<object>(\"objectContext\");\n\n            return new ValueTask<object>(new {\n                stringContext,\n                intContext,\n                objectContext\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/ActionTests/RulesEngineWithActionsTests.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n\n    [ExcludeFromCodeCoverage]\n    public class RulesEngineWithActionsTests\n    {\n\n        [Fact]\n        public async Task WhenExpressionIsSuccess_OutputExpressionAction_ReturnsExpressionEvaluation()\n        {\n            var engine = new RulesEngine(GetWorkflowWithActions());\n            var result = await engine.ExecuteActionWorkflowAsync(\"ActionWorkflow\", \"ExpressionOutputRuleTest\", new RuleParameter[0]);\n            Assert.NotNull(result);\n            Assert.Equal(2 * 2, result.Output);\n        }\n\n        [Fact]\n        public async Task WhenExpressionIsSuccess_ComplexOutputExpressionAction_ReturnsExpressionEvaluation()\n        {\n            var engine = new RulesEngine(GetWorkflowWithActions());\n            var result = await engine.ExecuteActionWorkflowAsync(\"ActionWorkflow\", \"ComplexOutputRuleTest\", new RuleParameter[0]);\n            Assert.NotNull(result);\n            dynamic output = result.Output;\n            Assert.Equal(2, output.test);\n        }\n\n        [Fact]\n        public async Task WhenExpressionIsSuccess_EvaluateRuleAction_ReturnsExpressionEvaluation()\n        {\n            var engine = new RulesEngine(GetWorkflowWithActions());\n            var result = await engine.ExecuteActionWorkflowAsync(\"ActionWorkflow\", \"EvaluateRuleTest\", new RuleParameter[0]);\n            Assert.NotNull(result);\n            Assert.Equal(2 * 2, result.Output);\n            Assert.Contains(result.Results, c => c.Rule.RuleName == \"ExpressionOutputRuleTest\");\n        }\n\n        [Fact]\n        public async Task ExecuteActionWorkflowAsync_CalledWithIncorrectWorkflowOrRuleName_ThrowsArgumentException()\n        {\n            var engine = new RulesEngine(GetWorkflowWithActions());\n            await Assert.ThrowsAsync<ArgumentException>(async () => await engine.ExecuteActionWorkflowAsync(\"WrongWorkflow\", \"ExpressionOutputRuleTest\", new RuleParameter[0]));\n            await Assert.ThrowsAsync<ArgumentException>(async () => await engine.ExecuteActionWorkflowAsync(\"ActionWorkflow\", \"WrongRule\", new RuleParameter[0]));\n        }\n\n\n        [Fact]\n        public async Task ExecuteActionWorkflowAsync_CalledWithNoActionsInWorkflow_ExecutesSuccessfully()\n        {\n\n            var engine = new RulesEngine(GetWorkflowsWithoutActions());\n            var result = await engine.ExecuteActionWorkflowAsync(\"NoActionWorkflow\", \"NoActionTest\", new RuleParameter[0]);\n            Assert.NotNull(result);\n            Assert.Null(result.Output);\n        }\n\n\n        [Fact]\n        public async Task ExecuteActionWorkflowAsync_SelfReferencingAction_NoFilter_ExecutesSuccessfully()\n        {\n\n            var engine = new RulesEngine(GetWorkflowWithActions());\n            var result = await engine.ExecuteActionWorkflowAsync(\"WorkflowWithGlobalsAndSelfRefActions\", \"RuleReferencingSameWorkflow\", new RuleParameter[0]);\n            Assert.NotNull(result);\n            Assert.Null(result.Output);\n        }\n\n        [Fact]\n        public async Task ExecuteActionWorkflowAsync_SelfReferencingAction_WithFilter_ExecutesSuccessfully()\n        {\n\n            var engine = new RulesEngine(GetWorkflowWithActions());\n            var result = await engine.ExecuteActionWorkflowAsync(\"WorkflowWithGlobalsAndSelfRefActions\", \"RuleReferencingSameWorkflowWithInputFilter\", new RuleParameter[0]);\n            Assert.NotNull(result);\n            Assert.Equal(4,result.Output);\n        }\n\n        private Workflow[] GetWorkflowsWithoutActions()\n        {\n            var workflow1 = new Workflow {\n                WorkflowName = \"NoActionWorkflow\",\n                Rules = new List<Rule>{\n                    new Rule{\n                        RuleName = \"NoActionTest\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"1 == 1\",\n                    }\n\n                }\n            };\n            return new[] { workflow1 };\n        }\n\n        private Workflow[] GetWorkflowWithActions()\n        {\n\n            var workflow1 = new Workflow {\n                WorkflowName = \"ActionWorkflow\",\n                Rules = new List<Rule>{\n                    new Rule{\n                        RuleName = \"ExpressionOutputRuleTest\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"1 == 1\",\n                        Actions = new RuleActions{\n                            OnSuccess = new ActionInfo{\n                                Name = \"OutputExpression\",\n                                Context = new Dictionary<string, object>{\n                                    {\"expression\", \"2*2\"}\n                                }\n                            }\n                        }\n                    },\n                    new Rule{\n                        RuleName = \"ComplexOutputRuleTest\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"1 == 1\",\n                        Actions = new RuleActions{\n                            OnSuccess = new ActionInfo{\n                                Name = \"OutputExpression\",\n                                Context = new Dictionary<string, object>{\n                                    {\"expression\", \"new (2 as test)\"}\n                                }\n                            }\n                        }\n                    },\n                    new Rule{\n                        RuleName = \"EvaluateRuleTest\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"1 == 1\",\n                        Actions = new RuleActions{\n                            OnSuccess = new ActionInfo{\n                                Name = \"EvaluateRule\",\n                                Context = new Dictionary<string, object>{\n                                    {\"workflowName\", \"ActionWorkflow\"},\n                                    {\"ruleName\",\"ExpressionOutputRuleTest\"}\n                                }\n                            }\n                        }\n                    }\n\n                }\n\n              \n            };\n\n            var workflow2 = new Workflow {\n                WorkflowName = \"WorkflowWithGlobalsAndSelfRefActions\",\n                GlobalParams = new[] {\n                    new ScopedParam {\n                        Name = \"global1\",\n                        Expression = \"\\\"Hello\\\"\"\n                    }\n                },\n                Rules = new[] {\n\n                    new Rule{\n                        RuleName = \"RuleReferencingSameWorkflow\",\n                        Expression = \"1 == 1\",\n                        Actions = new RuleActions {\n                            OnSuccess = new ActionInfo{\n                                Name = \"EvaluateRule\",\n                                Context = new Dictionary<string, object>{\n                                    {\"workflowName\", \"WorkflowWithGlobalsAndSelfRefActions\"},\n                                    {\"ruleName\",\"OtherRule\"}\n                                }\n                            }\n                        }\n                    },new Rule{\n                        RuleName = \"RuleReferencingSameWorkflowWithInputFilter\",\n                        Expression = \"1 == 1\",\n                        Actions = new RuleActions {\n                            OnSuccess = new ActionInfo{\n                                Name = \"EvaluateRule\",\n                                Context = new Dictionary<string, object>{\n                                    {\"workflowName\", \"WorkflowWithGlobalsAndSelfRefActions\"},\n                                    {\"ruleName\",\"OtherRule\"},\n                                    {\"inputFilter\",new string[] { } },\n                                    {\"additionalInputs\", new [] { \n                                        new ScopedParam(){\n                                            Name = \"additionalValue\",\n                                            Expression = \"1\"\n                                        }\n\n                                    } }\n                                }\n\n                            }\n                        }\n                    }\n\n\n                    , new Rule{\n                        RuleName = \"OtherRule\",\n                        Expression = \"additionalValue == 1\",\n                        Actions = new RuleActions {\n                             OnSuccess = new ActionInfo{\n                                Name = \"OutputExpression\",\n                                Context = new Dictionary<string, object>{\n                                    {\"expression\", \"2*2\"}\n                                }\n                            }\n                        }\n\n                    }\n\n                }\n\n            };\n            return new[] { workflow1, workflow2 };\n        }\n    }\n}"
  },
  {
    "path": "test/RulesEngine.UnitTest/BusinessRuleEngineTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\r\n// Licensed under the MIT License.\r\n\r\nusing Moq;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Converters;\r\nusing RulesEngine.Exceptions;\r\nusing RulesEngine.HelperFunctions;\r\nusing RulesEngine.Interfaces;\r\nusing RulesEngine.Models;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics.CodeAnalysis;\r\nusing System.Dynamic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Text.Json;\r\nusing System.Threading.Tasks;\r\nusing Xunit;\r\n\r\nnamespace RulesEngine.UnitTest\r\n{\r\n    [Trait(\"Category\", \"Unit\")]\r\n    [ExcludeFromCodeCoverage]\r\n    public class RulesEngineTest\r\n    {\r\n        [Theory]\r\n        [InlineData(\"rules1.json\")]\r\n        public void RulesEngine_New_ReturnsNotNull(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n            Assert.NotNull(re);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules2.json\")]\r\n        public async Task RulesEngine_InjectedRules_ContainsInjectedRules(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            List<RuleResultTree> result = await re.ExecuteAllRulesAsync(\"inputWorkflowReference\", input1, input2, input3);\r\n            Assert.NotNull(result);\r\n            Assert.True(result.Any());\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules2.json\")]\r\n        public async Task ExecuteRule_ReturnsListOfRuleResultTree(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            List<RuleResultTree> result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.Contains(result, c => c.IsSuccess);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules1.json\", \"rules6.json\")]\r\n        public async Task ExecuteRule_AddWorkflowWithSameName_ThrowsValidationException(string previousWorkflowFile, string newWorkflowFile)\r\n        {\r\n            var re = GetRulesEngine(previousWorkflowFile);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            // Run previous rules.\r\n            List<RuleResultTree> result1 = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result1);\r\n            Assert.IsType<List<RuleResultTree>>(result1);\r\n            Assert.Contains(result1, c => c.IsSuccess);\r\n\r\n            // Fetch and add new rules.\r\n            var newWorkflow = ParseAsWorkflow(newWorkflowFile);\r\n\r\n            Assert.Throws<RuleValidationException>(() => re.AddWorkflow(newWorkflow));\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules1.json\", \"rules6.json\")]\r\n        public async Task ExecuteRule_AddOrUpdateWorkflow_ExecutesUpdatedRules(string previousWorkflowFile, string newWorkflowFile)\r\n        {\r\n            var re = GetRulesEngine(previousWorkflowFile);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            // Run previous rules.\r\n            List<RuleResultTree> result1 = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result1);\r\n            Assert.IsType<List<RuleResultTree>>(result1);\r\n            Assert.Contains(result1, c => c.IsSuccess);\r\n\r\n            // Fetch and update new rules.\r\n            Workflow newWorkflow = ParseAsWorkflow(newWorkflowFile);\r\n            re.AddOrUpdateWorkflow(newWorkflow);\r\n\r\n            // Run new rules.\r\n            List<RuleResultTree> result2 = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result2);\r\n            Assert.IsType<List<RuleResultTree>>(result2);\r\n            Assert.DoesNotContain(result2, c => c.IsSuccess);\r\n\r\n            // New execution should have different result than previous execution.\r\n            var previousResults = result1.Select(c => new { c.Rule.RuleName, c.IsSuccess });\r\n            var newResults = result2.Select(c => new { c.Rule.RuleName, c.IsSuccess });\r\n            Assert.NotEqual(previousResults, newResults);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules2.json\")]\r\n        public void GetAllRegisteredWorkflows_ReturnsListOfAllWorkflows(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n            var workflow = re.GetAllRegisteredWorkflowNames();\r\n\r\n            Assert.NotNull(workflow);\r\n            Assert.Equal(2, workflow.Count);\r\n            Assert.Contains(\"inputWorkflow\", workflow);\r\n        }\r\n\r\n        [Fact]\r\n        public void GetAllRegisteredWorkflows_NoWorkflow_ReturnsEmptyList()\r\n        {\r\n            var re = new RulesEngine();\r\n            var workflow = re.GetAllRegisteredWorkflowNames();\r\n\r\n            Assert.NotNull(workflow);\r\n            Assert.Empty(workflow);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules2.json\")]\r\n        public async Task ExecuteRule_ManyInputs_ReturnsListOfRuleResultTree(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            dynamic input4 = GetInput1();\r\n            dynamic input5 = GetInput2();\r\n            dynamic input6 = GetInput3();\r\n\r\n            dynamic input7 = GetInput1();\r\n            dynamic input8 = GetInput2();\r\n            dynamic input9 = GetInput3();\r\n\r\n            dynamic input10 = GetInput1();\r\n            dynamic input11 = GetInput2();\r\n            dynamic input12 = GetInput3();\r\n\r\n            dynamic input13 = GetInput1();\r\n            dynamic input14 = GetInput2();\r\n            dynamic input15 = GetInput3();\r\n\r\n\r\n            dynamic input16 = GetInput1();\r\n            dynamic input17 = GetInput2();\r\n            dynamic input18 = GetInput3();\r\n\r\n            List<RuleResultTree> result = await re.ExecuteAllRulesAsync(\"inputWorkflow\",\r\n                            input1, input2, input3, input4, input5, input6, input7, input8, input9, input10, input11, input12, input13, input14, input15, input16, input17, input18);\r\n            //, input9, input10, input11, input12, input13, input14, input15, input16, input17, input18);\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.Contains(result, c => c.IsSuccess);\r\n        }\r\n\r\n\r\n        [Theory]\r\n        [InlineData(\"rules2.json\")]\r\n        public async Task ExecuteRule_CalledMultipleTimes_ReturnsSameResult(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            List<RuleResultTree> result1 = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result1);\r\n            Assert.IsType<List<RuleResultTree>>(result1);\r\n            Assert.Contains(result1, c => c.IsSuccess);\r\n\r\n            List<RuleResultTree> result2 = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result2);\r\n            Assert.IsType<List<RuleResultTree>>(result2);\r\n            Assert.Contains(result2, c => c.IsSuccess);\r\n\r\n            var expected = result1.Select(c => new { c.Rule.RuleName, c.IsSuccess });\r\n            var actual = result2.Select(c => new { c.Rule.RuleName, c.IsSuccess });\r\n            Assert.Equal(expected, actual);\r\n\r\n\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules2.json\")]\r\n        public async Task ExecuteRule_SingleObject_ReturnsListOfRuleResultTree(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            List<RuleResultTree> result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1);\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.DoesNotContain(result, c => c.IsSuccess);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules3.json\")]\r\n        public async Task ExecuteRule_ExceptionScenario_RulesInvalid(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            List<RuleResultTree> result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result);\r\n            Assert.False(string.IsNullOrEmpty(result[0].ExceptionMessage) || string.IsNullOrWhiteSpace(result[0].ExceptionMessage));\r\n        }\r\n\r\n\r\n        [Fact]\r\n        public void RulesEngine_New_IncorrectJSON_ThrowsException()\r\n        {\r\n            Assert.Throws<RuleValidationException>(() => {\r\n                var workflow = new Workflow();\r\n                var re = CreateRulesEngine(workflow);\r\n            });\r\n\r\n            Assert.Throws<RuleValidationException>(() => {\r\n                var workflow = new Workflow() { WorkflowName = \"test\" };\r\n                var re = CreateRulesEngine(workflow);\r\n            });\r\n        }\r\n\r\n\r\n        [Fact]\r\n        public void RulesEngine_AddOrUpdate_IncorrectJSON_ThrowsException()\r\n        {\r\n            Assert.Throws<RuleValidationException>(() => {\r\n                var workflow = new Workflow();\r\n                var re = new RulesEngine();\r\n                re.AddOrUpdateWorkflow(workflow);\r\n            });\r\n\r\n            Assert.Throws<RuleValidationException>(() => {\r\n                var workflow = new Workflow() { WorkflowName = \"test\" };\r\n                var re = new RulesEngine();\r\n                re.AddOrUpdateWorkflow(workflow);\r\n            });\r\n        }\r\n\r\n\r\n        [Theory]\r\n        [InlineData(\"rules1.json\")]\r\n        public async Task ExecuteRule_InvalidWorkFlow_ThrowsException(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n            dynamic input = GetInput1();\r\n\r\n            await Assert.ThrowsAsync<ArgumentException>(async () => { await re.ExecuteAllRulesAsync(\"inputWorkflow1\", input); });\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules1.json\")]\r\n        public async Task RemoveWorkflow_RemovesWorkflow(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result);\r\n            re.RemoveWorkflow(\"inputWorkflow\");\r\n\r\n            await Assert.ThrowsAsync<ArgumentException>(async () => await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3));\r\n        }\r\n\r\n\r\n        [Theory]\r\n        [InlineData(\"rules1.json\")]\r\n        public async Task ClearWorkflow_RemovesAllWorkflow(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n            re.ClearWorkflows();\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            await Assert.ThrowsAsync<ArgumentException>(async () => await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3));\r\n            await Assert.ThrowsAsync<ArgumentException>(async () => await re.ExecuteAllRulesAsync(\"inputWorkflowReference\", input1, input2, input3));\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules1.json\")]\r\n        [InlineData(\"rules2.json\")]\r\n        public async Task ExecuteRule_InputWithVariableProps_ReturnsResult(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = GetInput1();\r\n            dynamic input2 = GetInput2();\r\n            dynamic input3 = GetInput3();\r\n\r\n            List<RuleResultTree> result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.Contains(result, c => c.IsSuccess);\r\n\r\n            input3.hello = \"world\";\r\n\r\n            result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.Contains(result, c => c.IsSuccess);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules4.json\", true)]\r\n        [InlineData(\"rules4.json\", false)]\r\n        public async Task RulesEngine_Execute_Rule_For_Nested_Rule_Params_Returns_Success(string ruleFileName,bool fastExpressionEnabled)\r\n        {\r\n            var inputs = GetInputs4();\r\n\r\n            var ruleParams = new List<RuleParameter>();\r\n\r\n            for (var i = 0; i < inputs.Length; i++)\r\n            {\r\n                var input = inputs[i];\r\n                var obj = Utils.GetTypedObject(input);\r\n                ruleParams.Add(new RuleParameter($\"input{i + 1}\", obj));\r\n            }\r\n\r\n            var files = Directory.GetFiles(Directory.GetCurrentDirectory(), ruleFileName, SearchOption.AllDirectories);\r\n            if (files == null || files.Length == 0)\r\n            {\r\n                throw new Exception(\"Rules not found.\");\r\n            }\r\n\r\n            var fileData = File.ReadAllText(files[0]);\r\n            var bre = new RulesEngine(JsonConvert.DeserializeObject<Workflow[]>(fileData), new ReSettings {\r\n                UseFastExpressionCompiler = fastExpressionEnabled\r\n            });\r\n            var result = await bre.ExecuteAllRulesAsync(\"inputWorkflow\", ruleParams?.ToArray());\r\n            var ruleResult = result?.FirstOrDefault(r => string.Equals(r.Rule.RuleName, \"GiveDiscount10\", StringComparison.OrdinalIgnoreCase));\r\n            Assert.True(ruleResult.IsSuccess);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules2.json\")]\r\n        public async Task ExecuteRule_ReturnsProperErrorOnMissingRuleParameter(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            var input1 = new RuleParameter(\"customName\", GetInput1());\r\n            var input2 = new RuleParameter(\"input2\", GetInput2());\r\n            var input3 = new RuleParameter(\"input3\", GetInput3());\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", input1, input2, input3);\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules5.json\", \"hello\", true)]\r\n        [InlineData(\"rules5.json\", null, false)]\r\n        public async Task ExecuteRule_WithInjectedUtils_ReturnsListOfRuleResultTree(string ruleFileName, string propValue, bool expectedResult)\r\n        {\r\n            var reSettings = new ReSettings() {\r\n                CustomTypes = new Type[] { typeof(TestInstanceUtils) }\r\n            };\r\n\r\n            var re = GetRulesEngine(ruleFileName, reSettings);\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n\r\n            input1.Property1 = propValue;\r\n\r\n\r\n            var utils = new TestInstanceUtils();\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1), new RuleParameter(\"utils\", utils));\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.All(result, c => Assert.Equal(expectedResult, c.IsSuccess));\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules6.json\")]\r\n        public async Task ExecuteRule_RuleWithMethodExpression_ReturnsSucess(string ruleFileName)\r\n        {\r\n            Func<bool> func = () => true;\r\n\r\n            var re = GetRulesEngine(ruleFileName, new ReSettings {\r\n               CustomTypes = new[] { typeof(Func<bool>) }\r\n            });\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.Property1 = \"hello\";\r\n            input1.Boolean = false;\r\n            input1.Method = func;\r\n\r\n            var utils = new TestInstanceUtils();\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1));\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.All(result, c => Assert.True(c.IsSuccess));\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules7.json\")]\r\n        public async Task ExecuteRule_RuleWithUnaryExpression_ReturnsSucess(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.Boolean = false;\r\n\r\n            var utils = new TestInstanceUtils();\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1));\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.All(result, c => Assert.True(c.IsSuccess));\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules8.json\")]\r\n        public async Task ExecuteRule_RuleWithMemberAccessExpression_ReturnsSucess(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName);\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.Boolean = false;\r\n\r\n            var utils = new TestInstanceUtils();\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1));\r\n            Assert.NotNull(result);\r\n            Assert.IsType<List<RuleResultTree>>(result);\r\n            Assert.All(result, c => Assert.False(c.IsSuccess));\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules9.json\")]\r\n        public async Task ExecuteRule_MissingMethodInExpression_ReturnsRulesFailed(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = false });\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.Data = new { TestProperty = \"\" };\r\n            input1.Boolean = false;\r\n\r\n            var utils = new TestInstanceUtils();\r\n\r\n            await Assert.ThrowsAsync<RuleException>(async () => {\r\n                var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1));\r\n            });\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules9.json\")]\r\n        public async Task ExecuteRule_DynamicParsion_RulesEvaluationFailed(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = true });\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.Data = new { TestProperty = \"\" };\r\n            input1.Boolean = false;\r\n\r\n            var utils = new TestInstanceUtils();\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1));\r\n\r\n            Assert.NotNull(result);\r\n            Assert.StartsWith(\"Exception while parsing expression\", result[1].ExceptionMessage);\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules9.json\")]\r\n        public async Task ExecuteRuleWithIgnoreException_CompilationException_DoesNotReturnsAsErrorMessage(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName, new ReSettings() { EnableExceptionAsErrorMessage = true, IgnoreException = true });\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.Data = new { TestProperty = \"\" };\r\n            input1.Boolean = false;\r\n\r\n            var utils = new TestInstanceUtils();\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1));\r\n\r\n            Assert.NotNull(result);\r\n            Assert.False(result[1].ExceptionMessage.StartsWith(\"Exception while parsing expression\"));\r\n        }\r\n\r\n\r\n        [Theory]\r\n        [InlineData(\"rules10.json\")]\r\n        public async Task ExecuteRuleWithJsonElement(string ruleFileName)\r\n        {\r\n            var re = GetRulesEngine(ruleFileName, new ReSettings() {\r\n                                        EnableExceptionAsErrorMessage = true,\r\n                                        CustomTypes = new Type[] { typeof(System.Text.Json.JsonElement) }\r\n            \r\n                                            });\r\n\r\n            var input1 = new {\r\n                Data = System.Text.Json.JsonSerializer.SerializeToElement(new {\r\n                    category= \"abc\"\r\n                })\r\n            };\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"inputWorkflow\", new RuleParameter(\"input1\", input1));\r\n\r\n            Assert.NotNull(result);\r\n            Assert.All(result, c => Assert.True(c.IsSuccess));\r\n        }\r\n\r\n        [Theory]\r\n        [InlineData(\"rules11.json\")]\r\n        public async Task RulesEngineWithGlobalParam_RunsSuccessfully(string ruleFileName)\r\n        {\r\n\r\n            var re = GetRulesEngine(ruleFileName, new ReSettings() { });\r\n\r\n            var input1 = new[] {\r\n            new {\r\n                Value= 0.13259286,\r\n                ChangeDateTime= \"2023-07-28T19:57:07.432339Z\"\r\n            },\r\n            new {\r\n                Value= 0.09435427,\r\n                ChangeDateTime= \"2023-07-28T19:58:04.536459Z\"\r\n            },\r\n            new {\r\n                Value= 0.14896593,\r\n                ChangeDateTime= \"2023-07-28T19:59:08.682072Z\"\r\n            },\r\n            new {\r\n                Value= 0.12852388,\r\n                ChangeDateTime= \"2023-07-28T20:00:06.78036Z\"\r\n            },\r\n            new {\r\n                Value= 0.17011189,\r\n                ChangeDateTime= \"2023-07-28T20:00:54.873615Z\"\r\n            },\r\n            new {\r\n                Value= 0.0532116,\r\n                ChangeDateTime= \"2023-07-28T20:02:52.04049Z\"\r\n            },\r\n            new {\r\n                Value= 0.04064374,\r\n                ChangeDateTime= \"2023-07-28T20:03:54.168499Z\"\r\n            },\r\n            new {\r\n                Value= 0.03748944,\r\n                ChangeDateTime= \"2023-07-28T20:03:54.194786Z\"\r\n            },\r\n            new {\r\n                Value= 0.07752395,\r\n                ChangeDateTime= \"2023-07-28T20:06:32.451464Z\"\r\n            },\r\n            new {\r\n                Value= 0.07294922,\r\n                ChangeDateTime= \"2023-07-28T20:07:38.691755Z\"\r\n            },\r\n            new {\r\n                Value= 0.09892442,\r\n                ChangeDateTime= \"2023-07-28T20:08:37.98802Z\"\r\n            },\r\n            new {\r\n                Value= 0.06370641,\r\n                ChangeDateTime= \"2023-07-28T20:05:41.358461Z\"\r\n            },\r\n            new {\r\n                Value= 0.07550429,\r\n                ChangeDateTime= \"2023-07-28T20:09:48.129748Z\"\r\n            },\r\n            new {\r\n                Value= 0.0653021,\r\n                ChangeDateTime= \"2023-07-28T20:10:48.274482Z\"\r\n            },\r\n            new {\r\n                Value= 0.09304246,\r\n                ChangeDateTime= \"2023-07-28T20:11:49.436983Z\"\r\n            },\r\n            new {\r\n                Value= 0.0797422,\r\n                ChangeDateTime= \"2023-07-28T20:12:53.609118Z\"\r\n            },\r\n            new {\r\n                Value= 0.08211832,\r\n                ChangeDateTime= \"2023-07-28T20:13:52.699728Z\"\r\n            },\r\n            new {\r\n                Value= 0.06955433,\r\n                ChangeDateTime= \"2023-07-28T20:15:03.843289Z\"\r\n            },\r\n            new {\r\n                Value= 0.07626661,\r\n                ChangeDateTime= \"2023-07-28T20:15:03.870057Z\"\r\n            },\r\n            new {\r\n                Value= 0.05033984,\r\n                ChangeDateTime= \"2023-07-28T20:16:17.032262Z\"\r\n            },\r\n            new {\r\n                Value= 0.05202596,\r\n                ChangeDateTime= \"2023-07-28T20:17:20.172669Z\"\r\n            },\r\n            new {\r\n                Value= 0.06861198,\r\n                ChangeDateTime= \"2023-07-28T20:18:32.303309Z\"\r\n            },\r\n            new {\r\n                Value= 0.04935532,\r\n                ChangeDateTime= \"2023-07-28T20:19:33.451426Z\"\r\n            },\r\n            new {\r\n                Value= 0.04073699,\r\n                ChangeDateTime= \"2023-07-28T20:20:37.737395Z\"\r\n            },\r\n            new {\r\n                Value= 0.02164916,\r\n                ChangeDateTime= \"2023-07-28T20:21:38.883635Z\"\r\n            },\r\n            new {\r\n                Value= 0.01334031,\r\n                ChangeDateTime= \"2023-07-28T20:22:40.053193Z\"\r\n            },\r\n            new {\r\n                Value= 0.0336915,\r\n                ChangeDateTime= \"2023-07-28T20:23:44.240297Z\"\r\n            },\r\n            new {\r\n                Value= 0.04870055,\r\n                ChangeDateTime= \"2023-07-28T20:26:33.584756Z\"\r\n            },\r\n            new {\r\n                Value= 0.07125243,\r\n                ChangeDateTime= \"2023-07-28T20:28:11.7889Z\"\r\n            },\r\n            new {\r\n                Value= 0.04904275,\r\n                ChangeDateTime= \"2023-07-28T20:24:40.346216Z\"\r\n            },\r\n            new {\r\n                Value= 0.03625701,\r\n                ChangeDateTime= \"2023-07-28T20:27:20.707478Z\"\r\n            },\r\n            new {\r\n                Value= 0.05703328,\r\n                ChangeDateTime= \"2023-07-28T20:28:57.876436Z\"\r\n            },\r\n            new {\r\n                Value= 0.04364996,\r\n                ChangeDateTime= \"2023-07-28T20:25:43.496357Z\"\r\n            },\r\n            new {\r\n                Value= 0.07558272,\r\n                ChangeDateTime= \"2023-07-28T20:30:11.023295Z\"\r\n            },\r\n            new {\r\n                Value= 0.03073958,\r\n                ChangeDateTime= \"2023-07-28T20:33:00.347672Z\"\r\n            },\r\n            new {\r\n                Value= 0.0341309,\r\n                ChangeDateTime= \"2023-07-28T20:33:59.790621Z\"\r\n            },\r\n            new {\r\n                Value= 0.05270871,\r\n                ChangeDateTime= \"2023-07-28T20:31:15.166193Z\"\r\n            },\r\n            new {\r\n                Value= 0.09138862,\r\n                ChangeDateTime= \"2023-07-28T20:32:08.259273Z\"\r\n            },\r\n            new {\r\n                Value= 0.15922104,\r\n                ChangeDateTime= \"2023-07-28T20:35:12.963809Z\"\r\n            },\r\n            new {\r\n                Value= 0.11383641,\r\n                ChangeDateTime= \"2023-07-28T20:36:26.120815Z\"\r\n            },\r\n            new {\r\n                Value= 0.12404025,\r\n                ChangeDateTime= \"2023-07-28T20:37:37.27212Z\"\r\n            },\r\n            new {\r\n                Value= 0.06010197,\r\n                ChangeDateTime= \"2023-07-28T20:38:47.409412Z\"\r\n            },\r\n            new {\r\n                Value= 0.08396237,\r\n                ChangeDateTime= \"2023-07-28T20:39:37.504217Z\"\r\n            },\r\n            new {\r\n                Value= 0.06731881,\r\n                ChangeDateTime= \"2023-07-28T20:40:27.588895Z\"\r\n            },\r\n            new {\r\n                Value= 0.05617253,\r\n                ChangeDateTime= \"2023-07-28T20:41:33.760373Z\"\r\n            },\r\n            new {\r\n                Value= 0.0585155,\r\n                ChangeDateTime= \"2023-07-28T20:42:26.847144Z\"\r\n            },\r\n            new {\r\n                Value= 0.06793098,\r\n                ChangeDateTime= \"2023-07-28T20:43:36.988904Z\"\r\n            },\r\n            new {\r\n                Value= 0.06879344,\r\n                ChangeDateTime= \"2023-07-28T20:44:46.133926Z\"\r\n            },\r\n            new {\r\n                Value= 0.06931814,\r\n                ChangeDateTime= \"2023-07-28T20:45:50.275932Z\"\r\n            },\r\n            new {\r\n                Value= 0.04802603,\r\n                ChangeDateTime= \"2023-07-28T20:46:36.367289Z\"\r\n            },\r\n            new {\r\n                Value= 0.04036225,\r\n                ChangeDateTime= \"2023-07-28T20:47:27.484188Z\"\r\n            },\r\n            new {\r\n                Value= 0.04968483,\r\n                ChangeDateTime= \"2023-07-28T20:48:13.582228Z\"\r\n            },\r\n            new {\r\n                Value= 0.0773483,\r\n                ChangeDateTime= \"2023-07-28T19:49:16.354277Z\"\r\n            },\r\n            new {\r\n                Value= 0.08710921,\r\n                ChangeDateTime= \"2023-07-28T19:48:25.253743Z\"\r\n            },\r\n            new {\r\n                Value= 0.07665287,\r\n                ChangeDateTime= \"2023-07-28T19:50:25.496642Z\"\r\n            },\r\n            new {\r\n                Value= 0.06121748,\r\n                ChangeDateTime= \"2023-07-28T19:51:20.644955Z\"\r\n            },\r\n            new {\r\n                Value= 0.04179136,\r\n                ChangeDateTime= \"2023-07-28T19:52:26.793369Z\"\r\n            },\r\n            new {\r\n                Value= 0.13522345,\r\n                ChangeDateTime= \"2023-07-28T19:54:19.051669Z\"\r\n            },\r\n            new {\r\n                Value= 0.08536856,\r\n                ChangeDateTime= \"2023-07-28T19:56:04.287806Z\"\r\n            },\r\n            new {\r\n                Value= 0.05041369,\r\n                ChangeDateTime= \"2023-07-28T19:53:18.901696Z\"\r\n            },\r\n            new {\r\n                Value= 0.1627249,\r\n                ChangeDateTime= \"2023-07-28T19:55:13.160235Z\"\r\n            },\r\n            new {\r\n                Value= 0.05,\r\n                ChangeDateTime= \"2023-07-28T19:54:03.2197Z\"\r\n            },\r\n            new {\r\n                Value= 0.05,\r\n                ChangeDateTime= \"2023-07-28T19:56:00.802023Z\"\r\n            },\r\n            new {\r\n                Value= 0.02792705297470093,\r\n                ChangeDateTime= \"2023-07-28T20:49:03.6825337Z\"\r\n            }\r\n       }.ToList();\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"MyWorkflow\", new RuleParameter(\"input1\", input1));\r\n\r\n            Assert.NotNull(result);\r\n            Assert.False(result[0].IsSuccess);\r\n            Assert.True(result[1].IsSuccess);\r\n        }\r\n\r\n\r\n\r\n        [Fact]\r\n        public async Task ExecuteRule_RuntimeError_ShouldReturnAsErrorMessage()\r\n        {\r\n\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"TestWorkflow\",\r\n                Rules = new[] {\r\n                    new Rule {\r\n                        RuleName = \"ruleWithRuntimeError\",\r\n                        Expression = \"input1.Country.ToLower() == \\\"india\\\"\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine(new[] { workflow }, null);\r\n            var input = new RuleTestClass {\r\n                Country = null\r\n            };\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"TestWorkflow\", input);\r\n\r\n            Assert.NotNull(result);\r\n            Assert.All(result, rule => Assert.False(rule.IsSuccess));\r\n            Assert.All(result, rule => Assert.StartsWith(\"Error while executing rule :\", rule.ExceptionMessage));\r\n        }\r\n\r\n        [Fact]\r\n        public async Task ExecuteRule_RuntimeErrorInPreviousRun_ShouldReturnEmptyErrorMessage()\r\n        {\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"TestWorkflow\",\r\n                Rules = new[] {\r\n                    new Rule {\r\n                        RuleName = \"ruleWithRuntimeError\",\r\n                        Expression = \"input1.Country.ToLower() == \\\"india\\\"\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine(new[] { workflow }, null);\r\n            var input = new RuleTestClass {\r\n                Country = null\r\n            };\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"TestWorkflow\", input);\r\n\r\n            Assert.NotNull(result);\r\n            Assert.All(result, rule => Assert.False(rule.IsSuccess));\r\n            Assert.All(result, rule => Assert.StartsWith(\"Error while executing rule :\", rule.ExceptionMessage));\r\n\r\n            input.Country=\"india\";\r\n            result = await re.ExecuteAllRulesAsync(\"TestWorkflow\", input);\r\n\r\n            Assert.NotNull(result);\r\n            Assert.All(result, rule => Assert.True(rule.IsSuccess));\r\n            Assert.All(result, rule => Assert.Empty(rule.ExceptionMessage));\r\n        }\r\n        \r\n        [Fact]\r\n        public async Task ExecuteRule_RuntimeError_ThrowsException()\r\n        {\r\n\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"TestWorkflow\",\r\n                Rules = new[] {\r\n                    new Rule {\r\n                        RuleName = \"ruleWithRuntimeError\",\r\n                        Expression = \"input1.Country.ToLower() == \\\"india\\\"\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine(new[] { workflow }, new ReSettings {\r\n                EnableExceptionAsErrorMessage = false\r\n            });\r\n            var input = new RuleTestClass {\r\n                Country = null\r\n            };\r\n\r\n            _ = await Assert.ThrowsAsync<RuleException>(async () => await re.ExecuteAllRulesAsync(\"TestWorkflow\", input));\r\n\r\n        }\r\n\r\n        [Fact]\r\n        public async Task ExecuteRule_RuntimeError_IgnoreException_DoesNotReturnException()\r\n        {\r\n\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"TestWorkflow\",\r\n                Rules = new[] {\r\n                    new Rule {\r\n                        RuleName = \"ruleWithRuntimeError\",\r\n                        Expression = \"input1.Country.ToLower() == \\\"india\\\"\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine(new[] { workflow }, new ReSettings {\r\n                IgnoreException = true\r\n            });\r\n            var input = new RuleTestClass {\r\n                Country = null\r\n            };\r\n\r\n            var result = await re.ExecuteAllRulesAsync(\"TestWorkflow\", input);\r\n\r\n            Assert.NotNull(result);\r\n            Assert.All(result, rule => Assert.False(rule.IsSuccess));\r\n            Assert.All(result, rule => Assert.Empty(rule.ExceptionMessage));\r\n        }\r\n\r\n\r\n        [Fact]\r\n        public async Task RemoveWorkFlow_ShouldRemoveAllCompiledCache()\r\n        {\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"Test\",\r\n                Rules = new Rule[]{\r\n                    new Rule {\r\n                        RuleName = \"RuleWithLocalParam\",\r\n                        LocalParams = new List<LocalParam> {\r\n                            new LocalParam {\r\n                                Name = \"lp1\",\r\n                                Expression = \"true\"\r\n                            }\r\n                        },\r\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\r\n                        Expression = \"lp1 ==  true\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine();\r\n            re.AddWorkflow(workflow);\r\n\r\n            var result1 = await re.ExecuteAllRulesAsync(\"Test\", \"hello\");\r\n            Assert.True(result1.All(c => c.IsSuccess));\r\n\r\n            re.RemoveWorkflow(\"Test\");\r\n            workflow.Rules.First().LocalParams.First().Expression = \"false\";\r\n\r\n            re.AddWorkflow(workflow);\r\n            var result2 = await re.ExecuteAllRulesAsync(\"Test\", \"hello\");\r\n            Assert.True(result2.All(c => c.IsSuccess == false));\r\n        }\r\n\r\n        [Fact]\r\n        public async Task ClearWorkFlow_ShouldRemoveAllCompiledCache()\r\n        {\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"Test\",\r\n                Rules = new Rule[]{\r\n                    new Rule {\r\n                        RuleName = \"RuleWithLocalParam\",\r\n                        LocalParams = new LocalParam[] {\r\n                            new LocalParam {\r\n                                Name = \"lp1\",\r\n                                Expression = \"true\"\r\n                            }\r\n                        },\r\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\r\n                        Expression = \"lp1 ==  true\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine();\r\n            re.AddWorkflow(workflow);\r\n\r\n            var result1 = await re.ExecuteAllRulesAsync(\"Test\", \"hello\");\r\n            Assert.True(result1.All(c => c.IsSuccess));\r\n\r\n            re.ClearWorkflows();\r\n            workflow.Rules.First().LocalParams.First().Expression = \"false\";\r\n\r\n            re.AddWorkflow(workflow);\r\n            var result2 = await re.ExecuteAllRulesAsync(\"Test\", \"hello\");\r\n            Assert.True(result2.All(c => c.IsSuccess == false));\r\n        }\r\n\r\n        [Fact]\r\n        public async Task ExecuteRule_WithNullInput_ShouldNotThrowException()\r\n        {\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"Test\",\r\n                Rules = new Rule[]{\r\n                    new Rule {\r\n                        RuleName = \"RuleWithLocalParam\",\r\n\r\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\r\n                        Expression = \"input1 == null || input1.hello.world = \\\"wow\\\"\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine();\r\n            re.AddWorkflow(workflow);\r\n\r\n            var result1 = await re.ExecuteAllRulesAsync(\"Test\", new RuleParameter(\"input1\", value: null));\r\n            Assert.True(result1.All(c => c.IsSuccess));\r\n\r\n\r\n            var result2 = await re.ExecuteAllRulesAsync(\"Test\", new object[] { null });\r\n            Assert.True(result2.All(c => c.IsSuccess));\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.hello = new ExpandoObject();\r\n            input1.hello.world = \"wow\";\r\n\r\n            List<RuleResultTree> result3 = await re.ExecuteAllRulesAsync(\"Test\", input1);\r\n            Assert.True(result3.All(c => c.IsSuccess));\r\n\r\n        }\r\n\r\n        [Fact]\r\n        public async Task ExecuteRule_SpecialCharInWorkflowName_RunsSuccessfully()\r\n        {\r\n            var workflow = new Workflow {\r\n                WorkflowName = \"Exámple\",\r\n                Rules = new Rule[]{\r\n                    new Rule {\r\n                        RuleName = \"RuleWithLocalParam\",\r\n\r\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\r\n                        Expression = \"input1 == null || input1.hello.world = \\\"wow\\\"\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var workflowStr = \"{\\\"WorkflowName\\\":\\\"Exámple\\\",\\\"WorkflowsToInject\\\":null,\\\"GlobalParams\\\":null,\\\"Rules\\\":[{\\\"RuleName\\\":\\\"RuleWithLocalParam\\\",\\\"Properties\\\":null,\\\"Operator\\\":null,\\\"ErrorMessage\\\":null,\\\"Enabled\\\":true,\\\"ErrorType\\\":\\\"Warning\\\",\\\"RuleExpressionType\\\":\\\"LambdaExpression\\\",\\\"WorkflowsToInject\\\":null,\\\"Rules\\\":null,\\\"LocalParams\\\":null,\\\"Expression\\\":\\\"input1 == null || input1.hello.world = \\\\\\\"wow\\\\\\\"\\\",\\\"Actions\\\":null,\\\"SuccessEvent\\\":null}]}\";\r\n\r\n            var re = new RulesEngine(new string[] { workflowStr }, null);\r\n\r\n            dynamic input1 = new ExpandoObject();\r\n            input1.hello = new ExpandoObject();\r\n            input1.hello.world = \"wow\";\r\n\r\n            List<RuleResultTree> result3 = await re.ExecuteAllRulesAsync(\"Exámple\", input1);\r\n            Assert.True(result3.All(c => c.IsSuccess));\r\n\r\n        }\r\n\r\n        [Fact]\r\n        public void ContainsWorkFlowName_ShouldReturn()\r\n        {\r\n            const string ExistedWorkflowName = \"ExistedWorkflowName\";\r\n            const string NotExistedWorkflowName = \"NotExistedWorkflowName\";\r\n\r\n            var workflow = new Workflow {\r\n                WorkflowName = ExistedWorkflowName,\r\n                Rules = new Rule[]{\r\n                    new Rule {\r\n                        RuleName = \"Rule\",\r\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\r\n                        Expression = \"1==1\"\r\n                    }\r\n                }\r\n            };\r\n\r\n            var re = new RulesEngine();\r\n            re.AddWorkflow(workflow);\r\n\r\n            Assert.True(re.ContainsWorkflow(ExistedWorkflowName));\r\n            Assert.False(re.ContainsWorkflow(NotExistedWorkflowName));\r\n        }\r\n\r\n      \r\n\r\n\r\n\r\n        [Theory]\r\n        [InlineData(typeof(RulesEngine), typeof(IRulesEngine))]\r\n        public void Class_PublicMethods_ArePartOfInterface(Type classType, Type interfaceType)\r\n        {\r\n            var classMethods = classType.GetMethods(BindingFlags.DeclaredOnly |\r\n                        BindingFlags.Public |\r\n                        BindingFlags.Instance);\r\n\r\n\r\n            var interfaceMethods = interfaceType.GetMethods();\r\n\r\n\r\n            Assert.Equal(interfaceMethods.Count(), classMethods.Count());\r\n        }\r\n\r\n\r\n\r\n        private RulesEngine CreateRulesEngine(Workflow workflow)\r\n        {\r\n            var json = JsonConvert.SerializeObject(workflow);\r\n            return new RulesEngine(new string[] { json }, null);\r\n        }\r\n\r\n        private RulesEngine GetRulesEngine(string filename, ReSettings reSettings = null)\r\n        {\r\n            var data = GetFileContent(filename);\r\n\r\n            var injectWorkflow = new Workflow {\r\n                WorkflowName = \"inputWorkflowReference\",\r\n                WorkflowsToInject = new List<string> { \"inputWorkflow\" }\r\n            };\r\n\r\n            var injectWorkflowStr = JsonConvert.SerializeObject(injectWorkflow);\r\n            return new RulesEngine(new string[] { data, injectWorkflowStr }, reSettings);\r\n        }\r\n\r\n        private string GetFileContent(string filename)\r\n        {\r\n            var filePath = Path.Combine(Directory.GetCurrentDirectory() as string, \"TestData\", filename);\r\n            return File.ReadAllText(filePath);\r\n        }\r\n\r\n        private Workflow ParseAsWorkflow(string WorkflowsFileName)\r\n        {\r\n            string content = GetFileContent(WorkflowsFileName);\r\n            return JsonConvert.DeserializeObject<Workflow>(content);\r\n        }\r\n\r\n        private dynamic GetInput1()\r\n        {\r\n            var converter = new ExpandoObjectConverter();\r\n            var basicInfo = \"{\\\"name\\\": \\\"Dishant\\\",\\\"email\\\": \\\"abc@xyz.com\\\",\\\"creditHistory\\\": \\\"good\\\",\\\"country\\\": \\\"canada\\\",\\\"loyaltyFactor\\\": 3,\\\"totalPurchasesToDate\\\": 10000}\";\r\n            return JsonConvert.DeserializeObject<ExpandoObject>(basicInfo, converter);\r\n        }\r\n\r\n        private dynamic GetInput2()\r\n        {\r\n            var converter = new ExpandoObjectConverter();\r\n            var orderInfo = \"{\\\"totalOrders\\\": 5,\\\"recurringItems\\\": 2}\";\r\n            return JsonConvert.DeserializeObject<ExpandoObject>(orderInfo, converter);\r\n        }\r\n\r\n        private dynamic GetInput3()\r\n        {\r\n            var converter = new ExpandoObjectConverter();\r\n            var telemetryInfo = \"{\\\"noOfVisitsPerMonth\\\": 10,\\\"percentageOfBuyingToVisit\\\": 15}\";\r\n            return JsonConvert.DeserializeObject<ExpandoObject>(telemetryInfo, converter);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Gets the inputs.\r\n        /// </summary>\r\n        /// <returns>\r\n        /// The inputs.\r\n        /// </returns>\r\n        private static dynamic[] GetInputs4()\r\n        {\r\n            var basicInfo = \"{\\\"name\\\": \\\"Dishant\\\",\\\"email\\\": \\\"abc@xyz.com\\\",\\\"creditHistory\\\": \\\"good\\\",\\\"country\\\": \\\"canada\\\",\\\"loyaltyFactor\\\": 3,\\\"totalPurchasesToDate\\\": 70000}\";\r\n            var orderInfo = \"{\\\"totalOrders\\\": 50,\\\"recurringItems\\\": 2}\";\r\n            var telemetryInfo = \"{\\\"noOfVisitsPerMonth\\\": 10,\\\"percentageOfBuyingToVisit\\\": 15}\";\r\n            var laborCategoriesInput = \"[{\\\"country\\\": \\\"india\\\", \\\"loyaltyFactor\\\": 2, \\\"totalPurchasesToDate\\\": 20000}]\";\r\n            var currentLaborCategoryInput = \"{\\\"CurrentLaborCategoryProp\\\":\\\"TestVal2\\\"}\";\r\n\r\n            dynamic input1 = JsonConvert.DeserializeObject<List<RuleTestClass>>(laborCategoriesInput);\r\n            dynamic input2 = JsonConvert.DeserializeObject<ExpandoObject>(currentLaborCategoryInput);\r\n            dynamic input3 = JsonConvert.DeserializeObject<ExpandoObject>(telemetryInfo);\r\n            dynamic input4 = JsonConvert.DeserializeObject<ExpandoObject>(basicInfo);\r\n            dynamic input5 = JsonConvert.DeserializeObject<ExpandoObject>(orderInfo);\r\n\r\n            var inputs = new dynamic[]\r\n                {\r\n                    input1,\r\n                    input2,\r\n                    input3,\r\n                    input4,\r\n                    input5\r\n                };\r\n\r\n            return inputs;\r\n        }\r\n\r\n        [ExcludeFromCodeCoverage]\r\n        public class TestInstanceUtils\r\n        {\r\n            public bool CheckExists(string str)\r\n            {\r\n                if (!string.IsNullOrEmpty(str))\r\n                {\r\n                    return true;\r\n                }\r\n\r\n                return false;\r\n            }\r\n\r\n        }\r\n\r\n    }\r\n}\r\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/CaseSensitiveTests.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class CaseSensitiveTests\n    {\n        [Theory]\n        [InlineData(true,true,false)]\n        [InlineData(false,true,true)]\n        public async Task CaseSensitiveTest(bool caseSensitive, bool expected1, bool expected2)\n        {\n            var reSettings = new ReSettings {\n                IsExpressionCaseSensitive = caseSensitive\n            };\n\n\n            var worflow = new Workflow {\n                WorkflowName = \"CaseSensitivityTest\",\n                Rules = new[] {\n                    new Rule {\n                        RuleName = \"check same case1\",\n                        Expression = \"input1 == \\\"hello\\\"\"\n                    },\n                    new Rule {\n                        RuleName = \"check same case2\",\n                        Expression = \"INPUT1 == \\\"hello\\\"\"\n                    }\n                }\n            };\n\n            var re = new RulesEngine(new[] { worflow }, reSettings);\n            var result = await re.ExecuteAllRulesAsync(\"CaseSensitivityTest\", \"hello\");\n\n            Assert.Equal(expected1, result[0].IsSuccess);\n            Assert.Equal(expected2, result[1].IsSuccess);\n        }\n\n\n\n\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/CustomTypeProviderTests.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing Moq;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class CustomTypeProviderTests : IDisposable\n    {\n        public void Dispose()\n        {\n        }\n\n        private CustomTypeProvider CreateProvider(params Type[] customTypes)\n        {\n            return new CustomTypeProvider(customTypes);\n        }\n\n        [Fact]\n        public void GetCustomTypes_DefaultProvider_IncludesEnumerableAndObject()\n        {\n            var provider = CreateProvider();\n            var allTypes = provider.GetCustomTypes();\n            Assert.NotEmpty(allTypes);\n            Assert.Contains(typeof(System.Linq.Enumerable), allTypes);\n            Assert.Contains(typeof(object), allTypes);\n        }\n\n        [Fact]\n        public void GetCustomTypes_WithListOfGuid_ContainsIEnumerableOfGuid()\n        {\n            var initial = new[] { typeof(List<Guid>) };\n            var provider = CreateProvider(initial);\n            var allTypes = provider.GetCustomTypes();\n            Assert.Contains(typeof(IEnumerable<Guid>), allTypes);\n            Assert.Contains(typeof(List<Guid>), allTypes);\n            Assert.Contains(typeof(System.Linq.Enumerable), allTypes);\n            Assert.Contains(typeof(object), allTypes);\n        }\n\n        [Fact]\n        public void GetCustomTypes_ListOfListString_ContainsIEnumerableOfListString()\n        {\n            var nestedListType = typeof(List<List<string>>);\n            var provider = CreateProvider(nestedListType);\n            var allTypes = provider.GetCustomTypes();\n            Assert.Contains(typeof(IEnumerable<List<string>>), allTypes);\n            Assert.Contains(nestedListType, allTypes);\n            Assert.Contains(typeof(System.Linq.Enumerable), allTypes);\n            Assert.Contains(typeof(object), allTypes);\n        }\n\n        [Fact]\n        public void GetCustomTypes_ArrayOfStringArrays_ContainsIEnumerableOfStringArray()\n        {\n            var arrayType = typeof(string[][]);\n            var provider = CreateProvider(arrayType);\n            var allTypes = provider.GetCustomTypes();\n            Assert.Contains(typeof(IEnumerable<string[]>), allTypes);\n            Assert.Contains(arrayType, allTypes);\n            Assert.Contains(typeof(System.Linq.Enumerable), allTypes);\n            Assert.Contains(typeof(object), allTypes);\n        }\n\n        [Fact]\n        public void GetCustomTypes_NullableIntArray_ContainsIEnumerableOfNullableInt()\n        {\n            var nullableInt = typeof(int?);\n            var arrayType = typeof(int?[]);\n            var provider = CreateProvider(arrayType);\n            var allTypes = provider.GetCustomTypes();\n            Assert.Contains(typeof(IEnumerable<int?>), allTypes);\n            Assert.Contains(arrayType, allTypes);\n            Assert.Contains(typeof(System.Linq.Enumerable), allTypes);\n            Assert.Contains(typeof(object), allTypes);\n        }\n\n        [Fact]\n        public void GetCustomTypes_MultipleTypes_NoDuplicates()\n        {\n            var repeatedType = typeof(List<string>);\n            var provider = CreateProvider(repeatedType, repeatedType);\n            var allTypes = provider.GetCustomTypes();\n            var matches = allTypes.Where(t => t == repeatedType).ToList();\n            Assert.Single(matches);\n            var interfaceMatches = allTypes.Where(t => t == typeof(IEnumerable<string>)).ToList();\n            Assert.Single(interfaceMatches);\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/EmptyRulesTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing Newtonsoft.Json;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Dynamic;\nusing System.Linq;\nusing System.Text.Json.Serialization;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class EmptyRulesTest\n    {\n        [Fact]\n        private async Task EmptyRules_ReturnsExepectedResults()\n        {\n            var workflow = GetEmptyWorkflow();\n            var reSettings = new ReSettings { };\n            RulesEngine rulesEngine = new RulesEngine();\n\n            Func<Task> action = () => {\n                new RulesEngine(workflow, reSettings: reSettings);\n                return Task.CompletedTask;\n            };\n\n            Exception ex = await Assert.ThrowsAsync<Exceptions.RuleValidationException>(action);\n\n            Assert.Contains(\"Atleast one of Rules or WorkflowsToInject must be not empty\", ex.Message);\n        }\n        [Fact]\n        private async Task NestedRulesWithEmptyNestedActions_ReturnsExepectedResults()\n        {\n            var workflow = GetEmptyNestedWorkflows();\n            var reSettings = new ReSettings { };\n            RulesEngine rulesEngine = new RulesEngine();\n\n            Func<Task> action = () => {\n                new RulesEngine(workflow, reSettings: reSettings);\n                return Task.CompletedTask;\n            };\n\n            Exception ex = await Assert.ThrowsAsync<Exceptions.RuleValidationException>(action);\n\n            Assert.Contains(\"Atleast one of Rules or WorkflowsToInject must be not empty\", ex.Message);\n        }\n\n        private Workflow[] GetEmptyWorkflow()\n        {\n            return new[] {\n                new Workflow {\n                    WorkflowName = \"EmptyRulesTest\",\n                    Rules = new Rule[] {\n                    }\n                }\n            };\n        }\n\n        private Workflow[] GetEmptyNestedWorkflows()\n        {\n            return new[] {\n                new Workflow {\n                    WorkflowName = \"EmptyNestedRulesTest\",\n                    Rules = new Rule[] {\n                        new Rule {\n                            RuleName = \"AndRuleTrueFalse\",\n                            Operator = \"And\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule1\",\n                                    Expression = \"input1.TrueValue == true\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule1\",\n                                    Expression = \"input1.TrueValue == false\"\n                                }\n\n                            }\n                        },\n                        new Rule {\n                            RuleName = \"OrRuleTrueFalse\",\n                            Operator = \"Or\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule2\",\n                                    Expression = \"input1.TrueValue == true\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule2\",\n                                    Expression = \"input1.TrueValue == false\"\n                                }\n\n                            }\n                        },\n                        new Rule {\n                            RuleName = \"AndRuleFalseTrue\",\n                            Operator = \"And\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule3\",\n                                    Expression = \"input1.TrueValue == false\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule4\",\n                                    Expression = \"input1.TrueValue == true\"\n                                }\n\n                            }\n                        },\n                         new Rule {\n                            RuleName = \"OrRuleFalseTrue\",\n                            Operator = \"Or\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule3\",\n                                    Expression = \"input1.TrueValue == false\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule4\",\n                                    Expression = \"input1.TrueValue == true\"\n                                }\n\n                            }\n                         }\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"EmptyNestedRulesActionsTest\",\n                    Rules = new Rule[] {\n                        new Rule {\n                            RuleName = \"AndRuleTrueFalse\",\n                            Operator = \"And\",\n                            Rules = new Rule[] {\n\n                            },\n                            Actions =  new RuleActions {\n                                        OnFailure = new ActionInfo{\n                                            Name = \"OutputExpression\",\n                                            Context = new Dictionary<string, object> {\n                                                { \"Expression\", \"input1.TrueValue\" }\n                                            }\n                                        }\n                                    }\n                        }\n                    }\n                }\n\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/ExpressionUtilsTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.HelperFunctions;\nusing System.Diagnostics.CodeAnalysis;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class ExpressionUtilsTest\n    {\n        [Fact]\n        public void CheckContainsTest()\n        {\n            var result = ExpressionUtils.CheckContains(\"\", \"\");\n            Assert.False(result);\n\n            result = ExpressionUtils.CheckContains(null, \"\");\n            Assert.False(result);\n\n            result = ExpressionUtils.CheckContains(\"4\", \"1,2,3,4,5\");\n            Assert.True(result);\n\n            result = ExpressionUtils.CheckContains(\"6\", \"1,2,3,4,5\");\n            Assert.False(result);\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/LambdaExpressionBuilderTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.Models;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class LambdaExpressionBuilderTest\n    {\n        [Fact]\n        public void BuildExpressionForRuleTest()\n        {\n            var reSettings = new ReSettings();\n            var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, new RuleExpressionParser(reSettings));\n            var builder = objBuilderFactory.RuleGetExpressionBuilder(RuleExpressionType.LambdaExpression);\n\n            var ruleParameters = new RuleParameter[] {\n                new RuleParameter(\"RequestType\",\"Sales\"),\n                new RuleParameter(\"RequestStatus\", \"Active\"),\n                new RuleParameter(\"RegistrationStatus\", \"InProcess\")\n            };\n\n\n            var mainRule = new Rule {\n                RuleName = \"rule1\",\n                Operator = \"And\",\n                Rules = new List<Rule>()\n            };\n\n            var dummyRule = new Rule {\n                RuleName = \"testRule1\",\n                RuleExpressionType = RuleExpressionType.LambdaExpression,\n                Expression = \"RequestType == \\\"vod\\\"\"\n            };\n\n            mainRule.Rules = mainRule.Rules.Append(dummyRule);\n            var func = builder.BuildDelegateForRule(dummyRule, ruleParameters);\n\n            Assert.NotNull(func);\n            Assert.Equal(typeof(RuleResultTree), func.Method.ReturnType);\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/ListofRuleResultTreeExtensionTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing RulesEngine.Extensions;\nusing RulesEngine.Models;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class ListofRuleResultTreeExtensionTest\n    {\n        [Fact]\n        public void OnSuccessWithSuccessTest()\n        {\n            var rulesResultTree = new List<RuleResultTree>()\n            {\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = true,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 1\"\n                    }\n                },\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = false,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 2\"\n                    }\n                },\n\n            };\n\n            var successEventName = string.Empty;\n\n            rulesResultTree.OnSuccess((eventName) => {\n                successEventName = eventName;\n            });\n\n            Assert.Equal(\"Test Rule 1\", successEventName);\n        }\n\n        [Fact]\n        public void OnSuccessWithSuccessWithEventTest()\n        {\n            var rulesResultTree = new List<RuleResultTree>()\n            {\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = true,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 1\",\n                        SuccessEvent = \"Event 1\"\n                    }\n                },\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = false,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 2\"\n                    }\n                },\n\n            };\n\n            var successEventName = string.Empty;\n\n            rulesResultTree.OnSuccess((eventName) => {\n                successEventName = eventName;\n            });\n\n            Assert.Equal(\"Event 1\", successEventName);\n        }\n\n        [Fact]\n        public void OnSuccessWithouSuccessTest()\n        {\n            var rulesResultTree = new List<RuleResultTree>()\n            {\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = false,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 1\"\n                    }\n                },\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = false,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 2\"\n                    }\n                },\n\n            };\n\n            var successEventName = string.Empty;\n\n            rulesResultTree.OnSuccess((eventName) => {\n                successEventName = eventName;\n            });\n\n            Assert.Equal(successEventName, string.Empty);\n        }\n\n\n        [Fact]\n        public void OnFailWithSuccessTest()\n        {\n            var rulesResultTree = new List<RuleResultTree>()\n            {\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = true,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 1\"\n                    }\n                },\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = false,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 2\"\n                    }\n                },\n\n            };\n\n            var successEventName = true;\n\n            rulesResultTree.OnFail(() => {\n                successEventName = false;\n            });\n\n            Assert.True(successEventName);\n        }\n\n        [Fact]\n        public void OnFailWithoutSuccessTest()\n        {\n            var rulesResultTree = new List<RuleResultTree>()\n            {\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = false,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 1\"\n                    }\n                },\n                new RuleResultTree()\n                {\n                    ChildResults = null,\n                    ExceptionMessage = string.Empty,\n                    Inputs = new Dictionary<string, object>(),\n                    IsSuccess = false,\n                    Rule = new Rule()\n                    {\n                        RuleName = \"Test Rule 2\"\n                    }\n                },\n\n            };\n\n            var successEventName = true;\n\n            rulesResultTree.OnFail(() => {\n                successEventName = false;\n            });\n\n            Assert.False(successEventName);\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/NestedRulesTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing Newtonsoft.Json;\nusing RulesEngine.Models;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Dynamic;\nusing System.Linq;\nusing System.Text.Json.Serialization;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class NestedRulesTest\n    {\n\n        [Theory]\n        [InlineData(NestedRuleExecutionMode.All)]\n        [InlineData(NestedRuleExecutionMode.Performance)]\n        public async Task NestedRulesShouldFollowExecutionMode(NestedRuleExecutionMode mode)\n        {\n            var workflow = GetWorkflow();\n            var reSettings = new ReSettings { NestedRuleExecutionMode = mode };\n            var rulesEngine = new RulesEngine(workflow, reSettings: reSettings);\n            dynamic input1 = new ExpandoObject();\n            input1.trueValue = true;\n\n            List<RuleResultTree> result = await rulesEngine.ExecuteAllRulesAsync(\"NestedRulesTest\", input1);\n            var andResults = result.Where(c => c.Rule.Operator == \"And\").ToList();\n            var orResults = result.Where(c => c.Rule.Operator == \"Or\").ToList();\n            Assert.All(andResults,\n                c => Assert.False(c.IsSuccess)\n                );\n            Assert.All(orResults,\n                c => Assert.True(c.IsSuccess));\n\n            if (mode == NestedRuleExecutionMode.All)\n            {\n                Assert.All(andResults,\n                    c => Assert.Equal(c.Rule.Rules.Count(), c.ChildResults.Count()));\n                Assert.All(orResults,\n                    c => Assert.Equal(c.Rule.Rules.Count(), c.ChildResults.Count()));\n            }\n            else if (mode == NestedRuleExecutionMode.Performance)\n            {\n                Assert.All(andResults,\n                    c => {\n                        Assert.Equal(c.IsSuccess, c.ChildResults.Last().IsSuccess);\n                        Assert.Single(c.ChildResults.Where(d => c.IsSuccess == d.IsSuccess));\n                        Assert.True(c.ChildResults.SkipLast(1).All(d => d.IsSuccess == true));\n                    });\n\n                Assert.All(orResults,\n                    c => {\n                        Assert.Equal(c.IsSuccess, c.ChildResults.Last().IsSuccess);\n                        Assert.Single(c.ChildResults.Where(d => c.IsSuccess == d.IsSuccess));\n                        Assert.True(c.ChildResults.SkipLast(1).All(d => d.IsSuccess == false));\n                    });\n\n            }\n\n\n        }\n\n        [Fact]\n        private async Task NestedRulesWithNestedActions_ReturnsCorrectResults()\n        {\n            var workflow = GetWorkflow();\n            var reSettings = new ReSettings { };\n            var rulesEngine = new RulesEngine(workflow, reSettings: reSettings);\n            dynamic input1 = new ExpandoObject();\n            input1.trueValue = true;\n\n            List<RuleResultTree> result = await rulesEngine.ExecuteAllRulesAsync(\"NestedRulesActionsTest\", input1);\n\n            Assert.False(result[0].IsSuccess);\n            Assert.Equal(input1.trueValue, result[0].ActionResult.Output);\n            Assert.All(result[0].ChildResults, (childResult) => Assert.Equal(input1.trueValue, childResult.ActionResult.Output));\n        }\n\n        [Fact]\n        private async Task NestedRulesWithNestedActions_WorkflowParsedWithSystemTextJson_ReturnsCorrectResults()\n        {\n            var workflow = GetWorkflow();\n            var workflowStr = JsonConvert.SerializeObject(workflow);\n\n            var serializationOptions = new System.Text.Json.JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } };\n\n\n            var workflowViaTextJson = System.Text.Json.JsonSerializer.Deserialize<Workflow[]>(workflowStr, serializationOptions);\n\n            var reSettings = new ReSettings { };\n            var rulesEngine = new RulesEngine(workflowViaTextJson, reSettings: reSettings);\n            dynamic input1 = new ExpandoObject();\n            input1.trueValue = true;\n\n            List<RuleResultTree> result = await rulesEngine.ExecuteAllRulesAsync(\"NestedRulesActionsTest\", input1);\n\n            Assert.False(result[0].IsSuccess);\n            Assert.Equal(input1.trueValue, result[0].ActionResult.Output);\n            Assert.All(result[0].ChildResults, (childResult) => Assert.Equal(input1.trueValue, childResult.ActionResult.Output));\n\n\n        }\n\n\n\n        private Workflow[] GetWorkflow()\n        {\n            return new[] {\n                new Workflow {\n                    WorkflowName = \"NestedRulesTest\",\n                    Rules = new Rule[] {\n                        new Rule {\n                            RuleName = \"AndRuleTrueFalse\",\n                            Operator = \"And\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule1\",\n                                    Expression = \"input1.TrueValue == true\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule1\",\n                                    Expression = \"input1.TrueValue == false\"\n                                }\n\n                            }\n                        },\n                        new Rule {\n                            RuleName = \"OrRuleTrueFalse\",\n                            Operator = \"Or\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule2\",\n                                    Expression = \"input1.TrueValue == true\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule2\",\n                                    Expression = \"input1.TrueValue == false\"\n                                }\n\n                            }\n                        },\n                        new Rule {\n                            RuleName = \"AndRuleFalseTrue\",\n                            Operator = \"And\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule3\",\n                                    Expression = \"input1.TrueValue == false\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule4\",\n                                    Expression = \"input1.TrueValue == true\"\n                                }\n\n                            }\n                        },\n                         new Rule {\n                            RuleName = \"OrRuleFalseTrue\",\n                            Operator = \"Or\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule3\",\n                                    Expression = \"input1.TrueValue == false\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule4\",\n                                    Expression = \"input1.TrueValue == true\"\n                                }\n\n                            }\n                         }\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"NestedRulesActionsTest\",\n                    Rules = new Rule[] {\n                        new Rule {\n                            RuleName = \"AndRuleTrueFalse\",\n                            Operator = \"And\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule1\",\n                                    Expression = \"input1.TrueValue == true\",\n                                    Actions =  new RuleActions {\n                                        OnSuccess = new ActionInfo{\n                                            Name = \"OutputExpression\",\n                                            Context = new Dictionary<string, object> {\n                                                { \"Expression\", \"input1.TrueValue\" }\n                                            }\n                                        }\n                                    }\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule1\",\n                                    Expression = \"input1.TrueValue == false\",\n                                    Actions =  new RuleActions {\n                                        OnFailure = new ActionInfo{\n                                            Name = \"OutputExpression\",\n                                            Context = new Dictionary<string, object> {\n                                                { \"Expression\", \"input1.TrueValue\" }\n                                            }\n                                        }\n                                    }\n                                }\n                            },\n                            Actions =  new RuleActions {\n                                        OnFailure = new ActionInfo{\n                                            Name = \"OutputExpression\",\n                                            Context = new Dictionary<string, object> {\n                                                { \"Expression\", \"input1.TrueValue\" }\n                                            }\n                                        }\n                                    }\n                        }\n                    }\n                }\n\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/ParameterNameChangeTest.cs",
    "content": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Dynamic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class ParameterNameChangeTest\n    {\n        [Fact]\n        public async Task RunTwiceTest_ReturnsExpectedResults()\n        {\n            var workflow = new Workflow {\n                WorkflowName = \"ParameterNameChangeWorkflow\",\n                Rules = new Rule[] {\n                    new Rule {\n                        RuleName = \"ParameterNameChangeRule\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"test.blah == 1\"\n                    }\n                }\n            };\n            var engine = new RulesEngine();\n            engine.AddOrUpdateWorkflow(workflow);\n\n            dynamic dynamicBlah = new ExpandoObject();\n            dynamicBlah.blah = (Int64)1;\n            var input_pass = new RuleParameter(\"test\", dynamicBlah);\n            var input_fail = new RuleParameter(\"SOME_OTHER_NAME\", dynamicBlah);\n            // RuleParameter name matches expression, so should pass.\n            var pass_results = await engine.ExecuteAllRulesAsync(\"ParameterNameChangeWorkflow\", input_pass);\n            // RuleParameter name DOES NOT MATCH expression, so should fail.\n            var fail_results = await engine.ExecuteAllRulesAsync(\"ParameterNameChangeWorkflow\", input_fail);\n            Assert.True(pass_results.First().IsSuccess);\n            Assert.False(fail_results.First().IsSuccess);\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RuleCompilerTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.Models;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class RuleCompilerTest\n    {\n        [Fact]\n        public void RuleCompiler_NullCheck()\n        {\n            Assert.Throws<ArgumentNullException>(() => new RuleCompiler(null, null));\n            var reSettings = new ReSettings();\n            var parser = new RuleExpressionParser(reSettings);\n            Assert.Throws<ArgumentNullException>(() => new RuleCompiler(null, null));\n        }\n\n        [Fact]\n        public void RuleCompiler_CompileRule_ThrowsException()\n        {\n            var reSettings = new ReSettings();\n            var parser = new RuleExpressionParser(reSettings);\n            var compiler = new RuleCompiler(new RuleExpressionBuilderFactory(reSettings, parser),null);\n            Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, RuleExpressionType.LambdaExpression,null,null));\n            Assert.Throws<ArgumentNullException>(() => compiler.CompileRule(null, RuleExpressionType.LambdaExpression, new RuleParameter[] { null },null));\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RuleExpressionBuilderFactoryTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.Models;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class RuleExpressionBuilderFactoryTest\n    {\n        [Theory]\n        [InlineData(RuleExpressionType.LambdaExpression, typeof(LambdaExpressionBuilder))]\n        public void RuleGetExpressionBuilderTest(RuleExpressionType expressionType, Type expectedExpressionBuilderType)\n        {\n            var reSettings = new ReSettings();\n            var parser = new RuleExpressionParser(reSettings);\n            var objBuilderFactory = new RuleExpressionBuilderFactory(reSettings, parser);\n            var builder = objBuilderFactory.RuleGetExpressionBuilder(expressionType);\n\n            var builderType = builder.GetType();\n            Assert.Equal(expectedExpressionBuilderType.ToString(), builderType.ToString());\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RuleExpressionParserTests/RuleExpressionParserTests.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing Newtonsoft.Json.Linq;\nusing RulesEngine.ExpressionBuilders;\nusing RulesEngine.Models;\nusing System.Diagnostics.CodeAnalysis;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest.RuleExpressionParserTests\n{\n    [ExcludeFromCodeCoverage]\n    public class RuleExpressionParserTests\n    {\n        public RuleExpressionParserTests() { \n           \n        \n        }\n\n\n        [Fact]\n        public void TestExpressionWithJObject()\n        {\n            var settings = new ReSettings {\n                CustomTypes = new[]\n                {\n                    typeof(JObject),\n                    typeof(JToken),\n                    typeof(JArray)\n                }\n            };\n            var parser = new RuleExpressionParser(settings);\n\n            var json = @\"{\n               \"\"list\"\": [\n                    { \"\"item1\"\": \"\"hello\"\", \"\"item3\"\": 1 },\n                    { \"\"item2\"\": \"\"world\"\" }\n               ]\n            }\";\n            var input = JObject.Parse(json);\n\n            var result1 = parser.Evaluate<object>(\n                \"Convert.ToInt32(input[\\\"list\\\"][0][\\\"item3\\\"]) == 1\",\n                new[] { new RuleParameter(\"input\", input) }\n            );\n            Assert.True((bool)result1);\n\n            var result2 = parser.Evaluate<object>(\n                \"Convert.ToString(input[\\\"list\\\"][1][\\\"item2\\\"]) == \\\"world\\\"\",\n                new[] { new RuleParameter(\"input\", input) }\n            );\n            Assert.True((bool)result2);\n\n            var result3 = parser.Evaluate<object>(\n                \"string.Concat(\" +\n                  \"Convert.ToString(input[\\\"list\\\"][0][\\\"item1\\\"]), \" +\n                  \"Convert.ToString(input[\\\"list\\\"][1][\\\"item2\\\"]))\",\n                new[] { new RuleParameter(\"input\", input) }\n            );\n            Assert.Equal(\"helloworld\", result3);\n        }\n\n        [Theory]\n        [InlineData(false)]\n        public void TestExpressionWithDifferentCompilerSettings(bool fastExpressionEnabled){\n            var ruleParser = new RuleExpressionParser(new Models.ReSettings() { UseFastExpressionCompiler = fastExpressionEnabled });\n\n            decimal? d1 = null;\n            var result = ruleParser.Evaluate<bool>(\"d1 < 20\", new[] { Models.RuleParameter.Create(\"d1\", d1) });\n            Assert.False(result);\n        }\n    }\n\n    \n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RuleParameterTests.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing AutoFixture;\nusing RulesEngine.Models;\nusing System;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest;\npublic class RuleParameterTests\n{\n    [Fact]\n    public void Create_SetsPropertiesCorrectly()\n    {\n        var fixture = new Fixture();\n        var name = fixture.Create<string>();\n        var type = fixture.Create<Type>();\n\n        var result = RuleParameter.Create(name, type);\n\n        Assert.Equal(name, result.Name);\n        Assert.Equal(type, result.Type);\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RuleTestClass.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n//  Licensed under the MIT License.\n\nusing Newtonsoft.Json;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class RuleTestClass\n    {\n        [JsonProperty(\"country\")]\n        public string Country { get; set; }\n\n        [JsonProperty(\"loyaltyFactor\")]\n        public int loyaltyFactor { get; set; }\n        public int TotalPurchasesToDate { get; set; }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RuleValidationTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing Newtonsoft.Json;\nusing RulesEngine.HelperFunctions;\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Dynamic;\nusing System.Linq;\nusing System.Text.Json.Serialization;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class RuleValidationTest\n    {\n        [Fact]\n        public async Task NullExpressionithLambdaExpression_ReturnsExepectedResults()\n        {\n            var workflow = GetNullExpressionithLambdaExpressionWorkflow();\n            var reSettings = new ReSettings { };\n            RulesEngine rulesEngine = new RulesEngine();\n\n            Func<Task> action = () => {\n                new RulesEngine(workflow, reSettings: reSettings);\n                return Task.CompletedTask;\n            };\n\n            Exception ex = await Assert.ThrowsAsync<Exceptions.RuleValidationException>(action);\n\n            Assert.Contains(Constants.LAMBDA_EXPRESSION_EXPRESSION_NULL_ERRMSG, ex.Message);\n\n        }\n\n        [Fact]\n        public async Task NestedRulesWithMissingOperator_ReturnsExepectedResults()\n        {\n            var workflow = GetEmptyOperatorWorkflow();\n            var reSettings = new ReSettings { };\n            RulesEngine rulesEngine = new RulesEngine();\n\n            Func<Task> action = () => {\n                new RulesEngine(workflow, reSettings: reSettings);\n                return Task.CompletedTask;\n            };\n\n            Exception ex = await Assert.ThrowsAsync<Exceptions.RuleValidationException>(action);\n\n            Assert.Contains(Constants.OPERATOR_RULES_ERRMSG, ex.Message);\n\n        }\n\n        private Workflow[] GetNullExpressionithLambdaExpressionWorkflow()\n        {\n            return new[] {\n                new Workflow {\n                    WorkflowName = \"NestedRulesTest\",\n                    Rules = new Rule[] {\n                        new Rule {\n                            RuleName = \"TestRule\",\n                            RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        }\n                    }\n                }\n            };\n        }\n\n        private Workflow[] GetEmptyOperatorWorkflow()\n        {\n            return new[] {\n                new Workflow {\n                    WorkflowName = \"NestedRulesTest\",\n                    Rules = new Rule[] {\n                        new Rule {\n                            RuleName = \"AndRuleTrueFalse\",\n                            Expression = \"true == true\",\n                            Rules = new Rule[] {\n                                new Rule{\n                                    RuleName = \"trueRule1\",\n                                    Expression = \"input1.TrueValue == true\",\n                                },\n                                new Rule {\n                                    RuleName = \"falseRule1\",\n                                    Expression = \"input1.TrueValue == false\"\n                                }\n\n                            }\n                        }\n                    }\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RulesEnabledTests.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\nusing RulesEngine.Models;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class RulesEnabledTests\n    {\n        public RulesEnabledTests()\n        {\n\n        }\n\n        [Theory]\n        [InlineData(\"RuleEnabledFeatureTest\", new bool[] { true, true })]\n        [InlineData(\"RuleEnabledNestedFeatureTest\", new bool[] { true, true, false })]\n        public async Task RulesEngine_ShouldOnlyExecuteEnabledRules(string workflowName, bool[] expectedRuleResults)\n        {\n            var workflow = GetWorkflows();\n            var rulesEngine = new RulesEngine(workflow, reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false });\n            var input1 = new {\n                TrueValue = true\n            };\n            var result = await rulesEngine.ExecuteAllRulesAsync(workflowName, input1);\n            Assert.NotNull(result);\n            Assert.True(NestedEnabledCheck(result));\n\n            Assert.Equal(expectedRuleResults.Length, result.Count);\n            for (var i = 0; i < expectedRuleResults.Length; i++)\n            {\n                Assert.Equal(expectedRuleResults[i], result[i].IsSuccess);\n            }\n        }\n\n\n        [Theory]\n        [InlineData(\"RuleEnabledFeatureTest\", new bool[] { true, true })]\n        [InlineData(\"RuleEnabledNestedFeatureTest\", new bool[] { true, true, false })]\n        public async Task WorkflowUpdatedRuleEnabled_ShouldReflect(string workflowName, bool[] expectedRuleResults)\n        {\n            var workflow = GetWorkflows().Single(c => c.WorkflowName == workflowName);\n            var rulesEngine = new RulesEngine(reSettings: new ReSettings() { EnableExceptionAsErrorMessage = false});\n            rulesEngine.AddWorkflow(workflow);\n            var input1 = new {\n                TrueValue = true\n            };\n            var result = await rulesEngine.ExecuteAllRulesAsync(workflowName, input1);\n            Assert.NotNull(result);\n            Assert.True(NestedEnabledCheck(result));\n\n            Assert.Equal(expectedRuleResults.Length, result.Count);\n            for (var i = 0; i < expectedRuleResults.Length; i++)\n            {\n                Assert.Equal(expectedRuleResults[i], result[i].IsSuccess);\n            }\n\n            rulesEngine.RemoveWorkflow(workflowName);\n\n            var firstRule = workflow.Rules.First();\n\n            firstRule.Enabled = false;\n            rulesEngine.AddWorkflow(workflow);\n\n            var expectedLength = workflow.Rules.Count(c => c.Enabled);\n\n            var result2 = await rulesEngine.ExecuteAllRulesAsync(workflowName, input1);\n            Assert.Equal(expectedLength, result2.Count);\n\n            Assert.DoesNotContain(result2, c => c.Rule.RuleName == firstRule.RuleName);\n        }\n\n        private bool NestedEnabledCheck(IEnumerable<RuleResultTree> ruleResults)\n        {\n            var areAllRulesEnabled = ruleResults.All(c => c.Rule.Enabled);\n            if (areAllRulesEnabled)\n            {\n                foreach (var ruleResult in ruleResults)\n                {\n                    if (ruleResult.ChildResults?.Any() == true)\n                    {\n                        var areAllChildRulesEnabled = NestedEnabledCheck(ruleResult.ChildResults);\n                        if (areAllChildRulesEnabled == false)\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            return areAllRulesEnabled;\n        }\n\n        private Workflow[] GetWorkflows()\n        {\n            return new[] {\n                new Workflow {\n                    WorkflowName = \"RuleEnabledFeatureTest\",\n                    Rules = new List<Rule> {\n                        new Rule {\n                            RuleName = \"RuleWithoutEnabledFieldMentioned\",\n                            Expression = \"input1.TrueValue == true\"\n                        },\n                        new Rule {\n                            RuleName = \"RuleWithEnabledSetToTrue\",\n                            Expression = \"input1.TrueValue == true\",\n                            Enabled = true\n                        },\n                        new Rule {\n                            RuleName = \"RuleWithEnabledSetToFalse\",\n                            Expression = \"input1.TrueValue == true\",\n                            Enabled = false\n                        }\n\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"RuleEnabledNestedFeatureTest\",\n                    Rules = new List<Rule> {\n                        new Rule {\n                            RuleName = \"RuleWithoutEnabledFieldMentioned\",\n                            Operator = \"And\",\n                            Rules = new List<Rule> {\n                                new Rule {\n                                    RuleName = \"RuleWithoutEnabledField\",\n                                    Expression = \"input1.TrueValue\"\n                                }\n                            }\n                        },\n                        new Rule {\n                            RuleName = \"RuleWithOneChildSetToFalse\",\n                            Expression = \"input1.TrueValue == true\",\n                            Operator = \"And\",\n                            Rules = new List<Rule>{\n                                new Rule {\n                                    RuleName = \"RuleWithEnabledFalse\",\n                                    Expression = \"input1.TrueValue\",\n                                    Enabled = false,\n                                },\n                                new Rule {\n                                    RuleName = \"RuleWithEnabledTrue\",\n                                    Expression = \"input1.TrueValue\",\n                                    Enabled = true\n                                }\n                            }\n\n                        },\n                        new Rule {\n                            RuleName = \"RuleWithParentSetToFalse\",\n                            Operator = \"And\",\n                            Enabled = false,\n                            Rules = new List<Rule>{\n                                new Rule {\n                                    RuleName = \"RuleWithEnabledTrue\",\n                                    Expression = \"input1.TrueValue\",\n                                    Enabled = true\n                                }\n                            }\n                        },\n                        new Rule {\n                            RuleName = \"RuleWithAllChildSetToFalse\",\n                            Operator = \"And\",\n                            Enabled = true,\n                            Rules  = new List<Rule>{\n                                new Rule {\n                                    RuleName = \"ChildRuleWithEnabledFalse\",\n                                    Expression = \"input1.TrueValue\",\n                                    Enabled = false\n                                }\n                            }\n                        }\n\n                    }\n                }\n            };\n        }\n\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/RulesEngine.UnitTest.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>\n    <SignAssembly>True</SignAssembly>\n    <AssemblyOriginatorKeyFile>..\\..\\signing\\RulesEngine-publicKey.snk</AssemblyOriginatorKeyFile>\n    <DelaySign>True</DelaySign>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"AutoFixture\" Version=\"5.0.0-preview0012\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.12.0\" />\n    <PackageReference Include=\"Moq\" Version=\"4.20.72\" />\n    <PackageReference Include=\"System.Text.Json\" Version=\"9.0.2\" />\n    <PackageReference Include=\"xunit\" Version=\"2.9.3\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"3.0.2\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"coverlet.collector\" Version=\"6.0.4\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\RulesEngine\\RulesEngine.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Update=\"TestData\\rules1.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules11.json\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules4.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules3.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules2.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules5.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules6.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules7.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules8.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules10.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Update=\"TestData\\rules9.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "test/RulesEngine.UnitTest/ScopedParamsTest.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class MyObject\n    {\n        public string Name { get; set; }\n\n        public int Count { get; set; }\n    }\n\n    [ExcludeFromCodeCoverage]\n    public class ScopedParamsTest\n    {\n        [Theory]\n        [InlineData(\"NoLocalAndGlobalParams\")]\n        [InlineData(\"LocalParamsOnly\")]\n        [InlineData(\"GlobalParamsOnly\")]\n        [InlineData(\"GlobalAndLocalParams\")]\n        [InlineData(\"GlobalParamReferencedInLocalParams\")]\n        [InlineData(\"GlobalParamReferencedInNextGlobalParams\")]\n        [InlineData(\"LocalParamReferencedInNextLocalParams\")]\n        [InlineData(\"GlobalParamAndLocalParamsInNestedRules\")]\n        public async Task BasicWorkflows_ReturnsTrue(string workflowName)\n        {\n            var workflow = GetWorkflowList();\n\n            var engine = new RulesEngine();\n            engine.AddWorkflow(workflow);\n\n            var input1 = new {\n                trueValue = true,\n                falseValue = false\n            };\n\n            var result = await engine.ExecuteAllRulesAsync(workflowName, input1);\n            Assert.True(result.All(c => c.IsSuccess));\n\n            CheckResultTreeContainsAllInputs(workflowName, result);\n\n        }\n\n        [Theory]\n        [InlineData(\"GlobalAndLocalParams\")]\n        public async Task WorkflowUpdate_GlobalParam_ShouldReflect(string workflowName)\n        {\n            var workflow = GetWorkflowList();\n\n            var engine = new RulesEngine();\n            engine.AddWorkflow(workflow);\n\n            var input1 = new {\n                trueValue = true,\n                falseValue = false\n            };\n\n            var result = await engine.ExecuteAllRulesAsync(workflowName, input1);\n            Assert.True(result.All(c => c.IsSuccess));\n\n            var workflowToUpdate = workflow.Single(c => c.WorkflowName == workflowName);\n            engine.RemoveWorkflow(workflowName);\n            workflowToUpdate.GlobalParams.First().Expression = \"true == false\";\n            engine.AddWorkflow(workflowToUpdate);\n\n            var result2 = await engine.ExecuteAllRulesAsync(workflowName, input1);\n\n            Assert.True(result2.All(c => c.IsSuccess == false));\n        }\n\n\n        [Theory]\n        [InlineData(\"GlobalParamsOnly\", new[] { false })]\n        [InlineData(\"LocalParamsOnly\", new[] { false, true })]\n        [InlineData(\"GlobalAndLocalParams\", new[] { false })]\n        public async Task DisabledScopedParam_ShouldReflect(string workflowName, bool[] outputs)\n        {\n            var workflow = GetWorkflowList();\n\n            var engine = new RulesEngine(new string[] { }, new ReSettings {\n                EnableScopedParams = false\n            });\n            engine.AddWorkflow(workflow);\n\n            var input1 = new {\n                trueValue = true,\n                falseValue = false\n            };\n\n            var result = await engine.ExecuteAllRulesAsync(workflowName, input1);\n            for (var i = 0; i < result.Count; i++)\n            {\n                Assert.Equal(result[i].IsSuccess, outputs[i]);\n                if (result[i].IsSuccess == false)\n                {\n                    Assert.StartsWith(\"Exception while parsing expression\", result[i].ExceptionMessage);\n                }\n            }\n        }\n\n        [Theory]\n        [InlineData(\"GlobalParamsOnly\")]\n        [InlineData(\"LocalParamsOnly2\")]\n        [InlineData(\"GlobalParamsOnlyWithComplexInput\")]\n        public async Task ErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName)\n        {\n            var workflow = GetWorkflowList();\n\n            var engine = new RulesEngine(new string[] { }, null);\n            engine.AddWorkflow(workflow);\n\n            var input = new { };\n            var result = await engine.ExecuteAllRulesAsync(workflowName, input);\n\n            Assert.All(result, c => { \n                Assert.False(c.IsSuccess);\n                Assert.StartsWith(\"Error while compiling rule\", c.ExceptionMessage);\n            });\n\n        }\n\n        [Theory]\n        [InlineData(\"GlobalParamsOnlyWithComplexInput\")]\n        [InlineData(\"LocalParamsOnlyWithComplexInput\")]\n        public async Task RuntimeErrorInScopedParam_ShouldAppearAsErrorMessage(string workflowName)\n        {\n            var workflow = GetWorkflowList();\n\n            var engine = new RulesEngine(new string[] { }, null);\n            engine.AddWorkflow(workflow);\n\n\n\n            var input = new RuleTestClass();\n            var result = await engine.ExecuteAllRulesAsync(workflowName, input);\n\n            Assert.All(result, c => {\n                Assert.False(c.IsSuccess);\n                Assert.StartsWith(\"Error while executing scoped params for rule\", c.ExceptionMessage);\n            });\n\n\n        }\n\n        [Theory]\n        [InlineData(\"LocalParam_CorrectAnswer\")]\n        public async Task LocalParam_GivesCorrectAnswer(string workflowName)\n        {\n            var workflow = GetWorkflowList();\n\n            var reSettingsWithCustomTypes = new ReSettings { CustomTypes = new Type[] {  } };\n            var bre = new RulesEngine(workflow, reSettingsWithCustomTypes);\n\n            var myObject = new MyObject() {\n                Name = \"My Object\",\n                Count = 2\n            };\n\n            var rp1 = new RuleParameter(\"myObj\", myObject);\n\n            List<RuleResultTree> resultList = await bre.ExecuteAllRulesAsync(workflowName, rp1);\n            Assert.True(resultList[0].IsSuccess);\n\n            myObject.Count = 3;\n\n            resultList = await bre.ExecuteAllRulesAsync(workflowName, rp1);\n            Assert.False(resultList[0].IsSuccess);\n\n        }\n\n\n\n        private void CheckResultTreeContainsAllInputs(string workflowName, List<RuleResultTree> result)\n        {\n            var workflow = GetWorkflowList().Single(c => c.WorkflowName == workflowName);\n            var expectedInputs = new List<string>() { \"input1\" };\n            expectedInputs.AddRange(workflow.GlobalParams?.Select(c => c.Name) ?? new List<string>());\n\n\n            foreach (var resultTree in result)\n            {\n                CheckInputs(expectedInputs, resultTree);\n            }\n\n        }\n\n        private static void CheckInputs(IEnumerable<string> expectedInputs, RuleResultTree resultTree)\n        {\n            Assert.All(expectedInputs, input => Assert.True(resultTree.Inputs.ContainsKey(input)));\n\n            var localParamNames = resultTree.Rule.LocalParams?.Select(c => c.Name) ?? new List<string>();\n            Assert.All(localParamNames, input => Assert.True(resultTree.Inputs.ContainsKey(input)));\n\n            if (resultTree.ChildResults?.Any() == true)\n            {\n                foreach (var childResultTree in resultTree.ChildResults)\n                {\n                    CheckInputs(expectedInputs.Concat(localParamNames), childResultTree);\n                }\n\n            }\n\n        }\n        private Workflow[] GetWorkflowList()\n        {\n            return new Workflow[] {\n                new Workflow {\n                    WorkflowName = \"NoLocalAndGlobalParams\",\n                    Rules = new List<Rule> {\n                        new Rule {\n                            RuleName = \"TruthTest\",\n                            Expression = \"input1.trueValue\"\n                        }\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"LocalParamsOnly\",\n                    Rules = new List<Rule> {\n                        new Rule {\n\n                            RuleName = \"WithLocalParam\",\n                            LocalParams = new List<ScopedParam> {\n                                new ScopedParam {\n                                    Name = \"localParam1\",\n                                    Expression = \"input1.trueValue\"\n                                }\n                            },\n                            Expression = \"localParam1 == true\"\n                        },\n                        new Rule {\n\n                            RuleName = \"WithoutLocalParam\",\n                            Expression = \"input1.falseValue == false\"\n                        },\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"LocalParamsOnly2\",\n                    Rules = new List<Rule> {\n                        new Rule {\n\n                            RuleName = \"WithLocalParam\",\n                            LocalParams = new List<ScopedParam> {\n                                new ScopedParam {\n                                    Name = \"localParam1\",\n                                    Expression = \"input1.trueValue\"\n                                }\n                            },\n                            Expression = \"localParam1 == true\"\n                        }\n                    }\n                },\n\n                new Workflow {\n                    WorkflowName = \"GlobalParamsOnly\",\n                    GlobalParams = new List<ScopedParam> {\n                        new ScopedParam {\n                            Name = \"globalParam1\",\n                            Expression = \"input1.falseValue == false\"\n                        }\n                    },\n                    Rules = new List<Rule> {\n                        new Rule {\n                            RuleName = \"TrueTest\",\n                            Expression = \"globalParam1 == true\"\n                        }\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"GlobalAndLocalParams\",\n                    GlobalParams = new List<ScopedParam> {\n                        new ScopedParam {\n                            Name = \"globalParam1\",\n                            Expression = \"input1.falseValue == false\"\n                        }\n                    },\n                    Rules = new List<Rule> {\n                        new Rule {\n                            RuleName = \"WithLocalParam\",\n                            LocalParams = new List<ScopedParam> {\n                                new ScopedParam {\n                                    Name = \"localParam1\",\n                                    Expression = \"input1.trueValue\"\n                                }\n                            },\n                            Expression = \"globalParam1 == true && localParam1 == true\"\n                        },\n                    }\n\n                },\n                new Workflow {\n                    WorkflowName = \"GlobalParamReferencedInLocalParams\",\n                    GlobalParams = new List<ScopedParam> {\n                        new ScopedParam {\n                            Name = \"globalParam1\",\n                            Expression = \"\\\"testString\\\"\"\n                        }\n                    },\n                    Rules = new List<Rule> {\n                        new Rule {\n\n                            RuleName = \"WithLocalParam\",\n                            LocalParams = new List<ScopedParam> {\n                                new ScopedParam {\n                                    Name = \"localParam1\",\n                                    Expression = \"globalParam1.ToUpper()\"\n                                }\n                            },\n                            Expression = \"globalParam1 == \\\"testString\\\" && localParam1 == \\\"TESTSTRING\\\"\"\n                        },\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"GlobalParamReferencedInNextGlobalParams\",\n                    GlobalParams = new List<ScopedParam> {\n                        new ScopedParam {\n                            Name = \"globalParam1\",\n                            Expression = \"\\\"testString\\\"\"\n                        },\n                        new ScopedParam {\n                            Name = \"globalParam2\",\n                            Expression = \"globalParam1.ToUpper()\"\n                        }\n                    },\n                    Rules = new List<Rule> {\n                        new Rule {\n                            RuleName = \"WithLocalParam\",\n                            Expression = \"globalParam1 == \\\"testString\\\" && globalParam2 == \\\"TESTSTRING\\\"\"\n                        },\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"LocalParamReferencedInNextLocalParams\",\n                    Rules = new List<Rule> {\n                        new Rule {\n                            LocalParams = new List<ScopedParam> {\n                                new ScopedParam {\n                                    Name = \"localParam1\",\n                                    Expression = \"\\\"testString\\\"\"\n                                },\n                                new ScopedParam {\n                                    Name = \"localParam2\",\n                                    Expression = \"localParam1.ToUpper()\"\n                                }\n                            },\n                            RuleName = \"WithLocalParam\",\n                            Expression = \"localParam1 == \\\"testString\\\" && localParam2 == \\\"TESTSTRING\\\"\"\n                        },\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"GlobalParamAndLocalParamsInNestedRules\",\n                    GlobalParams = new List<ScopedParam> {\n                        new ScopedParam {\n                            Name = \"globalParam1\",\n                            Expression = @\"\"\"hello\"\"\"\n                        }\n                    },\n                    Rules = new List<Rule> {\n                        new Rule {\n                           RuleName = \"NestedRuleTest\",\n                           Operator = \"And\",\n                           LocalParams = new List<ScopedParam> {\n                                new ScopedParam {\n                                    Name = \"localParam1\",\n                                    Expression = @\"\"\"world\"\"\"\n                                }\n                           },\n                           Rules =  new List<Rule>{\n                               new Rule{\n                                   RuleName = \"NestedRule1\",\n                                   Expression = \"globalParam1 == \\\"hello\\\" && localParam1 == \\\"world\\\"\"\n                               },\n                               new Rule {\n                                   RuleName = \"NestedRule2\",\n                                   LocalParams = new List<ScopedParam> {\n                                       new ScopedParam {\n                                           Name = \"nestedLocalParam1\",\n                                           Expression = \"globalParam1 + \\\" \\\" + localParam1\"\n                                       }\n                                   },\n                                   Expression = \"nestedLocalParam1 == \\\"hello world\\\"\"\n                               }\n\n                           }\n\n                        }\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"LocalParamsOnlyWithComplexInput\",\n                    Rules = new List<Rule> {\n                        new Rule {\n\n                            RuleName = \"WithLocalParam\",\n                            LocalParams = new List<ScopedParam> {\n                                new ScopedParam {\n                                    Name = \"localParam1\",\n                                    Expression = \"input1.Country.ToLower()\"\n                                }\n                            },\n                            Expression = \"localParam1 == \\\"hello\\\"\"\n                        }\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"GlobalParamsOnlyWithComplexInput\",\n                    GlobalParams = new List<ScopedParam> {\n                        new ScopedParam {\n                            Name = \"globalParam1\",\n                            Expression = \"input1.Country.ToLower()\"\n                        }\n                    },\n                    Rules = new List<Rule> {\n                        new Rule {\n                            RuleName = \"TrueTest\",\n                            Expression = \"globalParam1 == \\\"hello\\\"\"\n                        },\n                        new Rule {\n                            RuleName = \"TrueTest2\",\n                            Expression = \"globalParam1.ToUpper() == \\\"HELLO\\\"\"\n                        }\n                    }\n                },\n                new Workflow {\n                    WorkflowName = \"LocalParam_CorrectAnswer\",\n                    Rules = new List<Rule> {\n                        new Rule\n                        {\n                            RuleName = \"Test Rule\",\n                            LocalParams = new List<LocalParam>\n                            {\n                                new LocalParam\n                                {\n                                    Name = \"threshold\",\n                                    Expression = \"3\"\n                                },\n                                new LocalParam\n                                {\n                                    Name = \"myList\",\n                                    Expression = \"new int[]{ 1, 2, 3, 4, 5 }\"\n                                }\n                            },\n                            SuccessEvent = \"Count is within tolerance.\",\n                            ErrorMessage = \"Not as expected.\",\n                            Expression = \"myList.Where(x => x < threshold).Contains(myObj.Count)\",\n                            RuleExpressionType = RuleExpressionType.LambdaExpression\n                        }\n                    }\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules1.json",
    "content": "{\n  \"WorkflowName\": \"inputWorkflow\",\n  \"Rules\": [\n    {\n      \"RuleName\": \"GiveDiscount10\",\n      \"SuccessEvent\": \"10\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"RuleExpressionType\": \"LambdaExpression\",\n      \"Expression\": \"input1.country == \\\"canada\\\" AND input1.loyaltyFactor <= 4\"\n    }\n  ]\n}"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules10.json",
    "content": "{\n    \"WorkflowName\": \"inputWorkflow\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"SuccessEvent\": \"10\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.Data.GetProperty(\\\"category\\\").GetString() == \\\"abc\\\"\"\n      }\n    ]\n  } "
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules11.json",
    "content": "{\n  \"WorkflowName\": \"MyWorkflow\",\n  \"WorkflowsToInject\": null,\n  \"RuleExpressionType\": 0,\n  \"GlobalParams\": [\n    {\n      \"Name\": \"threshold\",\n      \"Expression\": \"double.Parse(\\u00220.25\\u0022)\"\n    }\n  ],\n  \"Rules\": [\n    {\n      \"RuleName\": \"Activation\",\n      \"Properties\": null,\n      \"Operator\": null,\n      \"ErrorMessage\": null,\n      \"Enabled\": true,\n      \"RuleExpressionType\": 0,\n      \"WorkflowsToInject\": null,\n      \"Rules\": null,\n      \"LocalParams\": [\n        {\n          \"Name\": \"ruleCount\",\n          \"Expression\": \"int.Parse(\\u002215\\u0022)\"\n        }\n      ],\n      \"Expression\": \"input1.Count \\u003E= ruleCount \\u0026\\u0026 input1.Where(x =\\u003E x.Value \\u003E= threshold).Count() \\u003E= ruleCount\",\n      \"Actions\": null,\n      \"SuccessEvent\": null\n    },\n    {\n      \"RuleName\": \"Deactivation\",\n      \"Properties\": null,\n      \"Operator\": null,\n      \"ErrorMessage\": null,\n      \"Enabled\": true,\n      \"RuleExpressionType\": 0,\n      \"WorkflowsToInject\": null,\n      \"Rules\": null,\n      \"LocalParams\": [\n        {\n          \"Name\": \"ruleCount\",\n          \"Expression\": \"int.Parse(\\u002230\\u0022)\"\n        }\n      ],\n      \"Expression\": \"input1.Count \\u003E= ruleCount \\u0026\\u0026 input1.OrderByDescending(o =\\u003E o.ChangeDateTime).Take(ruleCount).All(a =\\u003E a.Value \\u003C threshold)\",\n      \"Actions\": null,\n      \"SuccessEvent\": null\n    }\n  ]\n}"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules2.json",
    "content": "{\n  \"WorkflowName\": \"inputWorkflow\",\n  \"Rules\": [\n    {\n      \"RuleName\": \"Rule1\",\n      \"Operator\": \"Or\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"Rules\": [\n        {\n          \"RuleName\": \"GiveDiscount10\",\n          \"SuccessEvent\": \"10\",\n          \"ErrorMessage\": \"One or more adjust rules failed.\",\n          \"ErrorType\": \"Error\",\n          \"RuleExpressionType\": \"LambdaExpression\",\n          \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n        },\n        {\n          \"RuleName\": \"GiveDiscount20\",\n          \"SuccessEvent\": \"20\",\n          \"ErrorMessage\": \"One or more adjust rules failed.\",\n          \"ErrorType\": \"Error\",\n          \"RuleExpressionType\": \"LambdaExpression\",\n          \"Expression\": \"input1.country == \\\"india\\\" AND input1.loyaltyFactor == 3 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 2\"\n        },\n        {\n          \"RuleName\": \"GiveDiscount25\",\n          \"SuccessEvent\": \"25\",\n          \"ErrorMessage\": \"One or more adjust rules failed.\",\n          \"ErrorType\": \"Error\",\n          \"RuleExpressionType\": \"LambdaExpression\",\n          \"Expression\": \"input1.country != \\\"india\\\" AND input1.loyaltyFactor >= 2 AND input1.totalPurchasesToDate >= 10000 AND input2.totalOrders > 2 AND input3.noOfVisitsPerMonth > 5\"\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules3.json",
    "content": "{\n  \"WorkflowName\": \"inputWorkflow\",\n  \"Rules\": [\n    {\n      \"RuleName\": \"GiveDiscount10\",\n      \"SuccessEvent\": \"10\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"RuleExpressionType\": \"LambdaExpression\",\n      \"Expression\": \"input1.couy == \\\"india\\\" AND input1.loyaltyFactor <= 2 AND input1.totalPurchasesToDate >= 5000 AND input2.totalOrders > 2 AND input2.noOfVisitsPerMonth > 2\"\n    }\n  ]\n}"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules4.json",
    "content": "[\n  {\n    \"WorkflowName\": \"inputWorkflow\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"SuccessEvent\": \"10\",\n        \"ErrorMessage\": \"One or more adjust rules failed, with loyaltyFactor : $(model1.loyaltyFactor), country : $(model1.country), totalPurchasesToDate : $(model1.totalPurchasesToDate), model2 : $(model2)\",\n        \"ErrorType\": \"Error\",\n        \"localParams\": [\n          {\n            \"Name\": \"model1\",\n            \"Expression\": \"input1.FirstOrDefault(country.Equals(\\\"india\\\", StringComparison.OrdinalIgnoreCase))\"\n          },\n          {\n            \"Name\": \"model2\",\n            \"Expression\": \"model1.country == \\\"india\\\"\"\n          }\n        ],\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"model1.country == \\\"india\\\" AND model1.loyaltyFactor <= 2 AND model1.totalPurchasesToDate >= 5000 AND model2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount100\",\n        \"SuccessEvent\": \"10\",\n        \"ErrorType\": \"Error\",\n        \"localParams\": [\n          {\n            \"Name\": \"model1\",\n            \"Expression\": \"input1.FirstOrDefault(country.Equals(\\\"india\\\", StringComparison.OrdinalIgnoreCase))\"\n          },\n          {\n            \"Name\": \"model2\",\n            \"Expression\": \"model1.country == \\\"india\\\"\"\n          }\n        ],\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"model1.country == \\\"india\\\" AND model1.loyaltyFactor < 0 AND model1.totalPurchasesToDate >= 5000 AND model2\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount25\",\n        \"SuccessEvent\": \"25\",\n        \"ErrorMessage\": \"One or more adjust rules failed, country : $(input4.country), loyaltyFactor : $(input4.loyaltyFactor), totalPurchasesToDate : $(input4.totalPurchasesToDate), totalOrders : $(input5.totalOrders), noOfVisitsPerMonth : $(input30.noOfVisitsPerMonth), $(model2)\",\n        \"ErrorType\": \"Error\",\n        \"localParams\": [\n          {\n            \"Name\": \"model1\",\n            \"Expression\": \"input1.FirstOrDefault(country.Equals(\\\"india\\\", StringComparison.OrdinalIgnoreCase))\"\n          },\n          {\n            \"Name\": \"model2\",\n            \"Expression\": \"model1.country == \\\"india\\\"\"\n          }\n        ],\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input4.country == \\\"india\\\" AND input4.loyaltyFactor >= 2 AND input4.totalPurchasesToDate <= 10 AND input5.totalOrders > 2 AND input3.noOfVisitsPerMonth > 5\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount30\",\n        \"SuccessEvent\": \"30\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input4.loyaltyFactor > 30 AND input4.totalPurchasesToDate >= 50000 AND input4.totalPurchasesToDate <= 100000 AND input5.totalOrders > 5 AND input3.noOfVisitsPerMonth > 15\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount35\",\n        \"SuccessEvent\": \"35\",\n        \"ErrorMessage\": \"One or more adjust rules failed, totalPurchasesToDate : $(input4.totalPurchasesToDate), totalOrders : $(input5.totalOrders)\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input4.loyaltyFactor > 30 AND input4.totalPurchasesToDate >= 100000 AND input5.totalOrders > 15 AND input3.noOfVisitsPerMonth > 25\"\n      }\n    ]\n  }\n]"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules5.json",
    "content": "{\n  \"WorkflowName\": \"inputWorkflow\",\n  \"Rules\": [\n    {\n      \"RuleName\": \"upperCaseAccess\",\n      \"SuccessEvent\": \"10\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"RuleExpressionType\": \"LambdaExpression\",\n      \"Expression\": \"utils.CheckExists(String(input1.Property1)) == true\"\n    },\n    {\n      \"RuleName\": \"lowerCaseAccess\",\n      \"SuccessEvent\": \"10\",\n      \"ErrorMessage\": \"One or more adjust rules failed.\",\n      \"ErrorType\": \"Error\",\n      \"RuleExpressionType\": \"LambdaExpression\",\n      \"Expression\": \"utils.CheckExists(String(input1.property1)) == true\"\n    }\n  ]\n} "
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules6.json",
    "content": "{\n    \"WorkflowName\": \"inputWorkflow\",\n    \"Rules\": [\n        {\n            \"RuleName\": \"GiveDiscount10\",\n            \"SuccessEvent\": \"10\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.Property1.Contains(\\\"hell\\\")\"\n        },\n        {\n            \"RuleName\": \"GiveDiscount20\",\n            \"SuccessEvent\": \"20\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.Property1.Contains(\\\"hell\\\") && !input1.Boolean\"\n        },\n        {\n            \"RuleName\": \"GiveDiscount30\",\n            \"SuccessEvent\": \"30\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.Method.Invoke()\"\n        }\n    ]\n}"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules7.json",
    "content": "{\n    \"WorkflowName\": \"inputWorkflow\",\n    \"Rules\": [\n        {\n            \"RuleName\": \"GiveDiscount10\",\n            \"SuccessEvent\": \"10\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"!input1.Boolean\"\n        },\n        {\n            \"RuleName\": \"GiveDiscount20\",\n            \"SuccessEvent\": \"20\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"!input1.Boolean && true\"\n        }\n    ]\n}"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules8.json",
    "content": "{\n    \"WorkflowName\": \"inputWorkflow\",\n    \"Rules\": [\n        {\n            \"RuleName\": \"GiveDiscount10\",\n            \"SuccessEvent\": \"10\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.Boolean\"\n        },\n        {\n            \"RuleName\": \"GiveDiscount20\",\n            \"SuccessEvent\": \"20\",\n            \"ErrorMessage\": \"One or more adjust rules failed.\",\n            \"ErrorType\": \"Error\",\n            \"RuleExpressionType\": \"LambdaExpression\",\n            \"Expression\": \"input1.Boolean && true || (input1.Boolean)\"\n        }\n    ]\n}"
  },
  {
    "path": "test/RulesEngine.UnitTest/TestData/rules9.json",
    "content": "{\n    \"WorkflowName\": \"inputWorkflow\",\n    \"Rules\": [\n      {\n        \"RuleName\": \"GiveDiscount10\",\n        \"SuccessEvent\": \"10\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.Boolean\"\n      },\n      {\n        \"RuleName\": \"GiveDiscount20\",\n        \"SuccessEvent\": \"20\",\n        \"ErrorMessage\": \"One or more adjust rules failed.\",\n        \"ErrorType\": \"Error\",\n        \"RuleExpressionType\": \"LambdaExpression\",\n        \"Expression\": \"input1.Boolean && input1.Data.NotExistingMethod()\"\n      }\n    ]\n  } "
  },
  {
    "path": "test/RulesEngine.UnitTest/TypedClassTests.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing RulesEngine.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class TypedClassTests\n    {\n        public class Transazione\n        {\n            public static string StaticProperty { get; set; } = \"Hello\";\n            public List<Attore> Attori { get; set; } = new();\n        }\n        public class Attore\n        {\n            public Guid Id { get; internal set; }\n            public string Nome { get; internal set; }\n            public RuoloAttore RuoloAttore { get; internal set; }\n        }\n\n        public enum RuoloAttore\n        {\n            A,\n            B,\n            C\n        }\n\n        [Fact]\n        public async Task TypedClassTest()\n        {\n            Workflow workflow = new() {\n                WorkflowName = \"Conferimento\",\n                Rules = new Rule[] {\n                    new() {\n                        RuleName = \"Attore Da\",\n                        Enabled = true,\n                        ErrorMessage = \"Attore Da Id must be defined\",\n                        SuccessEvent = \"10\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"transazione.Attori.Any(a => a.RuoloAttore == 1)\",\n                    },\n                    new() {\n                        RuleName = \"Attore A\",\n                        Enabled = true,\n                        ErrorMessage = \"Attore A must be defined\",\n                        SuccessEvent = \"10\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"transazione.Attori != null\",\n                    },\n                }\n            };\n            var reSettings = new ReSettings() {\n                CustomTypes = new Type[] {\n                },\n                AutoRegisterInputType = false\n            };\n            var re = new RulesEngine(reSettings);\n            re.AddWorkflow(workflow);\n\n            var param = new Transazione {\n                    Attori = new List<Attore>{\n                    new Attore{\n                        RuoloAttore = RuoloAttore.B,\n                   \n                    },\n                    new Attore {\n                         RuoloAttore = RuoloAttore.C\n                    }\n                }\n\n            };\n\n            var result = await  re.ExecuteAllRulesAsync(\"Conferimento\", new RuleParameter(\"transazione\", param));\n\n            Assert.All(result, (res) => Assert.True(res.IsSuccess));\n\n        }\n\n\n        [Fact]\n        public async Task TypedClassInputSameNameAsTypeTest()\n        {\n            Workflow workflow = new() {\n                WorkflowName = \"Conferimento\",\n                Rules = new Rule[] {\n                    new() {\n                        RuleName = \"Attore Da\",\n                        Enabled = true,\n                        ErrorMessage = \"Attore Da Id must be defined\",\n                        SuccessEvent = \"10\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"transazione.Attori.Any(a => a.RuoloAttore == 1)\",\n                    },\n                    new() {\n                        RuleName = \"Attore A\",\n                        Enabled = true,\n                        ErrorMessage = \"Attore A must be defined\",\n                        SuccessEvent = \"10\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"transazione.Attori != null\",\n                    }\n                   \n                }\n            };\n            var reSettings = new ReSettings() {\n                CustomTypes = new Type[] {\n                    typeof(Transazione)\n                }\n            };\n            var re = new RulesEngine(reSettings);\n            re.AddWorkflow(workflow);\n\n            var param = new Transazione {\n                Attori = new List<Attore>{\n                    new Attore{\n                        RuoloAttore = RuoloAttore.B,\n\n                    },\n                    new Attore {\n                         RuoloAttore = RuoloAttore.C\n                    }\n                }\n\n            };\n\n            var result = await re.ExecuteAllRulesAsync(\"Conferimento\", new RuleParameter(\"Transazione\", param));\n\n            Assert.All(result, (res) => Assert.True(res.IsSuccess));\n\n        }\n\n\n        [Fact]\n        public async Task TypedClassBothAccessibleTestWhenCaseInsensitive()\n        {\n            Workflow workflow = new() {\n                WorkflowName = \"Conferimento\",\n                Rules = new Rule[] {\n                    new() {\n                        RuleName = \"Attore Da\",\n                        Enabled = true,\n                        ErrorMessage = \"Attore Da Id must be defined\",\n                        SuccessEvent = \"10\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"transazione.Attori.Any(a => a.RuoloAttore == 1)\",\n                    },\n                    new() {\n                        RuleName = \"Attore A\",\n                        Enabled = true,\n                        ErrorMessage = \"Attore A must be defined\",\n                        SuccessEvent = \"10\",\n                        RuleExpressionType = RuleExpressionType.LambdaExpression,\n                        Expression = \"transazione.Attori != null\",\n                    },\n                     new() {\n                        RuleName = \"Static FieldTest\",\n                        Expression = \"Transazione.StaticProperty == \\\"Hello\\\"\"\n                    }\n                }\n            };\n            var reSettings = new ReSettings() {\n                CustomTypes = new Type[] {\n                    typeof(Transazione)\n                },\n                IsExpressionCaseSensitive = true\n            };\n            var re = new RulesEngine(reSettings);\n            re.AddWorkflow(workflow);\n\n            var param = new Transazione {\n                Attori = new List<Attore>{\n                    new Attore{\n                        RuoloAttore = RuoloAttore.B,\n\n                    },\n                    new Attore {\n                         RuoloAttore = RuoloAttore.C\n                    }\n                }\n\n            };\n\n            var result = await re.ExecuteAllRulesAsync(\"Conferimento\", new RuleParameter(\"transazione\", param));\n\n            Assert.All(result, (res) => Assert.True(res.IsSuccess));\n\n        }\n    }\n}\n"
  },
  {
    "path": "test/RulesEngine.UnitTest/UtilsTests.cs",
    "content": "﻿// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nusing Newtonsoft.Json.Linq;\nusing RulesEngine.HelperFunctions;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Dynamic;\nusing Xunit;\n\nnamespace RulesEngine.UnitTest\n{\n    [ExcludeFromCodeCoverage]\n    public class TestClass\n    {\n        public string Test { get; set; }\n        public List<int> TestList { get; set; }\n    }\n\n    [Trait(\"Category\", \"Unit\")]\n    [ExcludeFromCodeCoverage]\n    public class UtilsTests\n    {\n\n        [Fact]\n        public void GetTypedObject_dynamicObject()\n        {\n            dynamic obj = new ExpandoObject();\n            obj.Test = \"hello\";\n            obj.TestList = new List<int> { 1, 2, 3 };\n            object typedobj = Utils.GetTypedObject(obj);\n            Assert.IsNotType<ExpandoObject>(typedobj);\n            Assert.NotNull(typedobj.GetType().GetProperty(\"Test\"));\n        }\n\n        [Fact]\n        public void GetTypedObject_dynamicObject_multipleObjects()\n        {\n            dynamic obj = new ExpandoObject();\n            obj.Test = \"hello\";\n            obj.TestList = new List<int> { 1, 2, 3 };\n            dynamic obj2 = new ExpandoObject();\n            obj2.Test = \"world\";\n            obj2.TestList = new List<int> { 1, 2, 3 };\n            object typedobj = Utils.GetTypedObject(obj);\n            object typedobj2 = Utils.GetTypedObject(obj2);\n            Assert.IsNotType<ExpandoObject>(typedobj);\n            Assert.NotNull(typedobj.GetType().GetProperty(\"Test\"));\n            Assert.Equal(typedobj.GetType(), typedobj2.GetType());\n        }\n\n\n        [Fact]\n        public void GetTypedObject_nonDynamicObject()\n        {\n            var obj = new {\n                Test = \"hello\"\n            };\n            var typedobj = Utils.GetTypedObject(obj);\n            Assert.IsNotType<ExpandoObject>(typedobj);\n            Assert.NotNull(typedobj.GetType().GetProperty(\"Test\"));\n        }\n\n\n        [Fact]\n        public void GetJObject_nonDynamicObject()\n        {\n            dynamic obj = JObject.FromObject(new {\n                Test = \"hello\"\n            });\n            dynamic typedobj = Utils.GetTypedObject(obj);\n            Assert.IsNotType<ExpandoObject>(typedobj);\n            Assert.IsType<JObject>(typedobj);\n            Assert.NotNull(typedobj.Test);\n        }\n\n\n        [Fact]\n        public void CreateObject_dynamicObject()\n        {\n            dynamic obj = new ExpandoObject();\n            obj.Test = \"test\";\n            obj.TestList = new List<int> { 1, 2, 3 };\n\n            object newObj = Utils.CreateObject(typeof(TestClass), obj);\n            Assert.IsNotType<ExpandoObject>(newObj);\n            Assert.NotNull(newObj.GetType().GetProperty(\"Test\"));\n\n        }\n\n        [Fact]\n        public void CreateAbstractType_dynamicObject()\n        {\n            dynamic obj = new ExpandoObject();\n            obj.Test = \"test\";\n            obj.TestList = new List<int> { 1, 2, 3 };\n            obj.testEmptyList = new List<object>();\n\n            Type type = Utils.CreateAbstractClassType(obj);\n            Assert.NotEqual(typeof(ExpandoObject), type);\n            Assert.NotNull(type.GetProperty(\"Test\"));\n\n        }\n\n\n    }\n}\n"
  }
]