diff --git a/README.md b/README.md
index 9055a71..dcfda84 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,17 @@
# FeatureOne v4.0.0
-[![NuGet version](https://badge.fury.io/nu/FeatureOne.svg)](https://badge.fury.io/nu/FeatureOne) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/NinjaRocks/FeatureOne/blob/master/License.md) [![build-master](https://github.com/NinjaRocks/FeatureOne/actions/workflows/Build-Master.yml/badge.svg)](https://github.com/NinjaRocks/FeatureOne/actions/workflows/Build-Master.yml) [![GitHub Release](https://img.shields.io/github/v/release/ninjarocks/FeatureOne?logo=github&sort=semver)](https://github.com/ninjarocks/FeatureOne/releases/latest)
+[![GitHub Release](https://img.shields.io/github/v/release/ninjarocks/FeatureOne?logo=github&sort=semver)](https://github.com/ninjarocks/FeatureOne/releases/latest)
+[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/NinjaRocks/FeatureOne/blob/master/License.md) [![build-master](https://github.com/NinjaRocks/FeatureOne/actions/workflows/Build-Master.yml/badge.svg)](https://github.com/NinjaRocks/FeatureOne/actions/workflows/Build-Master.yml)
[![CodeQL](https://github.com/NinjaRocks/FeatureOne/actions/workflows/codeql.yml/badge.svg)](https://github.com/NinjaRocks/FeatureOne/actions/workflows/codeql.yml) [![.Net](https://img.shields.io/badge/.Net-8.0-blue)](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
.Net Library to implement feature toggles.
--
-> #### Nuget Packages
-> ---
-> `FeatureOne` - Provides core funtionality to implement feature toggles with `no` backend storage provider. Needs package consumer to provide `IStorageProvider` implementation. Ideal for use case that requires custom storage backend. Please see below for more details.
->
-> Backend Storage Providers
->>i. `FeatureOne.SQL` - Provides SQL storage provider for implementing feature toggles using `SQL` backend.
->>
->>ii. `FeatureOne.File` - Provides File storage provider for implementing feature toggles using `File System` backend.
+#### Nuget Packages
+| Latest | Details |
+| -------- | --------|
+| ![NuGet Version](https://img.shields.io/nuget/v/FeatureOne?style=for-the-badge&label=FeatureOne&labelColor=green) | Provides core funtionality to implement feature toggles with `no` backend storage provider. Needs package consumer to provide `IStorageProvider` implementation. Ideal for use case that requires custom storage backend. Please see below for more details. |
+| ![NuGet Version](https://img.shields.io/nuget/v/FeatureOne.SQL?style=for-the-badge&label=FeatureOne.SQL&labelColor=green) | Provides SQL storage provider for implementing feature toggles using `SQL` backend. |
+|![NuGet Version](https://img.shields.io/nuget/v/FeatureOne.File?style=for-the-badge&label=FeatureOne.File&labelColor=green) | Provides File storage provider for implementing feature toggles using `File System` backend. |
## Concept
### What is a feature toggle?
@@ -26,479 +25,43 @@
### The benefits of feature toggles
> The primary benefit of feature flagging is that it mitigates the risks associated with releasing changes to an application. Whether it be a new feature release or a small refactor, there is always the inherent risk of releasing new regressions. To mitigate this, changes to an application can be placed behind feature toggles, allowing them to be turned “on” or “off” in the event of an emergency.
-How to use FeatureOne
---
-### Step 1. Add Feature IsEnabled Check in Code.
-In order to release a new functionality or feature - say eg. Dashboard Widget.
-Add logical check in codebase to wrap the functionality under a `feature toggle`.
-> the logical check evaluates status of the toggle configured for the feature in store at runtime.
-
-```
- var featureName = "dashboard_widget"; // Name of functionality or feature to toggle.
- if(Features.Current.IsEnable(featureName){ // See other IsEnable() overloads
- showDashboardWidget();
-}
-```
-
-
-### Step 2. Add Feature Toggle Definition to Storage
-Add a `toggle` definition to storage ie. a store in database or file or other storage medium.
-A toggle constitutes a collection of `conditions` that evaluate separately when the toggle is run. You can additionally specify an `operator` in the toggle definition to determine the overall success to include success of `any` constituent condition or success of `all` consituent conditions.
-> Toggles run at runtime based on consitituent conditions that evaluate separately against user claims (generally logged in user principal).
-
-Below is a serialized JSON representation of a Feature Toggle.
-```
-{
- "feature_name":{ -- Feature name
- "toggle":{ -- Toggle definition for the feature
-
- "operator":"any|all", -- Logical Operator - any (OR) & all (AND)
- -- ie. Evaluate overall toggle to true when `any` condition is met or
- -- `all` conditions are met.
-
- "conditions":[{ -- collection of conditions
- "type":"simple|regex" -- type of condition
-
- .... other type specific properties, See below for details.
- }]
- }
- }
-}
-```
-
-### Condition Types
-There are two types of toggle conditions that can be used out of box.
-
-#### i. Simple Condition
-`Simple` condition allows toggle with simple enable or disable of the given feature. User claims are not taken into account for this condition.
-
-Below is the serialized representation of toggle with simple condition.
-```
-{
- "dashboard_widget":{
- "toggle":{
- "conditions":[{
- "type":"Simple", -- Simple Condition.
- "isEnabled":true|false -- Enabled or disable the feature.
- }]
- }
- }
-}
-```
-C# representation of a feature with simple toggle is
-```
-var feature = new Feature
-{
- Name ="dashboard_widget", // Feature Name
- Toggle = new Toggle // Toggle definition
- {
- // Logical operator to be applied when evaluating consituent conditions.
- Operator = Operator.Any, // Default is Any (Logical OR)
-
- Conditions = new[]
- {
- // Simple condition that can be set to true/false for feature to be enabled/disabled.
- new SimpleCondition { IsEnabled = true }
- }
- }
-}
-```
-#### ii. Regex Condition
-`Regex` condition allows evaluating a regex expression against specified user claim value to enable a given feature.
-
-Below is the serialized representation of toggle with regex condition.
-```
- {
- "dashboard_widget":{
- "toggle":{
-
- "conditions":[{
- "type":"Regex", -- Regex Condition
- "claim":"email", -- Claim 'email' to be used for evaluation.
- "expression":"*@gbk.com" -- Regex expression to be used for evaluation.
- }]
- }
- }
- }
-```
-C# representation of a feature with regex toggle is
-```
-
-var feature = new Feature
-{
- Name ="dashboard_widget", // Feature Name
- Toggle = new Toggle // Toggle definition
- {
- Operator = Operator.Any,
- Conditions = new[]
- {
- // Regex condition that evalues role of user to be administrator to enable the feature.
- new RegexCondition { Claim = "role", Expression = "administrator" }
- }
- }
-}
-```
-
-### Step 3. Implement Storage Provider.
-To use FeatureOne, you need to provide implementation for `Storage Provider` to get all the feature toggles from storage medium of choice.
-Implement `IStorageProvider` interface to return feature toggles from storage.
-The interface has `GetByName()` method that returns an array of `IFeature`
-```
- ///
- /// Interface to implement storage provider.
- ///
- public interface IStorageProvider
- {
- ///
- /// Implement to get storage feature toggles by a given name.
- ///
- /// Array of Features
- IFeature[] GetByName(string name);
- }
-```
-A production storage provider should be an implementation with `API` , `SQL` or `File system` storage backend.
-
-An implementation option is to store features as serialized json to backend medium. Ideally, you may also want to use `caching` in the production implementation to optimise calls to the storage backend.
-
-
-Below is an example of dummy provider implementation.
-```
-public class CustomStoreProvider : IStorageProvider
- {
- public Feature[] GetByName(string name)
- {
- return new[] {
- new Feature("feature-01",new Toggle(Operator.Any, new[]{ new SimpleCondition{IsEnabled=true}})),
- new Feature("feature-02",new Toggle(Operator.All, new SimpleCondition { IsEnabled = false }, new RegexCondition{Claim="email", Expression= "*@gbk.com" }))
- };
- }
- }
-
-```
-### Step 4. Bootstrap Initialialization
-In bootstrap code, initialize the `Features` class with dependencies as shown below.
-
-i. With `storage provider` implementation.
-```
- var storageProvider = new CustomStorageProviderImpl();
- Features.Initialize(() => new Features(new FeatureStore(storageProvider)));
-```
-
-ii. With `storage provider` and `logger` implementations.
-```
- var logger = new CustomLoggerImpl();
- var storageProvider = new CustomStorageProviderImpl();
-
- Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger));
-```
-
-How to Extend FeatureOne
---
-
-### i. Toggle Condition
-You could implement your own condition by extending the `ICondition` interface.
-The interface provides `evaluate()` method that returns a boolean result of evaluating logic against list of input claims.
-```
- ///
- /// Interface to implement toggle condition.
- ///
- public interface ICondition
- {
- ///
- /// Implement method to evaulate toggle condition.
- ///
- /// List of user claims; could be empty
- ///
- bool Evaluate(IDictionary claims);
- }
-```
-Example below shows sample implementation of a custom condition.
-
-```
- // toggle condition to show feature after given hour during the day.
- public class TimeCondition : ICondition
- {
- public int Hour {get; set;} = 12;
-
- public bool Evaluate(IDictionary claims)
- {
- return (DateTime.Now.Hour > Hour);
- }
- }
-```
- Example usage of above condition in toggle to allow non-admin users access to a feature only after 12 hrs.
-
- C# representation of the feature is
-
-```
-var feature = new Feature
-{
- Name ="feature_pen_test", // Feature Name
- Toggle = new Toggle // Toggle definition
- {
- Operator = Operator.Any, // Enabled when one of below conditions are true.
- Conditions = new[]
- {
- // Custom condition - allow access after 12 o'clock
- new TimeCondition { Hour = 12 },
- // Regex condition for allowing admin users by role claim.
- new RegexCondition { Claim = "role", Expression = "^administrator$"}
- }
- }
-}
-```
-JSON Serialized representation is
- ```
- {
- "feature_pen_test":{
- "toggle":{
- "operator":"any", -- Any below condition evaluation to true should succeed the toggle.
- "conditions":[{
- "type":"Time", -- Time condition to allow access after 12 o'clock.
- "Hour":14
- },
- {
- "type":"Regex", -- Regex to allow admin access
- "claim":"role",
- "expression":"^administrator$"
- }]
- }
- }
-
-```
-
-`Please Note` Any custom condition implementation should only include `primitive type` properties to work with `default` ICondition `deserialization`. When you need to implement a much complex toggle condition with `non-primitive` properties then you need to provide `custom` implementation of `IConditionDeserializer` to support its deserialization to toggle condition object.
-
-### ii. Logger
-You could optionally provide an implementation of a logger by wrapping your favourite logging libaray under `IFeatureLogger` interface.
-Please see the interface definition below.
->This implementation is optional and when no logger is provided FeatureOne will not log any errors, warnings or information.
-```
- ///
- /// Interface to implement custom logger.
- ///
- public interface IFeatureLogger
- {
- ///
- /// Implement the debug log method
- ///
- /// log message
- void Debug(string message);
-
- ///
- /// Implement the error log method
- ///
- /// log message
- /// exception
- void Error(string message, Exception ex = null);
-
- ///
- /// Implement the info log method
- ///
- /// log message
- void Info(string message);
-
- ///
- /// Implement the warn log method
- ///
- /// log message
- void Warn(string message);
- }
-```
-## FeatureOne.SQL - Feature toggles with SQL Backend.
-In addition to all FeatureOne offerings, the `FeatureOne.SQL` package provides out of box SQL storage provider.
-
-SQL support can easily be installed as a separate nuget package.
-```
-$ dotnet add package FeatureOne.SQL --version {latest}
-```
-### Step 1 - Configure Database Provider
-To register a database provider, You need to add the relevant db factory with a specific `ProviderName` to `DbProviderFactories` in the bootstrap code.
-ie.
-`DbProviderFactories.RegisterFactory("ProviderName", ProviderFactory)`
-
-After adding the provider factory you need to pass the same provider in the `connection settings` of SQLConfiguration.
-
-> Below is the list of most common provider factories yu could configure.
->
- - MSSQL - DbProviderFactories.RegisterFactory("System.Data.SqlClient", SqlClientFactory.Instance);
- - ODBC - DbProviderFactories.RegisterFactory("System.Data.Odbc", OdbcFactory.Instance);
- - OleDb - DbProviderFactories.RegisterFactory("System.Data.OleDb", OleDbFactory.Instance);
- - SQLite - DbProviderFactories.RegisterFactory("System.Data.SQLite", SQLiteFactory.Instance);
- - MySQL - DbProviderFactories.RegisterFactory("MySql.Data.MySqlClient", MySqlClientFactory.Instance);
- - PostgreSQL - DbProviderFactories.RegisterFactory("Npgsql", NpgsqlFactory.Instance);
->
-
-### STEP 2 - Setup Feature Table (Database)
-> Requires creating a feature table with columns for feature name, toggle definition and feature archival.
-
-SQL SCRIPT below.
-```
-CREATE TABLE TFeatures (
- Id INT NOT NULL IDENTITY PRIMARY KEY,
- Name VARCHAR(255) NOT NULL,
- Toggle NVARCHAR(4000) NOT NULL,
- Archived BIT CONSTRAINT DF_TFeatures_Archived DEFAULT (0)
-);
-```
-
-#### Example Table Record
-> Feature toggles need to be `scripted` to backend database in JSON format.
-
-Please see example entries below.
-
-| Name |Toggle | Archived |
-||||
-| dashboard_widget |{ "conditions":[{ "type":"Simple", "isEnabled": true }] } | false |
-|pen_test_dashboard| { "operator":"any", "conditions":[{ "type":"simple", "isEnabled":false}, { "type":"Regex", "claim":"email","expression":"^[a-zA-Z0-9_.+-]+@gbk.com" }]} | false|
-
-### STEP 3 - Bootstrap initialization
-> See below bootstrap initialization for FeatureOne with MS SQL backend.
-
+## Getting Started?
+### i. Installation
+Install the latest nuget package as appropriate.
-#### SQL Configuration - Set connection string and other settings.
+`FeatureOne` - for installing FeatureOne for custom `IStorageProvider` implementation.
```
- var sqlConfiguration = new SQLConfiguration
- {
- // provider specific connection settings.
- ConnectionSettings = new ConnectionSettings
- {
- Providername = "System.Data.SqlClient", -- same provider name as register with db factory.
- ConnectionString ="Data Source=Powerstation; Initial Catalog=Features; Integrated Security=SSPI;"
- },
-
- // Table and column name overrides.
- FeatureTable = new FeatureTable
- {
- TableName = "[Features].[dbo].[TFeatures]",
- NameColumn = "[Name]",
- ToggleColumn = "[Toggle]",
- ArchivedColumn = "[Archived]"
- },
-
- // Enable cache with absolute expiry in Minutes.
- CacheSettings = new CacheSettings
- {
- EnableCache = true,
- Expiry = new CacheExpiry
- {
- InMinutes = 60,
- Type = CacheExpiryType.Absolute
- }
- }
- }
+NuGet\Install-Package FeatureOne
```
-i. With SQL configuration.
-```
- -- Register db factory
- DbProviderFactories.RegisterFactory("System.Data.SqlClient", SqlClientFactory.Instance);
-
- var storageProvider = new SQlStorageProvider(sqlConfiguration);
- Features.Initialize(() => new Features(new FeatureStore(storageProvider)));
+`FeatureOne.SQL` - for installing FeatureOne with SQL storage provider.
```
-ii. With Custom logger implementation, default is no logger.
+NuGet\Install-Package FeatureOne.SQL
```
- var logger = new CustomLoggerImpl();
- var storageProvider = new SQlStorageProvider(sqlConfiguration, logger);
-
- Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger));
-```
-
-iii. With other overloads - Custom cache and Toggle Condition deserializer.
-```
- var toggleConditionDeserializer = CustomConditionDeserializerImpl(); // Implements IConditionDeserializer
- var featureCache = CustomFeatureCache(); // Implements ICache
-
- var storageProvider = new SQlStorageProvider(sqlConfiguration, featureCache, toggleConditionDeserializer);
-
- Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger));
-```
-
-## FeatureOne.File - Feature toggles with File system Backend.
-In addition to all FeatureOne offerings, the `FeatureOne.File` package provides out of box File storage provider.
-
-File support can easily be installed as a separate nuget package.
+`FeatureOne.File` - for installing FeatureOne with File system storage provider.
```
-$ dotnet add package FeatureOne.File --version {latest}
+NuGet\Install-Package FeatureOne.File
```
-### File Setup
-> Requires creating a feature file with JSON feature toggles as shown below.
-File - `Features.json`
-```
-{
- "gbk_dashboard": {
- "toggle": {
- "operator": "any",
- "conditions": [{
- "type": "simple",
- "isEnabled": false
- },
- {
- "type": "Regex",
- "claim": "email",
- "expression": "^[a-zA-Z0-9_.+-]+@gbk.com"
- }
- ]
- }
- },
- "dashboard_widget": {
- "toggle": {
- "conditions": [{
- "type": "simple",
- "isEnabled": true
- }]
- }
- }
-}
-```
-### Bootstrap initialization
-> See below bootstrap initialization for FeatureOne with SQL backend.
+### ii. Developer Guide
+Please see [Developer Guide](/DeveloperGuide.md) for details on how to implement schemio in your project.
-#### File Configuration - Set file path string and cache settings.
-```
- var configuration = new FileConfiguration
- {
- // Absolute path to the feature file.
- FilePath ="C:\Work\Features.json",
-
- // Enable cache with absolute expiry in Minutes.
- CacheSettings = new CacheSettings
- {
- EnableCache = true,
- Expiry = new CacheExpiry
- {
- InMinutes = 60,
- Type = CacheExpiryType.Absolute
- }
- }
- }
-```
-i. With File configuration.
-```
- var storageProvider = new FileStorageProvider(configuration);
- Features.Initialize(() => new Features(new FeatureStore(configuration)));
-```
-ii. With Custom logger implementation, default is no logger.
-```
- var logger = new CustomLoggerImpl();
- var storageProvider = new FileStorageProvider(configuration, logger);
+## Support
- Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger));
-```
+If you are having problems, please let me know by [raising a new issue](https://github.com/CodeShayk/FeatureOne/issues/new/choose).
-iii. With other overloads - Custom cache and Toggle Condition deserializer.
-```
- var toggleConditionDeserializer = CustomConditionDeserializerImpl(); // Implements IConditionDeserializer
- var featureCache = CustomFeatureCache(); // Implements ICache
+## License
- var storageProvider = new FileStorageProvider(configuration, featureCache, toggleConditionDeserializer);
+This project is licensed with the [MIT license](LICENSE).
- Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger));
-```
+## Version History
+The main branch is now on .NET 8.0. The following previous versions are available:
+| Version | Release Notes | Developer Guide |
+| -------- | --------|--------|
+| [`v4.0.0`](https://github.com/CodeShayk/FeatureOne/tree/v4.0.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v4.0.0) | [Guide](https://github.com/CodeShayk/FeatureOne/blob/v4.0.0/DeveloperGuide.md) |
+| [`v3.0.0`](https://github.com/CodeShayk/FeatureOne/tree/v3.0.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v3.0.0) | [Guide](https://github.com/CodeShayk/FeatureOne/blob/v3.0.0/DeveloperGuide.md) |
+| [`v2.0.0`](https://github.com/CodeShayk/FeatureOne/tree/v2.0.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v2.0.0) | [Guide](https://github.com/CodeShayk/FeatureOne/blob/v2.0.0/DeveloperGuide.md) |
+## Credits
+Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :)
-Credits
---
-Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :)