|
5 | 5 | using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
|
6 | 6 | using System;
|
7 | 7 | using System.Collections.Generic;
|
| 8 | +using System.Diagnostics; |
8 | 9 | using System.Linq;
|
9 | 10 | using System.Security.Cryptography;
|
10 | 11 | using System.Text;
|
@@ -319,12 +320,98 @@ private List<KeyValuePair<string, string>> ProcessMicrosoftSchemaFeatureFlag(Fea
|
319 | 320 | keyValues.Add(new KeyValuePair<string, string>($"{telemetryPath}:{FeatureManagementConstants.Metadata}:{FeatureManagementConstants.ETag}", setting.ETag.ToString()));
|
320 | 321 |
|
321 | 322 | keyValues.Add(new KeyValuePair<string, string>($"{telemetryPath}:{FeatureManagementConstants.Enabled}", telemetry.Enabled.ToString()));
|
| 323 | + |
| 324 | + if (featureFlag.Allocation != null) |
| 325 | + { |
| 326 | + string allocationId = CalculateAllocationId(featureFlag); |
| 327 | + |
| 328 | + if (allocationId != null) |
| 329 | + { |
| 330 | + keyValues.Add(new KeyValuePair<string, string>($"{telemetryPath}:{FeatureManagementConstants.Metadata}:{FeatureManagementConstants.AllocationId}", allocationId)); |
| 331 | + } |
| 332 | + } |
322 | 333 | }
|
323 | 334 | }
|
324 | 335 |
|
325 | 336 | return keyValues;
|
326 | 337 | }
|
327 | 338 |
|
| 339 | + private string CalculateAllocationId(FeatureFlag flag) |
| 340 | + { |
| 341 | + Debug.Assert(flag.Allocation != null); |
| 342 | + |
| 343 | + StringBuilder inputBuilder = new StringBuilder(); |
| 344 | + |
| 345 | + // Seed |
| 346 | + inputBuilder.Append($"seed={flag.Allocation.Seed ?? string.Empty}"); |
| 347 | + |
| 348 | + var allocatedVariants = new HashSet<string>(); |
| 349 | + |
| 350 | + // DefaultWhenEnabled |
| 351 | + if (flag.Allocation.DefaultWhenEnabled != null) |
| 352 | + { |
| 353 | + allocatedVariants.Add(flag.Allocation.DefaultWhenEnabled); |
| 354 | + } |
| 355 | + |
| 356 | + inputBuilder.Append($"\ndefault_when_enabled={flag.Allocation.DefaultWhenEnabled ?? string.Empty}"); |
| 357 | + |
| 358 | + // Percentiles |
| 359 | + inputBuilder.Append("\npercentiles="); |
| 360 | + |
| 361 | + if (flag.Allocation.Percentile != null && flag.Allocation.Percentile.Any()) |
| 362 | + { |
| 363 | + IEnumerable<FeaturePercentileAllocation> sortedPercentiles = flag.Allocation.Percentile |
| 364 | + .Where(p => p.From != p.To) |
| 365 | + .OrderBy(p => p.From) |
| 366 | + .ToList(); |
| 367 | + |
| 368 | + allocatedVariants.UnionWith(sortedPercentiles.Select(p => p.Variant)); |
| 369 | + |
| 370 | + inputBuilder.Append(string.Join(";", sortedPercentiles.Select(p => $"{p.From},{p.Variant.ToBase64String()},{p.To}"))); |
| 371 | + } |
| 372 | + |
| 373 | + // If there's no custom seed and no variants allocated, stop now and return null |
| 374 | + if (flag.Allocation.Seed == null && |
| 375 | + !allocatedVariants.Any()) |
| 376 | + { |
| 377 | + return null; |
| 378 | + } |
| 379 | + |
| 380 | + // Variants |
| 381 | + inputBuilder.Append("\nvariants="); |
| 382 | + |
| 383 | + if (allocatedVariants.Any() && flag.Variants != null && flag.Variants.Any()) |
| 384 | + { |
| 385 | + IEnumerable<FeatureVariant> sortedVariants = flag.Variants |
| 386 | + .Where(variant => allocatedVariants.Contains(variant.Name)) |
| 387 | + .OrderBy(variant => variant.Name) |
| 388 | + .ToList(); |
| 389 | + |
| 390 | + inputBuilder.Append(string.Join(";", sortedVariants.Select(v => |
| 391 | + { |
| 392 | + var variantValue = string.Empty; |
| 393 | + |
| 394 | + if (v.ConfigurationValue.ValueKind != JsonValueKind.Null && v.ConfigurationValue.ValueKind != JsonValueKind.Undefined) |
| 395 | + { |
| 396 | + variantValue = v.ConfigurationValue.SerializeWithSortedKeys(); |
| 397 | + } |
| 398 | + |
| 399 | + return $"{v.Name.ToBase64String()},{(variantValue)}"; |
| 400 | + }))); |
| 401 | + } |
| 402 | + |
| 403 | + // Example input string |
| 404 | + // input == "seed=123abc\ndefault_when_enabled=Control\npercentiles=0,Blshdk,20;20,Test,100\nvariants=TdLa,standard;Qfcd,special" |
| 405 | + string input = inputBuilder.ToString(); |
| 406 | + |
| 407 | + using (SHA256 sha256 = SHA256.Create()) |
| 408 | + { |
| 409 | + byte[] truncatedHash = new byte[15]; |
| 410 | + Array.Copy(sha256.ComputeHash(Encoding.UTF8.GetBytes(input)), truncatedHash, 15); |
| 411 | + return truncatedHash.ToBase64Url(); |
| 412 | + } |
| 413 | + } |
| 414 | + |
328 | 415 | private FormatException CreateFeatureFlagFormatException(string jsonPropertyName, string settingKey, string foundJsonValueKind, string expectedJsonValueKind)
|
329 | 416 | {
|
330 | 417 | return new FormatException(string.Format(
|
|
0 commit comments