Skip to content

Commit 458e698

Browse files
mrm9084Copilot
andauthored
App Configuration - Telemetry Usage Update (#40519)
* added telemetry * Added Tests * Remove debug * Update testcase.py * Update sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_constants.py Co-authored-by: Copilot <[email protected]> * Update assets.json * Update assets.json * Changed to + * Update cspell.json --------- Co-authored-by: Copilot <[email protected]>
1 parent 2f9e93b commit 458e698

File tree

8 files changed

+272
-5
lines changed

8 files changed

+272
-5
lines changed

.vscode/cspell.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,6 +1843,13 @@
18431843
"adfsasdfzsd"
18441844
]
18451845
},
1846+
{
1847+
"filename": "sdk/appconfiguration/azure-appconfiguration-provider/**",
1848+
"words": [
1849+
"aicc",
1850+
"AICC"
1851+
]
1852+
},
18461853
{
18471854
"filename": "sdk/personalizer/test-resources.json",
18481855
"words": [

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

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
from ._constants import (
2929
FEATURE_MANAGEMENT_KEY,
3030
FEATURE_FLAG_KEY,
31+
APP_CONFIG_AI_MIME_PROFILE,
32+
APP_CONFIG_AICC_MIME_PROFILE,
3133
)
3234
from ._azureappconfigurationproviderbase import (
3335
AzureAppConfigurationProviderBase,
@@ -353,6 +355,8 @@ def refresh(self, **kwargs) -> None: # pylint: disable=too-many-statements
353355
self._uses_key_vault,
354356
self._uses_load_balancing,
355357
is_failover_request,
358+
self._uses_ai_configuration,
359+
self._uses_aicc_configuration,
356360
)
357361

358362
try:
@@ -421,6 +425,8 @@ def _load_all(self, **kwargs):
421425
self._uses_key_vault,
422426
self._uses_load_balancing,
423427
is_failover_request,
428+
self._uses_ai_configuration,
429+
self._uses_aicc_configuration,
424430
)
425431
try:
426432
configuration_settings, sentinel_keys = client.load_configuration_settings(
@@ -470,6 +476,10 @@ def _load_all(self, **kwargs):
470476
raise exception
471477

472478
def _process_configurations(self, configuration_settings: List[ConfigurationSetting]) -> Dict[str, Any]:
479+
# Reset feature flag usage
480+
self._uses_ai_configuration = False
481+
self._uses_aicc_configuration = False
482+
473483
configuration_settings_processed = {}
474484
for config in configuration_settings:
475485
if isinstance(config, FeatureFlagConfigurationSetting):
@@ -488,6 +498,10 @@ def _process_key_value(self, config):
488498
if is_json_content_type(config.content_type) and not isinstance(config, FeatureFlagConfigurationSetting):
489499
# Feature flags are of type json, but don't treat them as such
490500
try:
501+
if APP_CONFIG_AI_MIME_PROFILE in config.content_type:
502+
self._uses_ai_configuration = True
503+
if APP_CONFIG_AICC_MIME_PROFILE in config.content_type:
504+
self._uses_aicc_configuration = True
491505
return json.loads(config.value)
492506
except json.JSONDecodeError:
493507
# If the value is not a valid JSON, treat it like regular string value

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ def update_correlation_context_header(
6565
uses_key_vault,
6666
uses_load_balancing,
6767
is_failover_request,
68+
uses_ai_configuration,
69+
uses_aicc_configuration,
6870
) -> Dict[str, str]:
6971
if os.environ.get(REQUEST_TRACING_DISABLED_ENVIRONMENT_VARIABLE, default="").lower() == "true":
7072
return headers
@@ -106,8 +108,19 @@ def update_correlation_context_header(
106108
if is_failover_request:
107109
correlation_context += ",Failover"
108110

111+
features = ""
112+
109113
if uses_load_balancing:
110-
correlation_context += ",Features=LB"
114+
features += "LB+"
115+
116+
if uses_ai_configuration:
117+
features += "AI+"
118+
119+
if uses_aicc_configuration:
120+
features += "AICC+"
121+
122+
if features:
123+
correlation_context += ",Features=" + features[:-1]
111124

112125
headers["Correlation-Context"] = correlation_context
113126
return headers
@@ -274,6 +287,8 @@ def __init__(self, **kwargs: Any) -> None:
274287
self._feature_flag_refresh_enabled = kwargs.pop("feature_flag_refresh_enabled", False)
275288
self._feature_filter_usage: Mapping[str, bool] = {}
276289
self._uses_load_balancing = kwargs.pop("load_balancing_enabled", False)
290+
self._uses_ai_configuration = False
291+
self._uses_aicc_configuration = False # AI Chat Completion
277292
self._update_lock = Lock()
278293
self._refresh_lock = Lock()
279294

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@
3131
PERCENTAGE_FILTER_KEY = "PRCNT" # cspell:disable-line
3232
TIME_WINDOW_FILTER_KEY = "TIME"
3333
TARGETING_FILTER_KEY = "TRGT" # cspell:disable-line
34+
35+
# Mime profiles
36+
APP_CONFIG_AI_MIME_PROFILE = "https://azconfig.io/mime-profiles/ai/"
37+
APP_CONFIG_AICC_MIME_PROFILE = "https://azconfig.io/mime-profiles/ai/chat-completion"

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
from .._constants import (
3333
FEATURE_MANAGEMENT_KEY,
3434
FEATURE_FLAG_KEY,
35+
APP_CONFIG_AI_MIME_PROFILE,
36+
APP_CONFIG_AICC_MIME_PROFILE,
3537
)
3638
from .._azureappconfigurationproviderbase import (
3739
AzureAppConfigurationProviderBase,
@@ -368,6 +370,8 @@ async def refresh(self, **kwargs) -> None: # pylint: disable=too-many-statement
368370
self._uses_key_vault,
369371
self._uses_load_balancing,
370372
is_failover_request,
373+
self._uses_ai_configuration,
374+
self._uses_aicc_configuration,
371375
)
372376

373377
try:
@@ -438,6 +442,8 @@ async def _load_all(self, **kwargs):
438442
self._uses_key_vault,
439443
self._uses_load_balancing,
440444
is_failover_request,
445+
self._uses_ai_configuration,
446+
self._uses_aicc_configuration,
441447
)
442448
try:
443449
configuration_settings, sentinel_keys = await client.load_configuration_settings(
@@ -489,6 +495,10 @@ async def _load_all(self, **kwargs):
489495
raise exception
490496

491497
async def _process_configurations(self, configuration_settings: List[ConfigurationSetting]) -> Dict[str, Any]:
498+
# Reset feature flag usage
499+
self._uses_ai_configuration = False
500+
self._uses_aicc_configuration = False
501+
492502
configuration_settings_processed = {}
493503
for config in configuration_settings:
494504
if isinstance(config, FeatureFlagConfigurationSetting):
@@ -507,6 +517,10 @@ async def _process_key_value(self, config):
507517
if is_json_content_type(config.content_type) and not isinstance(config, FeatureFlagConfigurationSetting):
508518
# Feature flags are of type json, but don't treat them as such
509519
try:
520+
if APP_CONFIG_AI_MIME_PROFILE in config.content_type:
521+
self._uses_ai_configuration = True
522+
if APP_CONFIG_AICC_MIME_PROFILE in config.content_type:
523+
self._uses_aicc_configuration = True
510524
return json.loads(config.value)
511525
except json.JSONDecodeError:
512526
# If the value is not a valid JSON, treat it like regular string value

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

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
# license information.
55
# --------------------------------------------------------------------------
66
from azure.appconfiguration.provider import SettingSelector, AzureAppConfigurationKeyVaultOptions
7+
from azure.appconfiguration.provider.aio import AzureAppConfigurationProvider
78
from devtools_testutils.aio import recorded_by_proxy_async
89
from async_preparers import app_config_decorator_async
910
from asynctestcase import AppConfigTestCase, has_feature_flag
1011
from test_constants import FEATURE_MANAGEMENT_KEY
12+
from unittest.mock import MagicMock, patch
13+
import asyncio
14+
from azure.appconfiguration.provider._azureappconfigurationproviderbase import (
15+
update_correlation_context_header,
16+
)
1117

1218

1319
class TestAppConfigurationProvider(AppConfigTestCase):
@@ -111,6 +117,107 @@ async def test_provider_secret_resolver_options(self, appconfiguration_connectio
111117
) as client:
112118
assert client["secret"] == "Reslover Value"
113119

120+
@app_config_decorator_async
121+
@recorded_by_proxy_async
122+
async def test_process_key_value_content_type(self, appconfiguration_connection_string):
123+
with patch(
124+
"azure.appconfiguration.provider.aio._azureappconfigurationproviderasync.AsyncConfigurationClientManager"
125+
) as MockClientManager:
126+
# Mock the client manager and its methods
127+
mock_client_manager = MockClientManager.return_value
128+
mock_client_manager.load_configuration_settings.return_value = [
129+
{"key": "test_key", "value": '{"key": "value"}', "content_type": "application/json"}
130+
]
131+
132+
# Create the provider with the mocked client manager
133+
provider = AzureAppConfigurationProvider(connection_string="mock_connection_string")
134+
provider._replica_client_manager = mock_client_manager
135+
136+
# Call the method to process key-value pairs
137+
processed_value = await provider._process_key_value(
138+
MagicMock(content_type="application/json", value='{"key": "value"}')
139+
)
140+
141+
# Assert the processed value is as expected
142+
assert processed_value == {"key": "value"}
143+
assert provider._uses_ai_configuration == False
144+
assert provider._uses_aicc_configuration == False
145+
headers = update_correlation_context_header(
146+
{},
147+
"fake-request",
148+
0,
149+
False,
150+
[],
151+
False,
152+
False,
153+
False,
154+
provider._uses_ai_configuration,
155+
provider._uses_aicc_configuration,
156+
)
157+
assert headers["Correlation-Context"] == "RequestType=fake-request"
158+
159+
mock_client_manager.load_configuration_settings.return_value = [
160+
{
161+
"key": "test_key",
162+
"value": '{"key": "value"}',
163+
"content_type": "https://azconfig.io/mime-profiles/ai/",
164+
}
165+
]
166+
processed_value = await provider._process_key_value(
167+
MagicMock(
168+
content_type='application/json; profile="https://azconfig.io/mime-profiles/ai/"',
169+
value='{"key": "value"}',
170+
)
171+
)
172+
173+
assert processed_value == {"key": "value"}
174+
assert provider._uses_ai_configuration == True
175+
assert provider._uses_aicc_configuration == False
176+
headers = update_correlation_context_header(
177+
{},
178+
"fake-request",
179+
0,
180+
False,
181+
[],
182+
False,
183+
False,
184+
False,
185+
provider._uses_ai_configuration,
186+
provider._uses_aicc_configuration,
187+
)
188+
assert headers["Correlation-Context"] == "RequestType=fake-request,Features=AI"
189+
190+
mock_client_manager.load_configuration_settings.return_value = [
191+
{
192+
"key": "test_key",
193+
"value": '{"key": "value"}',
194+
"content_type": 'application/json; profile="https://azconfig.io/mime-profiles/ai/chat-completion"',
195+
}
196+
]
197+
processed_value = await provider._process_key_value(
198+
MagicMock(
199+
content_type='application/json; profile="https://azconfig.io/mime-profiles/ai/chat-completion"',
200+
value='{"key": "value"}',
201+
)
202+
)
203+
204+
assert processed_value == {"key": "value"}
205+
assert provider._uses_ai_configuration == True
206+
assert provider._uses_aicc_configuration == True
207+
headers = update_correlation_context_header(
208+
{},
209+
"fake-request",
210+
0,
211+
False,
212+
[],
213+
False,
214+
False,
215+
False,
216+
provider._uses_ai_configuration,
217+
provider._uses_aicc_configuration,
218+
)
219+
assert headers["Correlation-Context"] == "RequestType=fake-request,Features=AI+AICC"
220+
114221

115222
async def secret_resolver(secret_id):
116223
return "Reslover Value"

0 commit comments

Comments
 (0)