Skip to content

Commit 2a8df74

Browse files
Merge branch 'main' of https://github.com/Azure/AppConfiguration-JavaScriptProvider into zhiyuanliang/secret-refresh
2 parents f70f491 + 986e3f0 commit 2a8df74

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

src/AzureAppConfigurationImpl.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
9191
#secretRefreshEnabled: boolean = false;
9292
#secretReferences: ConfigurationSetting[] = []; // cached key vault references
9393
#secretRefreshTimer: RefreshTimer;
94+
#resolveSecretInParallel: boolean = false;
95+
9496
/**
9597
* Selectors of key-values obtained from @see AzureAppConfigurationOptions.selectors
9698
*/
@@ -181,6 +183,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
181183
this.#secretRefreshEnabled = true;
182184
this.#secretRefreshTimer = new RefreshTimer(secretRefreshIntervalInMs);
183185
}
186+
this.#resolveSecretInParallel = options.keyVaultOptions.parallelSecretResolutionEnabled ?? false;
184187
}
185188
this.#adapters.push(new AzureKeyVaultKeyValueAdapter(options?.keyVaultOptions, this.#secretRefreshTimer));
186189
this.#adapters.push(new JsonKeyValueAdapter());
@@ -504,7 +507,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
504507
async #loadSelectedAndWatchedKeyValues() {
505508
this.#secretReferences = []; // clear all cached key vault reference configuration settings
506509
const keyValues: [key: string, value: unknown][] = [];
507-
const loadedSettings = await this.#loadConfigurationSettings();
510+
const loadedSettings: ConfigurationSetting[] = await this.#loadConfigurationSettings();
508511
if (this.#refreshEnabled && !this.#watchAll) {
509512
await this.#updateWatchedKeyValuesEtag(loadedSettings);
510513
}
@@ -514,14 +517,30 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
514517
this.#aiConfigurationTracing.reset();
515518
}
516519

517-
// adapt configuration settings to key-values
520+
const secretResolutionPromises: Promise<void>[] = [];
518521
for (const setting of loadedSettings) {
519-
if (this.#secretRefreshEnabled && isSecretReference(setting)) {
520-
this.#secretReferences.push(setting);
522+
if (isSecretReference(setting)) {
523+
if (this.#secretRefreshEnabled) {
524+
this.#secretReferences.push(setting);
525+
}
526+
if (this.#resolveSecretInParallel) {
527+
// secret references are resolved asynchronously to improve performance
528+
const secretResolutionPromise = this.#processKeyValue(setting)
529+
.then(([key, value]) => {
530+
keyValues.push([key, value]);
531+
});
532+
secretResolutionPromises.push(secretResolutionPromise);
533+
continue;
534+
}
521535
}
536+
// adapt configuration settings to key-values
522537
const [key, value] = await this.#processKeyValue(setting);
523538
keyValues.push([key, value]);
524539
}
540+
if (secretResolutionPromises.length > 0) {
541+
// wait for all secret resolution promises to be resolved
542+
await Promise.all(secretResolutionPromises);
543+
}
525544

526545
this.#clearLoadedKeyValues(); // clear existing key-values in case of configuration setting deletion
527546
for (const [k, v] of keyValues) {
@@ -566,7 +585,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
566585
*/
567586
async #loadFeatureFlags() {
568587
const loadFeatureFlag = true;
569-
const featureFlagSettings = await this.#loadConfigurationSettings(loadFeatureFlag);
588+
const featureFlagSettings: ConfigurationSetting[] = await this.#loadConfigurationSettings(loadFeatureFlag);
570589

571590
if (this.#requestTracingEnabled && this.#featureFlagTracing !== undefined) {
572591
// Reset old feature flag tracing in order to track the information present in the current response from server.

src/keyvault/KeyVaultOptions.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ export interface KeyVaultOptions {
3535
*/
3636
secretResolver?: (keyVaultReference: URL) => string | Promise<string>;
3737

38+
/**
39+
* Specifies whether to resolve the secret value in parallel.
40+
*
41+
* @remarks
42+
* If not specified, the default value is false.
43+
*/
44+
parallelSecretResolutionEnabled?: boolean;
45+
3846
/**
3947
* Specifies the refresh interval in milliseconds for periodically reloading secret from Key Vault.
4048
*

test/keyvault.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@ describe("key vault reference", function () {
127127
expect(settings.get("TestKey")).eq("SecretValue");
128128
expect(settings.get("TestKey2")).eq("SecretValue2");
129129
});
130+
131+
it("should resolve key vault reference in parallel", async () => {
132+
const settings = await load(createMockedConnectionString(), {
133+
keyVaultOptions: {
134+
credential: createMockedTokenCredential(),
135+
parallelSecretResolutionEnabled: true
136+
}
137+
});
138+
expect(settings).not.undefined;
139+
expect(settings.get("TestKey")).eq("SecretValue");
140+
expect(settings.get("TestKeyFixedVersion")).eq("OldSecretValue");
141+
});
130142
});
131143

132144
describe("key vault secret refresh", function () {

0 commit comments

Comments
 (0)