FeatureOne 1.0.0

Suggested Alternatives

FeatureOne 2.0.0

Additional Details

IStorageProvider contract has changed. Upgrade to v2.0.0 or later

There is a newer version of this package available.
See the version list below for details.
dotnet add package FeatureOne --version 1.0.0
NuGet\Install-Package FeatureOne -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FeatureOne" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add FeatureOne --version 1.0.0
#r "nuget: FeatureOne, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install FeatureOne as a Cake Addin
#addin nuget:?package=FeatureOne&version=1.0.0

// Install FeatureOne as a Cake Tool
#tool nuget:?package=FeatureOne&version=1.0.0

FeatureOne

NuGet version License: MIT CI GitHub Release CodeQL .Net Stardard

.Net Library to implement feature toggles.

What is a feature toggle?

Feature toggle is a mechanism that allows code to be turned “on” or “off” remotely without the need for a deploy. Feature toggles are commonly used in applications to gradually roll out new features, allowing teams to test changes on a small subset of users before releasing them to everyone.

How feature toggles work

Feature toggle is typically a logical check added to codebase to execute or ignore certain functionality in context based on evaluated status of the toggle at runitme.

In code, the functionality to be released is wrapped so that it can be controlled by the status of a feature toggle. If the status of the feature toggle is “on”, then the wrapped functionality is executed. If the status of the feature toggle is “off”, then the wrapped functionality is skipped. The statuses of each feature is provided by a store provider external to the application.

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, claimsPrincipal){ // 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).

JSON Syntax for Feature Toggle is below

{
  "feature_name":{ -- Feature name
        "toggle":{ -- Toggle details for the feature 

            "operator":"any|all", -- 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.
	      }]		  
	  } 		  
  }
}
ii. Regex Condition

Regex condition allows evaluating a regex expression against specified user claim 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 evaulation.
		   }]		  
	  }	  
   }
 }

Step 3. Provide Storage Implementation.

Implement IStorageProvider interface to get configured feature toggles from storage. The interface has Get() method that returns a collection of KeyValuePair<string, string> with key mapping to featureName and value mapping to json string representation of the toggle

    /// <summary>
    /// Interface to implement storage provider.
    /// </summary>
    public interface IStorageProvider
    {
        /// <summary>
        ///  Implement this method to get all feature toggles from storage.
        /// </summary>
        /// <remarks>
        /// Example:
        ///  Key - "dashboard_widget"
        ///  Value - "{\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": true}]}"
	/// </remarks>
	/// <returns>KeyValuePair Array</returns>
        IEnumerable<KeyValuePair<string, string>> Get();
    }

Step 4. Bootstrap Initialialization

In bootstrap code, initialize the Features class with dependencies as shown below.

  1. With storage provider implementation.
   var storageProvider = new SQlStoreProviderImpl();
   Features.Initialize(() => new Features(new FeatureStore(storageProvider)));
  1. With storage provider and logger implementation.
   var logger = new CustomLoggerImpl();
   var storageProvider = new SQlStoreProviderImpl();

   Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger), logger));
  1. With storage provider, logger and custom toggle deserializer implementation.
   var logger = new CustomLoggerImpl();
   var storageProvider = new SQlStoreProviderImpl();
   var toggleDeserializer = new CustomToggleDeserializer();

   Features.Initialize(() => new Features(new FeatureStore(storageProvider, logger, toggleDeserializer), 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.

    /// <summary>
    /// Interface to implement toggle condition.
    /// </summary>
    public interface ICondition
    {
        /// <summary>
        /// Implement method to evaulate toggle condition.
        /// </summary>
        /// <param name="claims">List of user claims; could be empty</param>
        /// <returns></returns>
        bool Evaluate(IDictionary<string, string> claims);
    }

Please Note The condition class should only include primitive data type properties for default deserialization. Example below of custom condition .

   public class TimeCondition : ICondition
   {
        // toggle to show feature after given hour during the day.
	public int Hour {get; set;} = 12; // Primitive int property.

	bool Evaluate(IDictionary<string, string> claims)
	{
		return (DateTime.Now.Hour > 12);	 
	}
   }

Example usage of above condition in toggle to allow non-admin users access to a feature only after 14 hrs.

 {
       "operator":"any", -- Any below condition evaluation to true should succeed the toggle.
       "conditions":[{             
              "type":"Time", -- Time condition to all access to all after 14hrs
              "Hour":14
       },
       {   
              "type":"Regex", -- Regex to allow admin access
              "claim":"user_role",
              "expression":"^administrator$"
       }]
 }

ii. Storage Provider

To use FeatureOne, you need to provide implementation of Storage Provider to get all the feature toggles from storage medium of choice. Implement IStorageProvider interface to return the key-value pairs with feature name and json string toggle.

Below is an example of dummy provider implementation.

A production implementation should be a provider with API , SQL or File system storage. Ideally, you may also use caching of feature toggles in the production implementation to optimise calls to storage medium.

public class CustomStoreProvider : IStorageProvider
    {
        public IEnumerable<KeyValuePair<string, string>> Get()
        {
            return new[] {
                    new KeyValuePair<string, string>("feature-01", "{\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": true}]}"),
                    new KeyValuePair<string, string>("feature-02", "{\"operator\":\"all\",\"conditions\":[{\"type\":\"Simple\",\"isEnabled\": false}, {\"type\":\"RegexCondition\",\"claim\":\"email\",\"expression\":\"*@gbk.com\"}]}")
                };
        }
    }

iii. 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.

    /// <summary>
    /// Interface to implement custom logger.
    /// </summary>
    public interface IFeatureLogger
    {
        /// <summary>
        /// Implement the debug log method
        /// </summary>
        /// <param name="message">log message</param>
        void Debug(string message);

        /// <summary>
        /// Implement the error log method
        /// </summary>
        /// <param name="message">log message</param>
        void Error(string message);

        /// <summary>
        /// Implement the info log method
        /// </summary>
        /// <param name="message">log message</param>
        void Info(string message);

        /// <summary>
        /// Implement the warn log method
        /// </summary>
        /// <param name="message">log message</param>
        void Warn(string message);
    }

Credits

Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! 😃

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on FeatureOne:

Package Downloads
FeatureOne.SQL

.Net library to implement feature toggles with SQL storage.

FeatureOne.File

.Net library to implement feature toggles with File system storage.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
4.0.0 106 4/5/2024
3.0.0 214 6/27/2023
2.0.4 220 5/24/2023
2.0.0 177 5/22/2023
1.0.7 184 5/18/2023
1.0.0 231 12/9/2022