From c2e18d7b2c2c458e6951fc88f17e1ec0ebc13884 Mon Sep 17 00:00:00 2001 From: Ross Grambo Date: Fri, 18 Oct 2024 13:08:23 -0700 Subject: [PATCH 1/7] Adding allocation id --- .../Extensions/StringExtensions.cs | 9 + .../FeatureManagementConstants.cs | 1 + .../FeatureManagementKeyValueAdapter.cs | 86 +++++++++ .../FeatureManagementTests.cs | 164 ++++++++++++++++++ 4 files changed, 260 insertions(+) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs index 7bcf7212..8b2c488d 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/StringExtensions.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. // +using System; + namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions { internal static class LabelFilters @@ -16,5 +18,12 @@ public static string NormalizeNull(this string s) { return s == LabelFilters.Null ? null : s; } + + public static string ToBase64String(this string s) + { + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(s); + + return Convert.ToBase64String(bytes); + } } } diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs index c6d86d84..aa573a1e 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs @@ -44,6 +44,7 @@ internal class FeatureManagementConstants public const string ETag = "ETag"; public const string FeatureFlagId = "FeatureFlagId"; public const string FeatureFlagReference = "FeatureFlagReference"; + public const string AllocationId = "AllocationId"; // Dotnet schema keys public const string DotnetSchemaSectionName = "FeatureManagement"; diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs index 2b0e0833..9e367884 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs @@ -319,12 +319,98 @@ private List> ProcessMicrosoftSchemaFeatureFlag(Fea keyValues.Add(new KeyValuePair($"{telemetryPath}:{FeatureManagementConstants.Metadata}:{FeatureManagementConstants.ETag}", setting.ETag.ToString())); keyValues.Add(new KeyValuePair($"{telemetryPath}:{FeatureManagementConstants.Enabled}", telemetry.Enabled.ToString())); + + string allocationId = CalculateAllocationId(featureFlag); + + if (allocationId != null) + { + keyValues.Add(new KeyValuePair($"{telemetryPath}:{FeatureManagementConstants.Metadata}:{FeatureManagementConstants.AllocationId}", allocationId)); + } } } return keyValues; } + private string CalculateAllocationId(FeatureFlag flag) + { + if (flag.Allocation == null) + { + return null; + } + + StringBuilder inputBuilder = new StringBuilder(); + + // Seed + inputBuilder.Append($"seed={flag.Allocation.Seed ?? ""}"); + + var allocatedVariants = new HashSet(); + + // DefaultWhenEnabled + if (flag.Allocation.DefaultWhenEnabled != null) + { + allocatedVariants.Add(flag.Allocation.DefaultWhenEnabled); + } + + inputBuilder.Append($"\ndefault_when_enabled={flag.Allocation.DefaultWhenEnabled ?? ""}"); + + // Percentiles + inputBuilder.Append("\npercentiles="); + + if (flag.Allocation.Percentile != null && flag.Allocation.Percentile.Any()) + { + var sortedPercentiles = flag.Allocation.Percentile + .Where(p => p.From != p.To) + .OrderBy(p => p.From) + .ToList(); + + allocatedVariants.UnionWith(sortedPercentiles.Select(p => p.Variant)); + + inputBuilder.Append(string.Join(";", sortedPercentiles.Select(p => $"{p.From},{p.Variant.ToBase64String()},{p.To}"))); + } + + // If there's no custom seed and no variants allocated, stop now and return null + if (flag.Allocation.Seed == null && + !allocatedVariants.Any()) + { + return null; + } + + // Variants + inputBuilder.Append("\nvariants="); + + if (allocatedVariants.Any() && flag.Variants != null && flag.Variants.Any()) + { + var sortedVariants = flag.Variants + .Where(variant => allocatedVariants.Contains(variant.Name)) + .OrderBy(variant => variant.Name) + .ToList(); + + inputBuilder.Append(string.Join(";", sortedVariants.Select(v => + { + var variantValue = ""; + + if (v.ConfigurationValue.ValueKind != JsonValueKind.Null && v.ConfigurationValue.ValueKind != JsonValueKind.Undefined) + { + variantValue = JsonSerializer.Serialize(v.ConfigurationValue); + } + + return $"{v.Name.ToBase64String()},{(variantValue)}"; + }))); + } + + // Example input string + // input == "seed=123abc\ndefault_when_enabled=Control\npercentiles=0,Blshdk,20;20,Test,100\nvariants=TdLa,standard;Qfcd,special" + string input = inputBuilder.ToString(); + + using (SHA256 sha256 = SHA256.Create()) + { + byte[] truncatedHash = new byte[15]; + Array.Copy(sha256.ComputeHash(Encoding.UTF8.GetBytes(input)), truncatedHash, 15); + return truncatedHash.ToBase64Url(); + } + } + private FormatException CreateFeatureFlagFormatException(string jsonPropertyName, string settingKey, string foundJsonValueKind, string expectedJsonValueKind) { return new FormatException(string.Format( diff --git a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs index 44c20b4d..2bfc415e 100644 --- a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs +++ b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs @@ -622,6 +622,111 @@ public class FeatureManagementTests eTag: new ETag("c3c231fd-39a0-4cb6-3237-4614474b92c1")) }; + List _allocationIdFeatureFlagCollection = new List + { + ConfigurationModelFactory.ConfigurationSetting( + key: FeatureManagementConstants.FeatureFlagMarker + "TelemetryVariant", + value: @" + { + ""id"": ""TelemetryVariant"", + ""enabled"": true, + ""variants"": [ + { + ""name"": ""True_Override"", + ""configuration_value"": ""default"", + ""status_override"": ""Disabled"" + } + ], + ""allocation"": { + ""default_when_enabled"": ""True_Override"" + }, + ""telemetry"": { + ""enabled"": ""true"" + } + } + ", + contentType: FeatureManagementConstants.ContentType + ";charset=utf-8", + eTag: new ETag("cmwBRcIAq1jUyKL3Kj8bvf9jtxBrFg-R-ayExStMC90")), + + ConfigurationModelFactory.ConfigurationSetting( + key: FeatureManagementConstants.FeatureFlagMarker + "TelemetryVariantPercentile", + value: @" + { + ""id"": ""TelemetryVariant"", + ""enabled"": true, + ""variants"": [ + { + ""name"": ""True_Override"", + ""configuration_value"": { + ""someKey"": ""someValue"", + ""someOtherKey"": { + ""someSubKey"": ""someSubValue"" + } + } + } + ], + ""allocation"": { + ""default_when_enabled"": ""True_Override"", + ""percentile"": [ + { + ""variant"": ""True_Override"", + ""from"": 0, + ""to"": 100 + } + ] + }, + ""telemetry"": { + ""enabled"": ""true"" + } + } + ", + label: "label", + contentType: FeatureManagementConstants.ContentType + ";charset=utf-8", + eTag: new ETag("cmwBRcIAq1jUyKL3Kj8bvf9jtxBrFg-R-ayExStMC90")), + + // Quote of the day test + ConfigurationModelFactory.ConfigurationSetting( + key: FeatureManagementConstants.FeatureFlagMarker + "Greeting", + value: @" + { + ""id"": ""Greeting"", + ""description"": """", + ""enabled"": true, + ""variants"": [ + { + ""name"": ""On"", + ""configuration_value"": true + }, + { + ""name"": ""Off"", + ""configuration_value"": false + } + ], + ""allocation"": { + ""percentile"": [ + { + ""variant"": ""On"", + ""from"": 0, + ""to"": 50 + }, + { + ""variant"": ""Off"", + ""from"": 50, + ""to"": 100 + } + ], + ""default_when_enabled"": ""Off"", + ""default_when_disabled"": ""Off"" + }, + ""telemetry"": { + ""enabled"": true + } + } + ", + contentType: FeatureManagementConstants.ContentType + ";charset=utf-8", + eTag: new ETag("8kS3pc_cQmWnfLY9LQ1cd-RfR6_nQqH6sgdlL9eCgek")), + }; + TimeSpan RefreshInterval = TimeSpan.FromSeconds(1); [Fact] @@ -1972,6 +2077,65 @@ public void WithTelemetry() Assert.Equal("Tag2Value", config["feature_management:feature_flags:1:telemetry:metadata:Tags.Tag1"]); } + [Fact] + public void WithAllocationId() + { + var mockResponse = new Mock(); + var mockClient = new Mock(MockBehavior.Strict); + + mockClient.Setup(c => c.GetConfigurationSettingsAsync(It.IsAny(), It.IsAny())) + .Returns(new MockAsyncPageable(_allocationIdFeatureFlagCollection)); + + var config = new ConfigurationBuilder() + .AddAzureAppConfiguration(options => + { + options.ClientManager = TestHelpers.CreateMockedConfigurationClientManager(mockClient.Object); + options.Connect(TestHelpers.PrimaryConfigStoreEndpoint, new DefaultAzureCredential()); + options.UseFeatureFlags(); + }) + .Build(); + + byte[] featureFlagIdHash; + + using (HashAlgorithm hashAlgorithm = SHA256.Create()) + { + featureFlagIdHash = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes($"{FeatureManagementConstants.FeatureFlagMarker}TelemetryVariant\n")); + } + + string featureFlagId = Convert.ToBase64String(featureFlagIdHash) + .TrimEnd('=') + .Replace('+', '-') + .Replace('/', '_'); + + // Validate TelemetryVariant + Assert.Equal("True", config["feature_management:feature_flags:0:telemetry:enabled"]); + Assert.Equal("TelemetryVariant", config["feature_management:feature_flags:0:id"]); + + Assert.Equal(featureFlagId, config["feature_management:feature_flags:0:telemetry:metadata:FeatureFlagId"]); + + Assert.Equal($"{TestHelpers.PrimaryConfigStoreEndpoint}kv/{FeatureManagementConstants.FeatureFlagMarker}TelemetryVariant", config["feature_management:feature_flags:0:telemetry:metadata:FeatureFlagReference"]); + + Assert.Equal("MExY1waco2tqen4EcJKK", config["feature_management:feature_flags:0:telemetry:metadata:AllocationId"]); + + // Validate TelemetryVariantPercentile + Assert.Equal("True", config["feature_management:feature_flags:1:telemetry:enabled"]); + Assert.Equal("TelemetryVariant", config["feature_management:feature_flags:1:id"]); + + Assert.Equal($"{TestHelpers.PrimaryConfigStoreEndpoint}kv/{FeatureManagementConstants.FeatureFlagMarker}TelemetryVariantPercentile?label=label", config["feature_management:feature_flags:1:telemetry:metadata:FeatureFlagReference"]); + + Assert.Equal("SCq5T7Bb1k6tWfWjI3qz", config["feature_management:feature_flags:1:telemetry:metadata:AllocationId"]); + + // Validate Greeting + Assert.Equal("True", config["feature_management:feature_flags:2:telemetry:enabled"]); + Assert.Equal("Greeting", config["feature_management:feature_flags:2:id"]); + + Assert.Equal("63pHsrNKDSi5Zfe_FvZPSegwbsEo5TS96hf4k7cc4Zw", config["feature_management:feature_flags:2:telemetry:metadata:FeatureFlagId"]); + + Assert.Equal($"{TestHelpers.PrimaryConfigStoreEndpoint}kv/{FeatureManagementConstants.FeatureFlagMarker}Greeting", config["feature_management:feature_flags:2:telemetry:metadata:FeatureFlagReference"]); + + Assert.Equal("L0m7_ulkdsaQmz6dSw4r", config["feature_management:feature_flags:2:telemetry:metadata:AllocationId"]); + } + [Fact] public void WithRequirementType() { From 1c2c525eee10bf6cb1c7695e1df8bee1504e45fb Mon Sep 17 00:00:00 2001 From: Sami Sadfa Date: Mon, 21 Oct 2024 14:59:24 -0700 Subject: [PATCH 2/7] serialize with sorted keys --- .../FeatureManagementKeyValueAdapter.cs | 6 +- .../JsonElementExtensions.cs | 87 +++++++++++++++++++ .../FeatureManagementTests.cs | 4 +- 3 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs index 9e367884..3076cd99 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs @@ -359,7 +359,7 @@ private string CalculateAllocationId(FeatureFlag flag) if (flag.Allocation.Percentile != null && flag.Allocation.Percentile.Any()) { - var sortedPercentiles = flag.Allocation.Percentile + IEnumerable sortedPercentiles = flag.Allocation.Percentile .Where(p => p.From != p.To) .OrderBy(p => p.From) .ToList(); @@ -381,7 +381,7 @@ private string CalculateAllocationId(FeatureFlag flag) if (allocatedVariants.Any() && flag.Variants != null && flag.Variants.Any()) { - var sortedVariants = flag.Variants + IEnumerable sortedVariants = flag.Variants .Where(variant => allocatedVariants.Contains(variant.Name)) .OrderBy(variant => variant.Name) .ToList(); @@ -392,7 +392,7 @@ private string CalculateAllocationId(FeatureFlag flag) if (v.ConfigurationValue.ValueKind != JsonValueKind.Null && v.ConfigurationValue.ValueKind != JsonValueKind.Undefined) { - variantValue = JsonSerializer.Serialize(v.ConfigurationValue); + variantValue = v.ConfigurationValue.SerializeWithSortedKeys(); } return $"{v.Name.ToBase64String()},{(variantValue)}"; diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs new file mode 100644 index 00000000..a4be3b2a --- /dev/null +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs @@ -0,0 +1,87 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; + +namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement +{ + internal static class JsonElementExtensions + { + public static string SerializeWithSortedKeys(this JsonElement rootElement) + { + using var stream = new MemoryStream(); + + using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = false })) + { + WriteElementWithSortedKeys(rootElement, writer); + } + + return Encoding.UTF8.GetString(stream.ToArray()); + } + + private static void WriteElementWithSortedKeys(JsonElement element, Utf8JsonWriter writer) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + writer.WriteStartObject(); + + foreach (JsonProperty property in element.EnumerateObject().OrderBy(p => p.Name)) + { + writer.WritePropertyName(property.Name); + WriteElementWithSortedKeys(property.Value, writer); + } + + writer.WriteEndObject(); + break; + + case JsonValueKind.Array: + writer.WriteStartArray(); + + foreach (JsonElement item in element.EnumerateArray()) + { + WriteElementWithSortedKeys(item, writer); + } + + writer.WriteEndArray(); + break; + + case JsonValueKind.String: + writer.WriteStringValue(element.GetString()); + break; + + case JsonValueKind.Number: + if (element.TryGetInt32(out int intValue)) + { + writer.WriteNumberValue(intValue); + } + else if (element.TryGetInt64(out long longValue)) + { + writer.WriteNumberValue(longValue); + } + else + { + writer.WriteNumberValue(element.GetDouble()); + } + + break; + + case JsonValueKind.True: + writer.WriteBooleanValue(true); + break; + + case JsonValueKind.False: + writer.WriteBooleanValue(false); + break; + + case JsonValueKind.Null: + writer.WriteNullValue(); + break; + + default: + throw new InvalidOperationException($"Unsupported JsonValueKind: {element.ValueKind}"); + } + } + } +} diff --git a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs index 2bfc415e..0a94c0f4 100644 --- a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs +++ b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs @@ -658,10 +658,10 @@ public class FeatureManagementTests { ""name"": ""True_Override"", ""configuration_value"": { - ""someKey"": ""someValue"", ""someOtherKey"": { ""someSubKey"": ""someSubValue"" - } + }, + ""someKey"": ""someValue"" } } ], From cb4e61558b86125c4dda0b1c8fbb5975b80a404f Mon Sep 17 00:00:00 2001 From: Sami Sadfa Date: Mon, 21 Oct 2024 15:08:09 -0700 Subject: [PATCH 3/7] use string empty --- .../FeatureManagement/FeatureManagementKeyValueAdapter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs index 3076cd99..dcfc70d5 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs @@ -342,7 +342,7 @@ private string CalculateAllocationId(FeatureFlag flag) StringBuilder inputBuilder = new StringBuilder(); // Seed - inputBuilder.Append($"seed={flag.Allocation.Seed ?? ""}"); + inputBuilder.Append($"seed={flag.Allocation.Seed ?? string.Empty}"); var allocatedVariants = new HashSet(); @@ -352,7 +352,7 @@ private string CalculateAllocationId(FeatureFlag flag) allocatedVariants.Add(flag.Allocation.DefaultWhenEnabled); } - inputBuilder.Append($"\ndefault_when_enabled={flag.Allocation.DefaultWhenEnabled ?? ""}"); + inputBuilder.Append($"\ndefault_when_enabled={flag.Allocation.DefaultWhenEnabled ?? string.Empty}"); // Percentiles inputBuilder.Append("\npercentiles="); @@ -388,7 +388,7 @@ private string CalculateAllocationId(FeatureFlag flag) inputBuilder.Append(string.Join(";", sortedVariants.Select(v => { - var variantValue = ""; + var variantValue = string.Empty; if (v.ConfigurationValue.ValueKind != JsonValueKind.Null && v.ConfigurationValue.ValueKind != JsonValueKind.Undefined) { From 49ef1fc8b8ae75911835d30a0f25fa3360cbffd1 Mon Sep 17 00:00:00 2001 From: Sami Sadfa Date: Mon, 21 Oct 2024 15:11:51 -0700 Subject: [PATCH 4/7] nit --- .../FeatureManagementKeyValueAdapter.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs index dcfc70d5..c29827b9 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -320,11 +321,14 @@ private List> ProcessMicrosoftSchemaFeatureFlag(Fea keyValues.Add(new KeyValuePair($"{telemetryPath}:{FeatureManagementConstants.Enabled}", telemetry.Enabled.ToString())); - string allocationId = CalculateAllocationId(featureFlag); - - if (allocationId != null) + if (featureFlag.Allocation != null) { - keyValues.Add(new KeyValuePair($"{telemetryPath}:{FeatureManagementConstants.Metadata}:{FeatureManagementConstants.AllocationId}", allocationId)); + string allocationId = CalculateAllocationId(featureFlag); + + if (allocationId != null) + { + keyValues.Add(new KeyValuePair($"{telemetryPath}:{FeatureManagementConstants.Metadata}:{FeatureManagementConstants.AllocationId}", allocationId)); + } } } } @@ -334,10 +338,7 @@ private List> ProcessMicrosoftSchemaFeatureFlag(Fea private string CalculateAllocationId(FeatureFlag flag) { - if (flag.Allocation == null) - { - return null; - } + Debug.Assert(flag.Allocation != null); StringBuilder inputBuilder = new StringBuilder(); From 86fde179dd388012c0a1523e3e8aa885a0500714 Mon Sep 17 00:00:00 2001 From: Sami Sadfa Date: Mon, 21 Oct 2024 15:37:52 -0700 Subject: [PATCH 5/7] rename ff id to TelemetryVariantPercentile --- tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs index 0a94c0f4..235aec84 100644 --- a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs +++ b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs @@ -652,7 +652,7 @@ public class FeatureManagementTests key: FeatureManagementConstants.FeatureFlagMarker + "TelemetryVariantPercentile", value: @" { - ""id"": ""TelemetryVariant"", + ""id"": ""TelemetryVariantPercentile"", ""enabled"": true, ""variants"": [ { @@ -2119,7 +2119,7 @@ public void WithAllocationId() // Validate TelemetryVariantPercentile Assert.Equal("True", config["feature_management:feature_flags:1:telemetry:enabled"]); - Assert.Equal("TelemetryVariant", config["feature_management:feature_flags:1:id"]); + Assert.Equal("TelemetryVariantPercentile", config["feature_management:feature_flags:1:id"]); Assert.Equal($"{TestHelpers.PrimaryConfigStoreEndpoint}kv/{FeatureManagementConstants.FeatureFlagMarker}TelemetryVariantPercentile?label=label", config["feature_management:feature_flags:1:telemetry:metadata:FeatureFlagReference"]); From c191d7f5666b11ba20156932ddff9c50f88c4a00 Mon Sep 17 00:00:00 2001 From: Sami Sadfa Date: Mon, 21 Oct 2024 15:58:15 -0700 Subject: [PATCH 6/7] add more values --- .../FeatureManagement/JsonElementExtensions.cs | 7 +++++-- .../Tests.AzureAppConfiguration/FeatureManagementTests.cs | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs index a4be3b2a..88fb4d5c 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs @@ -60,11 +60,14 @@ private static void WriteElementWithSortedKeys(JsonElement element, Utf8JsonWrit { writer.WriteNumberValue(longValue); } - else + else if (element.TryGetDecimal(out decimal decimalValue)) + { + writer.WriteNumberValue(element.GetDecimal()); + } + else if (element.TryGetDouble(out double doubleValue)) { writer.WriteNumberValue(element.GetDouble()); } - break; case JsonValueKind.True: diff --git a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs index 235aec84..7e49e8ab 100644 --- a/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs +++ b/tests/Tests.AzureAppConfiguration/FeatureManagementTests.cs @@ -661,7 +661,10 @@ public class FeatureManagementTests ""someOtherKey"": { ""someSubKey"": ""someSubValue"" }, - ""someKey"": ""someValue"" + ""someKey4"": [3, 1, 4, true], + ""someKey"": ""someValue"", + ""someKey3"": 3.14, + ""someKey2"": 3 } } ], @@ -2123,7 +2126,7 @@ public void WithAllocationId() Assert.Equal($"{TestHelpers.PrimaryConfigStoreEndpoint}kv/{FeatureManagementConstants.FeatureFlagMarker}TelemetryVariantPercentile?label=label", config["feature_management:feature_flags:1:telemetry:metadata:FeatureFlagReference"]); - Assert.Equal("SCq5T7Bb1k6tWfWjI3qz", config["feature_management:feature_flags:1:telemetry:metadata:AllocationId"]); + Assert.Equal("YsdJ4pQpmhYa8KEhRLUn", config["feature_management:feature_flags:1:telemetry:metadata:AllocationId"]); // Validate Greeting Assert.Equal("True", config["feature_management:feature_flags:2:telemetry:enabled"]); From 603cd0f9901b7714d5ae89385d24433330111ee2 Mon Sep 17 00:00:00 2001 From: Sami Sadfa Date: Mon, 21 Oct 2024 16:09:15 -0700 Subject: [PATCH 7/7] dotnet format --- .../FeatureManagement/JsonElementExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs index 88fb4d5c..fc7f8b26 100644 --- a/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs +++ b/src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/JsonElementExtensions.cs @@ -68,6 +68,7 @@ private static void WriteElementWithSortedKeys(JsonElement element, Utf8JsonWrit { writer.WriteNumberValue(element.GetDouble()); } + break; case JsonValueKind.True: