177 lines
5.2 KiB
C#
177 lines
5.2 KiB
C#
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using StellaOps.FeatureFlags.Providers;
|
|
|
|
namespace StellaOps.FeatureFlags;
|
|
|
|
/// <summary>
|
|
/// Extension methods for configuring feature flag services.
|
|
/// </summary>
|
|
public static class FeatureFlagServiceCollectionExtensions
|
|
{
|
|
/// <summary>
|
|
/// Adds the feature flag service with default options.
|
|
/// </summary>
|
|
public static IServiceCollection AddFeatureFlags(
|
|
this IServiceCollection services)
|
|
{
|
|
return services.AddFeatureFlags(_ => { });
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the feature flag service with configuration.
|
|
/// </summary>
|
|
public static IServiceCollection AddFeatureFlags(
|
|
this IServiceCollection services,
|
|
Action<FeatureFlagOptions> configure)
|
|
{
|
|
services.Configure(configure);
|
|
services.TryAddSingleton<IFeatureFlagService, CompositeFeatureFlagService>();
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a configuration-based feature flag provider.
|
|
/// </summary>
|
|
/// <param name="services">The service collection.</param>
|
|
/// <param name="sectionName">Configuration section name (default: "FeatureFlags").</param>
|
|
/// <param name="priority">Provider priority (lower = higher priority).</param>
|
|
public static IServiceCollection AddConfigurationFeatureFlags(
|
|
this IServiceCollection services,
|
|
string sectionName = "FeatureFlags",
|
|
int priority = 50)
|
|
{
|
|
services.AddSingleton<IFeatureFlagProvider>(sp =>
|
|
{
|
|
var configuration = sp.GetRequiredService<IConfiguration>();
|
|
return new ConfigurationFeatureFlagProvider(configuration, sectionName, priority);
|
|
});
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a custom feature flag provider.
|
|
/// </summary>
|
|
public static IServiceCollection AddFeatureFlagProvider<TProvider>(
|
|
this IServiceCollection services)
|
|
where TProvider : class, IFeatureFlagProvider
|
|
{
|
|
services.AddSingleton<IFeatureFlagProvider, TProvider>();
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a custom feature flag provider using a factory.
|
|
/// </summary>
|
|
public static IServiceCollection AddFeatureFlagProvider(
|
|
this IServiceCollection services,
|
|
Func<IServiceProvider, IFeatureFlagProvider> factory)
|
|
{
|
|
services.AddSingleton(factory);
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an in-memory feature flag provider for testing.
|
|
/// </summary>
|
|
public static IServiceCollection AddInMemoryFeatureFlags(
|
|
this IServiceCollection services,
|
|
IDictionary<string, bool> flags,
|
|
int priority = 0)
|
|
{
|
|
services.AddSingleton<IFeatureFlagProvider>(
|
|
new InMemoryFeatureFlagProvider(flags, priority));
|
|
|
|
return services;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// In-memory feature flag provider for testing and overrides.
|
|
/// </summary>
|
|
public sealed class InMemoryFeatureFlagProvider : FeatureFlagProviderBase
|
|
{
|
|
private readonly Dictionary<string, bool> _flags;
|
|
private readonly Dictionary<string, object?> _variants;
|
|
|
|
/// <summary>
|
|
/// Creates a new in-memory provider with the specified flags.
|
|
/// </summary>
|
|
public InMemoryFeatureFlagProvider(
|
|
IDictionary<string, bool> flags,
|
|
int priority = 0)
|
|
{
|
|
_flags = new Dictionary<string, bool>(flags, StringComparer.OrdinalIgnoreCase);
|
|
_variants = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
|
|
Priority = priority;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override string Name => "InMemory";
|
|
|
|
/// <inheritdoc />
|
|
public override int Priority { get; }
|
|
|
|
/// <inheritdoc />
|
|
public override Task<FeatureFlagResult?> TryGetFlagAsync(
|
|
string flagKey,
|
|
FeatureFlagEvaluationContext context,
|
|
CancellationToken ct = default)
|
|
{
|
|
if (_flags.TryGetValue(flagKey, out var enabled))
|
|
{
|
|
_variants.TryGetValue(flagKey, out var variant);
|
|
return Task.FromResult<FeatureFlagResult?>(
|
|
CreateResult(flagKey, enabled, variant, "From in-memory provider"));
|
|
}
|
|
|
|
return Task.FromResult<FeatureFlagResult?>(null);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override Task<IReadOnlyList<FeatureFlagDefinition>> ListFlagsAsync(
|
|
CancellationToken ct = default)
|
|
{
|
|
var flags = _flags.Select(kvp => new FeatureFlagDefinition(
|
|
kvp.Key,
|
|
null,
|
|
kvp.Value,
|
|
kvp.Value)).ToList();
|
|
|
|
return Task.FromResult<IReadOnlyList<FeatureFlagDefinition>>(flags);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets a flag value.
|
|
/// </summary>
|
|
public void SetFlag(string key, bool enabled, object? variant = null)
|
|
{
|
|
_flags[key] = enabled;
|
|
if (variant is not null)
|
|
{
|
|
_variants[key] = variant;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a flag.
|
|
/// </summary>
|
|
public void RemoveFlag(string key)
|
|
{
|
|
_flags.Remove(key);
|
|
_variants.Remove(key);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all flags.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
_flags.Clear();
|
|
_variants.Clear();
|
|
}
|
|
}
|