Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,858 changes: 1,858 additions & 0 deletions CoverageResults/2f995bed-6a04-41f7-869e-8202020b2959/coverage.cobertura.xml

Large diffs are not rendered by default.

1,858 changes: 1,858 additions & 0 deletions CoverageResults2/9b766267-cb66-45dc-b88c-9597939dfa8d/coverage.cobertura.xml

Large diffs are not rendered by default.

1,858 changes: 1,858 additions & 0 deletions CoverageResults3/870b732e-9338-4793-ad0c-19c55d4963c8/coverage.cobertura.xml

Large diffs are not rendered by default.

70 changes: 62 additions & 8 deletions DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,18 @@ var feature = new Feature
#### 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.
Below is the serialized representation of toggle with regex condition.
```
{
"dashboard_widget":{
"toggle":{
"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.
}]
}
}]
}
}
}
```
Expand All @@ -119,8 +119,8 @@ var feature = new Feature
Name ="dashboard_widget", // Feature Name
Toggle = new Toggle // Toggle definition
{
Operator = Operator.Any,
Conditions = new[]
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" }
Expand All @@ -129,6 +129,60 @@ var feature = new Feature
}
```

#### iii. Relational Condition
`Relational` condition (class `RelationalCondition`) allows evaluating a user claim value against a fixed value using a relational operator. This is useful for enabling features based on user tiers, roles, or any string-comparable claim.

Supported operators (`RelationalOperator` enum):

| Operator | Description |
|---|---|
| `Equals` | Claim value equals the configured value |
| `NotEquals` | Claim value does not equal the configured value |
| `GreaterThan` | Claim value is lexicographically greater than the configured value |
| `GreaterThanOrEqual` | Claim value is lexicographically greater than or equal to the configured value |
| `LessThanOrEqual` | Claim value is lexicographically less than or equal to the configured value |
| `LessThan` | Defined in enum but **not yet implemented** — always returns `false` |

> **Note:** String comparison is ordinal (via `string.Compare`). Both the claim value and the configured value are trimmed of leading/trailing whitespace before comparison.

Below is the serialized representation of a toggle with a logical condition.
```
{
"dashboard_widget":{
"toggle":{
"operator":"any",
"conditions":[{
"type":"Relational", -- Relational Condition
"claim":"tier", -- Claim name to evaluate
"operator":"GreaterThanOrEqual", -- Relational operator
"value":"gold" -- Value to compare the claim against
}]
}
}
}
```
C# representation of a feature with a logical condition toggle is
```
var feature = new Feature
{
Name = "dashboard_widget", // Feature Name
Toggle = new Toggle // Toggle definition
{
Operator = Operator.Any,
Conditions = new[]
{
// Relational condition — enable feature for users with tier >= "gold" (lexicographic order).
new RelationalCondition
{
Claim = "tier",
Operator = RelationalOperator.GreaterThanOrEqual,
Value = "gold"
}
}
}
}
```

### 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.
Expand Down
2 changes: 1 addition & 1 deletion GitVersion.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
next-version: 5.1.0
next-version: 5.2.0
tag-prefix: '[vV]'
mode: ContinuousDeployment
branches:
Expand Down
2 changes: 1 addition & 1 deletion License.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 Code Shayk
Copyright (c) 2026 Code Shayk

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@

# <img src="https://github.com/CodeShayk/FeatureOne/blob/master/images/feature-flag.png" alt="feature" style="width:60px;"/> FeatureOne v5.1.0
# <img src="https://github.com/CodeShayk/FeatureOne/blob/master/images/feature-flag.png" alt="feature-flag" style="width:60px;"/> FeatureOne v5.2.0
[![GitHub Release](https://img.shields.io/github/v/release/CodeShayk/FeatureOne?logo=github&sort=semver)](https://github.com/CodeShayk/FeatureOne/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/CodeShayk/FeatureOne/blob/master/License.md) [![build-master](https://github.com/CodeShayk/FeatureOne/actions/workflows/Build-Master.yml/badge.svg)](https://github.com/CodeShayk/FeatureOne/actions/workflows/Build-Master.yml)
[![CodeQL](https://github.com/CodeShayk/FeatureOne/actions/workflows/codeql.yml/badge.svg)](https://github.com/CodeShayk/FeatureOne/actions/workflows/codeql.yml)
[![.Net](https://img.shields.io/badge/.Net_Framework-4.6.2-blue)](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net46)
[![.Net](https://img.shields.io/badge/.Net_Standard-2.1-blue)](https://dotnet.microsoft.com/en-us/download/netstandard/2.1)
[![.Net](https://img.shields.io/badge/.Net_Standard-2.1-green)](https://dotnet.microsoft.com/en-us/download/netstandard/2.1)
[![.Net](https://img.shields.io/badge/.Net-9.0-blue)](https://dotnet.microsoft.com/en-us/download/dotnet/9.0)
[![.Net](https://img.shields.io/badge/.Net-10.0-blue)](https://dotnet.microsoft.com/en-us/download/dotnet/10.0)

.Net Library to implement feature toggles.
--
#### Nuget Packages
| Package | Latest | Details |
| --------| --------| --------|
|FeatureOne |[![NuGet version](https://badge.fury.io/nu/FeatureOne.svg)](https://badge.fury.io/nu/FeatureOne) | Provides core functionality 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. **v5.1.0**: Security fixes, DI integration, DateRangeCondition. |
|FeatureOne.SQL| [![NuGet version](https://badge.fury.io/nu/FeatureOne.SQL.svg)](https://badge.fury.io/nu/FeatureOne.SQL) | Provides SQL storage provider for implementing feature toggles using `SQL` backend. **v5.1.0**: Security fixes, DI integration, enhanced configuration. |
|FeatureOne.File |[![NuGet version](https://badge.fury.io/nu/FeatureOne.File.svg)](https://badge.fury.io/nu/FeatureOne.File) | Provides File storage provider for implementing feature toggles using `File System` backend. **v5.1.0**: Security fixes, DI integration, enhanced configuration. |
|FeatureOne |[![NuGet version](https://badge.fury.io/nu/FeatureOne.svg)](https://badge.fury.io/nu/FeatureOne) | Provides core functionality 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. **v5.2.0**: RelationalCondition, net10.0 support, package upgrades, expanded test coverage. |
|FeatureOne.SQL| [![NuGet version](https://badge.fury.io/nu/FeatureOne.SQL.svg)](https://badge.fury.io/nu/FeatureOne.SQL) | Provides SQL storage provider for implementing feature toggles using `SQL` backend. **v5.2.0**: net10.0 support, package upgrades. |
|FeatureOne.File |[![NuGet version](https://badge.fury.io/nu/FeatureOne.File.svg)](https://badge.fury.io/nu/FeatureOne.File) | Provides File storage provider for implementing feature toggles using `File System` backend. **v5.2.0**: net10.0 support, package upgrades. |

## Concept
### What is a feature toggle?
Expand Down Expand Up @@ -62,6 +61,8 @@ The following previous versions are available:

| Version | Release Notes |
| ----------------------------------------------------------------| ----------------------------------------------------------------------|
| [`v5.2.0`](https://github.com/CodeShayk/FeatureOne/tree/v5.2.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v5.2.0) |
| [`v5.1.0`](https://github.com/CodeShayk/FeatureOne/tree/v5.1.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v5.1.0) |
| [`v5.0.0`](https://github.com/CodeShayk/FeatureOne/tree/v5.0.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v5.0.0) |
| [`v4.0.0`](https://github.com/CodeShayk/FeatureOne/tree/v4.0.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v4.0.0) |
| [`v3.0.0`](https://github.com/CodeShayk/FeatureOne/tree/v3.0.0) | [Notes](https://github.com/CodeShayk/FeatureOne/releases/tag/v3.0.0) |
Expand All @@ -73,6 +74,7 @@ The following previous versions are available:
|--------|-------------|------|-------------|---------------------|
| v5.0.0 | Previous | Initial | Core feature toggle functionality | N/A (Initial release) |
| v5.1.0 | Nov 03, 2025 | Minor | **Security fixes** (ReDoS protection, secure type loading), **architectural improvements** (prefix matching, dependency injection), **new features** (DateRangeCondition, configuration validation), **DI integration** | High - maintains all existing functionality with minor security-related behavioral changes |
| v5.2.0 | Mar 18, 2026 | Minor | **New condition** (RelationalCondition with 5 relational operators), **target framework** (added net10.0, removed netstandard2.0 and net8.0), **package upgrades** (all MS packages to 10.0.5), **expanded test coverage** (98%+ line coverage) | High - fully backward compatible, additive changes only |

## Credits
Thank you for reading. Please fork, explore, contribute and report. Happy Coding !! :)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<coverage line-rate="0" branch-rate="0" version="1.9" timestamp="1773873205" lines-covered="0" lines-valid="0" branches-covered="0" branches-valid="0">
<sources />
<packages />
</coverage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<CoverageSession>
<Summary numSequencePoints="0" visitedSequencePoints="0" numBranchPoints="0" visitedBranchPoints="0" sequenceCoverage="0" branchCoverage="0" maxCyclomaticComplexity="1" minCyclomaticComplexity="1" visitedClasses="0" numClasses="0" visitedMethods="0" numMethods="0" />
<Modules />
</CoverageSession>
34 changes: 15 additions & 19 deletions src/FeatureOne.File/FeatureOne.File.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net462;netstandard2.1;net9.0</TargetFrameworks>
<TargetFrameworks>netstandard2.1;net9.0;net10.0</TargetFrameworks>
<Nullable>disable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
Expand All @@ -22,25 +22,22 @@
<RepositoryUrl>https://github.com/codeshayk/FeatureOne</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>feature-toggle; feature-flag; feature-flags; feature-toggles; featureOne; File-system; File-Backend; File-Toggles;</PackageTags>
<Version>5.1.0</Version>
<Version>5.2.0</Version>
<PackageLicenseFile>License.md</PackageLicenseFile>
<PackageIcon>ninja-icon-16.png</PackageIcon>
<PackageIcon>feature-flag.png</PackageIcon>
<PackageReleaseNotes>
Release Notes v5.1.0. - Targets .Net Framework 4.6.2, .NetStandard 2.1 and .Net 9.0
Release Notes v5.2.0. - Targets .NetStandard 2.1, .Net 9.0 and .Net 10.0
Library to Implement Feature Toggles to hide/show program features with File system storage.
Security Fixes:
- Fixed RegexCondition ReDoS (Regular Expression Denial of Service) vulnerability with timeout validation
- Secured dynamic type loading in ConditionDeserializer with explicit safe type registry

Architectural Improvements:
- Fixed FindStartsWith implementation for actual prefix matching
- Implemented proper dependency injection patterns with null validation


New Features:
- Added DateRangeCondition for time-based feature toggles
- Added Configuration Validation System with clear error messages

Provides Out of box Simple and Regex toggle conditions.
- Added RelationalCondition for claim-based relational comparisons (Equals, NotEquals, GreaterThan, GreaterThanOrEqual, LessThanOrEqual)

Framework and Package Updates:
- Added net10.0 target framework
- Removed netstandard2.0 and net8.0 target frameworks
- Upgraded all Microsoft packages to 10.0.5

Provides Out of box Simple, Regex, DateRange and Relational toggle conditions.
Provides Out of box support for File system storage provider to store toggles on disk file.
Provides the support for default memory caching via configuration.
Provides extensibility for custom implementations ie.
Expand All @@ -56,7 +53,7 @@
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\ninja-icon-16.png">
<None Include="..\..\images\feature-flag.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
Expand All @@ -71,8 +68,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
</ItemGroup>

</Project>
37 changes: 16 additions & 21 deletions src/FeatureOne.SQL/FeatureOne.SQL.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net462;netstandard2.1;net9.0</TargetFrameworks>
<TargetFrameworks>netstandard2.1;net9.0;net10.0</TargetFrameworks>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
Expand All @@ -23,26 +23,23 @@
<RepositoryUrl>https://github.com/CodeShayk/FeatureOne</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>feature-toggle; feature-flag; feature-flags; feature-toggles; featureOne; SQL-Backend; SQL-Toggles; SQL</PackageTags>
<Version>5.1.0</Version>
<Version>5.2.0</Version>
<PackageLicenseFile>License.md</PackageLicenseFile>
<PackageIcon>ninja-icon-16.png</PackageIcon>
<PackageIcon>feature-flag.png</PackageIcon>
<PackageReleaseNotes>
Release Notes v5.1.0. - Targets .Net Framework 4.6.2, .NetStandard 2.1 and .Net 9.0
Release Notes v5.2.0. - Targets .NetStandard 2.1, .Net 9.0 and .Net 10.0
Library to Implement Feature Toggles to hide/show program features with SQL storage.
Security Fixes:
- Fixed RegexCondition ReDoS (Regular Expression Denial of Service) vulnerability with timeout validation
- Secured dynamic type loading in ConditionDeserializer with explicit safe type registry

Architectural Improvements:
- Fixed FindStartsWith implementation for actual prefix matching
- Implemented proper dependency injection patterns with null validation


New Features:
- Added DateRangeCondition for time-based feature toggles
- Added Configuration Validation System with clear error messages

- Added RelationalCondition for claim-based relational comparisons (Equals, NotEquals, GreaterThan, GreaterThanOrEqual, LessThanOrEqual)

Framework and Package Updates:
- Added net10.0 target framework
- Removed netstandard2.0 and net8.0 target frameworks
- Upgraded all Microsoft packages to 10.0.5

Supports configuring all Db providers - MSSQL, SQLite, ODBC, OLEDB, MySQL, PostgreSQL.
Provides Out of box Simple and Regex toggle conditions.
Provides Out of box Simple, Regex, DateRange, and Relational toggle conditions.
Provides the support for default memory caching via configuration.
Provides extensibility for custom implementations ie.
-- Provides extensibility for implementing custom toggle conditions for bespoke use cases.
Expand All @@ -58,7 +55,7 @@
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\ninja-icon-16.png">
<None Include="..\..\images\feature-flag.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
Expand All @@ -69,10 +66,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
<PackageReference Include="System.Text.Json" Version="10.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 5 additions & 5 deletions src/FeatureOne/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@

[assembly: System.Reflection.AssemblyCompanyAttribute("Code Shayk")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyCopyrightAttribute("2024")]
[assembly: System.Reflection.AssemblyCopyrightAttribute("2026")]
[assembly: System.Reflection.AssemblyDescriptionAttribute(".Net Library to implement feature toggles.")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("4.0.0")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("5.2.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("5.2.0")]
[assembly: System.Reflection.AssemblyProductAttribute("FeatureOne")]
[assembly: System.Reflection.AssemblyTitleAttribute("FeatureOne")]
[assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
[assembly: System.Reflection.AssemblyMetadataAttribute("RepositoryUrl", "https://github.com/TechNinjaLabs/FeatureOne")]
[assembly: System.Reflection.AssemblyVersionAttribute("5.2.0.0")]
[assembly: System.Reflection.AssemblyMetadataAttribute("RepositoryUrl", "https://github.com/CodeShayk/FeatureOne")]

// Generated by the MSBuild WriteCodeFragment class.

49 changes: 49 additions & 0 deletions src/FeatureOne/Core/Toggles/Conditions/RelationalCondition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;

namespace FeatureOne.Core.Toggles.Conditions
{
public class RelationalCondition : ICondition
{
public string Claim { get; set; }
public RelationalOperator Operator { get; set; }
public string Value { get; set; }

public bool Evaluate(IDictionary<string, string> claims)
{
if (claims == null)
return false;

if (!claims.Any(x => x.Key != null && x.Key.Equals(Claim)))
return false;

var claimValue = claims.First(x => x.Key.Equals(Claim)).Value?.Trim() ?? string.Empty;
var comparisonValue = Value?.Trim() ?? string.Empty;

switch (Operator)
{
case RelationalOperator.Equals:
return claimValue == comparisonValue;
case RelationalOperator.NotEquals:
return claimValue != comparisonValue;
case RelationalOperator.GreaterThan:
return string.Compare(claimValue, comparisonValue) > 0;
case RelationalOperator.GreaterThanOrEqual:
return string.Compare(claimValue, comparisonValue) >= 0;
case RelationalOperator.LessThanOrEqual:
return string.Compare(claimValue, comparisonValue) <= 0;
default:
return false;
}
}
}
public enum RelationalOperator
{
Equals,
NotEquals,
GreaterThan,
LessThan,
GreaterThanOrEqual,
LessThanOrEqual
}
}
Loading
Loading