From 662fe74d9ccf777a9a7f7c592b408c686f1362a4 Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Wed, 5 Jun 2024 13:26:05 +0800 Subject: [PATCH 1/5] rename constants with UPPER_SNAKE_CASE --- src/AzureAppConfigurationImpl.ts | 6 +-- src/load.ts | 2 +- src/requestTracing/constants.ts | 50 ++++++++++++------------- src/requestTracing/utils.ts | 64 ++++++++++++++++---------------- src/version.ts | 2 +- 5 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/AzureAppConfigurationImpl.ts b/src/AzureAppConfigurationImpl.ts index 746fb959..821f093a 100644 --- a/src/AzureAppConfigurationImpl.ts +++ b/src/AzureAppConfigurationImpl.ts @@ -11,7 +11,7 @@ import { DefaultRefreshIntervalInMs, MinimumRefreshIntervalInMs } from "./Refres import { Disposable } from "./common/disposable"; import { AzureKeyVaultKeyValueAdapter } from "./keyvault/AzureKeyVaultKeyValueAdapter"; import { RefreshTimer } from "./refresh/RefreshTimer"; -import { CorrelationContextHeaderName } from "./requestTracing/constants"; +import { CORRELATION_CONTEXT_HEADER_NAME } from "./requestTracing/constants"; import { createCorrelationContextHeader, requestTracingEnabled } from "./requestTracing/utils"; import { KeyFilter, LabelFilter, SettingSelector } from "./types"; @@ -142,7 +142,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { if (this.#requestTracingEnabled) { listOptions.requestOptions = { customHeaders: { - [CorrelationContextHeaderName]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) + [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) } } } @@ -348,7 +348,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { if (this.#requestTracingEnabled) { options.requestOptions = { customHeaders: { - [CorrelationContextHeaderName]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) + [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) } } } diff --git a/src/load.ts b/src/load.ts index b98db7d4..3e18aeaf 100644 --- a/src/load.ts +++ b/src/load.ts @@ -84,7 +84,7 @@ function instanceOfTokenCredential(obj: unknown) { function getClientOptions(options?: AzureAppConfigurationOptions): AppConfigurationClientOptions | undefined { // user-agent - let userAgentPrefix = RequestTracing.UserAgentPrefix; // Default UA for JavaScript Provider + let userAgentPrefix = RequestTracing.USER_AGENT_PREFIX; // Default UA for JavaScript Provider const userAgentOptions = options?.clientOptions?.userAgentOptions; if (userAgentOptions?.userAgentPrefix) { userAgentPrefix = `${userAgentOptions.userAgentPrefix} ${userAgentPrefix}`; // Prepend if UA prefix specified by user diff --git a/src/requestTracing/constants.ts b/src/requestTracing/constants.ts index a08e36f4..be33aa5d 100644 --- a/src/requestTracing/constants.ts +++ b/src/requestTracing/constants.ts @@ -1,48 +1,48 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { Version } from "../version"; +import { VERSION } from "../version"; -export const RequestTracingDisabledEnvironmentVariable = "AZURE_APP_CONFIGURATION_TRACING_DISABLED"; +export const ENV_AZURE_APP_CONFIGURATION_TRACING_DISABLED = "AZURE_APP_CONFIGURATION_TRACING_DISABLED"; // User Agent -export const UserAgentPrefix = `javascript-appconfiguration-provider/${Version}`; +export const USER_AGENT_PREFIX = `javascript-appconfiguration-provider/${VERSION}`; // Correlation Context -export const CorrelationContextHeaderName = "Correlation-Context"; +export const CORRELATION_CONTEXT_HEADER_NAME = "Correlation-Context"; // Env -export const NodeJSEnvironmentVariable = "NODE_ENV"; -export const NodeJSDevEnvironmentVariableValue = "development"; -export const EnvironmentKey = "Env"; -export const DevEnvironmentValue = "Dev"; +export const NODEJS_ENV_VAR = "NODE_ENV"; +export const NODEJS_DEV_ENV_VAL = "development"; +export const ENV_KEY = "Env"; +export const DEV_ENV_VAL = "Dev"; // Host Type -export const HostTypeKey = "Host"; +export const HOST_TYPE_KEY = "Host"; export enum HostType { - AzureFunction = "AzureFunction", - AzureWebApp = "AzureWebApp", - ContainerApp = "ContainerApp", - Kubernetes = "Kubernetes", - ServiceFabric = "ServiceFabric", + AZURE_FUNCTION = "AzureFunction", + AZURE_WEB_APP = "AzureWebApp", + CONTAINER_APP = "ContainerApp", + KUBERNETES = "Kubernetes", + SERVICE_FABRIC = "ServiceFabric", // Client-side - Browser = "Web", - WebWorker = "WebWorker" + BROWSER = "Web", + WEB_WORKER = "WebWorker" } // Environment variables to identify Host type. -export const AzureFunctionEnvironmentVariable = "FUNCTIONS_EXTENSION_VERSION"; -export const AzureWebAppEnvironmentVariable = "WEBSITE_SITE_NAME"; -export const ContainerAppEnvironmentVariable = "CONTAINER_APP_NAME"; -export const KubernetesEnvironmentVariable = "KUBERNETES_PORT"; -export const ServiceFabricEnvironmentVariable = "Fabric_NodeName"; // See: https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-environment-variables-reference +export const AZURE_FUNCTION_ENV_VAR = "FUNCTIONS_EXTENSION_VERSION"; +export const AZURE_WEB_APP_ENV_VAR = "WEBSITE_SITE_NAME"; +export const CONTAINER_APP_ENV_VAR = "CONTAINER_APP_NAME"; +export const KUBERNETES_ENV_VAR = "KUBERNETES_PORT"; +export const SERVICE_FABRIC_ENV_VAR = "Fabric_NodeName"; // See: https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-environment-variables-reference // Request Type -export const RequestTypeKey = "RequestType"; +export const REQUEST_TYPE_KEY = "RequestType"; export enum RequestType { - Startup = "Startup", - Watch = "Watch" + STARTUP = "Startup", + WATCH = "Watch" } // Tag names -export const KeyVaultConfiguredTag = "UsesKeyVault"; +export const KEY_VAULT_CONFIGURED_TAG = "UsesKeyVault"; diff --git a/src/requestTracing/utils.ts b/src/requestTracing/utils.ts index 8966a308..dda38d13 100644 --- a/src/requestTracing/utils.ts +++ b/src/requestTracing/utils.ts @@ -3,21 +3,21 @@ import { AzureAppConfigurationOptions } from "../AzureAppConfigurationOptions"; import { - AzureFunctionEnvironmentVariable, - AzureWebAppEnvironmentVariable, - ContainerAppEnvironmentVariable, - DevEnvironmentValue, - EnvironmentKey, + AZURE_FUNCTION_ENV_VAR, + AZURE_WEB_APP_ENV_VAR, + CONTAINER_APP_ENV_VAR, + DEV_ENV_VAL, + ENV_KEY, HostType, - HostTypeKey, - KeyVaultConfiguredTag, - KubernetesEnvironmentVariable, - NodeJSDevEnvironmentVariableValue, - NodeJSEnvironmentVariable, - RequestTracingDisabledEnvironmentVariable, + HOST_TYPE_KEY, + KEY_VAULT_CONFIGURED_TAG, + KUBERNETES_ENV_VAR, + NODEJS_DEV_ENV_VAL, + NODEJS_ENV_VAR, + ENV_AZURE_APP_CONFIGURATION_TRACING_DISABLED, RequestType, - RequestTypeKey, - ServiceFabricEnvironmentVariable + REQUEST_TYPE_KEY, + SERVICE_FABRIC_ENV_VAR } from "./constants"; // Utils @@ -29,15 +29,15 @@ export function createCorrelationContextHeader(options: AzureAppConfigurationOpt UsersKeyVault */ const keyValues = new Map(); - keyValues.set(RequestTypeKey, isInitialLoadCompleted ? RequestType.Watch : RequestType.Startup); - keyValues.set(HostTypeKey, getHostType()); - keyValues.set(EnvironmentKey, isDevEnvironment() ? DevEnvironmentValue : undefined); + keyValues.set(REQUEST_TYPE_KEY, isInitialLoadCompleted ? RequestType.WATCH : RequestType.STARTUP); + keyValues.set(HOST_TYPE_KEY, getHostType()); + keyValues.set(ENV_KEY, isDevEnvironment() ? DEV_ENV_VAL : undefined); const tags: string[] = []; if (options?.keyVaultOptions) { const { credential, secretClients, secretResolver } = options.keyVaultOptions; if (credential !== undefined || secretClients?.length || secretResolver !== undefined) { - tags.push(KeyVaultConfiguredTag); + tags.push(KEY_VAULT_CONFIGURED_TAG); } } @@ -55,7 +55,7 @@ export function createCorrelationContextHeader(options: AzureAppConfigurationOpt } export function requestTracingEnabled(): boolean { - const requestTracingDisabledEnv = getEnvironmentVariable(RequestTracingDisabledEnvironmentVariable); + const requestTracingDisabledEnv = getEnvironmentVariable(ENV_AZURE_APP_CONFIGURATION_TRACING_DISABLED); const disabled = requestTracingDisabledEnv?.toLowerCase() === "true"; return !disabled; } @@ -71,27 +71,27 @@ function getEnvironmentVariable(name: string) { function getHostType(): string | undefined { let hostType: string | undefined; - if (getEnvironmentVariable(AzureFunctionEnvironmentVariable)) { - hostType = HostType.AzureFunction; - } else if (getEnvironmentVariable(AzureWebAppEnvironmentVariable)) { - hostType = HostType.AzureWebApp; - } else if (getEnvironmentVariable(ContainerAppEnvironmentVariable)) { - hostType = HostType.ContainerApp; - } else if (getEnvironmentVariable(KubernetesEnvironmentVariable)) { - hostType = HostType.Kubernetes; - } else if (getEnvironmentVariable(ServiceFabricEnvironmentVariable)) { - hostType = HostType.ServiceFabric; + if (getEnvironmentVariable(AZURE_FUNCTION_ENV_VAR)) { + hostType = HostType.AZURE_FUNCTION; + } else if (getEnvironmentVariable(AZURE_WEB_APP_ENV_VAR)) { + hostType = HostType.AZURE_WEB_APP; + } else if (getEnvironmentVariable(CONTAINER_APP_ENV_VAR)) { + hostType = HostType.CONTAINER_APP; + } else if (getEnvironmentVariable(KUBERNETES_ENV_VAR)) { + hostType = HostType.KUBERNETES; + } else if (getEnvironmentVariable(SERVICE_FABRIC_ENV_VAR)) { + hostType = HostType.SERVICE_FABRIC; } else if (isBrowser()) { - hostType = HostType.Browser; + hostType = HostType.BROWSER; } else if (isWebWorker()) { - hostType = HostType.WebWorker; + hostType = HostType.WEB_WORKER; } return hostType; } function isDevEnvironment(): boolean { - const envType = getEnvironmentVariable(NodeJSEnvironmentVariable); - if (NodeJSDevEnvironmentVariableValue === envType?.toLowerCase()) { + const envType = getEnvironmentVariable(NODEJS_ENV_VAR); + if (NODEJS_DEV_ENV_VAL === envType?.toLowerCase()) { return true; } return false; diff --git a/src/version.ts b/src/version.ts index 6da52662..694d8ff9 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,4 +1,4 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export const Version = "1.0.0-preview"; \ No newline at end of file +export const VERSION = "1.0.0-preview"; \ No newline at end of file From e85674d8f0129a00dfcbde87a6c680a54ffd5a30 Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Wed, 5 Jun 2024 13:51:19 +0800 Subject: [PATCH 2/5] extract listConfigurationSettings with trace --- src/AzureAppConfigurationImpl.ts | 15 ++++++--------- src/requestTracing/utils.ts | 27 ++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/AzureAppConfigurationImpl.ts b/src/AzureAppConfigurationImpl.ts index 821f093a..3826bec5 100644 --- a/src/AzureAppConfigurationImpl.ts +++ b/src/AzureAppConfigurationImpl.ts @@ -12,7 +12,7 @@ import { Disposable } from "./common/disposable"; import { AzureKeyVaultKeyValueAdapter } from "./keyvault/AzureKeyVaultKeyValueAdapter"; import { RefreshTimer } from "./refresh/RefreshTimer"; import { CORRELATION_CONTEXT_HEADER_NAME } from "./requestTracing/constants"; -import { createCorrelationContextHeader, requestTracingEnabled } from "./requestTracing/utils"; +import { createCorrelationContextHeader, listConfigurationSettingsWithTrace, requestTracingEnabled } from "./requestTracing/utils"; import { KeyFilter, LabelFilter, SettingSelector } from "./types"; export class AzureAppConfigurationImpl implements AzureAppConfiguration { @@ -139,15 +139,12 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { keyFilter: selector.keyFilter, labelFilter: selector.labelFilter }; - if (this.#requestTracingEnabled) { - listOptions.requestOptions = { - customHeaders: { - [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) - } - } - } - const settings = this.#client.listConfigurationSettings(listOptions); + const settings = listConfigurationSettingsWithTrace(this.#client, listOptions, { + requestTracingEnabled: this.#requestTracingEnabled, + initialLoadCompleted: this.#isInitialLoadCompleted, + appConfigOptions: this.#options + }); for await (const setting of settings) { if (!isFeatureFlag(setting)) { // exclude feature flags diff --git a/src/requestTracing/utils.ts b/src/requestTracing/utils.ts index dda38d13..e90ee984 100644 --- a/src/requestTracing/utils.ts +++ b/src/requestTracing/utils.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { AppConfigurationClient, ListConfigurationSettingsOptions } from "@azure/app-configuration"; import { AzureAppConfigurationOptions } from "../AzureAppConfigurationOptions"; import { AZURE_FUNCTION_ENV_VAR, @@ -17,10 +18,34 @@ import { ENV_AZURE_APP_CONFIGURATION_TRACING_DISABLED, RequestType, REQUEST_TYPE_KEY, - SERVICE_FABRIC_ENV_VAR + SERVICE_FABRIC_ENV_VAR, + CORRELATION_CONTEXT_HEADER_NAME } from "./constants"; // Utils +export function listConfigurationSettingsWithTrace( + client: AppConfigurationClient, + listOptions: ListConfigurationSettingsOptions, + requestTracingOptions: { + requestTracingEnabled: boolean; + initialLoadCompleted: boolean; + appConfigOptions: AzureAppConfigurationOptions | undefined; + }) { + + const { requestTracingEnabled, initialLoadCompleted, appConfigOptions } = requestTracingOptions; + + const actualListOptions = { ...listOptions }; + if (requestTracingEnabled) { + actualListOptions.requestOptions = { + customHeaders: { + [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted) + } + } + } + + return client.listConfigurationSettings(actualListOptions); +} + export function createCorrelationContextHeader(options: AzureAppConfigurationOptions | undefined, isInitialLoadCompleted: boolean): string { /* RequestType: 'Startup' during application starting up, 'Watch' after startup completed. From 1a753b2f309a011e45d1c7fb1b282b99c7812fc5 Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Wed, 5 Jun 2024 14:04:58 +0800 Subject: [PATCH 3/5] extract getConfigurationSetting with trace --- src/AzureAppConfigurationImpl.ts | 40 +++++++++++++++++--------------- src/requestTracing/utils.ts | 35 ++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/AzureAppConfigurationImpl.ts b/src/AzureAppConfigurationImpl.ts index 3826bec5..5e5bc684 100644 --- a/src/AzureAppConfigurationImpl.ts +++ b/src/AzureAppConfigurationImpl.ts @@ -11,8 +11,7 @@ import { DefaultRefreshIntervalInMs, MinimumRefreshIntervalInMs } from "./Refres import { Disposable } from "./common/disposable"; import { AzureKeyVaultKeyValueAdapter } from "./keyvault/AzureKeyVaultKeyValueAdapter"; import { RefreshTimer } from "./refresh/RefreshTimer"; -import { CORRELATION_CONTEXT_HEADER_NAME } from "./requestTracing/constants"; -import { createCorrelationContextHeader, listConfigurationSettingsWithTrace, requestTracingEnabled } from "./requestTracing/utils"; +import { getConfigurationSettingWithTrace, listConfigurationSettingsWithTrace, requestTracingEnabled } from "./requestTracing/utils"; import { KeyFilter, LabelFilter, SettingSelector } from "./types"; export class AzureAppConfigurationImpl implements AzureAppConfiguration { @@ -140,11 +139,14 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { labelFilter: selector.labelFilter }; - const settings = listConfigurationSettingsWithTrace(this.#client, listOptions, { - requestTracingEnabled: this.#requestTracingEnabled, - initialLoadCompleted: this.#isInitialLoadCompleted, - appConfigOptions: this.#options - }); + const settings = listConfigurationSettingsWithTrace( + { + requestTracingEnabled: this.#requestTracingEnabled, + initialLoadCompleted: this.#isInitialLoadCompleted, + appConfigOptions: this.#options + }, + this.#client, listOptions + ); for await (const setting of settings) { if (!isFeatureFlag(setting)) { // exclude feature flags @@ -170,7 +172,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { } else { // Send a request to retrieve key-value since it may be either not loaded or loaded with a different label or different casing const { key, label } = sentinel; - const response = await this.#getConfigurationSettingWithTrace({ key, label }); + const response = await this.#getConfigurationSetting({ key, label }); if (response) { sentinel.etag = response.etag; } else { @@ -266,7 +268,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { // try refresh if any of watched settings is changed. let needRefresh = false; for (const sentinel of this.#sentinels.values()) { - const response = await this.#getConfigurationSettingWithTrace(sentinel, { + const response = await this.#getConfigurationSetting(sentinel, { onlyIfChanged: true }); @@ -338,18 +340,18 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { return key; } - async #getConfigurationSettingWithTrace(configurationSettingId: ConfigurationSettingId, customOptions?: GetConfigurationSettingOptions): Promise { + /** + * Get a configuration setting by key and label. If the setting is not found, return undefine instead of throwing an error. + */ + async #getConfigurationSetting(configurationSettingId: ConfigurationSettingId, customOptions?: GetConfigurationSettingOptions): Promise { let response: GetConfigurationSettingResponse | undefined; try { - const options = { ...customOptions ?? {} }; - if (this.#requestTracingEnabled) { - options.requestOptions = { - customHeaders: { - [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) - } - } - } - response = await this.#client.getConfigurationSetting(configurationSettingId, options); + response = await getConfigurationSettingWithTrace({ + requestTracingEnabled: this.#requestTracingEnabled, + initialLoadCompleted: this.#isInitialLoadCompleted, + appConfigOptions: this.#options + }, this.#client, configurationSettingId, customOptions); + } catch (error) { if (error instanceof RestError && error.statusCode === 404) { response = undefined; diff --git a/src/requestTracing/utils.ts b/src/requestTracing/utils.ts index e90ee984..06751315 100644 --- a/src/requestTracing/utils.ts +++ b/src/requestTracing/utils.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { AppConfigurationClient, ListConfigurationSettingsOptions } from "@azure/app-configuration"; +import { AppConfigurationClient, ConfigurationSettingId, GetConfigurationSettingOptions, ListConfigurationSettingsOptions } from "@azure/app-configuration"; import { AzureAppConfigurationOptions } from "../AzureAppConfigurationOptions"; import { AZURE_FUNCTION_ENV_VAR, @@ -24,14 +24,14 @@ import { // Utils export function listConfigurationSettingsWithTrace( - client: AppConfigurationClient, - listOptions: ListConfigurationSettingsOptions, requestTracingOptions: { requestTracingEnabled: boolean; initialLoadCompleted: boolean; appConfigOptions: AzureAppConfigurationOptions | undefined; - }) { - + }, + client: AppConfigurationClient, + listOptions: ListConfigurationSettingsOptions +) { const { requestTracingEnabled, initialLoadCompleted, appConfigOptions } = requestTracingOptions; const actualListOptions = { ...listOptions }; @@ -46,6 +46,31 @@ export function listConfigurationSettingsWithTrace( return client.listConfigurationSettings(actualListOptions); } +export function getConfigurationSettingWithTrace( + requestTracingOptions: { + requestTracingEnabled: boolean; + initialLoadCompleted: boolean; + appConfigOptions: AzureAppConfigurationOptions | undefined; + }, + client: AppConfigurationClient, + configurationSettingId: ConfigurationSettingId, + getOptions?: GetConfigurationSettingOptions, +) { + const { requestTracingEnabled, initialLoadCompleted, appConfigOptions } = requestTracingOptions; + const actualGetOptions = { ...getOptions }; + + if (requestTracingEnabled) { + actualGetOptions.requestOptions = { + customHeaders: { + [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted) + } + } + } + + return client.getConfigurationSetting(configurationSettingId, actualGetOptions); + +} + export function createCorrelationContextHeader(options: AzureAppConfigurationOptions | undefined, isInitialLoadCompleted: boolean): string { /* RequestType: 'Startup' during application starting up, 'Watch' after startup completed. From a1fe61d15505d7b09551d1abf4571a088aac8f05 Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Wed, 5 Jun 2024 14:09:31 +0800 Subject: [PATCH 4/5] rename constants with UPPER_SNAKE_CASE --- src/AzureAppConfigurationImpl.ts | 8 ++++---- src/RefreshOptions.ts | 4 ++-- src/load.ts | 4 ++-- src/refresh/RefreshTimer.ts | 16 ++++++++-------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/AzureAppConfigurationImpl.ts b/src/AzureAppConfigurationImpl.ts index 5e5bc684..99ed7ca7 100644 --- a/src/AzureAppConfigurationImpl.ts +++ b/src/AzureAppConfigurationImpl.ts @@ -7,7 +7,7 @@ import { AzureAppConfiguration, ConfigurationObjectConstructionOptions } from ". import { AzureAppConfigurationOptions } from "./AzureAppConfigurationOptions"; import { IKeyValueAdapter } from "./IKeyValueAdapter"; import { JsonKeyValueAdapter } from "./JsonKeyValueAdapter"; -import { DefaultRefreshIntervalInMs, MinimumRefreshIntervalInMs } from "./RefreshOptions"; +import { DEFAULT_REFRESH_INTERVAL_IN_MS, MIN_REFRESH_INTERVAL_IN_MS } from "./RefreshOptions"; import { Disposable } from "./common/disposable"; import { AzureKeyVaultKeyValueAdapter } from "./keyvault/AzureKeyVaultKeyValueAdapter"; import { RefreshTimer } from "./refresh/RefreshTimer"; @@ -32,7 +32,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { #isInitialLoadCompleted: boolean = false; // Refresh - #refreshInterval: number = DefaultRefreshIntervalInMs; + #refreshInterval: number = DEFAULT_REFRESH_INTERVAL_IN_MS; #onRefreshListeners: Array<() => any> = []; /** * Aka watched settings. @@ -63,8 +63,8 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { // custom refresh interval if (refreshIntervalInMs !== undefined) { - if (refreshIntervalInMs < MinimumRefreshIntervalInMs) { - throw new Error(`The refresh interval cannot be less than ${MinimumRefreshIntervalInMs} milliseconds.`); + if (refreshIntervalInMs < MIN_REFRESH_INTERVAL_IN_MS) { + throw new Error(`The refresh interval cannot be less than ${MIN_REFRESH_INTERVAL_IN_MS} milliseconds.`); } else { this.#refreshInterval = refreshIntervalInMs; diff --git a/src/RefreshOptions.ts b/src/RefreshOptions.ts index fd17183c..12a7474c 100644 --- a/src/RefreshOptions.ts +++ b/src/RefreshOptions.ts @@ -3,8 +3,8 @@ import { WatchedSetting } from "./WatchedSetting"; -export const DefaultRefreshIntervalInMs = 30 * 1000; -export const MinimumRefreshIntervalInMs = 1 * 1000; +export const DEFAULT_REFRESH_INTERVAL_IN_MS = 30 * 1000; +export const MIN_REFRESH_INTERVAL_IN_MS = 1 * 1000; export interface RefreshOptions { /** diff --git a/src/load.ts b/src/load.ts index 3e18aeaf..d5559c07 100644 --- a/src/load.ts +++ b/src/load.ts @@ -8,7 +8,7 @@ import { AzureAppConfigurationImpl } from "./AzureAppConfigurationImpl"; import { AzureAppConfigurationOptions, MaxRetries, MaxRetryDelayInMs } from "./AzureAppConfigurationOptions"; import * as RequestTracing from "./requestTracing/constants"; -const MinDelayForUnhandedError: number = 5000; // 5 seconds +const MIN_DELAY_FOR_UNHANDLED_ERROR: number = 5000; // 5 seconds /** * Loads the data from Azure App Configuration service and returns an instance of AzureAppConfiguration. @@ -70,7 +70,7 @@ export async function load( // load() method is called in the application's startup code path. // Unhandled exceptions cause application crash which can result in crash loops as orchestrators attempt to restart the application. // Knowing the intended usage of the provider in startup code path, we mitigate back-to-back crash loops from overloading the server with requests by waiting a minimum time to propagate fatal errors. - const delay = MinDelayForUnhandedError - (Date.now() - startTimestamp); + const delay = MIN_DELAY_FOR_UNHANDLED_ERROR - (Date.now() - startTimestamp); if (delay > 0) { await new Promise((resolve) => setTimeout(resolve, delay)); } diff --git a/src/refresh/RefreshTimer.ts b/src/refresh/RefreshTimer.ts index 3ae824b1..ce485947 100644 --- a/src/refresh/RefreshTimer.ts +++ b/src/refresh/RefreshTimer.ts @@ -16,14 +16,14 @@ * - Because of the jitter, the maximum backoff time is actually `MaximumBackoffInMs` * (1 + `JitterRatio`). */ -const MinimumBackoffInMs = 30 * 1000; // 30s -const MaximumBackoffInMs = 10 * 60 * 1000; // 10min -const MaxSafeExponential = 30; // Used to avoid overflow. bitwise operations in JavaScript are limited to 32 bits. It overflows at 2^31 - 1. -const JitterRatio = 0.25; +const MIN_BACKOFF_IN_MS = 30 * 1000; // 30s +const MAX_BACKOFF_IN_MS = 10 * 60 * 1000; // 10min +const MAX_SAFE_EXPONENTIAL = 30; // Used to avoid overflow. bitwise operations in JavaScript are limited to 32 bits. It overflows at 2^31 - 1. +const JITTER_RATIO = 0.25; export class RefreshTimer { - #minBackoff: number = MinimumBackoffInMs; - #maxBackoff: number = MaximumBackoffInMs; + #minBackoff: number = MIN_BACKOFF_IN_MS; + #maxBackoff: number = MAX_BACKOFF_IN_MS; #failedAttempts: number = 0; #backoffEnd: number; // Timestamp #interval: number; @@ -70,14 +70,14 @@ export class RefreshTimer { } // exponential: minBackoffMs * 2^(failedAttempts-1) - const exponential = Math.min(this.#failedAttempts - 1, MaxSafeExponential); + const exponential = Math.min(this.#failedAttempts - 1, MAX_SAFE_EXPONENTIAL); let calculatedBackoffMs = minBackoffMs * (1 << exponential); if (calculatedBackoffMs > maxBackoffMs) { calculatedBackoffMs = maxBackoffMs; } // jitter: random value between [-1, 1) * jitterRatio * calculatedBackoffMs - const jitter = JitterRatio * (Math.random() * 2 - 1); + const jitter = JITTER_RATIO * (Math.random() * 2 - 1); return calculatedBackoffMs * (1 + jitter); } From 1151aa2402008dd1c10cb97a31fb5b2148f0cd27 Mon Sep 17 00:00:00 2001 From: Yan Zhang Date: Tue, 11 Jun 2024 12:20:10 +0800 Subject: [PATCH 5/5] remove unrelated changes --- src/AzureAppConfigurationImpl.ts | 44 ++++++++++++------------ src/requestTracing/utils.ts | 58 +++----------------------------- 2 files changed, 27 insertions(+), 75 deletions(-) diff --git a/src/AzureAppConfigurationImpl.ts b/src/AzureAppConfigurationImpl.ts index 99ed7ca7..2e88f461 100644 --- a/src/AzureAppConfigurationImpl.ts +++ b/src/AzureAppConfigurationImpl.ts @@ -11,7 +11,8 @@ import { DEFAULT_REFRESH_INTERVAL_IN_MS, MIN_REFRESH_INTERVAL_IN_MS } from "./Re import { Disposable } from "./common/disposable"; import { AzureKeyVaultKeyValueAdapter } from "./keyvault/AzureKeyVaultKeyValueAdapter"; import { RefreshTimer } from "./refresh/RefreshTimer"; -import { getConfigurationSettingWithTrace, listConfigurationSettingsWithTrace, requestTracingEnabled } from "./requestTracing/utils"; +import { CORRELATION_CONTEXT_HEADER_NAME } from "./requestTracing/constants"; +import { createCorrelationContextHeader, requestTracingEnabled } from "./requestTracing/utils"; import { KeyFilter, LabelFilter, SettingSelector } from "./types"; export class AzureAppConfigurationImpl implements AzureAppConfiguration { @@ -139,14 +140,15 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { labelFilter: selector.labelFilter }; - const settings = listConfigurationSettingsWithTrace( - { - requestTracingEnabled: this.#requestTracingEnabled, - initialLoadCompleted: this.#isInitialLoadCompleted, - appConfigOptions: this.#options - }, - this.#client, listOptions - ); + if (this.#requestTracingEnabled) { + listOptions.requestOptions = { + customHeaders: { + [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) + } + } + } + + const settings = this.#client.listConfigurationSettings(listOptions); for await (const setting of settings) { if (!isFeatureFlag(setting)) { // exclude feature flags @@ -172,7 +174,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { } else { // Send a request to retrieve key-value since it may be either not loaded or loaded with a different label or different casing const { key, label } = sentinel; - const response = await this.#getConfigurationSetting({ key, label }); + const response = await this.#getConfigurationSettingWithTrace({ key, label }); if (response) { sentinel.etag = response.etag; } else { @@ -268,7 +270,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { // try refresh if any of watched settings is changed. let needRefresh = false; for (const sentinel of this.#sentinels.values()) { - const response = await this.#getConfigurationSetting(sentinel, { + const response = await this.#getConfigurationSettingWithTrace(sentinel, { onlyIfChanged: true }); @@ -340,18 +342,18 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { return key; } - /** - * Get a configuration setting by key and label. If the setting is not found, return undefine instead of throwing an error. - */ - async #getConfigurationSetting(configurationSettingId: ConfigurationSettingId, customOptions?: GetConfigurationSettingOptions): Promise { + async #getConfigurationSettingWithTrace(configurationSettingId: ConfigurationSettingId, customOptions?: GetConfigurationSettingOptions): Promise { let response: GetConfigurationSettingResponse | undefined; try { - response = await getConfigurationSettingWithTrace({ - requestTracingEnabled: this.#requestTracingEnabled, - initialLoadCompleted: this.#isInitialLoadCompleted, - appConfigOptions: this.#options - }, this.#client, configurationSettingId, customOptions); - + const options = { ...customOptions ?? {} }; + if (this.#requestTracingEnabled) { + options.requestOptions = { + customHeaders: { + [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted) + } + } + } + response = await this.#client.getConfigurationSetting(configurationSettingId, options); } catch (error) { if (error instanceof RestError && error.statusCode === 404) { response = undefined; diff --git a/src/requestTracing/utils.ts b/src/requestTracing/utils.ts index 06751315..e82ffe52 100644 --- a/src/requestTracing/utils.ts +++ b/src/requestTracing/utils.ts @@ -1,76 +1,26 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { AppConfigurationClient, ConfigurationSettingId, GetConfigurationSettingOptions, ListConfigurationSettingsOptions } from "@azure/app-configuration"; import { AzureAppConfigurationOptions } from "../AzureAppConfigurationOptions"; import { AZURE_FUNCTION_ENV_VAR, AZURE_WEB_APP_ENV_VAR, CONTAINER_APP_ENV_VAR, DEV_ENV_VAL, + ENV_AZURE_APP_CONFIGURATION_TRACING_DISABLED, ENV_KEY, - HostType, HOST_TYPE_KEY, + HostType, KEY_VAULT_CONFIGURED_TAG, KUBERNETES_ENV_VAR, NODEJS_DEV_ENV_VAL, NODEJS_ENV_VAR, - ENV_AZURE_APP_CONFIGURATION_TRACING_DISABLED, - RequestType, REQUEST_TYPE_KEY, - SERVICE_FABRIC_ENV_VAR, - CORRELATION_CONTEXT_HEADER_NAME + RequestType, + SERVICE_FABRIC_ENV_VAR } from "./constants"; // Utils -export function listConfigurationSettingsWithTrace( - requestTracingOptions: { - requestTracingEnabled: boolean; - initialLoadCompleted: boolean; - appConfigOptions: AzureAppConfigurationOptions | undefined; - }, - client: AppConfigurationClient, - listOptions: ListConfigurationSettingsOptions -) { - const { requestTracingEnabled, initialLoadCompleted, appConfigOptions } = requestTracingOptions; - - const actualListOptions = { ...listOptions }; - if (requestTracingEnabled) { - actualListOptions.requestOptions = { - customHeaders: { - [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted) - } - } - } - - return client.listConfigurationSettings(actualListOptions); -} - -export function getConfigurationSettingWithTrace( - requestTracingOptions: { - requestTracingEnabled: boolean; - initialLoadCompleted: boolean; - appConfigOptions: AzureAppConfigurationOptions | undefined; - }, - client: AppConfigurationClient, - configurationSettingId: ConfigurationSettingId, - getOptions?: GetConfigurationSettingOptions, -) { - const { requestTracingEnabled, initialLoadCompleted, appConfigOptions } = requestTracingOptions; - const actualGetOptions = { ...getOptions }; - - if (requestTracingEnabled) { - actualGetOptions.requestOptions = { - customHeaders: { - [CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted) - } - } - } - - return client.getConfigurationSetting(configurationSettingId, actualGetOptions); - -} - export function createCorrelationContextHeader(options: AzureAppConfigurationOptions | undefined, isInitialLoadCompleted: boolean): string { /* RequestType: 'Startup' during application starting up, 'Watch' after startup completed.