diff --git a/src/AzureAppConfigurationImpl.ts b/src/AzureAppConfigurationImpl.ts index 2e88f461..1ac77904 100644 --- a/src/AzureAppConfigurationImpl.ts +++ b/src/AzureAppConfigurationImpl.ts @@ -11,8 +11,7 @@ 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 { CORRELATION_CONTEXT_HEADER_NAME } from "./requestTracing/constants"; -import { createCorrelationContextHeader, requestTracingEnabled } from "./requestTracing/utils"; +import { getConfigurationSettingWithTrace, listConfigurationSettingsWithTrace, requestTracingEnabled } from "./requestTracing/utils"; import { KeyFilter, LabelFilter, SettingSelector } from "./types"; export class AzureAppConfigurationImpl implements AzureAppConfiguration { @@ -140,15 +139,16 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration { 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 requestTraceOptions = { + requestTracingEnabled: this.#requestTracingEnabled, + initialLoadCompleted: this.#isInitialLoadCompleted, + appConfigOptions: this.#options + }; + const settings = listConfigurationSettingsWithTrace( + requestTraceOptions, + this.#client, + listOptions + ); for await (const setting of settings) { if (!isFeatureFlag(setting)) { // exclude feature flags @@ -174,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.#getConfigurationSettingWithTrace({ key, label }); + const response = await this.#getConfigurationSetting({ key, label }); if (response) { sentinel.etag = response.etag; } else { @@ -270,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.#getConfigurationSettingWithTrace(sentinel, { + const response = await this.#getConfigurationSetting(sentinel, { onlyIfChanged: true }); @@ -342,18 +342,24 @@ 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); + const requestTraceOptions = { + requestTracingEnabled: this.#requestTracingEnabled, + initialLoadCompleted: this.#isInitialLoadCompleted, + appConfigOptions: this.#options + }; + response = await getConfigurationSettingWithTrace( + requestTraceOptions, + 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 e82ffe52..61dda15d 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, ConfigurationSettingId, GetConfigurationSettingOptions, ListConfigurationSettingsOptions } from "@azure/app-configuration"; import { AzureAppConfigurationOptions } from "../AzureAppConfigurationOptions"; import { AZURE_FUNCTION_ENV_VAR, @@ -17,10 +18,58 @@ import { NODEJS_ENV_VAR, REQUEST_TYPE_KEY, RequestType, - SERVICE_FABRIC_ENV_VAR + SERVICE_FABRIC_ENV_VAR, + CORRELATION_CONTEXT_HEADER_NAME } 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. @@ -115,4 +164,4 @@ function isWebWorker() { const importScriptsAsGlobalFunction = typeof importScripts === "function"; return workerGlobalScopeDefined && importScriptsAsGlobalFunction && isNavigatorDefinedAsExpected; -} \ No newline at end of file +}