Skip to content

Commit 646a580

Browse files
zhiyuanliang/remove-allocation-id-from-main (#176)
1 parent c79f4bd commit 646a580

File tree

3 files changed

+0
-205
lines changed

3 files changed

+0
-205
lines changed

src/AzureAppConfigurationImpl.ts

Lines changed: 0 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { IKeyValueAdapter } from "./IKeyValueAdapter.js";
99
import { JsonKeyValueAdapter } from "./JsonKeyValueAdapter.js";
1010
import { DEFAULT_REFRESH_INTERVAL_IN_MS, MIN_REFRESH_INTERVAL_IN_MS } from "./RefreshOptions.js";
1111
import { Disposable } from "./common/disposable.js";
12-
import { base64Helper, jsonSorter } from "./common/utils.js";
1312
import {
1413
FEATURE_FLAGS_KEY_NAME,
1514
FEATURE_MANAGEMENT_KEY_NAME,
@@ -20,16 +19,9 @@ import {
2019
ETAG_KEY_NAME,
2120
FEATURE_FLAG_ID_KEY_NAME,
2221
FEATURE_FLAG_REFERENCE_KEY_NAME,
23-
ALLOCATION_ID_KEY_NAME,
2422
ALLOCATION_KEY_NAME,
25-
DEFAULT_WHEN_ENABLED_KEY_NAME,
26-
PERCENTILE_KEY_NAME,
27-
FROM_KEY_NAME,
28-
TO_KEY_NAME,
2923
SEED_KEY_NAME,
30-
VARIANT_KEY_NAME,
3124
VARIANTS_KEY_NAME,
32-
CONFIGURATION_VALUE_KEY_NAME,
3325
CONDITIONS_KEY_NAME,
3426
CLIENT_FILTERS_KEY_NAME
3527
} from "./featureManagement/constants.js";
@@ -677,15 +669,10 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
677669

678670
if (featureFlag[TELEMETRY_KEY_NAME] && featureFlag[TELEMETRY_KEY_NAME][ENABLED_KEY_NAME] === true) {
679671
const metadata = featureFlag[TELEMETRY_KEY_NAME][METADATA_KEY_NAME];
680-
let allocationId = "";
681-
if (featureFlag[ALLOCATION_KEY_NAME] !== undefined) {
682-
allocationId = await this.#generateAllocationId(featureFlag);
683-
}
684672
featureFlag[TELEMETRY_KEY_NAME][METADATA_KEY_NAME] = {
685673
[ETAG_KEY_NAME]: setting.etag,
686674
[FEATURE_FLAG_ID_KEY_NAME]: await this.#calculateFeatureFlagId(setting),
687675
[FEATURE_FLAG_REFERENCE_KEY_NAME]: this.#createFeatureFlagReference(setting),
688-
...(allocationId !== "" && { [ALLOCATION_ID_KEY_NAME]: allocationId }),
689676
...(metadata || {})
690677
};
691678
}
@@ -769,116 +756,6 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
769756
}
770757
return featureFlagReference;
771758
}
772-
773-
async #generateAllocationId(featureFlag: any): Promise<string> {
774-
let rawAllocationId = "";
775-
// Only default variant when enabled and variants allocated by percentile involve in the experimentation
776-
// The allocation id is genearted from default variant when enabled and percentile allocation
777-
const variantsForExperimentation: string[] = [];
778-
779-
rawAllocationId += `seed=${featureFlag[ALLOCATION_KEY_NAME][SEED_KEY_NAME] ?? ""}\ndefault_when_enabled=`;
780-
781-
if (featureFlag[ALLOCATION_KEY_NAME][DEFAULT_WHEN_ENABLED_KEY_NAME]) {
782-
variantsForExperimentation.push(featureFlag[ALLOCATION_KEY_NAME][DEFAULT_WHEN_ENABLED_KEY_NAME]);
783-
rawAllocationId += `${featureFlag[ALLOCATION_KEY_NAME][DEFAULT_WHEN_ENABLED_KEY_NAME]}`;
784-
}
785-
786-
rawAllocationId += "\npercentiles=";
787-
788-
const percentileList = featureFlag[ALLOCATION_KEY_NAME][PERCENTILE_KEY_NAME];
789-
if (percentileList) {
790-
const sortedPercentileList = percentileList
791-
.filter(p =>
792-
(p[FROM_KEY_NAME] !== undefined) &&
793-
(p[TO_KEY_NAME] !== undefined) &&
794-
(p[VARIANT_KEY_NAME] !== undefined) &&
795-
(p[FROM_KEY_NAME] !== p[TO_KEY_NAME]))
796-
.sort((a, b) => a[FROM_KEY_NAME] - b[FROM_KEY_NAME]);
797-
798-
const percentileAllocation: string[] = [];
799-
for (const percentile of sortedPercentileList) {
800-
variantsForExperimentation.push(percentile[VARIANT_KEY_NAME]);
801-
percentileAllocation.push(`${percentile[FROM_KEY_NAME]},${base64Helper(percentile[VARIANT_KEY_NAME])},${percentile[TO_KEY_NAME]}`);
802-
}
803-
rawAllocationId += percentileAllocation.join(";");
804-
}
805-
806-
if (variantsForExperimentation.length === 0 && featureFlag[ALLOCATION_KEY_NAME][SEED_KEY_NAME] === undefined) {
807-
// All fields required for generating allocation id are missing, short-circuit and return empty string
808-
return "";
809-
}
810-
811-
rawAllocationId += "\nvariants=";
812-
813-
if (variantsForExperimentation.length !== 0) {
814-
const variantsList = featureFlag[VARIANTS_KEY_NAME];
815-
if (variantsList) {
816-
const sortedVariantsList = variantsList
817-
.filter(v =>
818-
(v[NAME_KEY_NAME] !== undefined) &&
819-
variantsForExperimentation.includes(v[NAME_KEY_NAME]))
820-
.sort((a, b) => (a.name > b.name ? 1 : -1));
821-
822-
const variantConfiguration: string[] = [];
823-
for (const variant of sortedVariantsList) {
824-
const configurationValue = JSON.stringify(variant[CONFIGURATION_VALUE_KEY_NAME], jsonSorter) ?? "";
825-
variantConfiguration.push(`${base64Helper(variant[NAME_KEY_NAME])},${configurationValue}`);
826-
}
827-
rawAllocationId += variantConfiguration.join(";");
828-
}
829-
}
830-
831-
let crypto;
832-
833-
// Check for browser environment
834-
if (typeof window !== "undefined" && window.crypto && window.crypto.subtle) {
835-
crypto = window.crypto;
836-
}
837-
// Check for Node.js environment
838-
else if (typeof global !== "undefined" && global.crypto) {
839-
crypto = global.crypto;
840-
}
841-
// Fallback to native Node.js crypto module
842-
else {
843-
try {
844-
if (typeof module !== "undefined" && module.exports) {
845-
crypto = require("crypto");
846-
}
847-
else {
848-
crypto = await import("crypto");
849-
}
850-
} catch (error) {
851-
console.error("Failed to load the crypto module:", error.message);
852-
throw error;
853-
}
854-
}
855-
856-
// Convert to UTF-8 encoded bytes
857-
const data = new TextEncoder().encode(rawAllocationId);
858-
859-
// In the browser, use crypto.subtle.digest
860-
if (crypto.subtle) {
861-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
862-
const hashArray = new Uint8Array(hashBuffer);
863-
864-
// Only use the first 15 bytes
865-
const first15Bytes = hashArray.slice(0, 15);
866-
867-
// btoa/atob is also available in Node.js 18+
868-
const base64String = btoa(String.fromCharCode(...first15Bytes));
869-
const base64urlString = base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
870-
return base64urlString;
871-
}
872-
// In Node.js, use the crypto module's hash function
873-
else {
874-
const hash = crypto.createHash("sha256").update(data).digest();
875-
876-
// Only use the first 15 bytes
877-
const first15Bytes = hash.slice(0, 15);
878-
879-
return first15Bytes.toString("base64url");
880-
}
881-
}
882759
}
883760

884761
function getValidSelectors(selectors: SettingSelector[]): SettingSelector[] {

src/common/utils.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
export function base64Helper(str: string): string {
5-
const bytes = new TextEncoder().encode(str); // UTF-8 encoding
6-
let chars = "";
7-
for (let i = 0; i < bytes.length; i++) {
8-
chars += String.fromCharCode(bytes[i]);
9-
}
10-
return btoa(chars);
11-
}
12-
13-
export function jsonSorter(key, value) {
14-
if (value === null) {
15-
return null;
16-
}
17-
if (Array.isArray(value)) {
18-
return value;
19-
}
20-
if (typeof value === "object") {
21-
return Object.fromEntries(Object.entries(value).sort());
22-
}
23-
return value;
24-
}
25-
264
export function shuffleList<T>(array: T[]): T[] {
275
for (let i = array.length - 1; i > 0; i--) {
286
const j = Math.floor(Math.random() * (i + 1));

test/featureFlag.test.ts

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -339,64 +339,4 @@ describe("feature flags", function () {
339339
expect(featureFlag.telemetry.metadata.FeatureFlagId).equals("Rc8Am7HIGDT7HC5Ovs3wKN_aGaaK_Uz1mH2e11gaK0o");
340340
expect(featureFlag.telemetry.metadata.FeatureFlagReference).equals(`${createMockedEndpoint()}/kv/.appconfig.featureflag/Telemetry_2?label=Test`);
341341
});
342-
343-
it("should not populate allocation id", async () => {
344-
const connectionString = createMockedConnectionString();
345-
const settings = await load(connectionString, {
346-
featureFlagOptions: {
347-
enabled: true,
348-
selectors: [ { keyFilter: "*" } ]
349-
}
350-
});
351-
expect(settings).not.undefined;
352-
expect(settings.get("feature_management")).not.undefined;
353-
const featureFlags = settings.get<any>("feature_management").feature_flags;
354-
expect(featureFlags).not.undefined;
355-
356-
const NoPercentileAndSeed = (featureFlags as any[]).find(item => item.id === "NoPercentileAndSeed");
357-
expect(NoPercentileAndSeed).not.undefined;
358-
expect(NoPercentileAndSeed?.telemetry.metadata.AllocationId).to.be.undefined;
359-
});
360-
361-
it("should populate allocation id", async () => {
362-
const connectionString = createMockedConnectionString();
363-
const settings = await load(connectionString, {
364-
featureFlagOptions: {
365-
enabled: true,
366-
selectors: [ { keyFilter: "*" } ]
367-
}
368-
});
369-
expect(settings).not.undefined;
370-
expect(settings.get("feature_management")).not.undefined;
371-
const featureFlags = settings.get<any>("feature_management").feature_flags;
372-
expect(featureFlags).not.undefined;
373-
374-
const SeedOnly = (featureFlags as any[]).find(item => item.id === "SeedOnly");
375-
expect(SeedOnly).not.undefined;
376-
expect(SeedOnly?.telemetry.metadata.AllocationId).equals("qZApcKdfXscxpgn_8CMf");
377-
378-
const DefaultWhenEnabledOnly = (featureFlags as any[]).find(item => item.id === "DefaultWhenEnabledOnly");
379-
expect(DefaultWhenEnabledOnly).not.undefined;
380-
expect(DefaultWhenEnabledOnly?.telemetry.metadata.AllocationId).equals("k486zJjud_HkKaL1C4qB");
381-
382-
const PercentileOnly = (featureFlags as any[]).find(item => item.id === "PercentileOnly");
383-
expect(PercentileOnly).not.undefined;
384-
expect(PercentileOnly?.telemetry.metadata.AllocationId).equals("5YUbmP0P5s47zagO_LvI");
385-
386-
const SimpleConfigurationValue = (featureFlags as any[]).find(item => item.id === "SimpleConfigurationValue");
387-
expect(SimpleConfigurationValue).not.undefined;
388-
expect(SimpleConfigurationValue?.telemetry.metadata.AllocationId).equals("QIOEOTQJr2AXo4dkFFqy");
389-
390-
const ComplexConfigurationValue = (featureFlags as any[]).find(item => item.id === "ComplexConfigurationValue");
391-
expect(ComplexConfigurationValue).not.undefined;
392-
expect(ComplexConfigurationValue?.telemetry.metadata.AllocationId).equals("4Bes0AlwuO8kYX-YkBWs");
393-
394-
const TelemetryVariantPercentile = (featureFlags as any[]).find(item => item.id === "TelemetryVariantPercentile");
395-
expect(TelemetryVariantPercentile).not.undefined;
396-
expect(TelemetryVariantPercentile?.telemetry.metadata.AllocationId).equals("YsdJ4pQpmhYa8KEhRLUn");
397-
398-
const Complete = (featureFlags as any[]).find(item => item.id === "Complete");
399-
expect(Complete).not.undefined;
400-
expect(Complete?.telemetry.metadata.AllocationId).equals("DER2rF-ZYog95c4CBZoi");
401-
});
402342
});

0 commit comments

Comments
 (0)