|
3 | 3 | # Licensed under the MIT License. See License.txt in the project root for |
4 | 4 | # license information. |
5 | 5 | # ------------------------------------------------------------------------- |
6 | | -import json |
7 | 6 | import time |
8 | 7 | import random |
9 | 8 | import hashlib |
10 | 9 | import base64 |
11 | 10 | from dataclasses import dataclass |
12 | | -from typing import Dict, List, Optional, Mapping, Any |
| 11 | +from typing import Dict, List, Mapping, Any |
13 | 12 | from azure.appconfiguration import ( # type:ignore # pylint:disable=no-name-in-module |
14 | 13 | FeatureFlagConfigurationSetting, |
15 | 14 | ) |
|
26 | 25 | ETAG_KEY, |
27 | 26 | FEATURE_FLAG_REFERENCE_KEY, |
28 | 27 | FEATURE_FLAG_ID_KEY, |
29 | | - ALLOCATION_ID_KEY, |
30 | 28 | ) |
31 | 29 |
|
32 | 30 | FALLBACK_CLIENT_REFRESH_EXPIRED_INTERVAL = 3600 # 1 hour in seconds |
|
39 | 37 | class _ConfigurationClientWrapperBase: |
40 | 38 | endpoint: str |
41 | 39 |
|
42 | | - @staticmethod |
43 | | - def _generate_allocation_id(feature_flag_value: Dict[str, JSON]) -> Optional[str]: |
44 | | - """ |
45 | | - Generates an allocation ID for the specified feature. |
46 | | - seed=123abc\ndefault_when_enabled=Control\npercentiles=0,Control,20;20,Test,100\nvariants=Control,standard;Test,special # pylint:disable=line-too-long |
47 | | -
|
48 | | - :param Dict[str, JSON] feature_flag_value: The feature to generate an allocation ID for. |
49 | | - :rtype: str |
50 | | - :return: The allocation ID. |
51 | | - """ |
52 | | - |
53 | | - allocation_id = "" |
54 | | - allocated_variants = [] |
55 | | - |
56 | | - allocation: Optional[JSON] = feature_flag_value.get("allocation") |
57 | | - |
58 | | - if not allocation: |
59 | | - return None |
60 | | - |
61 | | - # Seed |
62 | | - allocation_id = f"seed={allocation.get('seed', '')}" |
63 | | - |
64 | | - # DefaultWhenEnabled |
65 | | - if "default_when_enabled" in allocation: |
66 | | - allocated_variants.append(allocation.get("default_when_enabled")) |
67 | | - |
68 | | - allocation_id += f"\ndefault_when_enabled={allocation.get('default_when_enabled', '')}" |
69 | | - |
70 | | - # Percentile |
71 | | - allocation_id += "\npercentiles=" |
72 | | - |
73 | | - percentile = allocation.get("percentile") |
74 | | - |
75 | | - if percentile: |
76 | | - percentile_allocations = sorted( |
77 | | - (x for x in percentile if x.get("from") != x.get("to")), |
78 | | - key=lambda x: x.get("from"), |
79 | | - ) |
80 | | - |
81 | | - for percentile_allocation in percentile_allocations: |
82 | | - if "variant" in percentile_allocation: |
83 | | - allocated_variants.append(percentile_allocation.get("variant")) |
84 | | - |
85 | | - allocation_id += ";".join( |
86 | | - f"{pa.get('from')}," f"{base64.b64encode(pa.get('variant').encode()).decode()}," f"{pa.get('to')}" |
87 | | - for pa in percentile_allocations |
88 | | - ) |
89 | | - |
90 | | - if not allocated_variants and (not allocation or not allocation.get("seed")): |
91 | | - return None |
92 | | - |
93 | | - # Variants |
94 | | - allocation_id += "\nvariants=" |
95 | | - |
96 | | - variants_value = feature_flag_value.get("variants") |
97 | | - if variants_value and (isinstance(variants_value, list) or all(isinstance(v, dict) for v in variants_value)): |
98 | | - if ( |
99 | | - allocated_variants |
100 | | - and isinstance(variants_value, list) |
101 | | - and all(isinstance(v, dict) for v in variants_value) |
102 | | - ): |
103 | | - sorted_variants: List[Dict[str, Any]] = sorted( |
104 | | - (v for v in variants_value if v.get("name") in allocated_variants), |
105 | | - key=lambda v: v.get("name"), |
106 | | - ) |
107 | | - |
108 | | - for v in sorted_variants: |
109 | | - allocation_id += f"{base64.b64encode(v.get('name', '').encode()).decode()}," |
110 | | - if "configuration_value" in v: |
111 | | - allocation_id += ( |
112 | | - f"{json.dumps(v.get('configuration_value', ''), separators=(',', ':'), sort_keys=True)}" |
113 | | - ) |
114 | | - allocation_id += ";" |
115 | | - if sorted_variants: |
116 | | - allocation_id = allocation_id[:-1] |
117 | | - |
118 | | - # Create a sha256 hash of the allocation_id |
119 | | - hash_object = hashlib.sha256(allocation_id.encode()) |
120 | | - hash_digest = hash_object.digest() |
121 | | - |
122 | | - # Encode the first 15 bytes in base64 url |
123 | | - allocation_id_hash = base64.urlsafe_b64encode(hash_digest[:15]).decode() |
124 | | - return allocation_id_hash |
125 | | - |
126 | 40 | @staticmethod |
127 | 41 | def _calculate_feature_id(key, label): |
128 | 42 | basic_value = f"{key}\n" |
@@ -150,9 +64,6 @@ def _feature_flag_telemetry( |
150 | 64 | feature_flag_value[TELEMETRY_KEY][METADATA_KEY][FEATURE_FLAG_ID_KEY] = self._calculate_feature_id( |
151 | 65 | feature_flag.key, feature_flag.label |
152 | 66 | ) |
153 | | - allocation_id = self._generate_allocation_id(feature_flag_value) |
154 | | - if allocation_id: |
155 | | - feature_flag_value[TELEMETRY_KEY][METADATA_KEY][ALLOCATION_ID_KEY] = allocation_id |
156 | 67 |
|
157 | 68 | def _feature_flag_appconfig_telemetry( |
158 | 69 | self, feature_flag: FeatureFlagConfigurationSetting, filters_used: Dict[str, bool] |
|
0 commit comments