diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs index 57806731..7d53c234 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs @@ -34,5 +34,9 @@ internal class FeatureManagementConstants public const string ETag = "ETag"; public const string FeatureFlagId = "FeatureFlagId"; public const string FeatureFlagReference = "FeatureFlagReference"; + public const string Status = "Status"; + public const string AlwaysOnFilter = "AlwaysOn"; + public const string Conditional = "Conditional"; + public const string Disabled = "Disabled"; } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs index e22437ba..dbdacbf0 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs @@ -41,12 +41,12 @@ public Task>> ProcessKeyValue(Configura if (featureFlag.Enabled) { + keyValues.Add(new KeyValuePair($"{featureFlagPath}:{FeatureManagementConstants.Status}", FeatureManagementConstants.Conditional)); + //if (featureFlag.Conditions?.ClientFilters == null) if (featureFlag.Conditions?.ClientFilters == null || !featureFlag.Conditions.ClientFilters.Any()) // workaround since we are not yet setting client filters to null { - // - // Always on - keyValues.Add(new KeyValuePair(featureFlagPath, true.ToString())); + keyValues.Add(new KeyValuePair($"{featureFlagPath}:{FeatureManagementConstants.EnabledFor}:{0}:{FeatureManagementConstants.Name}", FeatureManagementConstants.AlwaysOnFilter)); } else { @@ -80,7 +80,7 @@ public Task>> ProcessKeyValue(Configura } else { - keyValues.Add(new KeyValuePair($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}", false.ToString())); + keyValues.Add(new KeyValuePair($"{featureFlagPath}:{FeatureManagementConstants.Status}", FeatureManagementConstants.Disabled)); } if (featureFlag.Variants != null) diff --git a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs index 02a3d3c8..852f053b 100644 --- a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs +++ b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs @@ -198,9 +198,6 @@ public class FeatureManagementTests ""enabled"": true, ""conditions"": { ""client_filters"": [ - { - ""name"": ""AlwaysOn"" - } ] }, ""variants"": [ @@ -278,9 +275,6 @@ public class FeatureManagementTests ""enabled"": true, ""conditions"": { ""client_filters"": [ - { - ""name"": ""AlwaysOn"" - } ] }, ""telemetry"": { @@ -577,8 +571,8 @@ public void SelectFeatureFlags() }) .Build(); - Assert.Equal("True", config["FeatureManagement:App1_Feature1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); // Verify that the feature flag that did not start with the specified prefix was not loaded Assert.Null(config["FeatureManagement:Feature1"]); @@ -620,10 +614,10 @@ public void MultipleSelectsInSameUseFeatureFlags() }) .Build(); - Assert.Equal("True", config["FeatureManagement:App1_Feature1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); - Assert.Equal("False", config["FeatureManagement:App2_Feature1"]); - Assert.Equal("True", config["FeatureManagement:App2_Feature2"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); + Assert.Equal("Disabled", config["FeatureManagement:App2_Feature1:Status"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App2_Feature2:EnabledFor:0:Name"]); // Verify that the feature flag that did not start with the specified prefix was not loaded Assert.Null(config["FeatureManagement:Feature1"]); @@ -661,7 +655,7 @@ public void KeepSelectorPrecedenceAfterDedup() }) .Build(); // label: App1_Label has higher precedence - Assert.Equal("True", config["FeatureManagement:Feature1"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:Feature1:EnabledFor:0:Name"]); } [Fact] @@ -733,10 +727,10 @@ public void MultipleCallsToUseFeatureFlags() }) .Build(); - Assert.Equal("True", config["FeatureManagement:App1_Feature1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); - Assert.Equal("False", config["FeatureManagement:App2_Feature1"]); - Assert.Equal("True", config["FeatureManagement:App2_Feature2"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); + Assert.Equal("Disabled", config["FeatureManagement:App2_Feature1:Status"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App2_Feature2:EnabledFor:0:Name"]); // Verify that the feature flag that did not start with the specified prefix was not loaded Assert.Null(config["FeatureManagement:Feature1"]); @@ -777,13 +771,13 @@ public void MultipleCallsToUseFeatureFlagsWithSelectAndLabel() .Build(); // Loaded from prefix1 and label1 - Assert.Equal("True", config["FeatureManagement:App1_Feature1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); // Loaded from label2 - Assert.Equal("False", config["FeatureManagement:App2_Feature1"]); - Assert.Equal("True", config["FeatureManagement:App2_Feature2"]); - Assert.Equal("True", config["FeatureManagement:Feature1"]); + Assert.Equal("Disabled", config["FeatureManagement:App2_Feature1:Status"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App2_Feature2:EnabledFor:0:Name"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:Feature1:EnabledFor:0:Name"]); } [Fact] @@ -827,10 +821,10 @@ public void DifferentCacheExpirationsForMultipleFeatureFlagRegistrations() }) .Build(); - Assert.Equal("True", config["FeatureManagement:App1_Feature1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); - Assert.Equal("False", config["FeatureManagement:App2_Feature1"]); - Assert.Equal("True", config["FeatureManagement:App2_Feature2"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); + Assert.Equal("Disabled", config["FeatureManagement:App2_Feature1:Status"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App2_Feature2:EnabledFor:0:Name"]); // update the value of App1_Feature1 feature flag with label1 featureFlagCollection[0] = ConfigurationModelFactory.ConfigurationSetting( @@ -878,9 +872,9 @@ public void DifferentCacheExpirationsForMultipleFeatureFlagRegistrations() Assert.Equal("Browser", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); Assert.Equal("Chrome", config["FeatureManagement:App1_Feature1:EnabledFor:0:Parameters:AllowedBrowsers:0"]); Assert.Equal("Edge", config["FeatureManagement:App1_Feature1:EnabledFor:0:Parameters:AllowedBrowsers:1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); - Assert.Equal("False", config["FeatureManagement:App2_Feature1"]); - Assert.Equal("True", config["FeatureManagement:App2_Feature2"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); + Assert.Equal("Disabled", config["FeatureManagement:App2_Feature1:Status"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App2_Feature2:EnabledFor:0:Name"]); // even though App2_Feature3 feature flag has been added, its value should not be loaded in config because label2 cache has not expired Assert.Null(config["FeatureManagement:App2_Feature3"]); @@ -920,11 +914,11 @@ public void OverwrittenCacheExpirationForSameFeatureFlagRegistrations() }) .Build(); - Assert.Equal("True", config["FeatureManagement:App1_Feature1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); - Assert.Equal("False", config["FeatureManagement:App2_Feature1"]); - Assert.Equal("True", config["FeatureManagement:App2_Feature2"]); - Assert.Equal("True", config["FeatureManagement:Feature1"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); + Assert.Equal("Disabled", config["FeatureManagement:App2_Feature1:Status"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App2_Feature2:EnabledFor:0:Name"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:Feature1:EnabledFor:0:Name"]); // update the value of App1_Feature1 feature flag with label1 featureFlagCollection[0] = ConfigurationModelFactory.ConfigurationSetting( @@ -954,11 +948,11 @@ public void OverwrittenCacheExpirationForSameFeatureFlagRegistrations() // The cache expiration time for feature flags was overwritten by second call to UseFeatureFlags. // Sleeping for cacheExpiration1 time should not update feature flags. - Assert.Equal("True", config["FeatureManagement:App1_Feature1"]); - Assert.Equal("False", config["FeatureManagement:App1_Feature2"]); - Assert.Equal("False", config["FeatureManagement:App2_Feature1"]); - Assert.Equal("True", config["FeatureManagement:App2_Feature2"]); - Assert.Equal("True", config["FeatureManagement:Feature1"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App1_Feature1:EnabledFor:0:Name"]); + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); + Assert.Equal("Disabled", config["FeatureManagement:App2_Feature1:Status"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:App2_Feature2:EnabledFor:0:Name"]); + Assert.Equal("AlwaysOn", config["FeatureManagement:Feature1:EnabledFor:0:Name"]); } [Fact] @@ -993,7 +987,7 @@ public void SelectAndRefreshSingleFeatureFlag() }) .Build(); - Assert.Equal("False", config["FeatureManagement:Feature1"]); + Assert.Equal("Disabled", config["FeatureManagement:Feature1:Status"]); // update the value of Feature1 feature flag with App1_Label featureFlagCollection[2] = ConfigurationModelFactory.ConfigurationSetting( @@ -1289,6 +1283,7 @@ public void WithVariants() }) .Build(); + Assert.Equal("AlwaysOn", config["FeatureManagement:VariantsFeature:EnabledFor:0:Name"]); Assert.Equal("Big", config["FeatureManagement:VariantsFeature:Variants:0:Name"]); Assert.Equal("600px", config["FeatureManagement:VariantsFeature:Variants:0:ConfigurationValue"]); Assert.Equal("Small", config["FeatureManagement:VariantsFeature:Variants:1:Name"]); @@ -1316,6 +1311,27 @@ public void WithVariants() Assert.Equal("13992821", config["FeatureManagement:VariantsFeature:Allocation:Seed"]); } + [Fact] + public void WithStatus() + { + var mockResponse = new Mock(); + var mockClient = new Mock(MockBehavior.Strict); + + mockClient.Setup(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Returns(new MockAsyncPageable(_featureFlagCollection)); + + var config = new ConfigurationBuilder() + .AddAzureAppConfiguration(options => + { + options.ClientManager = TestHelpers.CreateMockedConfigurationClientManager(mockClient.Object); + options.UseFeatureFlags(); + }) + .Build(); + + Assert.Equal("Disabled", config["FeatureManagement:App1_Feature2:Status"]); + Assert.Equal("Conditional", config["FeatureManagement:Feature1:Status"]); + } + [Fact] public void WithTelemetry() {