Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/AzureAppConfigurationImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ 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";
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";

Expand All @@ -33,7 +33,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.
Expand Down Expand Up @@ -64,8 +64,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;
Expand Down Expand Up @@ -139,10 +139,11 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
keyFilter: selector.keyFilter,
labelFilter: selector.labelFilter
};

if (this.#requestTracingEnabled) {
listOptions.requestOptions = {
customHeaders: {
[CorrelationContextHeaderName]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted)
[CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(this.#options, this.#isInitialLoadCompleted)
}
}
}
Expand Down Expand Up @@ -348,7 +349,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)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/RefreshOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down
6 changes: 3 additions & 3 deletions src/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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));
}
Expand All @@ -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
Expand Down
16 changes: 8 additions & 8 deletions src/refresh/RefreshTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
50 changes: 25 additions & 25 deletions src/requestTracing/constants.ts
Original file line number Diff line number Diff line change
@@ -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";
64 changes: 32 additions & 32 deletions src/requestTracing/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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_AZURE_APP_CONFIGURATION_TRACING_DISABLED,
ENV_KEY,
HOST_TYPE_KEY,
HostType,
HostTypeKey,
KeyVaultConfiguredTag,
KubernetesEnvironmentVariable,
NodeJSDevEnvironmentVariableValue,
NodeJSEnvironmentVariable,
RequestTracingDisabledEnvironmentVariable,
KEY_VAULT_CONFIGURED_TAG,
KUBERNETES_ENV_VAR,
NODEJS_DEV_ENV_VAL,
NODEJS_ENV_VAR,
REQUEST_TYPE_KEY,
RequestType,
RequestTypeKey,
ServiceFabricEnvironmentVariable
SERVICE_FABRIC_ENV_VAR
} from "./constants";

// Utils
Expand All @@ -29,15 +29,15 @@ export function createCorrelationContextHeader(options: AzureAppConfigurationOpt
UsersKeyVault
*/
const keyValues = new Map<string, string | undefined>();
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);
}
}

Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/version.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

export const Version = "1.0.0-preview";
export const VERSION = "1.0.0-preview";