Skip to content

Commit f60fd2a

Browse files
Merge branch 'main' of https://github.com/Azure/AppConfiguration-JavaScriptProvider into merge-main-to-preview
2 parents 0fd4a54 + 14f96fc commit f60fd2a

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/appConfigurationImpl.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -598,21 +598,19 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
598598
* 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.
599599
*/
600600
async #updateWatchedKeyValuesEtag(existingSettings: ConfigurationSetting[]): Promise<void> {
601+
const updatedSentinels: ConfigurationSettingId[] = [];
601602
for (const sentinel of this.#sentinels) {
602603
const matchedSetting = existingSettings.find(s => s.key === sentinel.key && s.label === sentinel.label);
603604
if (matchedSetting) {
604-
sentinel.etag = matchedSetting.etag;
605+
updatedSentinels.push( {...sentinel, etag: matchedSetting.etag} );
605606
} else {
606607
// Send a request to retrieve key-value since it may be either not loaded or loaded with a different label or different casing
607608
const { key, label } = sentinel;
608609
const response = await this.#getConfigurationSetting({ key, label });
609-
if (response) {
610-
sentinel.etag = response.etag;
611-
} else {
612-
sentinel.etag = undefined;
613-
}
610+
updatedSentinels.push( {...sentinel, etag: response?.etag} );
614611
}
615612
}
613+
this.#sentinels = updatedSentinels;
616614
}
617615

618616
/**
@@ -670,7 +668,6 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
670668
if (response?.statusCode === 200 // created or changed
671669
|| (response === undefined && sentinel.etag !== undefined) // deleted
672670
) {
673-
sentinel.etag = response?.etag;// update etag of the sentinel
674671
needRefresh = true;
675672
break;
676673
}

test/refresh.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,19 @@ function addSetting(key: string, value: any) {
2525
}
2626

2727
let listKvRequestCount = 0;
28+
let failNextListKv = false;
2829
const listKvCallback = () => {
30+
if (failNextListKv) {
31+
throw new Error("Intended error for test");
32+
}
2933
listKvRequestCount++;
3034
};
3135
let getKvRequestCount = 0;
36+
let failNextGetKv = false;
3237
const getKvCallback = () => {
38+
if (failNextGetKv) {
39+
throw new Error("Intended error for test");
40+
}
3341
getKvRequestCount++;
3442
};
3543

@@ -40,6 +48,7 @@ describe("dynamic refresh", function () {
4048
{ value: "red", key: "app.settings.fontColor" },
4149
{ value: "40", key: "app.settings.fontSize" },
4250
{ value: "30", key: "app.settings.fontSize", label: "prod" },
51+
{ value: "someValue", key: "sentinel" },
4352
{ value: "someValue", key: "TestTagKey", tags: { "env": "dev" } }
4453
].map(createMockedKeyValue);
4554
mockAppConfigurationClientListConfigurationSettings([mockedKVs], listKvCallback);
@@ -48,6 +57,8 @@ describe("dynamic refresh", function () {
4857

4958
afterEach(() => {
5059
restoreMocks();
60+
failNextListKv = false;
61+
failNextGetKv = false;
5162
listKvRequestCount = 0;
5263
getKvRequestCount = 0;
5364
});
@@ -239,6 +250,44 @@ describe("dynamic refresh", function () {
239250
expect(settings.get("app.settings.bgColor")).eq("white");
240251
});
241252

253+
it("should continue to refresh when previous refresh-all attempt failed", async () => {
254+
const connectionString = createMockedConnectionString();
255+
const settings = await load(connectionString, {
256+
selectors: [
257+
{ keyFilter: "app.settings.*" }
258+
],
259+
refreshOptions: {
260+
enabled: true,
261+
refreshIntervalInMs: 2000,
262+
watchedSettings: [
263+
{ key: "sentinel" }
264+
]
265+
}
266+
});
267+
expect(settings.get("app.settings.fontSize")).eq("40");
268+
expect(settings.get("app.settings.fontColor")).eq("red");
269+
expect(settings.get("sentinel")).to.be.undefined;
270+
expect(listKvRequestCount).eq(1);
271+
expect(getKvRequestCount).eq(1); // one getKv request for sentinel key
272+
273+
// change setting
274+
addSetting("app.settings.bgColor", "white");
275+
updateSetting("sentinel", "updatedValue");
276+
failNextListKv = true; // force next listConfigurationSettings request to fail
277+
await sleepInMs(2 * 1000 + 1);
278+
await settings.refresh(); // even if the provider detects the sentinel key change, this refresh will fail, so we won't get the updated value of sentinel
279+
expect(listKvRequestCount).eq(1);
280+
expect(getKvRequestCount).eq(2);
281+
expect(settings.get("app.settings.bgColor")).to.be.undefined;
282+
283+
failNextListKv = false;
284+
await sleepInMs(2 * 1000 + 1);
285+
await settings.refresh(); // should continue to refresh even if sentinel key doesn't change now
286+
expect(listKvRequestCount).eq(2);
287+
expect(getKvRequestCount).eq(4);
288+
expect(settings.get("app.settings.bgColor")).eq("white");
289+
});
290+
242291
it("should execute callbacks on successful refresh", async () => {
243292
const connectionString = createMockedConnectionString();
244293
const settings = await load(connectionString, {

0 commit comments

Comments
 (0)