diff --git a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs index d87a135b..dc1be39b 100644 --- a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs +++ b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs @@ -233,7 +233,7 @@ We support Allocation allocation = null; - List variants = null; + var variants = new List(); bool telemetryEnabled = false; @@ -349,8 +349,6 @@ We support IEnumerable variantsSections = configurationSection.GetSection(ConfigurationFields.VariantsSectionName).GetChildren(); - variants = new List(); - foreach (IConfigurationSection section in variantsSections) { if (int.TryParse(section.Key, out int _) && !string.IsNullOrEmpty(section[ConfigurationFields.NameKeyword])) @@ -452,9 +450,19 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection bool enabled = false; - IConfigurationSection conditions = configurationSection.GetSection(MicrosoftFeatureFlagFields.Conditions); + FeatureStatus featureStatus = FeatureStatus.Disabled; + + Allocation allocation = null; + + var variants = new List(); + + bool telemetryEnabled = false; + + Dictionary telemetryMetadata = null; + + IConfigurationSection conditionsSection = configurationSection.GetSection(MicrosoftFeatureFlagFields.Conditions); - string rawRequirementType = conditions[MicrosoftFeatureFlagFields.RequirementType]; + string rawRequirementType = conditionsSection[MicrosoftFeatureFlagFields.RequirementType]; string rawEnabled = configurationSection[MicrosoftFeatureFlagFields.Enabled]; @@ -470,7 +478,9 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection if (enabled) { - IEnumerable filterSections = conditions.GetSection(MicrosoftFeatureFlagFields.ClientFilters).GetChildren(); + featureStatus = FeatureStatus.Conditional; + + IEnumerable filterSections = conditionsSection.GetSection(MicrosoftFeatureFlagFields.ClientFilters).GetChildren(); if (filterSections.Any()) { @@ -498,11 +508,122 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection } } + IConfigurationSection allocationSection = configurationSection.GetSection(MicrosoftFeatureFlagFields.AllocationSectionName); + + if (allocationSection.Exists()) + { + allocation = new Allocation() + { + DefaultWhenDisabled = allocationSection[MicrosoftFeatureFlagFields.AllocationDefaultWhenDisabled], + DefaultWhenEnabled = allocationSection[MicrosoftFeatureFlagFields.AllocationDefaultWhenEnabled], + User = allocationSection.GetSection(MicrosoftFeatureFlagFields.UserAllocationSectionName).GetChildren().Select(userAllocation => + { + return new UserAllocation() + { + Variant = userAllocation[MicrosoftFeatureFlagFields.AllocationVariantKeyword], + Users = userAllocation.GetSection(MicrosoftFeatureFlagFields.UserAllocationUsers).Get>() + }; + }), + Group = allocationSection.GetSection(MicrosoftFeatureFlagFields.GroupAllocationSectionName).GetChildren().Select(groupAllocation => + { + return new GroupAllocation() + { + Variant = groupAllocation[MicrosoftFeatureFlagFields.AllocationVariantKeyword], + Groups = groupAllocation.GetSection(MicrosoftFeatureFlagFields.GroupAllocationGroups).Get>() + }; + }), + Percentile = allocationSection.GetSection(MicrosoftFeatureFlagFields.PercentileAllocationSectionName).GetChildren().Select(percentileAllocation => + { + double from = 0; + + double to = 0; + + string rawFrom = percentileAllocation[MicrosoftFeatureFlagFields.PercentileAllocationFrom]; + + string rawTo = percentileAllocation[MicrosoftFeatureFlagFields.PercentileAllocationTo]; + + if (!string.IsNullOrEmpty(rawFrom)) + { + from = ParseDouble(featureName, rawFrom, MicrosoftFeatureFlagFields.PercentileAllocationFrom); + } + + if (!string.IsNullOrEmpty(rawTo)) + { + to = ParseDouble(featureName, rawTo, MicrosoftFeatureFlagFields.PercentileAllocationTo); + } + + return new PercentileAllocation() + { + Variant = percentileAllocation[MicrosoftFeatureFlagFields.AllocationVariantKeyword], + From = from, + To = to + }; + }), + Seed = allocationSection[MicrosoftFeatureFlagFields.AllocationSeed] + }; + } + + IEnumerable variantsSections = configurationSection.GetSection(MicrosoftFeatureFlagFields.VariantsSectionName).GetChildren(); + + foreach (IConfigurationSection section in variantsSections) + { + if (int.TryParse(section.Key, out int _) && !string.IsNullOrEmpty(section[MicrosoftFeatureFlagFields.Name])) + { + StatusOverride statusOverride = StatusOverride.None; + + string rawStatusOverride = section[MicrosoftFeatureFlagFields.VariantDefinitionStatusOverride]; + + if (!string.IsNullOrEmpty(rawStatusOverride)) + { + statusOverride = ParseEnum(configurationSection.Key, rawStatusOverride, MicrosoftFeatureFlagFields.VariantDefinitionStatusOverride); + } + + var variant = new VariantDefinition() + { + Name = section[MicrosoftFeatureFlagFields.Name], + ConfigurationValue = section.GetSection(MicrosoftFeatureFlagFields.VariantDefinitionConfigurationValue), + ConfigurationReference = section[MicrosoftFeatureFlagFields.VariantDefinitionConfigurationReference], + StatusOverride = statusOverride + }; + + variants.Add(variant); + } + } + + IConfigurationSection telemetrySection = configurationSection.GetSection(MicrosoftFeatureFlagFields.Telemetry); + + if (telemetrySection.Exists()) + { + string rawTelemetryEnabled = telemetrySection[MicrosoftFeatureFlagFields.Enabled]; + + if (!string.IsNullOrEmpty(rawTelemetryEnabled)) + { + telemetryEnabled = ParseBool(featureName, rawTelemetryEnabled, MicrosoftFeatureFlagFields.Enabled); + } + + IConfigurationSection telemetryMetadataSection = telemetrySection.GetSection(MicrosoftFeatureFlagFields.Metadata); + + if (telemetryMetadataSection.Exists()) + { + telemetryMetadata = new Dictionary(); + + telemetryMetadata = telemetryMetadataSection.GetChildren().ToDictionary(x => x.Key, x => x.Value); + } + } + return new FeatureDefinition() { Name = featureName, EnabledFor = enabledFor, - RequirementType = requirementType + RequirementType = requirementType, + Status = featureStatus, + Allocation = allocation, + Variants = variants, + Telemetry = new TelemetryConfiguration + { + Enabled = telemetryEnabled, + Metadata = telemetryMetadata + } }; } diff --git a/src/Microsoft.FeatureManagement/ConfigurationFields.cs b/src/Microsoft.FeatureManagement/ConfigurationFields.cs index 01a1f698..47d0c6a4 100644 --- a/src/Microsoft.FeatureManagement/ConfigurationFields.cs +++ b/src/Microsoft.FeatureManagement/ConfigurationFields.cs @@ -17,8 +17,8 @@ internal static class ConfigurationFields public const string AllocationSectionName = "Allocation"; public const string AllocationDefaultWhenDisabled = "DefaultWhenDisabled"; public const string AllocationDefaultWhenEnabled = "DefaultWhenEnabled"; - public const string UserAllocationSectionName = "User"; public const string AllocationVariantKeyword = "Variant"; + public const string UserAllocationSectionName = "User"; public const string UserAllocationUsers = "Users"; public const string GroupAllocationSectionName = "Group"; public const string GroupAllocationGroups = "Groups"; diff --git a/src/Microsoft.FeatureManagement/MicrosoftFeatureFlagFields.cs b/src/Microsoft.FeatureManagement/MicrosoftFeatureFlagFields.cs index 21286e5c..a1a7a1fe 100644 --- a/src/Microsoft.FeatureManagement/MicrosoftFeatureFlagFields.cs +++ b/src/Microsoft.FeatureManagement/MicrosoftFeatureFlagFields.cs @@ -5,7 +5,7 @@ namespace Microsoft.FeatureManagement { // - // Microsoft feature flag schema: https://github.com/Azure/AppConfiguration/blob/main/docs/FeatureManagement/FeatureFlag.v1.1.0.schema.json + // Microsoft feature flag schema: https://github.com/Azure/AppConfiguration/blob/main/docs/FeatureManagement/FeatureFlag.v2.0.0.schema.json internal static class MicrosoftFeatureFlagFields { public const string FeatureFlagsSectionName = "FeatureFlags"; @@ -18,9 +18,34 @@ internal static class MicrosoftFeatureFlagFields public const string ClientFilters = "client_filters"; public const string RequirementType = "requirement_type"; + // + // Allocation keywords + public const string AllocationSectionName = "allocation"; + public const string AllocationDefaultWhenDisabled = "default_when_disabled"; + public const string AllocationDefaultWhenEnabled = "default_when_enabled"; + public const string AllocationVariantKeyword = "variant"; + public const string UserAllocationSectionName = "user"; + public const string UserAllocationUsers = "users"; + public const string GroupAllocationSectionName = "group"; + public const string GroupAllocationGroups = "groups"; + public const string PercentileAllocationSectionName = "percentile"; + public const string PercentileAllocationFrom = "from"; + public const string PercentileAllocationTo = "to"; + public const string AllocationSeed = "seed"; + // // Client filter keywords public const string Name = "name"; public const string Parameters = "parameters"; + + // Variants keywords + public const string VariantsSectionName = "variants"; + public const string VariantDefinitionConfigurationValue = "configuration_value"; + public const string VariantDefinitionConfigurationReference = "configuration_reference"; + public const string VariantDefinitionStatusOverride = "status_override"; + + // Telemetry keywords + public const string Telemetry = "telemetry"; + public const string Metadata = "metadata"; } } \ No newline at end of file diff --git a/tests/Tests.FeatureManagement/FeatureManagement.cs b/tests/Tests.FeatureManagement/FeatureManagement.cs index 97c13e68..1a61febb 100644 --- a/tests/Tests.FeatureManagement/FeatureManagement.cs +++ b/tests/Tests.FeatureManagement/FeatureManagement.cs @@ -109,203 +109,6 @@ public async Task ReadsTopLevelConfiguration() Assert.True(await featureManager.IsEnabledAsync(feature)); } - [Fact] - public async Task ReadsMicrosoftFeatureFlagSchema() - { - string json = @" - { - ""AllowedHosts"": ""*"", - ""FeatureManagement"": { - ""MyFeature"": true, - ""FeatureFlags"": [ - { - ""id"": ""Alpha"", - ""enabled"": true, - ""conditions"": { - ""client_filters"": [] - } - }, - { - ""id"": ""Beta"", - ""enabled"": true, - ""conditions"": { - ""client_filters"": [ - { - ""name"": ""Percentage"", - ""parameters"": { - ""Value"": 100 - } - }, - { - ""name"": ""Targeting"", - ""parameters"": { - ""Audience"": { - ""Users"": [""Jeff""], - ""Groups"": [], - ""DefaultRolloutPercentage"": 0 - } - } - } - ], - ""requirement_type"" : ""all"" - } - }, - { - ""id"": ""Sigma"", - ""enabled"": false, - ""conditions"": { - ""client_filters"": [ - { - ""name"": ""Percentage"", - ""parameters"": { - ""Value"": 100 - } - } - ] - } - }, - { - ""id"": ""Omega"", - ""enabled"": true, - ""conditions"": { - ""client_filters"": [ - { - ""name"": ""Percentage"", - ""parameters"": { - ""Value"": 100 - } - }, - { - ""name"": ""Percentage"", - ""parameters"": { - ""Value"": 0 - } - } - ] - } - } - ] - } - }"; - - var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - - IConfiguration config = new ConfigurationBuilder().AddJsonStream(stream).Build(); - - var services = new ServiceCollection(); - - services.AddSingleton(config) - .AddFeatureManagement(); - - ServiceProvider serviceProvider = services.BuildServiceProvider(); - - IFeatureManager featureManager = serviceProvider.GetRequiredService(); - - Assert.False(await featureManager.IsEnabledAsync("MyFeature")); - - Assert.True(await featureManager.IsEnabledAsync("Alpha")); - - Assert.True(await featureManager.IsEnabledAsync("Beta", new TargetingContext - { - UserId = "Jeff" - })); - - Assert.False(await featureManager.IsEnabledAsync("Beta", new TargetingContext - { - UserId = "Sam" - })); - - Assert.False(await featureManager.IsEnabledAsync("Sigma")); - - Assert.True(await featureManager.IsEnabledAsync("Omega")); - - json = @" - { - ""AllowedHosts"": ""*"", - ""FeatureManagement"": { - ""MyFeature"": true, - ""FeatureFlags"": [ - { - ""id"": ""Alpha"", - ""enabled"": true - } - ] - } - }"; - - stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - - config = new ConfigurationBuilder().AddJsonStream(stream).Build(); - - services = new ServiceCollection(); - - services.AddFeatureManagement(config.GetSection("FeatureManagement")); - - serviceProvider = services.BuildServiceProvider(); - - featureManager = serviceProvider.GetRequiredService(); - - Assert.False(await featureManager.IsEnabledAsync("MyFeature")); - - Assert.True(await featureManager.IsEnabledAsync("Alpha")); - - json = @" - { - ""AllowedHosts"": ""*"", - ""FeatureManagement"": { - ""MyFeature"": true, - ""FeatureFlags"": true - } - }"; - - stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - - config = new ConfigurationBuilder().AddJsonStream(stream).Build(); - - services = new ServiceCollection(); - - services.AddFeatureManagement(config.GetSection("FeatureManagement")); - - serviceProvider = services.BuildServiceProvider(); - - featureManager = serviceProvider.GetRequiredService(); - - Assert.True(await featureManager.IsEnabledAsync("MyFeature")); - - Assert.True(await featureManager.IsEnabledAsync("FeatureFlags")); - - json = @" - { - ""AllowedHosts"": ""*"", - ""FeatureManagement"": { - ""MyFeature"": true, - ""FeatureFlags"": { - ""EnabledFor"": [ - { - ""Name"": ""AlwaysOn"" - } - ] - } - } - }"; - - stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - - config = new ConfigurationBuilder().AddJsonStream(stream).Build(); - - services = new ServiceCollection(); - - services.AddFeatureManagement(config.GetSection("FeatureManagement")); - - serviceProvider = services.BuildServiceProvider(); - - featureManager = serviceProvider.GetRequiredService(); - - Assert.True(await featureManager.IsEnabledAsync("MyFeature")); - - Assert.True(await featureManager.IsEnabledAsync("FeatureFlags")); - } - [Fact] public void AddsScopedFeatureManagement() { diff --git a/tests/Tests.FeatureManagement/MicrosoftFeatureFlag.json b/tests/Tests.FeatureManagement/MicrosoftFeatureFlag.json new file mode 100644 index 00000000..a3267450 --- /dev/null +++ b/tests/Tests.FeatureManagement/MicrosoftFeatureFlag.json @@ -0,0 +1,72 @@ +{ + "ShoppingCart": { + "Big": { + "Size": 600, + "Color": "green" + } + }, + "FeatureManagement": { + "FeatureFlags": [ + { + "id": "AlwaysOnTestFeature", + "enabled": true, + "telemetry": { + "enabled": true, + "metadata": { + "Tags.Tag1": "Tag1Value", + "Tags.Tag2": "Tag2Value", + "Etag": "EtagValue", + "Label": "LabelValue" + } + }, + "conditions": { + "client_filters": [ + { + "name": "AlwaysOn" + } + ], + "requirement_type": "All" + }, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + }, + { + "name": "Big", + "configuration_reference": "ShoppingCart:Big", + "status_override": "Disabled" + } + ], + "allocation": { + "default_when_enabled": "Small", + "default_when_disabled": "Big", + "percentile": [ + { + "variant": "Small", + "from": 0, + "to": 50 + } + ], + "user": [ + { + "variant": "Small", + "users": [ + "Jeff" + ] + } + ], + "group": [ + { + "variant": "Big", + "groups": [ + "Group1" + ] + } + ], + "seed": 12345 + } + } + ] + } +} \ No newline at end of file diff --git a/tests/Tests.FeatureManagement/MicrosoftFeatureFlagSchema.cs b/tests/Tests.FeatureManagement/MicrosoftFeatureFlagSchema.cs new file mode 100644 index 00000000..c9f90407 --- /dev/null +++ b/tests/Tests.FeatureManagement/MicrosoftFeatureFlagSchema.cs @@ -0,0 +1,318 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.FeatureManagement; +using Microsoft.FeatureManagement.FeatureFilters; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Tests.FeatureManagement +{ + public class MicrosoftFeatureFlagSchemaTest + { + [Fact] + public async Task ReadsMicrosoftFeatureFlagSchemaIfAny() + { + string json = @" + { + ""AllowedHosts"": ""*"", + ""FeatureManagement"": { + ""MyFeature"": true, + ""FeatureFlags"": [ + { + ""id"": ""Alpha"", + ""enabled"": true + } + ] + } + }"; + + var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + IConfiguration config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + var services = new ServiceCollection(); + + services.AddSingleton(config) + .AddFeatureManagement(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + Assert.False(await featureManager.IsEnabledAsync("MyFeature")); + + Assert.True(await featureManager.IsEnabledAsync("Alpha")); + + json = @" + { + ""AllowedHosts"": ""*"", + ""FeatureManagement"": { + ""MyFeature"": true, + ""FeatureFlags"": true + } + }"; + + stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + services = new ServiceCollection(); + + services.AddSingleton(config) + .AddFeatureManagement(); + + serviceProvider = services.BuildServiceProvider(); + + featureManager = serviceProvider.GetRequiredService(); + + Assert.True(await featureManager.IsEnabledAsync("MyFeature")); + + Assert.True(await featureManager.IsEnabledAsync("FeatureFlags")); + + json = @" + { + ""AllowedHosts"": ""*"", + ""FeatureManagement"": { + ""MyFeature"": true, + ""FeatureFlags"": { + ""EnabledFor"": [ + { + ""Name"": ""AlwaysOn"" + } + ] + } + } + }"; + + stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + services = new ServiceCollection(); + + services.AddSingleton(config) + .AddFeatureManagement(); + + serviceProvider = services.BuildServiceProvider(); + + featureManager = serviceProvider.GetRequiredService(); + + Assert.True(await featureManager.IsEnabledAsync("MyFeature")); + + Assert.True(await featureManager.IsEnabledAsync("FeatureFlags")); + } + + [Fact] + public async Task ReadsTopLevelConfiguration() + { + string json = @" + { + ""AllowedHosts"": ""*"", + ""FeatureManagement"": { + ""MyFeature"": true, + ""FeatureFlags"": [ + { + ""id"": ""Alpha"", + ""enabled"": true + } + ] + } + }"; + + var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + IConfiguration config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + var services = new ServiceCollection(); + + services.AddFeatureManagement(config.GetSection("FeatureManagement")); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + Assert.False(await featureManager.IsEnabledAsync("MyFeature")); + + Assert.True(await featureManager.IsEnabledAsync("Alpha")); + + json = @" + { + ""AllowedHosts"": ""*"", + ""FeatureManagement"": { + ""MyFeature"": true, + ""FeatureFlags"": true + } + }"; + + stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + services = new ServiceCollection(); + + services.AddFeatureManagement(config.GetSection("FeatureManagement")); + + serviceProvider = services.BuildServiceProvider(); + + featureManager = serviceProvider.GetRequiredService(); + + Assert.True(await featureManager.IsEnabledAsync("MyFeature")); + + Assert.True(await featureManager.IsEnabledAsync("FeatureFlags")); + + json = @" + { + ""AllowedHosts"": ""*"", + ""FeatureManagement"": { + ""MyFeature"": true, + ""FeatureFlags"": { + ""EnabledFor"": [ + { + ""Name"": ""AlwaysOn"" + } + ] + } + } + }"; + + stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + services = new ServiceCollection(); + + services.AddFeatureManagement(config.GetSection("FeatureManagement")); + + serviceProvider = services.BuildServiceProvider(); + + featureManager = serviceProvider.GetRequiredService(); + + Assert.True(await featureManager.IsEnabledAsync("MyFeature")); + + Assert.True(await featureManager.IsEnabledAsync("FeatureFlags")); + } + + [Fact] + public async Task ReadsFeatureDefinition() + { + IConfiguration config = new ConfigurationBuilder().AddJsonFile("MicrosoftFeatureFlag.json").Build(); + + var featureDefinitionProvider = new ConfigurationFeatureDefinitionProvider(config); + + FeatureDefinition featureDefinition = await featureDefinitionProvider.GetFeatureDefinitionAsync(Features.AlwaysOnTestFeature); + + Assert.NotNull(featureDefinition); + + Assert.Equal(RequirementType.All, featureDefinition.RequirementType); + + Assert.Equal(FeatureStatus.Conditional, featureDefinition.Status); + + Assert.Equal("Small", featureDefinition.Allocation.DefaultWhenEnabled); + + Assert.Equal("Big", featureDefinition.Allocation.DefaultWhenDisabled); + + Assert.Equal("Small", featureDefinition.Allocation.User.First().Variant); + + Assert.Equal("Jeff", featureDefinition.Allocation.User.First().Users.First()); + + Assert.Equal("Big", featureDefinition.Allocation.Group.First().Variant); + + Assert.Equal("Group1", featureDefinition.Allocation.Group.First().Groups.First()); + + Assert.Equal("Small", featureDefinition.Allocation.Percentile.First().Variant); + + Assert.Equal(0, featureDefinition.Allocation.Percentile.First().From); + + Assert.Equal(50, featureDefinition.Allocation.Percentile.First().To); + + Assert.Equal("12345", featureDefinition.Allocation.Seed); + + VariantDefinition smallVariant = featureDefinition.Variants.FirstOrDefault(variant => string.Equals(variant.Name, "Small")); + + Assert.NotNull(smallVariant); + + Assert.Equal("300px", smallVariant.ConfigurationValue.Value); + + Assert.Equal(StatusOverride.None, smallVariant.StatusOverride); + + VariantDefinition bigVariant = featureDefinition.Variants.FirstOrDefault(variant => string.Equals(variant.Name, "Big")); + + Assert.NotNull(bigVariant); + + Assert.Equal("ShoppingCart:Big", bigVariant.ConfigurationReference); + + Assert.Equal(StatusOverride.Disabled, bigVariant.StatusOverride); + } + + [Fact] + public async Task ReadsFeatureFilterConfiguration() + { + string json = @" + { + ""AllowedHosts"": ""*"", + ""FeatureManagement"": { + ""FeatureFlags"": [ + { + ""id"": ""ConditionalFeature"", + ""enabled"": true, + ""conditions"": { + ""client_filters"": [ + { + ""name"": ""Test"", + ""parameters"": { + ""P1"": ""V1"" + } + } + ] + } + }, + ] + } + }"; + + var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + IConfiguration config = new ConfigurationBuilder().AddJsonStream(stream).Build(); + + var services = new ServiceCollection(); + + services.AddSingleton(config) + .AddFeatureManagement() + .AddFeatureFilter(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + IEnumerable featureFilters = serviceProvider.GetRequiredService>(); + + // + // Sync filter + TestFilter testFeatureFilter = (TestFilter)featureFilters.First(f => f is TestFilter); + + bool called = false; + + testFeatureFilter.Callback = (evaluationContext) => + { + called = true; + + Assert.Equal("V1", evaluationContext.Parameters["P1"]); + + Assert.Equal(Features.ConditionalFeature, evaluationContext.FeatureName); + + return Task.FromResult(true); + }; + + await featureManager.IsEnabledAsync(Features.ConditionalFeature); + + Assert.True(called); + } + } +} diff --git a/tests/Tests.FeatureManagement/Tests.FeatureManagement.csproj b/tests/Tests.FeatureManagement/Tests.FeatureManagement.csproj index 4d8697ad..35a3e69d 100644 --- a/tests/Tests.FeatureManagement/Tests.FeatureManagement.csproj +++ b/tests/Tests.FeatureManagement/Tests.FeatureManagement.csproj @@ -38,6 +38,9 @@ Always + + Always +