Skip to content

Commit 9b127d6

Browse files
authored
Configuration Setting Mapping (#42795)
* Config Map * configuration_mapper * fixing formatting * bug fix + tests * Updating docstrings + formatting * Update assets.json * updated tests * Update test_async_provider.py * Update _azureappconfigurationproviderasync.py
1 parent edfa280 commit 9b127d6

File tree

5 files changed

+140
-1
lines changed

5 files changed

+140
-1
lines changed

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_8a72ac47e0"
5+
"Tag": "python/appconfiguration/azure-appconfiguration-provider_c68d337f0e"
66
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ def load( # pylint: disable=docstring-keyword-should-match-keyword-only
103103
:keyword load_balancing_enabled: Optional flag to enable or disable the load balancing of replica endpoints. Default
104104
is False.
105105
:paramtype load_balancing_enabled: bool
106+
:keyword configuration_mapper: Optional function to map configuration settings. Enables transformation of
107+
configurations before they are added to the provider.
108+
:paramtype configuration_mapper: Optional[Callable[[ConfigurationSetting], None]]
106109
"""
107110

108111

@@ -166,6 +169,9 @@ def load( # pylint: disable=docstring-keyword-should-match-keyword-only
166169
:keyword load_balancing_enabled: Optional flag to enable or disable the load balancing of replica endpoints. Default
167170
is False.
168171
:paramtype load_balancing_enabled: bool
172+
:keyword configuration_mapper: Optional function to map configuration settings. Enables transformation of
173+
configurations before they are added to the provider.
174+
:paramtype configuration_mapper: Optional[Callable[[ConfigurationSetting], None]]
169175
"""
170176

171177

@@ -318,6 +324,7 @@ def __init__(self, **kwargs: Any) -> None:
318324
self._secret_clients: Dict[str, SecretClient] = {}
319325
self._on_refresh_success: Optional[Callable] = kwargs.pop("on_refresh_success", None)
320326
self._on_refresh_error: Optional[Callable[[Exception], None]] = kwargs.pop("on_refresh_error", None)
327+
self._configuration_mapper: Optional[Callable] = kwargs.pop("configuration_mapper", None)
321328

322329
def _attempt_refresh(self, client: ConfigurationClient, replica_count: int, is_failover_request: bool, **kwargs):
323330
settings_refreshed = False
@@ -516,6 +523,9 @@ def _process_configurations(self, configuration_settings: List[ConfigurationSett
516523
configuration_settings_processed = {}
517524
feature_flags_processed = []
518525
for settings in configuration_settings:
526+
if self._configuration_mapper:
527+
# If a map function is provided, use it to process the configuration setting
528+
self._configuration_mapper(settings)
519529
if isinstance(settings, FeatureFlagConfigurationSetting):
520530
# Feature flags are not processed like other settings
521531
feature_flag_value = self._process_feature_flag(settings)

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ async def load( # pylint: disable=docstring-keyword-should-match-keyword-only
109109
:keyword load_balancing_enabled: Optional flag to enable or disable the load balancing of replica endpoints. Default
110110
is False.
111111
:paramtype load_balancing_enabled: bool
112+
:keyword configuration_mapper: Optional function to map configuration settings. Enables transformation of
113+
configurations before they are added to the provider.
114+
:paramtype configuration_mapper: Optional[Callable[[ConfigurationSetting], Awaitable[None]]]
112115
"""
113116

114117

@@ -172,6 +175,9 @@ async def load( # pylint: disable=docstring-keyword-should-match-keyword-only
172175
:keyword load_balancing_enabled: Optional flag to enable or disable the load balancing of replica endpoints. Default
173176
is False.
174177
:paramtype load_balancing_enabled: bool
178+
:keyword configuration_mapper: Optional function to map configuration settings. Enables transformation of
179+
configurations before they are added to the provider.
180+
:paramtype configuration_mapper: Optional[Callable[[ConfigurationSetting], Awaitable[None]]]
175181
"""
176182

177183

@@ -334,6 +340,9 @@ def __init__(self, **kwargs: Any) -> None:
334340
self._on_refresh_error: Optional[Union[Callable[[Exception], Awaitable[None]], None]] = kwargs.pop(
335341
"on_refresh_error", None
336342
)
343+
self._configuration_mapper: Optional[Callable[[ConfigurationSetting], Awaitable[None]]] = kwargs.pop(
344+
"configuration_mapper", None
345+
)
337346

338347
async def _attempt_refresh(
339348
self, client: ConfigurationClient, replica_count: int, is_failover_request: bool, **kwargs
@@ -538,6 +547,8 @@ async def _process_configurations(self, configuration_settings: List[Configurati
538547
configuration_settings_processed = {}
539548
feature_flags_processed = []
540549
for settings in configuration_settings:
550+
if self._configuration_mapper:
551+
await self._configuration_mapper(settings)
541552
if isinstance(settings, FeatureFlagConfigurationSetting):
542553
# Feature flags are not processed like other settings
543554
feature_flag_value = self._process_feature_flag(settings)

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,64 @@ async def test_provider_tag_filters(self, appconfiguration_connection_string, ap
234234
assert has_feature_flag(client, "TaggedFeatureFlag")
235235
assert "message" not in client
236236

237+
# method: load
238+
@app_config_decorator_async
239+
@recorded_by_proxy_async
240+
async def test_configuration_mapper(self, appconfiguration_connection_string, appconfiguration_keyvault_secret_url):
241+
async def test_mapper(setting):
242+
if setting.key == "message":
243+
setting.value = "mapped"
244+
245+
async with await self.create_client(
246+
connection_string=appconfiguration_connection_string,
247+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
248+
configuration_mapper=test_mapper,
249+
) as client:
250+
assert client["message"] == "mapped"
251+
assert client["refresh_message"] == "original value"
252+
253+
# method: load
254+
@app_config_decorator_async
255+
@recorded_by_proxy_async
256+
async def test_configuration_mapper_with_trimming(
257+
self, appconfiguration_connection_string, appconfiguration_keyvault_secret_url
258+
):
259+
async def test_mapper(setting):
260+
if setting.key == "message":
261+
setting.value = "mapped"
262+
263+
async with await self.create_client(
264+
connection_string=appconfiguration_connection_string,
265+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
266+
configuration_mapper=test_mapper,
267+
trim_prefixes=["refresh_"],
268+
) as client:
269+
# Because our processing happens after mapping and refresh_message is alphabetically after message the override
270+
# value isn't used, as the mapped value is overridden by the first value.
271+
assert client["message"] == "original value"
272+
assert "refresh_message" not in client
273+
274+
# method: load
275+
@app_config_decorator_async
276+
@recorded_by_proxy_async
277+
async def test_configuration_mapper_with_feature_flags(
278+
self, appconfiguration_connection_string, appconfiguration_keyvault_secret_url
279+
):
280+
async def test_mapper(setting):
281+
if setting.key == ".appconfig.featureflag/Alpha":
282+
setting.content_type = "application/json"
283+
284+
async with await self.create_client(
285+
connection_string=appconfiguration_connection_string,
286+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
287+
feature_flag_enabled=True,
288+
configuration_mapper=test_mapper,
289+
trim_prefixes=[".appconfig.featureflag/"],
290+
) as client:
291+
# Feature Flags aren't modified by configuration mappers
292+
assert "Alpha" not in client
293+
assert client["feature_management"]["feature_flags"][0]["id"] == "Alpha"
294+
237295

238296
async def secret_resolver(secret_id):
239297
return "Resolver Value"

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,66 @@ def test_provider_special_chars_tag_filters(
296296
assert "only_second_tag" not in client
297297
assert "complex_tag" in client
298298

299+
# method: load
300+
@recorded_by_proxy
301+
@app_config_decorator
302+
def test_configuration_mapper(self, appconfiguration_connection_string, appconfiguration_keyvault_secret_url):
303+
def test_mapper(setting):
304+
if setting.key == "message":
305+
setting.value = "mapped"
306+
307+
client = self.create_client(
308+
connection_string=appconfiguration_connection_string,
309+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
310+
feature_flag_enabled=True,
311+
configuration_mapper=test_mapper,
312+
)
313+
assert client["message"] == "mapped"
314+
assert client["refresh_message"] == "original value"
315+
316+
# method: load
317+
@recorded_by_proxy
318+
@app_config_decorator
319+
def test_configuration_mapper_with_trimming(
320+
self, appconfiguration_connection_string, appconfiguration_keyvault_secret_url
321+
):
322+
def test_mapper(setting):
323+
if setting.key == "message":
324+
setting.value = "mapped"
325+
326+
client = self.create_client(
327+
connection_string=appconfiguration_connection_string,
328+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
329+
configuration_mapper=test_mapper,
330+
trim_prefixes=["refresh_"],
331+
)
332+
333+
# Because our processing happens after mapping and refresh_message is alphabetically after message the override
334+
# value isn't used, as the mapped value is overridden by the first value.
335+
assert client["message"] == "original value"
336+
assert "refresh_message" not in client
337+
338+
# method: load
339+
@recorded_by_proxy
340+
@app_config_decorator
341+
def test_configuration_mapper_with_feature_flags(
342+
self, appconfiguration_connection_string, appconfiguration_keyvault_secret_url
343+
):
344+
def test_mapper(setting):
345+
if setting.key == ".appconfig.featureflag/Alpha":
346+
setting.content_type = "application/json"
347+
348+
client = self.create_client(
349+
connection_string=appconfiguration_connection_string,
350+
keyvault_secret_url=appconfiguration_keyvault_secret_url,
351+
feature_flag_enabled=True,
352+
configuration_mapper=test_mapper,
353+
trim_prefixes=[".appconfig.featureflag/"],
354+
)
355+
# Feature Flags aren't modified by configuration mappers
356+
assert "Alpha" not in client
357+
assert client["feature_management"]["feature_flags"][0]["id"] == "Alpha"
358+
299359

300360
def secret_resolver(secret_id):
301361
return "Resolver Value"

0 commit comments

Comments
 (0)