{"id":18690843,"url":"https://github.com/wbaldoumas/atrea-policyengine","last_synced_at":"2025-10-10T21:11:51.992Z","repository":{"id":38412593,"uuid":"264760860","full_name":"wbaldoumas/atrea-policyengine","owner":"wbaldoumas","description":"A modular, composable policy engine for easy implementation of complex conditional processing pipelines.","archived":false,"fork":false,"pushed_at":"2025-03-21T22:31:50.000Z","size":1034,"stargazers_count":5,"open_issues_count":3,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-12T06:09:35.047Z","etag":null,"topics":["dotnet","engine","hacktoberfest","nuget","nuget-package","package","parallel-processing","policy","policy-engine"],"latest_commit_sha":null,"homepage":"https://wbaldoumas.github.io/atrea-policyengine","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wbaldoumas.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-17T21:33:21.000Z","updated_at":"2025-03-21T22:31:21.000Z","dependencies_parsed_at":"2023-11-15T00:27:47.625Z","dependency_job_id":"bd665b33-012b-4e75-a978-082d7a11170f","html_url":"https://github.com/wbaldoumas/atrea-policyengine","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbaldoumas%2Fatrea-policyengine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbaldoumas%2Fatrea-policyengine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbaldoumas%2Fatrea-policyengine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wbaldoumas%2Fatrea-policyengine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wbaldoumas","download_url":"https://codeload.github.com/wbaldoumas/atrea-policyengine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248525139,"owners_count":21118619,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dotnet","engine","hacktoberfest","nuget","nuget-package","package","parallel-processing","policy","policy-engine"],"created_at":"2024-11-07T10:51:15.534Z","updated_at":"2025-10-10T21:11:46.946Z","avatar_url":"https://github.com/wbaldoumas.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv id=\"top\"\u003e\u003c/div\u003e\n\n[![Build][github-checks-shield]][github-checks-url]\n[![Coverage][coverage-shield]][coverage-url]\n\n[![Version][nuget-version-shield]][nuget-url]\n[![Downloads][nuget-downloads-shield]][nuget-url]\n\n[![Contributors][contributors-shield]][contributors-url]\n[![Commits][last-commit-shield]][last-commit-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n\n[![LinkedIn][linkedin-shield]][linkedin-url]\n\n\u003c!-- PROJECT LOGO --\u003e\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/wbaldoumas/atrea-policyengine\"\u003e\n    \u003cimg src=\"images/engine_icon.png\" alt=\"Logo\" width=\"80\" height=\"80\"\u003e\n  \u003c/a\u003e\n  \u003ch1 align=\"center\"\u003eAtrea.PolicyEngine\u003c/h1\u003e\n  \u003cp align=\"center\"\u003e\n    A modular, composable policy engine for easy implementation of complex conditional processing pipelines.\n    \u003cbr /\u003e\n    \u003ca href=\"#documentation\"\u003e\u003cstrong\u003eExplore the docs »\u003c/strong\u003e\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"https://github.com/wbaldoumas/atrea-policyengine/tree/main/examples/Atrea.PolicyEngine.Examples/Examples\"\u003eView Examples\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/wbaldoumas/atrea-policyengine/issues\"\u003eReport Bug\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/wbaldoumas/atrea-policyengine/issues\"\u003eRequest Feature\u003c/a\u003e\n  \u003c/p\u003e\n  \u003ca href=\"https://github.com/wbaldoumas/atrea-policyengine\"\u003e\n    \u003cimg src=\"images/engine.png\" alt=\"Engine\" \u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eTable of Contents\u003c/summary\u003e\n\n- [Installation](#installation)\n- [Documentation](#documentation)\n  - [Basic Usage](#basic-usage)\n  - [Input Policies](#input-policies)\n    - [Implementing Input Policies](#implementing-input-policies)\n    - [Async Input Policies](#async-input-policies)\n    - [Compound Input Policies](#compound-input-policies)\n  - [Processors](#processors)\n  - [Output Policies](#output-policies)\n  - [Asynchronous and Parallel Processing](#asynchronous-and-parallel-processing)\n  - [Nesting Policy Engines](#nesting-policy-engines)\n  - [Code Examples](#code-examples)\n- [Contributing](#contributing)\n- [License](#license)\n- [Contact](#contact)\n- [Acknowledgements](#acknowledgements)\n\n\u003c/details\u003e\n\n\u003ca name=\"installation\"/\u003e\n\n## Installation\n\nPackage manager:\n```\nInstall-Package Atrea.PolicyEngine -Version 4.0.1\n```\n\n.NET CLI:\n```\ndotnet add package Atrea.PolicyEngine --version 4.0.1\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"documentation\"/\u003e\n\n## Documentation\n\n\u003ca name=\"basic-usage\"/\u003e\n\n### Basic Usage\n\nOnce [input policies](#input-policies), [processors](#processors), and [output policies](#output-policies) have been implemented, a policy engine can be built using the `PolicyEngineBuilder\u003cT\u003e`. In the example below we configure a policy engine which performs translations between natural languages.\n\n```cs\nvar engine = PolicyEngineBuilder\u003cTranslatableItem\u003e\n    .Configure()\n    .WithInputPolicies(\n        // Only translate items which have not yet been translated and are \n        // translations from US English to UK English.\n        new IsNotYetTranslated(),\n        new IsFromUsEnglish(),\n        new IsToUkEnglish()\n    ).WithProcessors(\n        // Use the Google machine translation API and a proprietary single-word\n        // translator to perform translations.\n        new GoogleTranslator(),\n        new SingleWordTranslator()\n    ).WithOutputPolicies(\n        // Once an item is translated, publish the translation to an event stream\n        // and mark the item as translated.\n        new PublishTranslation(),\n        new MarkItemTranslated()\n    ).Build();\n\nvar translatableItem = _repository.GetTranslatableItem();\n\n// Process the item.\nengine.Process(translatableItem);\n\n// Process multiple items at once.\nvar translatableItems =  _repository.GetTranslatableItems();\n\nengine.Process(translatableItems);\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"input-policies\"/\u003e\n\n### Input Policies\n\nInput policies can be thought of as the gatekeepers that guard the rest of a policy engine's processing and post-processing steps. They should be used to check whether a given item that has entered the policy engine should be processed or not.\n\nThe `IInputPolicy\u003cT\u003e` interface is implemented by a given input policy, whose `ShouldProcess(T item)` method can return one of three `InputPolicyResult` values: `Continue`, `Accept`, or `Reject`. How the policy engine handles these input policy results is outlined in the table below.\n\n| **Value** | **Behavior** |\n| --------- | ------------ |\n| `InputPolicyResult.Continue` | Accept the item by this specific input policy - continue evaluation of remaining input policies, or begin processing the item if this is the last input policy evaluated.\n| `InputPolicyResult.Accept` | Accept the item for processing - skip evaluation of any remaining input policies, and begin processing the item.\n| `InputPolicyResult.Reject` | Reject the item for processing - skip evaluation of any remaining input policies, do not process the item with the engine's processors nor apply post-processing with the engine's output policies.\n\nInput policies are run in the order that they are passed to the `PolicyEngineBuilder\u003cT\u003e.WithInputPolicies(...)` or `AsyncPolicyEngineBuilder\u003cT\u003e.WithAsyncInputPolicies(...)` methods. \n\nNote that when an async policy engine is configured with `AsyncPolicyEngineBuilder\u003cT\u003e.WithParallelInputPolicies(...)`:\n\n* All input policies are run and they are all run in parallel. \n* There is no meaningful difference between `InputPolicyResult.Continue` and `InputPolicyResult.Accept` amongst the individual input policies. \n* If one input policy evaluates to `InputPolicyResult.Reject` but another evaluates to `InputPolicyResult.Accept`, the item is accepted for processing by the policy engine and _not_ rejected.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"implementing-input-policies\"/\u003e\n\n#### Implementing Input Policies\n\nInput policies should aim to follow the [Single-Responsibility Principle](https://en.wikipedia.org/wiki/Single-responsibility_principle) such that each input policy inspects just one facet of information about the item to be processed by the engine. This allows for a much more flexibly configurable engine as well as better reusability for the input policies themselves.\n\nHere is an example of a **_poorly_** implemented input policy that is doing too much:\n\n```cs\npublic class ShouldCanadianFrenchToUsEnglishEngineProcess : IInputPolicy\u003cTranslatableItem\u003e\n{\n    public InputPolicyResult ShouldProcess(TranslatableItem item)\n    {\n        if (item.IsTranslated)\n        {\n            return InputPolicyResult.Reject;\n        }\n\n        if (item.IsQueuedByUser)\n        {\n            return InputPolicyResult.Accept;\n        }\n\n        if (item.FromLanguage != LanguageCode.CaFr)\n        {\n            return InputPolicyResult.Reject;\n        }\n\n        if (item.ToLanguage != LanguageCode.UsEn)\n        {\n            return InputPolicyResult.Reject;\n        }\n\n        return InputPolicyResult.Continue;\n    }\n}\n```\n\nHere are just some of the problems with the input policy implementation above:\n\n* It isn't reusable by other policy engines.\n* It doesn't follow the [Single-Responsibility Principle](https://en.wikipedia.org/wiki/Single-responsibility_principle).\n* It is hard to unit test all possible branches for this input policy.\n\nThis can be refactored into a cleaner implementation by breaking down each of the checks above into separate input policies:\n\n```cs\npublic class IsNotYetTranslated : IInputPolicy\u003cTranslatableItem\u003e\n{\n    public InputPolicyResult ShouldProcess(TranslatableItem item)\n    {\n        if (item.IsTranslated)\n        {\n            return InputPolicyResult.Reject;\n        }\n\n        return InputPolicyResult.Continue;\n    }\n}\n```\n\n```cs\npublic class IsQueuedByUser : IInputPolicy\u003cTranslatableItem\u003e\n{\n    public InputPolicyResult ShouldProcess(TranslatableItem item)\n    {\n        if (item.IsQueuedByUser)\n        {\n            return InputPolicyResult.Accept;\n        }\n\n        return InputPolicyResult.Continue;\n    }\n}\n```\n\n```cs\npublic class IsFromCanadianFrench : IInputPolicy\u003cTranslatableItem\u003e\n{\n    public InputPolicyResult ShouldProcess(TranslatableItem item)\n    {\n        if (item.FromLanguage != LanguageCode.CaFr)\n        {\n            return InputPolicyResult.Reject;\n        }\n\n        return InputPolicyResult.Continue;\n    }\n}\n```\n\n```cs\npublic class IsToUsEnglish : IInputPolicy\u003cTranslatableItem\u003e\n{\n    public InputPolicyResult ShouldProcess(TranslatableItem item)\n    {\n        if (item.ToLanguage != LanguageCode.UsEn)\n        {\n            return InputPolicyResult.Reject;\n        }\n\n        return InputPolicyResult.Continue;\n    }\n}\n```\n\nNote that although this produces more code, classes, and source files, each input policy follows the Single-Responsibility Principal, is reusable within the context of other policy engines, and is extremely easy to unit test! :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:\n\nThese can then be passed to the policy engine builder's `WithInputPolicies(...)` method as such:\n\n```cs\nvar engine = PolicyEngineBuilder\u003cTranslatableItem\u003e\n    .Configure()\n    .WithInputPolicies(\n        new IsNotYetTranslated(),\n        new IsQueuedByUser(),\n        new IsFromCanadianFrench(),\n        new IsToUsEnglish()\n    )\n    .WithProcessors(...)\n    .WithOutputPolicies(...)\n    .Build();\n```\n\n#### Async Input Policies\n\nIf some input policies are more complex and have dependencies that perform `async` operations, the `IAsyncInputPolicy\u003cT\u003e` interface can be implemented instead. See more about [asynchronous and parallel processing](#asynchronous-and-parallel-processing) below.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"compound-input-policies\"/\u003e\n\n#### Compound Input Policies\n\nThe Atrea.PolicyEngine library also provides a handful of useful compound input policies. These currently include `And\u003cT\u003e`, `Or\u003cT\u003e`, and `Xor\u003cT\u003e`.\n\nThese compound input policies can be created by passing other input policies constructor arguments:\n\n```cs\nvar isFromCanadianFrenchAndToUsEnglish = new And\u003cTranslatableItem\u003e(\n  new IsFromCanadianFrench(),\n  new IsToUsEnglish();\n)\n```\n\nor by using the built-in `IInputPolicy\u003cT\u003e` extension methods:\n\n```cs\nvar isFromCanadianFrenchToUsEnglish = new IsFromCanadianFrench().And(new IsToUsEnglish());\n```\n\nUsing these compound input policies allows for creation of complex input policies on the fly by composing together more granular input policies in an intuitive way.\n\n```cs\nvar isFromCanadianFrench = new IsFromCanadianFrench();\nvar isToCanadianFrench = new IsToCanadianFrench();\nvar isFromUsEnglish = new IsFromUsEnglish();\nvar isToUsEnglish = new IsToUsEnglish();\n\nvar isFromCanadianFrenchToUsEnglish = isFromCanadianFrench.And(isToUsEnglish);\nvar isFromUsEnglishToCanadianFrench = isFromUsEnglish.And(isToCanadianFrench);\n\nvar isCanadianFrenchUsEnglishTranslation = isFromCanadianFrenchToUsEnglish.Xor(\n    isFromUsEnglishToCanadianFrench\n);\n```\n\nA `Not\u003cT\u003e` input policy is also available to easily reverse the output of any given input policy.\n\n```cs\nvar isAlreadyTranslated = new Not\u003cTranslatableItem\u003e(new IsNotYetTranslated());\n```\n\n`Not\u003cT\u003e` is implemented in such a way that it produces the following `InputPolicyResult` values.\n\n| **InputPolicyResult** | **Not\\\u003cT\u003e InputPolicyResult**\n| --- | --- |\n| `InputPolicyResult.Continue` | `InputPolicyResult.Reject` \n| `InputPolicyResult.Accept` | `InputPolicyResult.Reject`\n| `InputPolicyResult.Reject` | `InputPolicyResult.Continue`\n\nVersions of these compound input policies that support `async` operations are also available with `AsyncAnd\u003cT\u003e`, `AsyncOr\u003cT\u003e`, `AsyncXor\u003cT\u003e`, and `AsyncNot\u003cT\u003e`.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"processors\"/\u003e\n\n### Processors\n\nA policy engine's processors should be where a brunt of the complex processing of items takes place. A given processor should implement the `IProcessor\u003cT\u003e` or `IAsyncProcessor\u003cT\u003e` interface, whose respective `Process(T item)` or `ProcessAsync(T item)` method will be called by the policy engine when an item has been accepted for processing by the engine's input policies. In our example, this is where we are actually reaching out to external APIs or data stores to perform machine translation between natural languages.\n\n```cs\npublic class GoogleTranslator : IProcessor\u003cTranslatableItem\u003e\n{\n    private readonly ITranslationClient _googleTranslationClient;\n\n    public GoogleTranslator(ITranslationClient googleTranslationClient)\n    {\n        _googleTranslationClient = googleTranslationClient;\n    }\n\n    public void Process(TranslatableItem item)\n    {\n        var response = _googleTranslationClient.TranslateText(\n            item.SourceText,\n            item.FromLanguage,\n            item.ToLanguage\n        );\n\n        item.TranslatedText = response.TranslatedText;\n    }\n}\n```\n\nProcessors can be configured to run synchronously, asynchronously, and in parallel. See more about [asynchronous and parallel processing](#asynchronous-and-parallel-processing) below.\n\n### Output Policies\n\nA policy engine's output policies can be thought of as light post-processors that should be run after the engine's main processing step has been completed. They shouldn't be doing any heavy lifting. In our example we have an output policy that is pushing messages to an event stream.\n\n```cs\npublic class PublishTranslation : IOutputPolicy\u003cTranslatableItem\u003e\n{\n    private readonly IKafkaProducer\u003cTranslatableItemMessage\u003e _messageProducer;\n\n    public PublishTranslation(IKafkaProducer\u003cTranslatableItemMessage\u003e messageProducer)\n    {\n        _messageProducer = messageProducer;\n    }\n\n    public void Apply(TranslatableItem item)\n    {\n        var message = new TranslatableItemMessage(item);\n\n        _messageProducer.Produce(message);\n    }\n}\n```\n\nOutput policies can be configured to run synchronously, asynchronously, and in parallel. See more about [asynchronous and parallel processing](#asynchronous-and-parallel-processing) below.\n\n### Asynchronous and Parallel Processing\n\nAsync and parallel processing is supported in a myriad of configurations by implementing the `IAsyncInputPolicy\u003cT\u003e`, `IAsyncProcessor\u003cT\u003e`, and `IAsyncOutputPolicy\u003cT\u003e` interfaces and using the `AsyncPolicyEngineBuilder\u003cT\u003e`. \n\nHere we configure async input policies to be awaited in order, processors to be run in parallel, and output policies to be run in parallel.\n\n```cs\nvar engine = AsyncPolicyEngineBuilder\u003cTranslatableItem\u003e\n    .Configure()\n    .WithAsyncInputPolicies(\n        // For this engine, we only want it to translate items which have not\n        // yet been translated, and are translations from Canadian French to US English.\n        new IsNotYetTranslated(),\n        new IsFromCanadianFrench(),\n        new IsToUsEnglish()\n    ).WithParallelProcessors(\n        // Use the Google and Microsoft machine translation APIs, and a proprietary cache-based\n        // translator to perform translations.\n        new GoogleTranslator(),\n        new MicrosoftTranslator(),\n        new CacheTranslator()\n    ).WithParallelOutputPolicies(\n        // Once an item is translated, publish the translation to an event stream\n        // and mark the item as translated.\n        new PublishTranslation(),\n        new MarkItemTranslated()\n    ).Build();\n\nvar translatableItem = _repository.GetTranslatableItem();\n\n// Process the item.\nawait engine.ProcessAsync(translatableItem);\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"nesting-policy-engines\"/\u003e\n\n### Nesting Policy Engines\n\nSince the `IPolicyEngine\u003cT\u003e` interface implements `IProcessor\u003cT\u003e`, policy engines can be composed together and nested within another encompassing policy engine and act as individual processors within that engine.\n\nIn the example below, imagine that we have methods to build a policy engine that performs translation between US English and Canadian French, one to perform translations between US English and UK English, and one that specifically handles values containing numeric text. A full code example can be seen [here](https://github.com/itabaiyu/atrea-policyengine/blob/itabaiyu_usage_documentation/examples/Atrea.PolicyEngine.Examples/Examples/NestedPolicyEngineExample.cs).\n\n```cs\nvar canadianFrenchTranslationEngine = BuildCanadianFrenchTranslationEngine();\nvar englishTranslationEngine = BuildEnglishTranslationEngine();\nvar numericTranslationEngine = BuildNumericTranslationEngine();\n\nvar translationEngine = PolicyEngineBuilder\u003cTranslatableItem\u003e\n    .Configure()\n    // Only process items which have not yet been translated.\n    .WithInputPolicies(NotYetTranslated)\n    .WithProcessors(\n        // Use the Canadian French, English, and numeric translation engines to\n        // perform translations.\n        canadianFrenchTranslationEngine,\n        englishTranslationEngine,\n        numericTranslationEngine\n    )\n    // No output policies needed, since each individual engine handles its own\n    // post-processing steps.\n    .WithoutOutputPolicies()\n    .Build();\n\nvar translatableItem = _repository.GetTranslatableItem();\n\ntranslationEngine.Process(translatableItem);\n```\n\nThis nesting of policy engines as processors is also possible with asynchronous by using the `AsyncPolicyEngineBuilder\u003cT\u003e` (see code example below).\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"code-examples\"/\u003e\n\n### Code Examples\n\nFull code examples can be found in this repository at the following links:\n\n* [Simple `PolicyEngineBuilder\u003cT\u003e` usage](https://github.com/itabaiyu/atrea-policyengine/blob/main/examples/Atrea.PolicyEngine.Examples/Examples/SimplePolicyEngineExample.cs)\n* [Simple `AsyncPolicyEngineBuilder\u003cT\u003e` usage](https://github.com/itabaiyu/atrea-policyengine/blob/main/examples/Atrea.PolicyEngine.Examples/Examples/SimpleAsyncPolicyEngineExample.cs)\n* [`PolicyEngineBuilder\u003cT\u003e` usage with compound input policies](https://github.com/itabaiyu/atrea-policyengine/blob/main/examples/Atrea.PolicyEngine.Examples/Examples/PolicyEngineWithCompoundInputPoliciesExample.cs)\n* [`AsyncPolicyEngineBuilder\u003cT\u003e` usage with compound input policies](https://github.com/itabaiyu/atrea-policyengine/blob/main/examples/Atrea.PolicyEngine.Examples/Examples/AsyncPolicyEngineWithCompoundInputPoliciesExample.cs)\n* [Nested policy engine configuration](https://github.com/itabaiyu/atrea-policyengine/blob/main/examples/Atrea.PolicyEngine.Examples/Examples/NestedPolicyEngineExample.cs)\n* [Nested async policy engine configuration](https://github.com/itabaiyu/atrea-policyengine/blob/main/examples/Atrea.PolicyEngine.Examples/Examples/NestedAsyncPolicyEngineExample.cs)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"contributing\"/\u003e\n\n## Contributing\n\nContributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. For detailed contributing guidelines, please see [CONTRIBUTING.md](CONTRIBUTING.md)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"license\"/\u003e\n\n## License\n\nDistributed under the `MIT` License. See `LICENSE` for more information.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003ca name=\"contact\"/\u003e\n\n## Contact\n\nWilliam Baldoumas - wbaldoumas.github@gmail.com\n\nProject Link: [https://github.com/wbaldoumas/atrea-policyengine](https://github.com/wbaldoumas/atrea-policyengine)\n\n\u003ca name=\"acknowledgements\"/\u003e\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n## Acknowledgements\n\nThis template was adapted from\n[https://github.com/othneildrew/Best-README-Template](https://github.com/othneildrew/Best-README-Template).\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n**Show your support by contributing or starring the repo!** :star: :star: :star: :star: :star: \n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --\u003e\n[contributors-shield]: https://img.shields.io/github/contributors/wbaldoumas/atrea-policyengine.svg?style=for-the-badge\n[contributors-url]: https://github.com/wbaldoumas/atrea-policyengine/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/wbaldoumas/atrea-policyengine.svg?style=for-the-badge\n[forks-url]: https://github.com/wbaldoumas/atrea-policyengine/network/members\n[stars-shield]: https://img.shields.io/github/stars/wbaldoumas/atrea-policyengine.svg?style=for-the-badge\n[stars-url]: https://github.com/wbaldoumas/atrea-policyengine/stargazers\n[issues-shield]: https://img.shields.io/github/issues/wbaldoumas/atrea-policyengine.svg?style=for-the-badge\n[issues-url]: https://github.com/wbaldoumas/atrea-policyengine/issues\n[license-shield]: https://img.shields.io/github/license/wbaldoumas/atrea-policyengine.svg?style=for-the-badge\n[license-url]: https://github.com/wbaldoumas/atrea-policyengine/blob/master/LICENSE\n[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge\u0026logo=linkedin\u0026colorB=555\n[linkedin-url]: https://linkedin.com/in/williambaldoumas\n[github-checks-shield]: https://img.shields.io/github/actions/workflow/status/wbaldoumas/atrea-policyengine/dotnet.yml?style=for-the-badge\n[github-checks-url]: https://github.com/wbaldoumas/atrea-policyengine/actions\n[coverage-shield]: https://img.shields.io/coveralls/github/wbaldoumas/atrea-policyengine?style=for-the-badge\n[coverage-url]: https://coveralls.io/github/wbaldoumas/atrea-policyengine?branch=main\n[nuget-version-shield]: https://img.shields.io/nuget/v/atrea.policyengine?style=for-the-badge\n[nuget-downloads-shield]: https://img.shields.io/nuget/dt/atrea.policyengine?style=for-the-badge\n[nuget-url]: https://www.nuget.org/packages/atrea.policyengine/\n[last-commit-shield]: https://img.shields.io/github/last-commit/wbaldoumas/atrea-policyengine?style=for-the-badge\n[last-commit-url]: https://github.com/wbaldoumas/atrea-policyengine/commits/main\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwbaldoumas%2Fatrea-policyengine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwbaldoumas%2Fatrea-policyengine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwbaldoumas%2Fatrea-policyengine/lists"}