Skip to content

Commit

Permalink
Add methods to retrieve all configured feature toggles
Browse files Browse the repository at this point in the history
  • Loading branch information
henkmollema committed Nov 15, 2023
1 parent 99005fc commit a34f5b4
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
Expand All @@ -10,6 +11,18 @@ namespace Webenable.FeatureToggles;
/// </summary>
public class ConfigFeatureToggleConfiguration(IConfiguration configuration) : FeatureToggleConfiguration
{
public override ValueTask<Dictionary<string, bool>> GetAll(CancellationToken cancellationToken = default)
{
var featureConfig = configuration.GetSection("Features");
if (featureConfig == null)
{
return new ValueTask<Dictionary<string, bool>>([]);
}

var toggles = featureConfig.GetChildren().ToDictionary(x => x.Key, x => bool.TryParse(x.Value, out var b) && b);
return new ValueTask<Dictionary<string, bool>>(toggles);
}

public override ValueTask<bool?> IsEnabledAsync(string featureName, CancellationToken cancellationToken = default)
{
// Resolve the feature section from the configuration.
Expand Down
24 changes: 21 additions & 3 deletions src/Webenable.FeatureToggles/DatabaseFeatureToggleConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dommel;
Expand All @@ -16,18 +18,31 @@ public class DatabaseFeatureToggleConfiguration(IOptions<FeatureToggleOptions> o
var factory = options.Value.DbConnectionFactory;
if (factory is not null)
{
using var con = await factory(cancellationToken);
await using var con = await factory(cancellationToken);
var toggle = await con.FirstOrDefaultAsync<FeatureToggleDto>(x => x.Name == featureName, cancellationToken: cancellationToken);
if (toggle is not null)
{
return toggle.State == FeatureToggleState.Enabled;
return toggle.IsEnabled;
}
}

// No database connection configured or feature toggle is not configured
return null;
}

public override async ValueTask<Dictionary<string, bool>> GetAll(CancellationToken cancellationToken = default)
{
var factory = options.Value.DbConnectionFactory;
if (factory is not null)
{
await using var con = await factory(cancellationToken);
var toggles = await con.GetAllAsync<FeatureToggleDto>(cancellationToken: cancellationToken);
return toggles.ToDictionary(x => x.Name!, x => x.IsEnabled);
}

return [];
}

public override int Order => base.Order - 10;
}

Expand All @@ -51,6 +66,9 @@ public class FeatureToggleDto
/// Gets or sets the state of the feature toggle.
/// </summary>
public FeatureToggleState State { get; set; }

[NotMapped]
public bool IsEnabled => State == FeatureToggleState.Enabled;
}

/// <summary>
Expand Down
24 changes: 24 additions & 0 deletions src/Webenable.FeatureToggles/FeatureRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ public interface IFeatureRouter
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to propagate cancellation.</param>
/// <returns><c>true</c> when the feature is enabled; otherwise, <c>false</c>.</returns>
ValueTask<bool> IsEnabledAsync(string featureName, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieves all feature toggles from all the available feature toggle configurations.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to propagate cancellation.</param>
/// <returns>A dictionary with configured feature toggles and whether they're enabled or not.</returns>
ValueTask<Dictionary<string, bool>> GetAll(CancellationToken cancellationToken = default);
}

/// <summary>
Expand Down Expand Up @@ -59,4 +66,21 @@ public async ValueTask<bool> IsEnabledAsync(string featureName, CancellationToke
// No provider configured this feature
return false;
}

/// <inheritdoc/>
public async ValueTask<Dictionary<string, bool>> GetAll(CancellationToken cancellationToken = default)
{
var result = new Dictionary<string, bool>();
foreach (var featureToggleConfiguration in featureToggleConfiguration.OrderBy(f => f.Order))
{
cancellationToken.ThrowIfCancellationRequested();

var toggles = await featureToggleConfiguration.GetAll(cancellationToken);
foreach (var toggle in toggles)
{
result[toggle.Key] = toggle.Value;
}
}
return result;
}
}
13 changes: 12 additions & 1 deletion src/Webenable.FeatureToggles/FeatureToggleConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Webenable.FeatureToggles;
Expand All @@ -19,6 +20,13 @@ public interface IFeatureToggleConfiguration
/// </returns>
ValueTask<bool?> IsEnabledAsync(string featureName, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieves all feature toggles in the configuration.
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to propagate cancellation.</param>
/// <returns>A dictionary with configured feature toggles and whether they're enabled or not.</returns>
ValueTask<Dictionary<string, bool>> GetAll(CancellationToken cancellationToken = default);

/// <summary>
/// The order which is used when evaluating feature toggles using a <see cref="IFeatureRouter"/>.
/// </summary>
Expand All @@ -33,6 +41,9 @@ public abstract class FeatureToggleConfiguration : IFeatureToggleConfiguration
/// <inheritdoc/>
public abstract ValueTask<bool?> IsEnabledAsync(string featureName, CancellationToken cancellationToken = default);

/// <inheritdoc/>
public abstract ValueTask<Dictionary<string, bool>> GetAll(CancellationToken cancellationToken = default);

/// <inheritdoc/>
public virtual int Order { get; } = 1000;
}
6 changes: 3 additions & 3 deletions src/Webenable.FeatureToggles/FeatureToggleOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -11,8 +11,8 @@ namespace Webenable.FeatureToggles;
public class FeatureToggleOptions
{
/// <summary>
/// Gets or sets a factory to create a <see cref="IDbConnection"/> to evaluate feature toggles using <see cref="DatabaseFeatureToggleConfiguration"/>.
/// Gets or sets a factory to create a <see cref="DbConnection"/> to evaluate feature toggles using <see cref="DatabaseFeatureToggleConfiguration"/>.
/// Leave null to disable feature toggles in the database.
/// </summary>
public Func<CancellationToken, Task<IDbConnection>>? DbConnectionFactory { get; set; }
public Func<CancellationToken, Task<DbConnection>>? DbConnectionFactory { get; set; }
}

0 comments on commit a34f5b4

Please sign in to comment.