diff --git a/src/Microsoft.FeatureManagement/FeatureManager.cs b/src/Microsoft.FeatureManagement/FeatureManager.cs index c35df318..91605ecb 100644 --- a/src/Microsoft.FeatureManagement/FeatureManager.cs +++ b/src/Microsoft.FeatureManagement/FeatureManager.cs @@ -684,6 +684,12 @@ private void BindSettings(IFeatureFilterMetadata filter, FeatureFilterEvaluation return; } + // Skip parameter binding if the provider has already supplied a parameters object. + if (context.Settings != null) + { + return; + } + if (!(context.Parameters is ConfigurationWrapper) || Cache == null) { context.Settings = binder.BindParameters(context.Parameters); diff --git a/tests/Tests.FeatureManagement/FeatureManagementTest.cs b/tests/Tests.FeatureManagement/FeatureManagementTest.cs index b307d892..a70e6a0d 100644 --- a/tests/Tests.FeatureManagement/FeatureManagementTest.cs +++ b/tests/Tests.FeatureManagement/FeatureManagementTest.cs @@ -1924,6 +1924,121 @@ public async Task TimeWindowFilterThrowsOnInvalidParametersObjectType() await Assert.ThrowsAsync(() => featureManager.IsEnabledAsync("BadFeature")); } + + [Fact] + public async Task TargetingFilterUsesParametersObject() + { + var services = new ServiceCollection(); + + var definitionProvider = new InMemoryFeatureDefinitionProvider( + new FeatureDefinition[] + { + new FeatureDefinition + { + Name = "TargetingFeature", + EnabledFor = new List() + { + new FeatureFilterConfiguration + { + Name = "Microsoft.Targeting", + ParametersObject = new TargetingFilterSettings + { + Audience = new Audience + { + Users = new List { "Jeff" }, + Groups = new List + { + new GroupRollout + { + Name = "Ring1", + RolloutPercentage = 100 + } + }, + DefaultRolloutPercentage = 0 + } + } + } + } + } + }); + + services.AddSingleton(definitionProvider) + .AddSingleton(new ConfigurationBuilder().Build()) + .AddFeatureManagement(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IVariantFeatureManager featureManager = serviceProvider.GetRequiredService(); + + // + // Targeted user should be enabled + var targetingContext = new TargetingContext + { + UserId = "Jeff", + Groups = new List { "Ring0" } + }; + + Assert.True(await featureManager.IsEnabledAsync("TargetingFeature", targetingContext)); + + // + // User in targeted group should be enabled + targetingContext = new TargetingContext + { + UserId = "NotTargeted", + Groups = new List { "Ring1" } + }; + + Assert.True(await featureManager.IsEnabledAsync("TargetingFeature", targetingContext)); + + // + // Non-targeted user should be disabled (0% default rollout) + targetingContext = new TargetingContext + { + UserId = "NotTargeted", + Groups = new List { "Ring0" } + }; + + Assert.False(await featureManager.IsEnabledAsync("TargetingFeature", targetingContext)); + } + + [Fact] + public async Task TargetingFilterThrowsOnInvalidParametersObjectType() + { + var services = new ServiceCollection(); + + var definitionProvider = new InMemoryFeatureDefinitionProvider( + new FeatureDefinition[] + { + new FeatureDefinition + { + Name = "BadFeature", + EnabledFor = new List() + { + new FeatureFilterConfiguration + { + Name = "Microsoft.Targeting", + ParametersObject = "wrong type" + } + } + } + }); + + services.AddSingleton(definitionProvider) + .AddSingleton(new ConfigurationBuilder().Build()) + .AddFeatureManagement(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IVariantFeatureManager featureManager = serviceProvider.GetRequiredService(); + + var targetingContext = new TargetingContext + { + UserId = "Jeff", + Groups = new List() + }; + + await Assert.ThrowsAsync(() => featureManager.IsEnabledAsync("BadFeature", targetingContext).AsTask()); + } } public class FeatureManagementVariantTest