diff --git a/src/Microsoft.FeatureManagement/FeatureManagementError.cs b/src/Microsoft.FeatureManagement/FeatureManagementError.cs index a1e3319f..4631dd09 100644 --- a/src/Microsoft.FeatureManagement/FeatureManagementError.cs +++ b/src/Microsoft.FeatureManagement/FeatureManagementError.cs @@ -16,6 +16,11 @@ public enum FeatureManagementError /// /// A feature filter configured for the feature being evaluated is an ambiguous reference to multiple registered feature filters. /// - AmbiguousFeatureFilter + AmbiguousFeatureFilter, + + /// + /// The specified feature does not exist. + /// + MissingFeature } } diff --git a/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs b/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs index 9322934e..0c444c1b 100644 --- a/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs +++ b/src/Microsoft.FeatureManagement/FeatureManagementOptions.cs @@ -13,5 +13,11 @@ public class FeatureManagementOptions /// If missing feature filters are not ignored an exception will be thrown when attempting to evaluate a feature that depends on a missing feature filter. /// public bool IgnoreMissingFeatureFilters { get; set; } + + /// + /// Controls the behavior of feature evaluation when the specified feature does not exist. + /// If missing features are not ignored, an exception will be thrown when attempting to evaluate them. + /// + public bool IgnoreMissingFeatures { get; set; } } } diff --git a/src/Microsoft.FeatureManagement/FeatureManager.cs b/src/Microsoft.FeatureManagement/FeatureManager.cs index 6f0b2a14..2bbe542b 100644 --- a/src/Microsoft.FeatureManagement/FeatureManager.cs +++ b/src/Microsoft.FeatureManagement/FeatureManager.cs @@ -141,6 +141,10 @@ private async Task IsEnabledAsync(string feature, TContext appCo } } } + else if (!_options.IgnoreMissingFeatures) + { + throw new FeatureManagementException(FeatureManagementError.MissingFeature, $"The feature declaration for specified feature '{feature}' was not found."); + } foreach (ISessionManager sessionManager in _sessionManagers) { diff --git a/tests/Tests.FeatureManagement/FeatureManagement.cs b/tests/Tests.FeatureManagement/FeatureManagement.cs index 1076d81c..5bf76f70 100644 --- a/tests/Tests.FeatureManagement/FeatureManagement.cs +++ b/tests/Tests.FeatureManagement/FeatureManagement.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Reflection; using System.Threading.Tasks; using Xunit; @@ -25,6 +26,7 @@ public class FeatureManagement private const string OffFeature = "OffFeature"; private const string ConditionalFeature = "ConditionalFeature"; private const string ContextualFeature = "ContextualFeature"; + private const string MissingFeature = "MissingFeature"; [Fact] public async Task ReadsConfiguration() @@ -33,6 +35,12 @@ public async Task ReadsConfiguration() var services = new ServiceCollection(); + services + .Configure(options => + { + options.IgnoreMissingFeatures = true; + }); + services .AddSingleton(config) .AddFeatureManagement() @@ -483,5 +491,59 @@ public async Task SwallowsExceptionForMissingFeatureFilter() Assert.False(isEnabled); } + + [Fact] + public async Task ThrowsOnMissingFeature() + { + IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + + var services = new ServiceCollection(); + + services + .Configure(options => + { + options.IgnoreMissingFeatures = false; + }); + + services + .AddSingleton(config) + .AddFeatureManagement() + .AddFeatureFilter(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + FeatureManagementException e = await Assert.ThrowsAsync(async () => await featureManager.IsEnabledAsync(MissingFeature)); + + Assert.Equal(FeatureManagementError.MissingFeature, e.Error); + } + + [Fact] + public async Task SwallowsOnMissingFeature() + { + IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + + var services = new ServiceCollection(); + + services + .Configure(options => + { + options.IgnoreMissingFeatures = true; + }); + + services + .AddSingleton(config) + .AddFeatureManagement() + .AddFeatureFilter(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + var isEnabled = await featureManager.IsEnabledAsync(ConditionalFeature); + + Assert.False(isEnabled); + } } }