Skip to content

Commit 48e1147

Browse files
Merge branch 'main' of https://github.com/Azure/AppConfiguration-JavaScriptProvider into zhiyuanliang/startup-timeout
2 parents 1a10c89 + 8a7a686 commit 48e1147

File tree

6 files changed

+4
-209
lines changed

6 files changed

+4
-209
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@azure/app-configuration-provider",
3-
"version": "2.0.0",
3+
"version": "2.0.1",
44
"description": "The JavaScript configuration provider for Azure App Configuration",
55
"main": "dist/index.js",
66
"module": "./dist-esm/index.js",

src/AzureAppConfigurationImpl.ts

Lines changed: 0 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { JsonKeyValueAdapter } from "./JsonKeyValueAdapter.js";
1010
import { DEFAULT_STARTUP_TIMEOUT_IN_MS } from "./StartupOptions.js";
1111
import { DEFAULT_REFRESH_INTERVAL_IN_MS, MIN_REFRESH_INTERVAL_IN_MS } from "./refresh/refreshOptions.js";
1212
import { Disposable } from "./common/disposable.js";
13-
import { base64Helper, jsonSorter } from "./common/utils.js";
1413
import {
1514
FEATURE_FLAGS_KEY_NAME,
1615
FEATURE_MANAGEMENT_KEY_NAME,
@@ -21,16 +20,9 @@ import {
2120
ETAG_KEY_NAME,
2221
FEATURE_FLAG_ID_KEY_NAME,
2322
FEATURE_FLAG_REFERENCE_KEY_NAME,
24-
ALLOCATION_ID_KEY_NAME,
2523
ALLOCATION_KEY_NAME,
26-
DEFAULT_WHEN_ENABLED_KEY_NAME,
27-
PERCENTILE_KEY_NAME,
28-
FROM_KEY_NAME,
29-
TO_KEY_NAME,
3024
SEED_KEY_NAME,
31-
VARIANT_KEY_NAME,
3225
VARIANTS_KEY_NAME,
33-
CONFIGURATION_VALUE_KEY_NAME,
3426
CONDITIONS_KEY_NAME,
3527
CLIENT_FILTERS_KEY_NAME
3628
} from "./featureManagement/constants.js";
@@ -746,15 +738,10 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
746738

747739
if (featureFlag[TELEMETRY_KEY_NAME] && featureFlag[TELEMETRY_KEY_NAME][ENABLED_KEY_NAME] === true) {
748740
const metadata = featureFlag[TELEMETRY_KEY_NAME][METADATA_KEY_NAME];
749-
let allocationId = "";
750-
if (featureFlag[ALLOCATION_KEY_NAME] !== undefined) {
751-
allocationId = await this.#generateAllocationId(featureFlag);
752-
}
753741
featureFlag[TELEMETRY_KEY_NAME][METADATA_KEY_NAME] = {
754742
[ETAG_KEY_NAME]: setting.etag,
755743
[FEATURE_FLAG_ID_KEY_NAME]: await this.#calculateFeatureFlagId(setting),
756744
[FEATURE_FLAG_REFERENCE_KEY_NAME]: this.#createFeatureFlagReference(setting),
757-
...(allocationId !== "" && { [ALLOCATION_ID_KEY_NAME]: allocationId }),
758745
...(metadata || {})
759746
};
760747
}
@@ -838,116 +825,6 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
838825
}
839826
return featureFlagReference;
840827
}
841-
842-
async #generateAllocationId(featureFlag: any): Promise<string> {
843-
let rawAllocationId = "";
844-
// Only default variant when enabled and variants allocated by percentile involve in the experimentation
845-
// The allocation id is genearted from default variant when enabled and percentile allocation
846-
const variantsForExperimentation: string[] = [];
847-
848-
rawAllocationId += `seed=${featureFlag[ALLOCATION_KEY_NAME][SEED_KEY_NAME] ?? ""}\ndefault_when_enabled=`;
849-
850-
if (featureFlag[ALLOCATION_KEY_NAME][DEFAULT_WHEN_ENABLED_KEY_NAME]) {
851-
variantsForExperimentation.push(featureFlag[ALLOCATION_KEY_NAME][DEFAULT_WHEN_ENABLED_KEY_NAME]);
852-
rawAllocationId += `${featureFlag[ALLOCATION_KEY_NAME][DEFAULT_WHEN_ENABLED_KEY_NAME]}`;
853-
}
854-
855-
rawAllocationId += "\npercentiles=";
856-
857-
const percentileList = featureFlag[ALLOCATION_KEY_NAME][PERCENTILE_KEY_NAME];
858-
if (percentileList) {
859-
const sortedPercentileList = percentileList
860-
.filter(p =>
861-
(p[FROM_KEY_NAME] !== undefined) &&
862-
(p[TO_KEY_NAME] !== undefined) &&
863-
(p[VARIANT_KEY_NAME] !== undefined) &&
864-
(p[FROM_KEY_NAME] !== p[TO_KEY_NAME]))
865-
.sort((a, b) => a[FROM_KEY_NAME] - b[FROM_KEY_NAME]);
866-
867-
const percentileAllocation: string[] = [];
868-
for (const percentile of sortedPercentileList) {
869-
variantsForExperimentation.push(percentile[VARIANT_KEY_NAME]);
870-
percentileAllocation.push(`${percentile[FROM_KEY_NAME]},${base64Helper(percentile[VARIANT_KEY_NAME])},${percentile[TO_KEY_NAME]}`);
871-
}
872-
rawAllocationId += percentileAllocation.join(";");
873-
}
874-
875-
if (variantsForExperimentation.length === 0 && featureFlag[ALLOCATION_KEY_NAME][SEED_KEY_NAME] === undefined) {
876-
// All fields required for generating allocation id are missing, short-circuit and return empty string
877-
return "";
878-
}
879-
880-
rawAllocationId += "\nvariants=";
881-
882-
if (variantsForExperimentation.length !== 0) {
883-
const variantsList = featureFlag[VARIANTS_KEY_NAME];
884-
if (variantsList) {
885-
const sortedVariantsList = variantsList
886-
.filter(v =>
887-
(v[NAME_KEY_NAME] !== undefined) &&
888-
variantsForExperimentation.includes(v[NAME_KEY_NAME]))
889-
.sort((a, b) => (a.name > b.name ? 1 : -1));
890-
891-
const variantConfiguration: string[] = [];
892-
for (const variant of sortedVariantsList) {
893-
const configurationValue = JSON.stringify(variant[CONFIGURATION_VALUE_KEY_NAME], jsonSorter) ?? "";
894-
variantConfiguration.push(`${base64Helper(variant[NAME_KEY_NAME])},${configurationValue}`);
895-
}
896-
rawAllocationId += variantConfiguration.join(";");
897-
}
898-
}
899-
900-
let crypto;
901-
902-
// Check for browser environment
903-
if (typeof window !== "undefined" && window.crypto && window.crypto.subtle) {
904-
crypto = window.crypto;
905-
}
906-
// Check for Node.js environment
907-
else if (typeof global !== "undefined" && global.crypto) {
908-
crypto = global.crypto;
909-
}
910-
// Fallback to native Node.js crypto module
911-
else {
912-
try {
913-
if (typeof module !== "undefined" && module.exports) {
914-
crypto = require("crypto");
915-
}
916-
else {
917-
crypto = await import("crypto");
918-
}
919-
} catch (error) {
920-
console.error("Failed to load the crypto module:", error.message);
921-
throw error;
922-
}
923-
}
924-
925-
// Convert to UTF-8 encoded bytes
926-
const data = new TextEncoder().encode(rawAllocationId);
927-
928-
// In the browser, use crypto.subtle.digest
929-
if (crypto.subtle) {
930-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
931-
const hashArray = new Uint8Array(hashBuffer);
932-
933-
// Only use the first 15 bytes
934-
const first15Bytes = hashArray.slice(0, 15);
935-
936-
// btoa/atob is also available in Node.js 18+
937-
const base64String = btoa(String.fromCharCode(...first15Bytes));
938-
const base64urlString = base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
939-
return base64urlString;
940-
}
941-
// In Node.js, use the crypto module's hash function
942-
else {
943-
const hash = crypto.createHash("sha256").update(data).digest();
944-
945-
// Only use the first 15 bytes
946-
const first15Bytes = hash.slice(0, 15);
947-
948-
return first15Bytes.toString("base64url");
949-
}
950-
}
951828
}
952829

953830
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));

src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
export const VERSION = "2.0.0";
4+
export const VERSION = "2.0.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)