diff --git a/OpenFeature.slnx b/OpenFeature.slnx
index 936079f40..db8f40024 100644
--- a/OpenFeature.slnx
+++ b/OpenFeature.slnx
@@ -48,7 +48,6 @@
-
@@ -58,7 +57,6 @@
-
diff --git a/src/OpenFeature.DependencyInjection/Diagnostics/FeatureCodes.cs b/src/OpenFeature.DependencyInjection/Diagnostics/FeatureCodes.cs
deleted file mode 100644
index 582ab39c9..000000000
--- a/src/OpenFeature.DependencyInjection/Diagnostics/FeatureCodes.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace OpenFeature.DependencyInjection.Diagnostics;
-
-///
-/// Contains identifiers for experimental features and diagnostics in the OpenFeature framework.
-///
-///
-/// Experimental - This class includes identifiers that allow developers to track and conditionally enable
-/// experimental features. Each identifier follows a structured code format to indicate the feature domain,
-/// maturity level, and unique identifier. Note that experimental features are subject to change or removal
-/// in future releases.
-///
-/// Basic Information
-/// These identifiers conform to OpenFeature’s Diagnostics Specifications, allowing developers to recognize
-/// and manage experimental features effectively.
-///
-///
-///
-///
-/// Code Structure:
-/// - "OF" - Represents the OpenFeature library.
-/// - "DI" - Indicates the Dependency Injection domain.
-/// - "001" - Unique identifier for a specific feature.
-///
-///
-internal static class FeatureCodes
-{
- ///
- /// Identifier for the experimental Dependency Injection features within the OpenFeature framework.
- ///
- ///
- /// OFDI001 identifier marks experimental features in the Dependency Injection (DI) domain.
- ///
- /// Usage:
- /// Developers can use this identifier to conditionally enable or test experimental DI features.
- /// It is part of the OpenFeature diagnostics system to help track experimental functionality.
- ///
- public const string NewDi = "OFDI001";
-}
diff --git a/src/OpenFeature.DependencyInjection/Guard.cs b/src/OpenFeature.DependencyInjection/Guard.cs
deleted file mode 100644
index 337a8290f..000000000
--- a/src/OpenFeature.DependencyInjection/Guard.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-
-namespace OpenFeature.DependencyInjection;
-
-[DebuggerStepThrough]
-internal static class Guard
-{
- public static void ThrowIfNull(object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
- {
- if (argument is null)
- throw new ArgumentNullException(paramName);
- }
-
- public static void ThrowIfNullOrWhiteSpace(string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
- {
- if (string.IsNullOrWhiteSpace(argument))
- throw new ArgumentNullException(paramName);
- }
-}
diff --git a/src/OpenFeature.DependencyInjection/IFeatureLifecycleManager.cs b/src/OpenFeature.DependencyInjection/IFeatureLifecycleManager.cs
deleted file mode 100644
index 4891f2e8b..000000000
--- a/src/OpenFeature.DependencyInjection/IFeatureLifecycleManager.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace OpenFeature.DependencyInjection;
-
-///
-/// Defines the contract for managing the lifecycle of a feature api.
-///
-public interface IFeatureLifecycleManager
-{
- ///
- /// Ensures that the feature provider is properly initialized and ready to be used.
- /// This method should handle all necessary checks, configuration, and setup required to prepare the feature provider.
- ///
- /// Propagates notification that operations should be canceled.
- /// A Task representing the asynchronous operation of initializing the feature provider.
- /// Thrown when the feature provider is not registered or is in an invalid state.
- ValueTask EnsureInitializedAsync(CancellationToken cancellationToken = default);
-
- ///
- /// Gracefully shuts down the feature api, ensuring all resources are properly disposed of and any persistent state is saved.
- /// This method should handle all necessary cleanup and shutdown operations for the feature provider.
- ///
- /// Propagates notification that operations should be canceled.
- /// A Task representing the asynchronous operation of shutting down the feature provider.
- ValueTask ShutdownAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/OpenFeature.DependencyInjection/Internal/EventHandlerDelegateWrapper.cs b/src/OpenFeature.DependencyInjection/Internal/EventHandlerDelegateWrapper.cs
deleted file mode 100644
index d31b3355c..000000000
--- a/src/OpenFeature.DependencyInjection/Internal/EventHandlerDelegateWrapper.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using OpenFeature.Constant;
-using OpenFeature.Model;
-
-namespace OpenFeature.DependencyInjection.Internal;
-
-internal record EventHandlerDelegateWrapper(
- ProviderEventTypes ProviderEventType,
- EventHandlerDelegate EventHandlerDelegate);
diff --git a/src/OpenFeature.DependencyInjection/Internal/FeatureLifecycleManager.cs b/src/OpenFeature.DependencyInjection/Internal/FeatureLifecycleManager.cs
deleted file mode 100644
index 1ecac4349..000000000
--- a/src/OpenFeature.DependencyInjection/Internal/FeatureLifecycleManager.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace OpenFeature.DependencyInjection.Internal;
-
-internal sealed partial class FeatureLifecycleManager : IFeatureLifecycleManager
-{
- private readonly Api _featureApi;
- private readonly IServiceProvider _serviceProvider;
- private readonly ILogger _logger;
-
- public FeatureLifecycleManager(Api featureApi, IServiceProvider serviceProvider, ILogger logger)
- {
- _featureApi = featureApi;
- _serviceProvider = serviceProvider;
- _logger = logger;
- }
-
- ///
- public async ValueTask EnsureInitializedAsync(CancellationToken cancellationToken = default)
- {
- this.LogStartingInitializationOfFeatureProvider();
-
- var options = _serviceProvider.GetRequiredService>().Value;
- if (options.HasDefaultProvider)
- {
- var featureProvider = _serviceProvider.GetRequiredService();
- await _featureApi.SetProviderAsync(featureProvider).ConfigureAwait(false);
- }
-
- foreach (var name in options.ProviderNames)
- {
- var featureProvider = _serviceProvider.GetRequiredKeyedService(name);
- await _featureApi.SetProviderAsync(name, featureProvider).ConfigureAwait(false);
- }
-
- var hooks = new List();
- foreach (var hookName in options.HookNames)
- {
- var hook = _serviceProvider.GetRequiredKeyedService(hookName);
- hooks.Add(hook);
- }
-
- _featureApi.AddHooks(hooks);
-
- var handlers = _serviceProvider.GetServices();
- foreach (var handler in handlers)
- {
- _featureApi.AddHandler(handler.ProviderEventType, handler.EventHandlerDelegate);
- }
- }
-
- ///
- public async ValueTask ShutdownAsync(CancellationToken cancellationToken = default)
- {
- this.LogShuttingDownFeatureProvider();
- await _featureApi.ShutdownAsync().ConfigureAwait(false);
- }
-
- [LoggerMessage(200, LogLevel.Information, "Starting initialization of the feature provider")]
- partial void LogStartingInitializationOfFeatureProvider();
-
- [LoggerMessage(200, LogLevel.Information, "Shutting down the feature provider")]
- partial void LogShuttingDownFeatureProvider();
-}
diff --git a/src/OpenFeature.DependencyInjection/MultiTarget/CallerArgumentExpressionAttribute.cs b/src/OpenFeature.DependencyInjection/MultiTarget/CallerArgumentExpressionAttribute.cs
deleted file mode 100644
index afbec6b06..000000000
--- a/src/OpenFeature.DependencyInjection/MultiTarget/CallerArgumentExpressionAttribute.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// @formatter:off
-// ReSharper disable All
-#if NETCOREAPP3_0_OR_GREATER
-// https://github.com/dotnet/runtime/issues/96197
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.CompilerServices.CallerArgumentExpressionAttribute))]
-#else
-#pragma warning disable
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.Runtime.CompilerServices;
-
-[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
-internal sealed class CallerArgumentExpressionAttribute : Attribute
-{
- public CallerArgumentExpressionAttribute(string parameterName)
- {
- ParameterName = parameterName;
- }
-
- public string ParameterName { get; }
-}
-#endif
diff --git a/src/OpenFeature.DependencyInjection/MultiTarget/IsExternalInit.cs b/src/OpenFeature.DependencyInjection/MultiTarget/IsExternalInit.cs
deleted file mode 100644
index 877141115..000000000
--- a/src/OpenFeature.DependencyInjection/MultiTarget/IsExternalInit.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// @formatter:off
-// ReSharper disable All
-#if NET5_0_OR_GREATER
-// https://github.com/dotnet/runtime/issues/96197
-[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.CompilerServices.IsExternalInit))]
-#else
-#pragma warning disable
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.ComponentModel;
-
-namespace System.Runtime.CompilerServices;
-
-///
-/// Reserved to be used by the compiler for tracking metadata.
-/// This class should not be used by developers in source code.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-static class IsExternalInit { }
-#endif
diff --git a/src/OpenFeature.DependencyInjection/OpenFeature.DependencyInjection.csproj b/src/OpenFeature.DependencyInjection/OpenFeature.DependencyInjection.csproj
deleted file mode 100644
index afefeb9a9..000000000
--- a/src/OpenFeature.DependencyInjection/OpenFeature.DependencyInjection.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- OpenFeature.DependencyInjection
- README.md
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/OpenFeature.DependencyInjection/OpenFeatureBuilder.cs b/src/OpenFeature.DependencyInjection/OpenFeatureBuilder.cs
deleted file mode 100644
index ae1e8c8fb..000000000
--- a/src/OpenFeature.DependencyInjection/OpenFeatureBuilder.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace OpenFeature.DependencyInjection;
-
-///
-/// Describes a backed by an .
-///
-/// The services being configured.
-public class OpenFeatureBuilder(IServiceCollection services)
-{
- /// The services being configured.
- public IServiceCollection Services { get; } = services;
-
- ///
- /// Indicates whether the evaluation context has been configured.
- /// This property is used to determine if specific configurations or services
- /// should be initialized based on the presence of an evaluation context.
- ///
- public bool IsContextConfigured { get; internal set; }
-
- ///
- /// Indicates whether the policy has been configured.
- ///
- public bool IsPolicyConfigured { get; internal set; }
-
- ///
- /// Gets a value indicating whether a default provider has been registered.
- ///
- public bool HasDefaultProvider { get; internal set; }
-
- ///
- /// Gets the count of domain-bound providers that have been registered.
- /// This count does not include the default provider.
- ///
- public int DomainBoundProviderRegistrationCount { get; internal set; }
-
- ///
- /// Validates the current configuration, ensuring that a policy is set when multiple providers are registered
- /// or when a default provider is registered alongside another provider.
- ///
- ///
- /// Thrown if multiple providers are registered without a policy, or if both a default provider
- /// and an additional provider are registered without a policy configuration.
- ///
- public void Validate()
- {
- if (!IsPolicyConfigured)
- {
- if (DomainBoundProviderRegistrationCount > 1)
- {
- throw new InvalidOperationException("Multiple providers have been registered, but no policy has been configured.");
- }
-
- if (HasDefaultProvider && DomainBoundProviderRegistrationCount == 1)
- {
- throw new InvalidOperationException("A default provider and an additional provider have been registered without a policy configuration.");
- }
- }
- }
-}
diff --git a/src/OpenFeature.DependencyInjection/OpenFeatureBuilderExtensions.cs b/src/OpenFeature.DependencyInjection/OpenFeatureBuilderExtensions.cs
deleted file mode 100644
index d676dc5e9..000000000
--- a/src/OpenFeature.DependencyInjection/OpenFeatureBuilderExtensions.cs
+++ /dev/null
@@ -1,382 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Options;
-using OpenFeature.Constant;
-using OpenFeature.DependencyInjection;
-using OpenFeature.DependencyInjection.Internal;
-using OpenFeature.Model;
-
-namespace OpenFeature;
-
-///
-/// Contains extension methods for the class.
-///
-#if NET8_0_OR_GREATER
-[System.Diagnostics.CodeAnalysis.Experimental(DependencyInjection.Diagnostics.FeatureCodes.NewDi)]
-#endif
-public static partial class OpenFeatureBuilderExtensions
-{
- ///
- /// This method is used to add a new context to the service collection.
- ///
- /// The instance.
- /// the desired configuration
- /// The instance.
- /// Thrown when the or action is null.
- public static OpenFeatureBuilder AddContext(this OpenFeatureBuilder builder, Action configure)
- {
- Guard.ThrowIfNull(builder);
- Guard.ThrowIfNull(configure);
-
- return builder.AddContext((b, _) => configure(b));
- }
-
- ///
- /// This method is used to add a new context to the service collection.
- ///
- /// The instance.
- /// the desired configuration
- /// The instance.
- /// Thrown when the or action is null.
- public static OpenFeatureBuilder AddContext(this OpenFeatureBuilder builder, Action configure)
- {
- Guard.ThrowIfNull(builder);
- Guard.ThrowIfNull(configure);
-
- builder.IsContextConfigured = true;
- builder.Services.TryAddTransient(provider =>
- {
- var contextBuilder = EvaluationContext.Builder();
- configure(contextBuilder, provider);
- return contextBuilder.Build();
- });
-
- return builder;
- }
-
- ///
- /// Adds a feature provider using a factory method without additional configuration options.
- /// This method adds the feature provider as a transient service and sets it as the default provider within the application.
- ///
- /// The used to configure feature flags.
- ///
- /// A factory method that creates and returns a
- /// instance based on the provided service provider.
- ///
- /// The updated instance with the default feature provider set and configured.
- /// Thrown if the is null, as a valid builder is required to add and configure providers.
- public static OpenFeatureBuilder AddProvider(this OpenFeatureBuilder builder, Func implementationFactory)
- => AddProvider(builder, implementationFactory, null);
-
- ///
- /// Adds a feature provider using a factory method to create the provider instance and optionally configures its settings.
- /// This method adds the feature provider as a transient service and sets it as the default provider within the application.
- ///
- /// Type derived from used to configure the feature provider.
- /// The used to configure feature flags.
- ///
- /// A factory method that creates and returns a
- /// instance based on the provided service provider.
- ///
- /// An optional delegate to configure the provider-specific options.
- /// The updated instance with the default feature provider set and configured.
- /// Thrown if the is null, as a valid builder is required to add and configure providers.
- public static OpenFeatureBuilder AddProvider(this OpenFeatureBuilder builder, Func implementationFactory, Action? configureOptions)
- where TOptions : OpenFeatureOptions
- {
- Guard.ThrowIfNull(builder);
-
- builder.HasDefaultProvider = true;
- builder.Services.PostConfigure(options => options.AddDefaultProviderName());
- if (configureOptions != null)
- {
- builder.Services.Configure(configureOptions);
- }
-
- builder.Services.TryAddTransient(implementationFactory);
- builder.AddClient();
- return builder;
- }
-
- ///
- /// Adds a feature provider for a specific domain using provided options and a configuration builder.
- ///
- /// Type derived from used to configure the feature provider.
- /// The used to configure feature flags.
- /// The unique name of the provider.
- ///
- /// A factory method that creates a feature provider instance.
- /// It adds the provider as a transient service unless it is already added.
- ///
- /// An optional delegate to configure the provider-specific options.
- /// The updated instance with the new feature provider configured.
- ///
- /// Thrown if either or is null or if the is empty.
- ///
- public static OpenFeatureBuilder AddProvider(this OpenFeatureBuilder builder, string domain, Func implementationFactory, Action? configureOptions)
- where TOptions : OpenFeatureOptions
- {
- Guard.ThrowIfNull(builder);
-
- builder.DomainBoundProviderRegistrationCount++;
-
- builder.Services.PostConfigure(options => options.AddProviderName(domain));
- if (configureOptions != null)
- {
- builder.Services.Configure(domain, configureOptions);
- }
-
- builder.Services.TryAddKeyedTransient(domain, (provider, key) =>
- {
- if (key == null)
- {
- throw new ArgumentNullException(nameof(key));
- }
- return implementationFactory(provider, key.ToString()!);
- });
-
- builder.AddClient(domain);
- return builder;
- }
-
- ///
- /// Adds a feature provider for a specified domain using the default options.
- /// This method configures a feature provider without custom options, delegating to the more generic AddProvider method.
- ///
- /// The used to configure feature flags.
- /// The unique name of the provider.
- ///
- /// A factory method that creates a feature provider instance.
- /// It adds the provider as a transient service unless it is already added.
- ///
- /// The updated instance with the new feature provider configured.
- ///
- /// Thrown if either or is null or if the is empty.
- ///
- public static OpenFeatureBuilder AddProvider(this OpenFeatureBuilder builder, string domain, Func implementationFactory)
- => AddProvider(builder, domain, implementationFactory, configureOptions: null);
-
- ///
- /// Adds a feature client to the service collection, configuring it to work with a specific context if provided.
- ///
- /// The instance.
- /// Optional: The name for the feature client instance.
- /// The instance.
- internal static OpenFeatureBuilder AddClient(this OpenFeatureBuilder builder, string? name = null)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- if (builder.IsContextConfigured)
- {
- builder.Services.TryAddScoped(static provider =>
- {
- var api = provider.GetRequiredService();
- var client = api.GetClient();
- var context = provider.GetRequiredService();
- client.SetContext(context);
- return client;
- });
- }
- else
- {
- builder.Services.TryAddScoped(static provider =>
- {
- var api = provider.GetRequiredService();
- return api.GetClient();
- });
- }
- }
- else
- {
- if (builder.IsContextConfigured)
- {
- builder.Services.TryAddKeyedScoped(name, static (provider, key) =>
- {
- var api = provider.GetRequiredService();
- var client = api.GetClient(key!.ToString());
- var context = provider.GetRequiredService();
- client.SetContext(context);
- return client;
- });
- }
- else
- {
- builder.Services.TryAddKeyedScoped(name, static (provider, key) =>
- {
- var api = provider.GetRequiredService();
- return api.GetClient(key!.ToString());
- });
- }
- }
-
- return builder;
- }
-
- ///
- /// Adds a default to the based on the policy name options.
- /// This method configures the dependency injection container to resolve the appropriate
- /// depending on the policy name selected.
- /// If no name is selected (i.e., null), it retrieves the default client.
- ///
- /// The instance.
- /// The configured instance.
- internal static OpenFeatureBuilder AddPolicyBasedClient(this OpenFeatureBuilder builder)
- {
- builder.Services.AddScoped(provider =>
- {
- var policy = provider.GetRequiredService>().Value;
- var name = policy.DefaultNameSelector(provider);
- if (name == null)
- {
- return provider.GetRequiredService();
- }
- return provider.GetRequiredKeyedService(name);
- });
-
- return builder;
- }
-
- ///
- /// Configures policy name options for OpenFeature using the specified options type.
- ///
- /// The type of options used to configure .
- /// The instance.
- /// A delegate to configure .
- /// The configured instance.
- /// Thrown when the or is null.
- public static OpenFeatureBuilder AddPolicyName(this OpenFeatureBuilder builder, Action configureOptions)
- where TOptions : PolicyNameOptions
- {
- Guard.ThrowIfNull(builder);
- Guard.ThrowIfNull(configureOptions);
-
- builder.IsPolicyConfigured = true;
-
- builder.Services.Configure(configureOptions);
- return builder;
- }
-
- ///
- /// Configures the default policy name options for OpenFeature.
- ///
- /// The instance.
- /// A delegate to configure .
- /// The configured instance.
- public static OpenFeatureBuilder AddPolicyName(this OpenFeatureBuilder builder, Action configureOptions)
- => AddPolicyName(builder, configureOptions);
-
- ///
- /// Adds a feature hook to the service collection using a factory method. Hooks added here are not domain-bound.
- ///
- /// The type of to be added.
- /// The instance.
- /// Optional factory for controlling how will be created in the DI container.
- /// The instance.
- public static OpenFeatureBuilder AddHook<
-#if NET
- [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
-#endif
- THook>(this OpenFeatureBuilder builder, Func? implementationFactory = null)
- where THook : Hook
- {
- return builder.AddHook(typeof(THook).Name, implementationFactory);
- }
-
- ///
- /// Adds a feature hook to the service collection. Hooks added here are not domain-bound.
- ///
- /// The type of to be added.
- /// The instance.
- /// Instance of Hook to inject into the OpenFeature context.
- /// The instance.
- public static OpenFeatureBuilder AddHook<
-#if NET
- [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
-#endif
- THook>(this OpenFeatureBuilder builder, THook hook)
- where THook : Hook
- {
- return builder.AddHook(typeof(THook).Name, hook);
- }
-
- ///
- /// Adds a feature hook to the service collection with a specified name. Hooks added here are not domain-bound.
- ///
- /// The type of to be added.
- /// The instance.
- /// The name of the that is being added.
- /// Instance of Hook to inject into the OpenFeature context.
- /// The instance.
- public static OpenFeatureBuilder AddHook<
-#if NET
- [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
-#endif
- THook>(this OpenFeatureBuilder builder, string hookName, THook hook)
- where THook : Hook
- {
- return builder.AddHook(hookName, _ => hook);
- }
-
- ///
- /// Adds a feature hook to the service collection using a factory method and specified name. Hooks added here are not domain-bound.
- ///
- /// The type of to be added.
- /// The instance.
- /// The name of the that is being added.
- /// Optional factory for controlling how will be created in the DI container.
- /// The instance.
- public static OpenFeatureBuilder AddHook<
-#if NET
- [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
-#endif
- THook>
- (this OpenFeatureBuilder builder, string hookName, Func? implementationFactory = null)
- where THook : Hook
- {
- builder.Services.PostConfigure(options => options.AddHookName(hookName));
-
- if (implementationFactory is not null)
- {
- builder.Services.TryAddKeyedSingleton(hookName, (serviceProvider, key) =>
- {
- return implementationFactory(serviceProvider);
- });
- }
- else
- {
- builder.Services.TryAddKeyedSingleton(hookName);
- }
-
- return builder;
- }
-
- ///
- /// Add a to allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions
- ///
- /// The instance.
- /// The type to handle.
- /// The handler which reacts to .
- /// The instance.
- public static OpenFeatureBuilder AddHandler(this OpenFeatureBuilder builder, ProviderEventTypes type, EventHandlerDelegate eventHandlerDelegate)
- {
- return AddHandler(builder, type, _ => eventHandlerDelegate);
- }
-
- ///
- /// Add a to allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions
- ///
- /// The instance.
- /// The type to handle.
- /// The handler factory for creating a handler which reacts to .
- /// The instance.
- public static OpenFeatureBuilder AddHandler(this OpenFeatureBuilder builder, ProviderEventTypes type, Func implementationFactory)
- {
- builder.Services.AddSingleton((serviceProvider) =>
- {
- var handler = implementationFactory(serviceProvider);
- return new EventHandlerDelegateWrapper(type, handler);
- });
-
- return builder;
- }
-}
diff --git a/src/OpenFeature.DependencyInjection/OpenFeatureOptions.cs b/src/OpenFeature.DependencyInjection/OpenFeatureOptions.cs
deleted file mode 100644
index e9cc3cb12..000000000
--- a/src/OpenFeature.DependencyInjection/OpenFeatureOptions.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-namespace OpenFeature.DependencyInjection;
-
-///
-/// Options to configure OpenFeature
-///
-public class OpenFeatureOptions
-{
- private readonly HashSet _providerNames = [];
-
- ///
- /// Determines if a default provider has been registered.
- ///
- public bool HasDefaultProvider { get; private set; }
-
- ///
- /// The type of the configured feature provider.
- ///
- public Type FeatureProviderType { get; protected internal set; } = null!;
-
- ///
- /// Gets a read-only list of registered provider names.
- ///
- public IReadOnlyCollection ProviderNames => _providerNames;
-
- ///
- /// Registers the default provider name if no specific name is provided.
- /// Sets to true.
- ///
- protected internal void AddDefaultProviderName() => AddProviderName(null);
-
- ///
- /// Registers a new feature provider name. This operation is thread-safe.
- ///
- /// The name of the feature provider to register. Registers as default if null.
- protected internal void AddProviderName(string? name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- HasDefaultProvider = true;
- }
- else
- {
- lock (_providerNames)
- {
- _providerNames.Add(name!);
- }
- }
- }
-
- private readonly HashSet _hookNames = [];
-
- internal IReadOnlyCollection HookNames => _hookNames;
-
- internal void AddHookName(string name)
- {
- lock (_hookNames)
- {
- _hookNames.Add(name);
- }
- }
-}
diff --git a/src/OpenFeature.DependencyInjection/OpenFeatureServiceCollectionExtensions.cs b/src/OpenFeature.DependencyInjection/OpenFeatureServiceCollectionExtensions.cs
deleted file mode 100644
index a24c67e78..000000000
--- a/src/OpenFeature.DependencyInjection/OpenFeatureServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Options;
-using OpenFeature.DependencyInjection;
-using OpenFeature.DependencyInjection.Internal;
-
-namespace OpenFeature;
-
-///
-/// Contains extension methods for the class.
-///
-public static partial class OpenFeatureServiceCollectionExtensions
-{
- ///
- /// Adds and configures OpenFeature services to the provided .
- ///
- /// The instance.
- /// A configuration action for customizing OpenFeature setup via
- /// The modified instance
- /// Thrown if or is null.
- public static IServiceCollection AddOpenFeature(this IServiceCollection services, Action configure)
- {
- Guard.ThrowIfNull(services);
- Guard.ThrowIfNull(configure);
-
- // Register core OpenFeature services as singletons.
- var api = new Api();
- Api.SetInstance(api);
- services.TryAddSingleton(api);
- services.TryAddSingleton();
-
- var builder = new OpenFeatureBuilder(services);
- configure(builder);
-
- // If a default provider is specified without additional providers,
- // return early as no extra configuration is needed.
- if (builder.HasDefaultProvider && builder.DomainBoundProviderRegistrationCount == 0)
- {
- return services;
- }
-
- // Validate builder configuration to ensure consistency and required setup.
- builder.Validate();
-
- if (!builder.IsPolicyConfigured)
- {
- // Add a default name selector policy to use the first registered provider name as the default.
- builder.AddPolicyName(options =>
- {
- options.DefaultNameSelector = provider =>
- {
- var options = provider.GetRequiredService>().Value;
- return options.ProviderNames.First();
- };
- });
- }
-
- builder.AddPolicyBasedClient();
- return services;
- }
-}
diff --git a/src/OpenFeature.DependencyInjection/PolicyNameOptions.cs b/src/OpenFeature.DependencyInjection/PolicyNameOptions.cs
deleted file mode 100644
index f77b019b1..000000000
--- a/src/OpenFeature.DependencyInjection/PolicyNameOptions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace OpenFeature.DependencyInjection;
-
-///
-/// Options to configure the default feature client name.
-///
-public class PolicyNameOptions
-{
- ///
- /// A delegate to select the default feature client name.
- ///
- public Func DefaultNameSelector { get; set; } = null!;
-}
diff --git a/src/OpenFeature.DependencyInjection/Providers/Memory/FeatureBuilderExtensions.cs b/src/OpenFeature.DependencyInjection/Providers/Memory/FeatureBuilderExtensions.cs
deleted file mode 100644
index d6346ad78..000000000
--- a/src/OpenFeature.DependencyInjection/Providers/Memory/FeatureBuilderExtensions.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using OpenFeature.Providers.Memory;
-
-namespace OpenFeature.DependencyInjection.Providers.Memory;
-
-///
-/// Extension methods for configuring feature providers with .
-///
-#if NET8_0_OR_GREATER
-[System.Diagnostics.CodeAnalysis.Experimental(Diagnostics.FeatureCodes.NewDi)]
-#endif
-public static partial class FeatureBuilderExtensions
-{
- ///
- /// Adds an in-memory feature provider to the with a factory for flags.
- ///
- /// The instance to configure.
- ///
- /// A factory function to provide an of flags.
- /// If null, an empty provider will be created.
- ///
- /// The instance for chaining.
- public static OpenFeatureBuilder AddInMemoryProvider(this OpenFeatureBuilder builder, Func?> flagsFactory)
- => builder.AddProvider(provider =>
- {
- var flags = flagsFactory(provider);
- if (flags == null)
- {
- return new InMemoryProvider();
- }
-
- return new InMemoryProvider(flags);
- });
-
- ///
- /// Adds an in-memory feature provider to the with a domain and factory for flags.
- ///
- /// The instance to configure.
- /// The unique domain of the provider.
- ///
- /// A factory function to provide an of flags.
- /// If null, an empty provider will be created.
- ///
- /// The instance for chaining.
- public static OpenFeatureBuilder AddInMemoryProvider(this OpenFeatureBuilder builder, string domain, Func?> flagsFactory)
- => AddInMemoryProvider(builder, domain, (provider, _) => flagsFactory(provider));
-
- ///
- /// Adds an in-memory feature provider to the with a domain and contextual flag factory.
- /// If null, an empty provider will be created.
- ///
- /// The instance to configure.
- /// The unique domain of the provider.
- ///
- /// A factory function to provide an of flags based on service provider and domain.
- ///
- /// The instance for chaining.
- public static OpenFeatureBuilder AddInMemoryProvider(this OpenFeatureBuilder builder, string domain, Func?> flagsFactory)
- => builder.AddProvider(domain, (provider, key) =>
- {
- var flags = flagsFactory(provider, key);
- if (flags == null)
- {
- return new InMemoryProvider();
- }
-
- return new InMemoryProvider(flags);
- });
-
- ///
- /// Adds an in-memory feature provider to the with optional flag configuration.
- ///
- /// The instance to configure.
- ///
- /// An optional delegate to configure feature flags in the in-memory provider.
- /// If null, an empty provider will be created.
- ///
- /// The instance for chaining.
- public static OpenFeatureBuilder AddInMemoryProvider(this OpenFeatureBuilder builder, Action>? configure = null)
- => builder.AddProvider(CreateProvider, options => ConfigureFlags(options, configure));
-
- ///
- /// Adds an in-memory feature provider with a specific domain to the with optional flag configuration.
- ///
- /// The instance to configure.
- /// The unique domain of the provider
- ///
- /// An optional delegate to configure feature flags in the in-memory provider.
- /// If null, an empty provider will be created.
- ///
- /// The instance for chaining.
- public static OpenFeatureBuilder AddInMemoryProvider(this OpenFeatureBuilder builder, string domain, Action>? configure = null)
- => builder.AddProvider(domain, CreateProvider, options => ConfigureFlags(options, configure));
-
- private static FeatureProvider CreateProvider(IServiceProvider provider, string domain)
- {
- var options = provider.GetRequiredService>().Get(domain);
- if (options.Flags == null)
- {
- return new InMemoryProvider();
- }
-
- return new InMemoryProvider(options.Flags);
- }
-
- private static FeatureProvider CreateProvider(IServiceProvider provider)
- {
- var options = provider.GetRequiredService>().Value;
- if (options.Flags == null)
- {
- return new InMemoryProvider();
- }
-
- return new InMemoryProvider(options.Flags);
- }
-
- private static void ConfigureFlags(InMemoryProviderOptions options, Action>? configure)
- {
- if (configure != null)
- {
- options.Flags = new Dictionary();
- configure.Invoke(options.Flags);
- }
- }
-}
diff --git a/src/OpenFeature.DependencyInjection/Providers/Memory/InMemoryProviderOptions.cs b/src/OpenFeature.DependencyInjection/Providers/Memory/InMemoryProviderOptions.cs
deleted file mode 100644
index ea5433f4e..000000000
--- a/src/OpenFeature.DependencyInjection/Providers/Memory/InMemoryProviderOptions.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using OpenFeature.Providers.Memory;
-
-namespace OpenFeature.DependencyInjection.Providers.Memory;
-
-///
-/// Options for configuring the in-memory feature flag provider.
-///
-public class InMemoryProviderOptions : OpenFeatureOptions
-{
- ///
- /// Gets or sets the feature flags to be used by the in-memory provider.
- ///
- ///
- /// This property allows you to specify a dictionary of flags where the key is the flag name
- /// and the value is the corresponding instance.
- /// If no flags are provided, the in-memory provider will start with an empty set of flags.
- ///
- public IDictionary? Flags { get; set; }
-}
diff --git a/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj b/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj
index d416bd75b..a3f5a6726 100644
--- a/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj
+++ b/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj
@@ -1,4 +1,4 @@
-
+
net9.0
@@ -17,8 +17,8 @@
+
-
diff --git a/test/OpenFeature.DependencyInjection.Tests/FeatureLifecycleManagerTests.cs b/test/OpenFeature.DependencyInjection.Tests/FeatureLifecycleManagerTests.cs
deleted file mode 100644
index 8dc6a80bc..000000000
--- a/test/OpenFeature.DependencyInjection.Tests/FeatureLifecycleManagerTests.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Logging.Abstractions;
-using OpenFeature.Constant;
-using OpenFeature.DependencyInjection.Internal;
-using OpenFeature.Model;
-
-namespace OpenFeature.DependencyInjection.Tests;
-
-public class FeatureLifecycleManagerTests
-{
- private readonly IServiceCollection _serviceCollection;
-
- public FeatureLifecycleManagerTests()
- {
- Api.Instance.SetContext(null);
- Api.Instance.ClearHooks();
-
- _serviceCollection = new ServiceCollection()
- .Configure(options =>
- {
- options.AddDefaultProviderName();
- });
- }
-
- [Fact]
- public async Task EnsureInitializedAsync_ShouldLogAndSetProvider_WhenProviderExists()
- {
- // Arrange
- var featureProvider = new NoOpFeatureProvider();
- _serviceCollection.AddSingleton(featureProvider);
-
- var serviceProvider = _serviceCollection.BuildServiceProvider();
- var sut = new FeatureLifecycleManager(Api.Instance, serviceProvider, NullLogger.Instance);
-
- // Act
- await sut.EnsureInitializedAsync().ConfigureAwait(true);
-
- // Assert
- Assert.Equal(featureProvider, Api.Instance.GetProvider());
- }
-
- [Fact]
- public async Task EnsureInitializedAsync_ShouldThrowException_WhenProviderDoesNotExist()
- {
- // Arrange
- _serviceCollection.RemoveAll();
-
- var serviceProvider = _serviceCollection.BuildServiceProvider();
- var sut = new FeatureLifecycleManager(Api.Instance, serviceProvider, NullLogger.Instance);
-
- // Act
- var act = () => sut.EnsureInitializedAsync().AsTask();
-
- // Assert
- var exception = await Assert.ThrowsAsync(act).ConfigureAwait(true);
- Assert.NotNull(exception);
- Assert.False(string.IsNullOrWhiteSpace(exception.Message));
- }
-
- [Fact]
- public async Task EnsureInitializedAsync_ShouldSetHook_WhenHooksAreRegistered()
- {
- // Arrange
- var featureProvider = new NoOpFeatureProvider();
- var hook = new NoOpHook();
-
- _serviceCollection.AddSingleton(featureProvider)
- .AddKeyedSingleton("NoOpHook", (_, key) => hook)
- .Configure(options =>
- {
- options.AddHookName("NoOpHook");
- });
-
- var serviceProvider = _serviceCollection.BuildServiceProvider();
- var sut = new FeatureLifecycleManager(Api.Instance, serviceProvider, NullLogger.Instance);
-
- // Act
- await sut.EnsureInitializedAsync().ConfigureAwait(true);
-
- // Assert
- var actual = Api.Instance.GetHooks().FirstOrDefault();
- Assert.Equal(hook, actual);
- }
-
- [Fact]
- public async Task EnsureInitializedAsync_ShouldSetHandler_WhenHandlersAreRegistered()
- {
- // Arrange
- EventHandlerDelegate eventHandlerDelegate = (_) => { };
- var featureProvider = new NoOpFeatureProvider();
- var handler = new EventHandlerDelegateWrapper(ProviderEventTypes.ProviderReady, eventHandlerDelegate);
-
- _serviceCollection.AddSingleton(featureProvider)
- .AddSingleton(_ => handler);
-
- var serviceProvider = _serviceCollection.BuildServiceProvider();
- var sut = new FeatureLifecycleManager(Api.Instance, serviceProvider, NullLogger.Instance);
-
- // Act
- await sut.EnsureInitializedAsync().ConfigureAwait(true);
- }
-
- [Fact]
- public async Task EnsureInitializedAsync_ShouldSetHandler_WhenMultipleHandlersAreRegistered()
- {
- // Arrange
- EventHandlerDelegate eventHandlerDelegate1 = (_) => { };
- EventHandlerDelegate eventHandlerDelegate2 = (_) => { };
- var featureProvider = new NoOpFeatureProvider();
- var handler1 = new EventHandlerDelegateWrapper(ProviderEventTypes.ProviderReady, eventHandlerDelegate1);
- var handler2 = new EventHandlerDelegateWrapper(ProviderEventTypes.ProviderReady, eventHandlerDelegate2);
-
- _serviceCollection.AddSingleton(featureProvider)
- .AddSingleton(_ => handler1)
- .AddSingleton(_ => handler2);
-
- var serviceProvider = _serviceCollection.BuildServiceProvider();
- var sut = new FeatureLifecycleManager(Api.Instance, serviceProvider, NullLogger.Instance);
-
- // Act
- await sut.EnsureInitializedAsync().ConfigureAwait(true);
- }
-}
diff --git a/test/OpenFeature.DependencyInjection.Tests/NoOpFeatureProvider.cs b/test/OpenFeature.DependencyInjection.Tests/NoOpFeatureProvider.cs
deleted file mode 100644
index ac3e52096..000000000
--- a/test/OpenFeature.DependencyInjection.Tests/NoOpFeatureProvider.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using OpenFeature.Model;
-
-namespace OpenFeature.DependencyInjection.Tests;
-
-// This class replicates the NoOpFeatureProvider implementation from src/OpenFeature/NoOpFeatureProvider.cs.
-// It is used here to facilitate unit testing without relying on the internal NoOpFeatureProvider class.
-// If the InternalsVisibleTo attribute is added to the OpenFeature project,
-// this class can be removed and the original NoOpFeatureProvider can be directly accessed for testing.
-internal sealed class NoOpFeatureProvider : FeatureProvider
-{
- private readonly Metadata _metadata = new Metadata(NoOpProvider.NoOpProviderName);
-
- public override Metadata GetMetadata()
- {
- return this._metadata;
- }
-
- public override Task> ResolveBooleanValueAsync(string flagKey, bool defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(NoOpResponse(flagKey, defaultValue));
- }
-
- public override Task> ResolveStringValueAsync(string flagKey, string defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(NoOpResponse(flagKey, defaultValue));
- }
-
- public override Task> ResolveIntegerValueAsync(string flagKey, int defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(NoOpResponse(flagKey, defaultValue));
- }
-
- public override Task> ResolveDoubleValueAsync(string flagKey, double defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(NoOpResponse(flagKey, defaultValue));
- }
-
- public override Task> ResolveStructureValueAsync(string flagKey, Value defaultValue, EvaluationContext? context = null, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(NoOpResponse(flagKey, defaultValue));
- }
-
- private static ResolutionDetails NoOpResponse(string flagKey, T defaultValue)
- {
- return new ResolutionDetails(
- flagKey,
- defaultValue,
- reason: NoOpProvider.ReasonNoOp,
- variant: NoOpProvider.Variant
- );
- }
-}
diff --git a/test/OpenFeature.DependencyInjection.Tests/NoOpHook.cs b/test/OpenFeature.DependencyInjection.Tests/NoOpHook.cs
deleted file mode 100644
index cee6ef1df..000000000
--- a/test/OpenFeature.DependencyInjection.Tests/NoOpHook.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using OpenFeature.Model;
-
-namespace OpenFeature.DependencyInjection.Tests;
-
-internal class NoOpHook : Hook
-{
- public override ValueTask BeforeAsync(HookContext context, IReadOnlyDictionary? hints = null, CancellationToken cancellationToken = default)
- {
- return base.BeforeAsync(context, hints, cancellationToken);
- }
-
- public override ValueTask AfterAsync(HookContext context, FlagEvaluationDetails details, IReadOnlyDictionary? hints = null, CancellationToken cancellationToken = default)
- {
- return base.AfterAsync(context, details, hints, cancellationToken);
- }
-
- public override ValueTask FinallyAsync(HookContext context, FlagEvaluationDetails evaluationDetails, IReadOnlyDictionary? hints = null, CancellationToken cancellationToken = default)
- {
- return base.FinallyAsync(context, evaluationDetails, hints, cancellationToken);
- }
-
- public override ValueTask ErrorAsync(HookContext context, Exception error, IReadOnlyDictionary? hints = null, CancellationToken cancellationToken = default)
- {
- return base.ErrorAsync(context, error, hints, cancellationToken);
- }
-}
diff --git a/test/OpenFeature.DependencyInjection.Tests/NoOpProvider.cs b/test/OpenFeature.DependencyInjection.Tests/NoOpProvider.cs
deleted file mode 100644
index 7bf20bcac..000000000
--- a/test/OpenFeature.DependencyInjection.Tests/NoOpProvider.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace OpenFeature.DependencyInjection.Tests;
-
-internal static class NoOpProvider
-{
- public const string NoOpProviderName = "No-op Provider";
- public const string ReasonNoOp = "No-op";
- public const string Variant = "No-op";
-}
diff --git a/test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj b/test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj
deleted file mode 100644
index d6bce29e8..000000000
--- a/test/OpenFeature.DependencyInjection.Tests/OpenFeature.DependencyInjection.Tests.csproj
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- net8.0;net9.0
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
diff --git a/test/OpenFeature.DependencyInjection.Tests/OpenFeatureBuilderExtensionsTests.cs b/test/OpenFeature.DependencyInjection.Tests/OpenFeatureBuilderExtensionsTests.cs
deleted file mode 100644
index f7cce0dfc..000000000
--- a/test/OpenFeature.DependencyInjection.Tests/OpenFeatureBuilderExtensionsTests.cs
+++ /dev/null
@@ -1,392 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using OpenFeature.DependencyInjection.Internal;
-using OpenFeature.Model;
-
-namespace OpenFeature.DependencyInjection.Tests;
-
-public partial class OpenFeatureBuilderExtensionsTests
-{
- private readonly IServiceCollection _services;
- private readonly OpenFeatureBuilder _systemUnderTest;
-
- public OpenFeatureBuilderExtensionsTests()
- {
- _services = new ServiceCollection();
- _systemUnderTest = new OpenFeatureBuilder(_services);
- }
-
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void AddContext_Delegate_ShouldAddServiceToCollection(bool useServiceProviderDelegate)
- {
- // Act
- var featureBuilder = useServiceProviderDelegate ?
- _systemUnderTest.AddContext(_ => { }) :
- _systemUnderTest.AddContext((_, _) => { });
-
- // Assert
- Assert.Equal(_systemUnderTest, featureBuilder);
- Assert.True(_systemUnderTest.IsContextConfigured, "The context should be configured.");
- Assert.Single(_services, serviceDescriptor =>
- serviceDescriptor.ServiceType == typeof(EvaluationContext) &&
- serviceDescriptor.Lifetime == ServiceLifetime.Transient);
- }
-
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void AddContext_Delegate_ShouldCorrectlyHandles(bool useServiceProviderDelegate)
- {
- // Arrange
- bool delegateCalled = false;
-
- _ = useServiceProviderDelegate ?
- _systemUnderTest.AddContext(_ => delegateCalled = true) :
- _systemUnderTest.AddContext((_, _) => delegateCalled = true);
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var context = serviceProvider.GetService();
-
- // Assert
- Assert.True(_systemUnderTest.IsContextConfigured, "The context should be configured.");
- Assert.NotNull(context);
- Assert.True(delegateCalled, "The delegate should be invoked.");
- }
-
-#if NET8_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.Experimental(Diagnostics.FeatureCodes.NewDi)]
-#endif
- [Theory]
- [InlineData(1, true, 0)]
- [InlineData(2, false, 1)]
- [InlineData(3, true, 0)]
- [InlineData(4, false, 1)]
- public void AddProvider_ShouldAddProviderToCollection(int providerRegistrationType, bool expectsDefaultProvider, int expectsDomainBoundProvider)
- {
- // Act
- var featureBuilder = providerRegistrationType switch
- {
- 1 => _systemUnderTest.AddProvider(_ => new NoOpFeatureProvider()),
- 2 => _systemUnderTest.AddProvider("test", (_, _) => new NoOpFeatureProvider()),
- 3 => _systemUnderTest.AddProvider(_ => new NoOpFeatureProvider(), o => { }),
- 4 => _systemUnderTest.AddProvider("test", (_, _) => new NoOpFeatureProvider(), o => { }),
- _ => throw new InvalidOperationException("Invalid mode.")
- };
-
- // Assert
- Assert.False(_systemUnderTest.IsContextConfigured, "The context should not be configured.");
- Assert.Equal(expectsDefaultProvider, _systemUnderTest.HasDefaultProvider);
- Assert.False(_systemUnderTest.IsPolicyConfigured, "The policy should not be configured.");
- Assert.Equal(expectsDomainBoundProvider, _systemUnderTest.DomainBoundProviderRegistrationCount);
- Assert.Equal(_systemUnderTest, featureBuilder);
- Assert.Single(_services, serviceDescriptor =>
- serviceDescriptor.ServiceType == typeof(FeatureProvider) &&
- serviceDescriptor.Lifetime == ServiceLifetime.Transient);
- }
-
- class TestOptions : OpenFeatureOptions { }
-
-#if NET8_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.Experimental(Diagnostics.FeatureCodes.NewDi)]
-#endif
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(3)]
- [InlineData(4)]
- public void AddProvider_ShouldResolveCorrectProvider(int providerRegistrationType)
- {
- // Arrange
- _ = providerRegistrationType switch
- {
- 1 => _systemUnderTest.AddProvider(_ => new NoOpFeatureProvider()),
- 2 => _systemUnderTest.AddProvider("test", (_, _) => new NoOpFeatureProvider()),
- 3 => _systemUnderTest.AddProvider(_ => new NoOpFeatureProvider(), o => { }),
- 4 => _systemUnderTest.AddProvider("test", (_, _) => new NoOpFeatureProvider(), o => { }),
- _ => throw new InvalidOperationException("Invalid mode.")
- };
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var provider = providerRegistrationType switch
- {
- 1 or 3 => serviceProvider.GetService(),
- 2 or 4 => serviceProvider.GetKeyedService("test"),
- _ => throw new InvalidOperationException("Invalid mode.")
- };
-
- // Assert
- Assert.NotNull(provider);
- Assert.IsType(provider);
- }
-
- [Theory]
- [InlineData(1, true, 1)]
- [InlineData(2, true, 1)]
- [InlineData(3, false, 2)]
- [InlineData(4, true, 1)]
- [InlineData(5, true, 1)]
- [InlineData(6, false, 2)]
- [InlineData(7, true, 2)]
- [InlineData(8, true, 2)]
- public void AddProvider_VerifiesDefaultAndDomainBoundProvidersBasedOnConfiguration(int providerRegistrationType, bool expectsDefaultProvider, int expectsDomainBoundProvider)
- {
- // Act
- var featureBuilder = providerRegistrationType switch
- {
- 1 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider())
- .AddProvider("test", (_, _) => new NoOpFeatureProvider()),
- 2 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider())
- .AddProvider("test", (_, _) => new NoOpFeatureProvider()),
- 3 => _systemUnderTest
- .AddProvider("test1", (_, _) => new NoOpFeatureProvider())
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider()),
- 4 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider(), o => { })
- .AddProvider("test", (_, _) => new NoOpFeatureProvider()),
- 5 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider(), o => { })
- .AddProvider("test", (_, _) => new NoOpFeatureProvider()),
- 6 => _systemUnderTest
- .AddProvider("test1", (_, _) => new NoOpFeatureProvider(), o => { })
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider()),
- 7 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider())
- .AddProvider("test", (_, _) => new NoOpFeatureProvider())
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider()),
- 8 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider(), o => { })
- .AddProvider("test", (_, _) => new NoOpFeatureProvider(), o => { })
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider(), o => { }),
- _ => throw new InvalidOperationException("Invalid mode.")
- };
-
- // Assert
- Assert.False(_systemUnderTest.IsContextConfigured, "The context should not be configured.");
- Assert.Equal(expectsDefaultProvider, _systemUnderTest.HasDefaultProvider);
- Assert.False(_systemUnderTest.IsPolicyConfigured, "The policy should not be configured.");
- Assert.Equal(expectsDomainBoundProvider, _systemUnderTest.DomainBoundProviderRegistrationCount);
- Assert.Equal(_systemUnderTest, featureBuilder);
- }
-
- [Theory]
- [InlineData(1, null)]
- [InlineData(2, "test")]
- [InlineData(3, "test2")]
- [InlineData(4, "test")]
- [InlineData(5, null)]
- [InlineData(6, "test1")]
- [InlineData(7, "test2")]
- [InlineData(8, null)]
- public void AddProvider_ConfiguresPolicyNameAcrossMultipleProviderSetups(int providerRegistrationType, string? policyName)
- {
- // Arrange
- var featureBuilder = providerRegistrationType switch
- {
- 1 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider())
- .AddProvider("test", (_, _) => new NoOpFeatureProvider())
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- 2 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider())
- .AddProvider("test", (_, _) => new NoOpFeatureProvider())
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- 3 => _systemUnderTest
- .AddProvider("test1", (_, _) => new NoOpFeatureProvider())
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider())
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- 4 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider(), o => { })
- .AddProvider("test", (_, _) => new NoOpFeatureProvider())
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- 5 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider(), o => { })
- .AddProvider("test", (_, _) => new NoOpFeatureProvider())
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- 6 => _systemUnderTest
- .AddProvider("test1", (_, _) => new NoOpFeatureProvider(), o => { })
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider())
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- 7 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider())
- .AddProvider("test", (_, _) => new NoOpFeatureProvider())
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider())
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- 8 => _systemUnderTest
- .AddProvider(_ => new NoOpFeatureProvider(), o => { })
- .AddProvider("test", (_, _) => new NoOpFeatureProvider(), o => { })
- .AddProvider("test2", (_, _) => new NoOpFeatureProvider(), o => { })
- .AddPolicyName(policy => policy.DefaultNameSelector = provider => policyName),
- _ => throw new InvalidOperationException("Invalid mode.")
- };
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var policy = serviceProvider.GetRequiredService>().Value;
- var name = policy.DefaultNameSelector(serviceProvider);
- var provider = name == null ?
- serviceProvider.GetService() :
- serviceProvider.GetRequiredKeyedService(name);
-
- // Assert
- Assert.True(featureBuilder.IsPolicyConfigured, "The policy should be configured.");
- Assert.NotNull(provider);
- Assert.IsType(provider);
- }
-
- [Fact]
- public void AddHook_AddsHookAsKeyedService()
- {
- // Arrange
- _systemUnderTest.AddHook();
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var hook = serviceProvider.GetKeyedService("NoOpHook");
-
- // Assert
- Assert.NotNull(hook);
- }
-
- [Fact]
- public void AddHook_AddsHookNameToOpenFeatureOptions()
- {
- // Arrange
- _systemUnderTest.AddHook(sp => new NoOpHook());
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var options = serviceProvider.GetRequiredService>();
-
- // Assert
- Assert.Contains(options.Value.HookNames, t => t == "NoOpHook");
- }
-
- [Fact]
- public void AddHook_WithSpecifiedNameToOpenFeatureOptions()
- {
- // Arrange
- _systemUnderTest.AddHook("my-custom-name");
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var hook = serviceProvider.GetKeyedService("my-custom-name");
-
- // Assert
- Assert.NotNull(hook);
- }
-
- [Fact]
- public void AddHook_WithSpecifiedNameAndImplementationFactory_AsKeyedService()
- {
- // Arrange
- _systemUnderTest.AddHook("my-custom-name", (serviceProvider) => new NoOpHook());
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var hook = serviceProvider.GetKeyedService("my-custom-name");
-
- // Assert
- Assert.NotNull(hook);
- }
-
- [Fact]
- public void AddHook_WithInstance_AddsHookAsKeyedService()
- {
- // Arrange
- var expectedHook = new NoOpHook();
- _systemUnderTest.AddHook(expectedHook);
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var actualHook = serviceProvider.GetKeyedService("NoOpHook");
-
- // Assert
- Assert.NotNull(actualHook);
- Assert.Equal(expectedHook, actualHook);
- }
-
- [Fact]
- public void AddHook_WithSpecifiedNameAndInstance_AddsHookAsKeyedService()
- {
- // Arrange
- var expectedHook = new NoOpHook();
- _systemUnderTest.AddHook("custom-hook", expectedHook);
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var actualHook = serviceProvider.GetKeyedService("custom-hook");
-
- // Assert
- Assert.NotNull(actualHook);
- Assert.Equal(expectedHook, actualHook);
- }
-
- [Fact]
- public void AddHandler_AddsEventHandlerDelegateWrapperAsKeyedService()
- {
- // Arrange
- EventHandlerDelegate eventHandler = (eventDetails) => { };
- _systemUnderTest.AddHandler(Constant.ProviderEventTypes.ProviderReady, eventHandler);
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var handler = serviceProvider.GetService();
-
- // Assert
- Assert.NotNull(handler);
- Assert.Equal(eventHandler, handler.EventHandlerDelegate);
- }
-
- [Fact]
- public void AddHandlerTwice_MultipleEventHandlerDelegateWrappersAsKeyedServices()
- {
- // Arrange
- EventHandlerDelegate eventHandler1 = (eventDetails) => { };
- EventHandlerDelegate eventHandler2 = (eventDetails) => { };
- _systemUnderTest.AddHandler(Constant.ProviderEventTypes.ProviderReady, eventHandler1);
- _systemUnderTest.AddHandler(Constant.ProviderEventTypes.ProviderReady, eventHandler2);
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var handler = serviceProvider.GetServices();
-
- // Assert
- Assert.NotEmpty(handler);
- Assert.Equal(eventHandler1, handler.ElementAt(0).EventHandlerDelegate);
- Assert.Equal(eventHandler2, handler.ElementAt(1).EventHandlerDelegate);
- }
-
- [Fact]
- public void AddHandler_WithImplementationFactory_AddsEventHandlerDelegateWrapperAsKeyedService()
- {
- // Arrange
- EventHandlerDelegate eventHandler = (eventDetails) => { };
- _systemUnderTest.AddHandler(Constant.ProviderEventTypes.ProviderReady, _ => eventHandler);
-
- var serviceProvider = _services.BuildServiceProvider();
-
- // Act
- var handler = serviceProvider.GetService();
-
- // Assert
- Assert.NotNull(handler);
- Assert.Equal(eventHandler, handler.EventHandlerDelegate);
- }
-}
diff --git a/test/OpenFeature.DependencyInjection.Tests/OpenFeatureServiceCollectionExtensionsTests.cs b/test/OpenFeature.DependencyInjection.Tests/OpenFeatureServiceCollectionExtensionsTests.cs
deleted file mode 100644
index ddda3f224..000000000
--- a/test/OpenFeature.DependencyInjection.Tests/OpenFeatureServiceCollectionExtensionsTests.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using NSubstitute;
-
-namespace OpenFeature.DependencyInjection.Tests;
-
-public class OpenFeatureServiceCollectionExtensionsTests
-{
- private readonly IServiceCollection _systemUnderTest;
- private readonly Action _configureAction;
-
- public OpenFeatureServiceCollectionExtensionsTests()
- {
- _systemUnderTest = new ServiceCollection();
- _configureAction = Substitute.For>();
- }
-
- [Fact]
- public void AddOpenFeature_ShouldRegisterApiInstanceAndLifecycleManagerAsSingleton()
- {
- // Act
- _systemUnderTest.AddOpenFeature(_configureAction);
-
- Assert.Single(_systemUnderTest, s => s.ServiceType == typeof(Api) && s.Lifetime == ServiceLifetime.Singleton);
- Assert.Single(_systemUnderTest, s => s.ServiceType == typeof(IFeatureLifecycleManager) && s.Lifetime == ServiceLifetime.Singleton);
- Assert.Single(_systemUnderTest, s => s.ServiceType == typeof(IFeatureClient) && s.Lifetime == ServiceLifetime.Scoped);
- }
-
- [Fact]
- public void AddOpenFeature_ShouldInvokeConfigureAction()
- {
- // Act
- _systemUnderTest.AddOpenFeature(_configureAction);
-
- // Assert
- _configureAction.Received(1).Invoke(Arg.Any());
- }
-}