Skip to content

Commit 33db94b

Browse files
mrm9084Copilot
andauthored
App Config Provider Bug Fix (#44012)
* Fix Refresh bug * Update CHANGELOG.md * Update sdk/appconfiguration/azure-appconfiguration-provider/CHANGELOG.md Co-authored-by: Copilot <[email protected]> * Update assets.json --------- Co-authored-by: Copilot <[email protected]>
1 parent 535e6a5 commit 33db94b

File tree

8 files changed

+118
-12
lines changed

8 files changed

+118
-12
lines changed

sdk/appconfiguration/azure-appconfiguration-provider/CHANGELOG.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
# Release History
22

3-
## 2.3.1 (Unreleased)
4-
5-
### Features Added
6-
7-
### Breaking Changes
3+
## 2.3.1 (2025-11-13)
84

95
### Bugs Fixed
106

11-
### Other Changes
7+
* Fixed a bug where calling the refresh method would always trigger it.
128

139
## 2.3.0 (2025-11-12)
1410

sdk/appconfiguration/azure-appconfiguration-provider/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/appconfiguration/azure-appconfiguration-provider",
5-
"Tag": "python/appconfiguration/azure-appconfiguration-provider_18bb5889fc"
5+
"Tag": "python/appconfiguration/azure-appconfiguration-provider_32bd63579a"
66
}

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_client_manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,21 @@ def get_updated_watched_settings(
197197
:rtype: Union[Dict[Tuple[str, str], str], None]
198198
"""
199199
updated_watched_settings = dict(watched_settings)
200+
trigger_refresh = False
200201
for (key, label), etag in watched_settings.items():
201202
changed, updated_watched_setting = self._check_configuration_setting(
202203
key=key, label=label, etag=etag, headers=headers, **kwargs
203204
)
204205
if changed and updated_watched_setting is not None:
205206
updated_watched_settings[(key, label)] = updated_watched_setting.etag
207+
trigger_refresh = True
206208
elif changed:
207209
# The key was deleted
208210
updated_watched_settings[(key, label)] = None
209-
return updated_watched_settings
211+
trigger_refresh = True
212+
if trigger_refresh:
213+
return updated_watched_settings
214+
return {}
210215

211216
@distributed_trace
212217
def try_check_feature_flags(

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/aio/_async_client_manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,16 +199,21 @@ async def get_updated_watched_settings(
199199
:rtype: Union[Dict[Tuple[str, str], str], None]
200200
"""
201201
updated_watched_settings = dict(watched_settings)
202+
trigger_refresh = False
202203
for (key, label), etag in watched_settings.items():
203204
changed, updated_watched_setting = await self._check_configuration_setting(
204205
key=key, label=label, etag=etag, headers=headers, **kwargs
205206
)
206207
if changed and updated_watched_setting is not None:
207208
updated_watched_settings[(key, label)] = updated_watched_setting.etag
209+
trigger_refresh = True
208210
elif changed:
209211
# The key was deleted
210212
updated_watched_settings[(key, label)] = None
211-
return updated_watched_settings
213+
trigger_refresh = True
214+
if trigger_refresh:
215+
return updated_watched_settings
216+
return {}
212217

213218
@distributed_trace
214219
async def try_check_feature_flags(

sdk/appconfiguration/azure-appconfiguration-provider/tests/aio/key_vault/test_async_secret_refresh.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ async def test_secret_refresh_with_updated_values(
8383
keyvault_secret_url=appconfiguration_keyvault_secret_url,
8484
keyvault_secret_url2=appconfiguration_keyvault_secret_url2,
8585
on_refresh_success=mock_callback,
86-
refresh_on=[WatchKey("secret")],
86+
refresh_on=[WatchKey("secret", "prod")],
8787
refresh_interval=1,
8888
secret_refresh_interval=1, # Using a short interval for testing
8989
)

sdk/appconfiguration/azure-appconfiguration-provider/tests/aio/test_async_provider_refresh.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pytest
99
import sys
1010

11+
from azure.appconfiguration import ConfigurationSetting
1112
from azure.appconfiguration.provider import WatchKey
1213
from devtools_testutils.aio import recorded_by_proxy_async
1314
from async_preparers import app_config_decorator_async
@@ -90,6 +91,57 @@ async def test_refresh(self, appconfiguration_endpoint_string, appconfiguration_
9091
assert client["refresh_message"] == "original value"
9192
assert mock_callback.call_count == 2
9293

94+
# method: refresh
95+
@app_config_decorator_async
96+
@recorded_by_proxy_async
97+
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Python 3.7 does not support AsyncMock")
98+
@pytest.mark.asyncio
99+
async def test_no_refresh(self, appconfiguration_endpoint_string, appconfiguration_keyvault_secret_url):
100+
101+
appconfig_client = self.create_aad_sdk_client(appconfiguration_endpoint_string)
102+
103+
watch_key = ConfigurationSetting(key="watch key", value="0")
104+
await appconfig_client.set_configuration_setting(watch_key)
105+
106+
mock_callback = Mock()
107+
async with await self.create_client(
108+
endpoint=appconfiguration_endpoint_string,
109+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
110+
refresh_on=[WatchKey("watch key")],
111+
refresh_interval=1,
112+
on_refresh_success=mock_callback,
113+
feature_flag_enabled=True,
114+
feature_flag_refresh_enabled=True,
115+
) as client:
116+
assert client["refresh_message"] == "original value"
117+
assert client["my_json"]["key"] == "value"
118+
assert FEATURE_MANAGEMENT_KEY in client
119+
assert has_feature_flag(client, "Alpha")
120+
121+
setting = await appconfig_client.get_configuration_setting(key="refresh_message")
122+
setting.value = "updated value"
123+
await appconfig_client.set_configuration_setting(setting)
124+
125+
# Waiting for the refresh interval to pass
126+
time.sleep(2)
127+
128+
await client.refresh()
129+
# No Change the Watch Key wasn't updated
130+
assert client["refresh_message"] == "original value"
131+
assert has_feature_flag(client, "Alpha", False)
132+
assert mock_callback.call_count == 0
133+
134+
watch_key.value = "1"
135+
await appconfig_client.set_configuration_setting(watch_key)
136+
137+
# Waiting for the refresh interval to pass
138+
time.sleep(2)
139+
140+
await client.refresh()
141+
assert client["refresh_message"] == "updated value"
142+
assert has_feature_flag(client, "Alpha", False)
143+
assert mock_callback.call_count == 1
144+
93145
# method: refresh
94146
@app_config_decorator_async
95147
@recorded_by_proxy_async

sdk/appconfiguration/azure-appconfiguration-provider/tests/key_vault/test_secret_refresh.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_secret_refresh_with_updated_values(
7575
keyvault_secret_url=appconfiguration_keyvault_secret_url,
7676
keyvault_secret_url2=appconfiguration_keyvault_secret_url2,
7777
on_refresh_success=mock_callback,
78-
refresh_on=[WatchKey("secret")],
78+
refresh_on=[WatchKey("secret", "prod")],
7979
refresh_interval=1,
8080
secret_refresh_interval=1, # Using a short interval for testing
8181
)

sdk/appconfiguration/azure-appconfiguration-provider/tests/test_provider_refresh.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from azure.appconfiguration.provider import WatchKey
1010
from devtools_testutils import recorded_by_proxy
1111
from preparers import app_config_decorator_aad
12-
from testcase import AppConfigTestCase, has_feature_flag
12+
from testcase import AppConfigTestCase, ConfigurationSetting, has_feature_flag
1313
from test_constants import FEATURE_MANAGEMENT_KEY
1414

1515

@@ -81,6 +81,54 @@ def test_refresh(self, appconfiguration_endpoint_string, appconfiguration_keyvau
8181
assert client["refresh_message"] == "original value"
8282
assert mock_callback.call_count == 2
8383

84+
@recorded_by_proxy
85+
@app_config_decorator_aad
86+
def test_no_refresh(self, appconfiguration_endpoint_string, appconfiguration_keyvault_secret_url):
87+
88+
appconfig_client = self.create_aad_sdk_client(appconfiguration_endpoint_string)
89+
90+
watch_key = ConfigurationSetting(key="watch key", value="0")
91+
appconfig_client.set_configuration_setting(watch_key)
92+
93+
mock_callback = Mock()
94+
client = self.create_client(
95+
endpoint=appconfiguration_endpoint_string,
96+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
97+
refresh_on=[WatchKey("watch key")],
98+
refresh_interval=1,
99+
on_refresh_success=mock_callback,
100+
feature_flag_enabled=True,
101+
feature_flag_refresh_enabled=True,
102+
)
103+
assert client["refresh_message"] == "original value"
104+
assert client["my_json"]["key"] == "value"
105+
assert FEATURE_MANAGEMENT_KEY in client
106+
assert has_feature_flag(client, "Alpha")
107+
108+
setting = appconfig_client.get_configuration_setting(key="refresh_message")
109+
setting.value = "updated value"
110+
appconfig_client.set_configuration_setting(setting)
111+
112+
# Waiting for the refresh interval to pass
113+
time.sleep(2)
114+
115+
client.refresh()
116+
# No Change the Watch Key wasn't updated
117+
assert client["refresh_message"] == "original value"
118+
assert has_feature_flag(client, "Alpha", False)
119+
assert mock_callback.call_count == 0
120+
121+
watch_key.value = "1"
122+
appconfig_client.set_configuration_setting(watch_key)
123+
124+
# Waiting for the refresh interval to pass
125+
time.sleep(2)
126+
127+
client.refresh()
128+
assert client["refresh_message"] == "updated value"
129+
assert has_feature_flag(client, "Alpha", False)
130+
assert mock_callback.call_count == 1
131+
84132
# method: refresh
85133
@recorded_by_proxy
86134
@app_config_decorator_aad

0 commit comments

Comments
 (0)