Skip to content

Commit ab8e567

Browse files
fix bug that previous secret reference will override the same key loaded later when secret refresh is enabled
1 parent f9c6749 commit ab8e567

File tree

1 file changed

+15
-11
lines changed

1 file changed

+15
-11
lines changed

src/appConfigurationImpl.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -479,10 +479,12 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
479479
* If true, loads feature flag using the feature flag selectors;
480480
* If false, loads key-value using the key-value selectors. Defaults to false.
481481
*/
482-
async #loadConfigurationSettings(loadFeatureFlag: boolean = false): Promise<ConfigurationSetting[]> {
482+
async #loadConfigurationSettings(loadFeatureFlag: boolean = false): Promise<Map<string, ConfigurationSetting>> {
483483
const selectors = loadFeatureFlag ? this.#ffSelectors : this.#kvSelectors;
484484
const funcToExecute = async (client) => {
485-
const loadedSettings: ConfigurationSetting[] = [];
485+
// Use a Map to deduplicate configuration settings by key. When multiple selectors return settings with the same key,
486+
// the configuration setting loaded by the later selector in the iteration order will override the one from the earlier selector.
487+
const loadedSettings: Map<string, ConfigurationSetting> = new Map<string, ConfigurationSetting>();
486488
// deep copy selectors to avoid modification if current client fails
487489
const selectorsToUpdate = JSON.parse(
488490
JSON.stringify(selectors)
@@ -506,7 +508,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
506508
pageEtags.push(page.etag ?? "");
507509
for (const setting of page.items) {
508510
if (loadFeatureFlag === isFeatureFlag(setting)) {
509-
loadedSettings.push(setting);
511+
loadedSettings.set(setting.key, setting);
510512
}
511513
}
512514
}
@@ -528,7 +530,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
528530
for await (const page of pageIterator) {
529531
for (const setting of page.items) {
530532
if (loadFeatureFlag === isFeatureFlag(setting)) {
531-
loadedSettings.push(setting);
533+
loadedSettings.set(setting.key, setting);
532534
}
533535
}
534536
}
@@ -543,7 +545,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
543545
return loadedSettings;
544546
};
545547

546-
return await this.#executeWithFailoverPolicy(funcToExecute) as ConfigurationSetting[];
548+
return await this.#executeWithFailoverPolicy(funcToExecute) as Map<string, ConfigurationSetting>;
547549
}
548550

549551
/**
@@ -552,7 +554,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
552554
async #loadSelectedAndWatchedKeyValues() {
553555
this.#secretReferences = []; // clear all cached key vault reference configuration settings
554556
const keyValues: [key: string, value: unknown][] = [];
555-
const loadedSettings: ConfigurationSetting[] = await this.#loadConfigurationSettings();
557+
const loadedSettings: Map<string, ConfigurationSetting> = await this.#loadConfigurationSettings();
556558
if (this.#refreshEnabled && !this.#watchAll) {
557559
await this.#updateWatchedKeyValuesEtag(loadedSettings);
558560
}
@@ -562,7 +564,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
562564
this.#aiConfigurationTracing.reset();
563565
}
564566

565-
for (const setting of loadedSettings) {
567+
for (const setting of loadedSettings.values()) {
566568
if (isSecretReference(setting)) {
567569
this.#secretReferences.push(setting); // cache secret references for resolve/refresh secret separately
568570
continue;
@@ -587,9 +589,10 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
587589
/**
588590
* Updates etag of watched settings from loaded data. If a watched setting is not covered by any selector, a request will be sent to retrieve it.
589591
*/
590-
async #updateWatchedKeyValuesEtag(existingSettings: ConfigurationSetting[]): Promise<void> {
592+
async #updateWatchedKeyValuesEtag(existingSettings: Map<string, ConfigurationSetting>): Promise<void> {
591593
for (const sentinel of this.#sentinels) {
592-
const matchedSetting = existingSettings.find(s => s.key === sentinel.key && s.label === sentinel.label);
594+
// try to find matching setting by key and label from the map's values
595+
const matchedSetting = Array.from(existingSettings.values()).find(s => s.key === sentinel.key && s.label === sentinel.label);
593596
if (matchedSetting) {
594597
sentinel.etag = matchedSetting.etag;
595598
} else {
@@ -621,7 +624,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
621624
*/
622625
async #loadFeatureFlags() {
623626
const loadFeatureFlag = true;
624-
const featureFlagSettings: ConfigurationSetting[] = await this.#loadConfigurationSettings(loadFeatureFlag);
627+
const featureFlagSettings: Map<string, ConfigurationSetting> = await this.#loadConfigurationSettings(loadFeatureFlag);
625628

626629
if (this.#requestTracingEnabled && this.#featureFlagTracing !== undefined) {
627630
// Reset old feature flag tracing in order to track the information present in the current response from server.
@@ -630,7 +633,8 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
630633

631634
// parse feature flags
632635
const featureFlags = await Promise.all(
633-
featureFlagSettings.map(setting => this.#parseFeatureFlag(setting))
636+
featureFlagSettings.values()
637+
.map(setting => this.#parseFeatureFlag(setting))
634638
);
635639

636640
// feature_management is a reserved key, and feature_flags is an array of feature flags

0 commit comments

Comments
 (0)