From cade912ed04ad02cba127ac99aa05510aaea4108 Mon Sep 17 00:00:00 2001 From: Ross Grambo Date: Mon, 16 Sep 2024 10:30:52 -0700 Subject: [PATCH 1/3] Adjusts to last one wins logic for flag provider --- .../ConfigurationFeatureDefinitionProvider.cs | 4 ++-- .../DotnetFeatureManagementSchema.json | 5 +++-- .../FeatureManagementTest.cs | 20 +++++++++++++++++++ tests/Tests.FeatureManagement/Features.cs | 1 + .../Tests.FeatureManagement/appsettings.json | 8 ++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs index 835c6d8e..c15f1835 100644 --- a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs +++ b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs @@ -156,7 +156,7 @@ private FeatureDefinition GetDotnetSchemaFeatureDefinition(string featureName) IEnumerable dotnetFeatureDefinitionSections = GetDotnetFeatureDefinitionSections(); IConfigurationSection configuration = dotnetFeatureDefinitionSections - .FirstOrDefault(section => + .LastOrDefault(section => string.Equals(section.Key, featureName, StringComparison.OrdinalIgnoreCase)); if (configuration == null) @@ -172,7 +172,7 @@ private FeatureDefinition GetMicrosoftSchemaFeatureDefinition(string featureName IEnumerable microsoftFeatureDefinitionSections = GetMicrosoftFeatureDefinitionSections(); IConfigurationSection configuration = microsoftFeatureDefinitionSections - .FirstOrDefault(section => + .LastOrDefault(section => string.Equals(section[MicrosoftFeatureManagementFields.Id], featureName, StringComparison.OrdinalIgnoreCase)); if (configuration == null) diff --git a/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json b/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json index 57b068b5..d4c11bbd 100644 --- a/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json +++ b/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json @@ -11,6 +11,7 @@ } } ] - } + }, + "DuplicateFeature": false } -} \ No newline at end of file +} diff --git a/tests/Tests.FeatureManagement/FeatureManagementTest.cs b/tests/Tests.FeatureManagement/FeatureManagementTest.cs index d8adf251..d12fe51c 100644 --- a/tests/Tests.FeatureManagement/FeatureManagementTest.cs +++ b/tests/Tests.FeatureManagement/FeatureManagementTest.cs @@ -427,6 +427,26 @@ public async Task CustomFeatureDefinitionProvider() Assert.True(called); } + + [Fact] + public async Task LastFeatureFlagWins() + { + IConfiguration configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + + IServiceCollection services = new ServiceCollection(); + + services + .AddSingleton(configuration) + .AddFeatureManagement(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + Assert.True(await featureManager.IsEnabledAsync(Features.DuplicateFlag)); + } } public class FeatureManagementFeatureFilterGeneralTest diff --git a/tests/Tests.FeatureManagement/Features.cs b/tests/Tests.FeatureManagement/Features.cs index 22c22201..e0746fef 100644 --- a/tests/Tests.FeatureManagement/Features.cs +++ b/tests/Tests.FeatureManagement/Features.cs @@ -30,5 +30,6 @@ static class Features public const string OnTelemetryTestFeature = "OnTelemetryTestFeature"; public const string OffTelemetryTestFeature = "OffTelemetryTestFeature"; public const string ContextualFeatureWithVariant = "ContextualFeatureWithVariant"; + public const string DuplicateFlag = "DuplicateFlag"; } } diff --git a/tests/Tests.FeatureManagement/appsettings.json b/tests/Tests.FeatureManagement/appsettings.json index a99a325d..b1ad1e72 100644 --- a/tests/Tests.FeatureManagement/appsettings.json +++ b/tests/Tests.FeatureManagement/appsettings.json @@ -530,6 +530,14 @@ "default_when_enabled": "Big", "default_when_disabled": "Small" } + }, + { + "id": "DuplicateFlag", + "enabled": false + }, + { + "id": "DuplicateFlag", + "enabled": true } ] } From 60a858a8eb03d0e567adce6f9c73ac5cf5e3cadd Mon Sep 17 00:00:00 2001 From: Ross Grambo Date: Mon, 16 Sep 2024 10:33:43 -0700 Subject: [PATCH 2/3] Removed unused schema --- .../Tests.FeatureManagement/DotnetFeatureManagementSchema.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json b/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json index d4c11bbd..d52a677a 100644 --- a/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json +++ b/tests/Tests.FeatureManagement/DotnetFeatureManagementSchema.json @@ -11,7 +11,6 @@ } } ] - }, - "DuplicateFeature": false + } } } From cadc4d219cb6ae04dec6c656778b4105d1c44b8d Mon Sep 17 00:00:00 2001 From: Ross Grambo Date: Mon, 16 Sep 2024 11:41:39 -0700 Subject: [PATCH 3/3] Reverted dotnet feature definition to continue to use FirstOrDefault --- .../ConfigurationFeatureDefinitionProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs index c15f1835..e5d4d1b1 100644 --- a/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs +++ b/src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs @@ -156,7 +156,7 @@ private FeatureDefinition GetDotnetSchemaFeatureDefinition(string featureName) IEnumerable dotnetFeatureDefinitionSections = GetDotnetFeatureDefinitionSections(); IConfigurationSection configuration = dotnetFeatureDefinitionSections - .LastOrDefault(section => + .FirstOrDefault(section => string.Equals(section.Key, featureName, StringComparison.OrdinalIgnoreCase)); if (configuration == null)