Skip to content

Azure App Configuration Provider Allocation Id #40730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a107c2b
Revert "Remove Telemetry from main (#37783)" (#37812)
mrm9084 Oct 9, 2024
be1b8fd
Allocation Id (#37840)
mrm9084 Oct 11, 2024
dad8a4d
Python Provider 2.0.0b2 Changelog update (#37860)
mrm9084 Oct 11, 2024
5c0ca90
Increment package version after release of azure-appconfiguration-pro…
azure-sdk Oct 14, 2024
561dc06
Update _client_manager_base.py (#38019)
mrm9084 Oct 22, 2024
c973431
App Config Allocation Id Update (#38065)
mrm9084 Oct 23, 2024
e994d82
Allocation id update (#38242)
mrm9084 Oct 31, 2024
68b9623
Update CHANGELOG.md (#38521)
mrm9084 Nov 13, 2024
470e175
Merge remote-tracking branch 'upstream/main' into feature/azure-appco…
mrm9084 Nov 13, 2024
7a02887
Merge branch 'feature/azure-appconfiguration-provider/variants' of ht…
mrm9084 Nov 13, 2024
0f57858
Increment package version after release of azure-appconfiguration-pro…
azure-sdk Nov 14, 2024
78c2fe0
Merge remote-tracking branch 'upstream/main' into feature/azure-appco…
mrm9084 Apr 8, 2025
68a5efe
fixing imports
mrm9084 Apr 8, 2025
84c78c3
Merge remote-tracking branch 'upstream/main' into feature/azure-appco…
mrm9084 Apr 10, 2025
b299b70
Updating Version and Changelog
mrm9084 Apr 10, 2025
ffffa36
Update _client_manager_base.py
mrm9084 Apr 10, 2025
ecb55a9
Increment package version after release of azure-appconfiguration-pro…
azure-sdk Apr 10, 2025
370186e
Merge remote-tracking branch 'upstream/main' into feature/azure-appco…
mrm9084 Apr 25, 2025
b99f070
Updating for release
mrm9084 Apr 25, 2025
df544d1
Update CHANGELOG.md
mrm9084 Apr 25, 2025
9335150
Update sdk/appconfiguration/azure-appconfiguration-provider/CHANGELOG.md
mrm9084 Apr 25, 2025
63c0d8b
Update sdk/appconfiguration/azure-appconfiguration-provider/azure/app…
mrm9084 Apr 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
# Release History

## 2.0.3 (Unreleased)
## 2.1.0 (2025-04-28)

### Features Added

### Breaking Changes
* Added AllocationId to the feature flag telemetry metadata when the feature flag has telemetry enabled.

## 2.1.0b1 (2025-04-10)

### Bugs Fixed

### Other Changes
* Updates the feature flag telemetry to use the provided endpoint instead of the endpoint of the store the feature flag was loaded from.
* Removes FeatureFlagId from feature flag telemetry.

## 2.0.2 (2025-04-17)

### Other Changes

* Updates telemetry for JSON usage.

## 2.0.1 (2025-03-07)

### Bugs Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# -------------------------------------------------------------------------
import json
import random
import base64
import hashlib
from dataclasses import dataclass
from typing import Dict, Mapping, Any, Optional
from typing import Dict, List, Optional, Mapping, Any
from azure.appconfiguration import ( # type:ignore # pylint:disable=no-name-in-module
FeatureFlagConfigurationSetting,
)
Expand All @@ -21,6 +24,7 @@
METADATA_KEY,
ETAG_KEY,
FEATURE_FLAG_REFERENCE_KEY,
ALLOCATION_ID_KEY,
)

FALLBACK_CLIENT_REFRESH_EXPIRED_INTERVAL = 3600 # 1 hour in seconds
Expand All @@ -33,6 +37,89 @@
class _ConfigurationClientWrapperBase:
endpoint: str

@staticmethod
def _generate_allocation_id(feature_flag_value: Dict[str, JSON]) -> Optional[str]:
"""
Generates an allocation ID for the specified feature.
seed=123abc\ndefault_when_enabled=Control\npercentiles=0,Control,20;20,Test,100\nvariants=Control,standard;Test,special # pylint:disable=line-too-long

:param Dict[str, JSON] feature_flag_value: The feature to generate an allocation ID for.
:rtype: str
:return: The allocation ID.
"""

allocation_id = ""
allocated_variants = []

allocation: Optional[JSON] = feature_flag_value.get("allocation")

if not allocation:
return None

# Seed
allocation_id = f"seed={allocation.get('seed', '')}"

# DefaultWhenEnabled
if "default_when_enabled" in allocation:
allocated_variants.append(allocation.get("default_when_enabled"))

allocation_id += f"\ndefault_when_enabled={allocation.get('default_when_enabled', '')}"

# Percentile
allocation_id += "\npercentiles="

percentile = allocation.get("percentile")

if percentile:
percentile_allocations = sorted(
(x for x in percentile if x.get("from") != x.get("to")),
key=lambda x: x.get("from"),
)

for percentile_allocation in percentile_allocations:
if "variant" in percentile_allocation:
allocated_variants.append(percentile_allocation.get("variant"))

allocation_id += ";".join(
f"{pa.get('from')}," f"{base64.b64encode(pa.get('variant').encode()).decode()}," f"{pa.get('to')}"
for pa in percentile_allocations
)

if not allocated_variants and not allocation.get("seed"):
return None

# Variants
allocation_id += "\nvariants="

variants_value = feature_flag_value.get("variants")
if variants_value and (isinstance(variants_value, list) or all(isinstance(v, dict) for v in variants_value)):
if (
allocated_variants
and isinstance(variants_value, list)
and all(isinstance(v, dict) for v in variants_value)
):
sorted_variants: List[Dict[str, Any]] = sorted(
(v for v in variants_value if v.get("name") in allocated_variants),
key=lambda v: v.get("name"),
)

for v in sorted_variants:
allocation_id += f"{base64.b64encode(v.get('name', '').encode()).decode()},"
if "configuration_value" in v:
allocation_id += (
f"{json.dumps(v.get('configuration_value', ''), separators=(',', ':'), sort_keys=True)}"
)
allocation_id += ";"
if sorted_variants:
allocation_id = allocation_id[:-1]

# Create a sha256 hash of the allocation_id
hash_object = hashlib.sha256(allocation_id.encode())
hash_digest = hash_object.digest()

# Encode the first 15 bytes in base64 url
return base64.urlsafe_b64encode(hash_digest[:15]).decode()

def _feature_flag_telemetry(
self, endpoint: str, feature_flag: FeatureFlagConfigurationSetting, feature_flag_value: Dict
):
Expand All @@ -48,6 +135,9 @@ def _feature_flag_telemetry(
feature_flag_reference += f"?label={feature_flag.label}"
if feature_flag_value[TELEMETRY_KEY].get("enabled"):
feature_flag_value[TELEMETRY_KEY][METADATA_KEY][FEATURE_FLAG_REFERENCE_KEY] = feature_flag_reference
allocation_id = self._generate_allocation_id(feature_flag_value)
if allocation_id:
feature_flag_value[TELEMETRY_KEY][METADATA_KEY][ALLOCATION_ID_KEY] = allocation_id

def _feature_flag_appconfig_telemetry(
self, feature_flag: FeatureFlagConfigurationSetting, filters_used: Dict[str, bool]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
TELEMETRY_KEY = "telemetry"
METADATA_KEY = "metadata"

ALLOCATION_ID_KEY = "AllocationId"
ETAG_KEY = "ETag"
FEATURE_FLAG_REFERENCE_KEY = "FeatureFlagReference"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
# license information.
# -------------------------------------------------------------------------

VERSION = "2.0.3"
VERSION = "2.1.0"