diff --git a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs index 5b30f56e..15b69a62 100644 --- a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs +++ b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs @@ -27,8 +27,8 @@ sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionProvider private const string FeatureManagementSectionName = "FeatureManagement"; private readonly IConfiguration _configuration; private readonly ConcurrentDictionary _definitions; - private IDisposable _changeSubscription; private readonly ILogger _logger; + private IDisposable _changeSubscription; private int _stale = 0; public ConfigurationFeatureDefinitionProvider(IConfiguration configuration, ILoggerFactory loggerFactory) @@ -42,6 +42,11 @@ public ConfigurationFeatureDefinitionProvider(IConfiguration configuration, ILog () => _stale = 1); } + /// + /// The option that controls the behavior when "FeatureManagement" section in the configuration is missing. + /// + public bool UseTopLevelConfiguration { get; init; } + public void Dispose() { _changeSubscription?.Dispose(); @@ -217,6 +222,13 @@ private IEnumerable GetFeatureDefinitionSections() return featureManagementConfigurationSection.GetChildren(); } + // + // There is no "FeatureManagement" section in the configuration + if (UseTopLevelConfiguration) + { + return _configuration.GetChildren(); + } + _logger.LogDebug($"No configuration section named '{FeatureManagementSectionName}' was found."); return Enumerable.Empty(); diff --git a/src/Microsoft.FeatureManagement/ServiceCollectionExtensions.cs b/src/Microsoft.FeatureManagement/ServiceCollectionExtensions.cs index f1cd89b2..bbd4a09e 100644 --- a/src/Microsoft.FeatureManagement/ServiceCollectionExtensions.cs +++ b/src/Microsoft.FeatureManagement/ServiceCollectionExtensions.cs @@ -69,6 +69,7 @@ public static IFeatureManagementBuilder AddFeatureManagement(this IServiceCollec /// /// Adds singleton and other required feature management services. + /// The registered will use the provided configuration and read from the top level if no "FeatureManagement" section can be found. /// /// The service collection that feature management services are added to. /// A specific instance that will be used to obtain feature settings. @@ -80,7 +81,13 @@ public static IFeatureManagementBuilder AddFeatureManagement(this IServiceCollec throw new ArgumentNullException(nameof(configuration)); } - services.AddSingleton(sp => new ConfigurationFeatureDefinitionProvider(configuration, sp.GetRequiredService())); + services.AddSingleton(sp => + new ConfigurationFeatureDefinitionProvider( + configuration, + sp.GetRequiredService()) + { + UseTopLevelConfiguration = true + }); return services.AddFeatureManagement(); } @@ -135,6 +142,7 @@ public static IFeatureManagementBuilder AddScopedFeatureManagement(this IService /// /// Adds scoped and other required feature management services. + /// The registered will use the provided configuration and read from the top level if no "FeatureManagement" section can be found. /// /// The service collection that feature management services are added to. /// A specific instance that will be used to obtain feature settings. @@ -146,7 +154,13 @@ public static IFeatureManagementBuilder AddScopedFeatureManagement(this IService throw new ArgumentNullException(nameof(configuration)); } - services.AddSingleton(sp => new ConfigurationFeatureDefinitionProvider(configuration, sp.GetRequiredService())); + services.AddSingleton(sp => + new ConfigurationFeatureDefinitionProvider( + configuration, + sp.GetRequiredService()) + { + UseTopLevelConfiguration = true + }); return services.AddScopedFeatureManagement(); } diff --git a/tests/Tests.FeatureManagement/FeatureManagement.cs b/tests/Tests.FeatureManagement/FeatureManagement.cs index 810043fe..b29976d4 100644 --- a/tests/Tests.FeatureManagement/FeatureManagement.cs +++ b/tests/Tests.FeatureManagement/FeatureManagement.cs @@ -72,8 +72,7 @@ public async Task ReadsOnlyFeatureManagementSection() services .AddSingleton(config) - .AddFeatureManagement() - .AddFeatureFilter(); + .AddFeatureManagement(); ServiceProvider serviceProvider = services.BuildServiceProvider(); @@ -87,6 +86,26 @@ public async Task ReadsOnlyFeatureManagementSection() } } + [Fact] + public async Task ReadsTopLevelConfiguration() + { + const string feature = "FeatureX"; + + var stream = new MemoryStream(Encoding.UTF8.GetBytes($"{{\"AllowedHosts\": \"*\", \"FeatureFlags\": {{\"{feature}\": true}}}}")); + + IConfiguration config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + var services = new ServiceCollection(); + + services.AddFeatureManagement(config.GetSection("FeatureFlags")); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + Assert.True(await featureManager.IsEnabledAsync(feature)); + } + [Fact] public void AddsScopedFeatureManagement() {