From 021723a4ee8068109131cb1221b82198886075f9 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Wed, 11 Sep 2024 13:25:35 -0700 Subject: [PATCH 01/11] Adding new telemetry values --- CHANGELOG.md | 5 + docs/conf.py | 2 +- featuremanagement/_featuremanagerbase.py | 5 +- featuremanagement/_models/_allocation.py | 6 +- featuremanagement/_version.py | 2 +- .../azuremonitor/_send_telemetry.py | 100 ++++ pyproject.toml | 2 +- .../feature_variant_sample_with_telemetry.py | 8 +- samples/telemetry_samples.json | 545 ++++++++++++++++++ 9 files changed, 664 insertions(+), 11 deletions(-) create mode 100644 samples/telemetry_samples.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ae64d8..2cca6db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Release History +## 2.0.0b2 (Unreleased) + +* Adds VariantAllocationPercentage, DefaultWhenEnabled, and AllocationId to telemetry. +* Allocation seed value is now None by default, and only defaults to `allocation\n` when assigning variants. + ## 2.0.0b1 (09/10/2024) * Adds support for Feature Variants. diff --git a/docs/conf.py b/docs/conf.py index 67f104f..0fd9889 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,7 +10,7 @@ project = "FeatureManagement" copyright = "2024, Microsoft" author = "Microsoft" -release = "2.0.0b1" +release = "2.0.0b2" # -- General configuration --------------------------------------------------- diff --git a/featuremanagement/_featuremanagerbase.py b/featuremanagement/_featuremanagerbase.py index 4c683c0..c5751ef 100644 --- a/featuremanagement/_featuremanagerbase.py +++ b/featuremanagement/_featuremanagerbase.py @@ -175,7 +175,10 @@ def _assign_variant( evaluation_event.reason = VariantAssignmentReason.GROUP variant_name = group_allocation.variant elif feature.allocation.percentile: - context_id = targeting_context.user_id + "\n" + feature.allocation.seed + seed = feature.allocation.seed + if not seed: + seed = "allocation\n" + feature.id + context_id = targeting_context.user_id + "\n" + seed box: float = self._is_targeted(context_id) for percentile_allocation in feature.allocation.percentile: if box == 100 and percentile_allocation.percentile_to == 100: diff --git a/featuremanagement/_models/_allocation.py b/featuremanagement/_models/_allocation.py index 9fd2250..de9e72e 100644 --- a/featuremanagement/_models/_allocation.py +++ b/featuremanagement/_models/_allocation.py @@ -103,13 +103,13 @@ class Allocation: Represents an allocation configuration for a feature flag. """ - def __init__(self, feature_name: str) -> None: + def __init__(self: str) -> None: self._default_when_enabled = None self._default_when_disabled = None self._user: List[UserAllocation] = [] self._group: List[GroupAllocation] = [] self._percentile: List[PercentileAllocation] = [] - self._seed = "allocation\n" + feature_name + self._seed = None @classmethod def convert_from_json(cls, json: Dict[str, Any], feature_name: str) -> Optional["Allocation"]: @@ -123,7 +123,7 @@ def convert_from_json(cls, json: Dict[str, Any], feature_name: str) -> Optional[ """ if not json: return None - allocation = cls(feature_name) + allocation = cls() allocation._default_when_enabled = json.get(DEFAULT_WHEN_ENABLED) allocation._default_when_disabled = json.get(DEFAULT_WHEN_DISABLED) allocation._user = [] diff --git a/featuremanagement/_version.py b/featuremanagement/_version.py index 32e04dd..99e8b1d 100644 --- a/featuremanagement/_version.py +++ b/featuremanagement/_version.py @@ -4,4 +4,4 @@ # license information. # ------------------------------------------------------------------------- -VERSION = "2.0.0b1" +VERSION = "2.0.0b2" diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index 0a8f946..359bc2a 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -6,6 +6,8 @@ import logging from typing import Dict, Optional from .._models import VariantAssignmentReason, EvaluationEvent +import hashlib +import base64 try: from azure.monitor.events.extension import track_event as azure_monitor_track_event # type: ignore @@ -25,6 +27,75 @@ EVENT_NAME = "FeatureEvaluation" +EVALUATION_EVENT_VERSION = "1.0.0" + +def _generate_allocation_id(feature): + """ + Generates an allocation ID for the specified feature. + + :param FeatureFlag feature: The feature to generate an allocation ID for. + :rtype: str + :return: The allocation ID. + """ + + # Seed + allocation_id = "seed=" + if feature.allocation and feature.allocation._seed: + allocation_id += "{0}".format(feature.allocation._seed) + + allocated_variants = [] + + # DefaultWhenEnabled + if feature.allocation and feature.allocation._default_when_enabled: + allocated_variants.append(feature.allocation._default_when_enabled) + + allocation_id += "\n" + allocation_id += "default_when_enabled=" + if feature.allocation and feature.allocation._default_when_enabled: + allocation_id += "{0}".format(feature.allocation._default_when_enabled) + + # Percentile + allocation_id += "\n" + allocation_id += "percentiles=" + + if feature.allocation and feature.allocation._percentile and len(feature.allocation._percentile) > 0: + percentile_allocations = feature.allocation._percentile + percentile_allocations = filter(lambda x: x.percentile_from != x.percentile_to, percentile_allocations) + percentile_allocations = sorted(percentile_allocations, key=lambda x: x.percentile_from) + + for percentile_allocation in percentile_allocations: + allocated_variants += percentile_allocation.variant + + variant_allocation_id = "" + for allocation in percentile_allocations: + variant_allocation_id = ";{0},{1},{2}".format(allocation.percentile_from, allocation.variant, allocation.percentile_to) + allocation_id += variant_allocation_id[1:] + + # Variants + allocation_id += "\n" + allocation_id += "variants=" + + if len(allocated_variants) > 0: + varients = feature._variants + varients = filter(lambda x: x.name in allocated_variants, varients) + varients = sorted(varients, key=lambda x: x.name) + variant_id = "" + for variant in varients: + variant_id = ";{0},{1}".format(variant.name, variant.configuration_value) + allocation_id += variant_id[1:] + + if feature.allocation and feature.allocation._seed and len(allocated_variants) == 0: + return None + + # 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 + allocation_id_hash = base64.urlsafe_b64encode(hash_digest[:15]).decode('utf-8') + breakpoint() + return allocation_id_hash + def track_event(event_name: str, user: str, event_properties: Optional[Dict[str, Optional[str]]] = None) -> None: """ @@ -55,12 +126,41 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: if evaluation_event.feature: event[FEATURE_NAME] = evaluation_event.feature.name event[ENABLED] = str(evaluation_event.enabled) + event["Version"] = EVALUATION_EVENT_VERSION + # VariantAllocationPercentage if evaluation_event.reason and evaluation_event.reason != VariantAssignmentReason.NONE: if evaluation_event.variant: event[VARIANT] = evaluation_event.variant.name event[REASON] = evaluation_event.reason.value + if evaluation_event.reason == VariantAssignmentReason.DEFAULT_WHEN_ENABLED: + allocation_percentage = 0 + + if evaluation_event.feature.allocation._percentile: + + for allocation in evaluation_event.feature.allocation._percentile: + if allocation.variant == evaluation_event.variant.name: + allocation_percentage += allocation.percentile_to - allocation.percentile_from + + event["VariantAssignmentPercentage"] = 100 - allocation_percentage + elif evaluation_event.reason == VariantAssignmentReason.PERCENTILE: + if evaluation_event.feature.allocation._percentile: + allocation_percentage = 0 + for allocation in evaluation_event.feature.allocation._percentile: + if allocation.variant == evaluation_event.variant.name: + allocation_percentage += allocation.percentile_to - allocation.percentile_from + event["VariantAssignmentPercentage"] = allocation_percentage + + # DefaultWhenEnabled + if evaluation_event.feature.allocation._default_when_enabled: + event["DefaultWhenEnabled"] = evaluation_event.feature.allocation._default_when_enabled + + # AllocationId + allocation_id = _generate_allocation_id(evaluation_event.feature) + if allocation_id: + event["AllocationId"] = allocation_id + if evaluation_event.feature and evaluation_event.feature.telemetry: for metadata_key, metadata_value in evaluation_event.feature.telemetry.metadata.items(): if metadata_key not in event: diff --git a/pyproject.toml b/pyproject.toml index e2aed8b..8da891e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ build-backend = "setuptools.build_meta" [project] name = "FeatureManagement" -version = "2.0.0b1" +version = "2.0.0b2" authors = [ { name="Microsoft Corporation", email="appconfig@microsoft.com" }, ] diff --git a/samples/feature_variant_sample_with_telemetry.py b/samples/feature_variant_sample_with_telemetry.py index d877aff..0e02cf5 100644 --- a/samples/feature_variant_sample_with_telemetry.py +++ b/samples/feature_variant_sample_with_telemetry.py @@ -7,7 +7,7 @@ import os import sys from random_filter import RandomFilter -from featuremanagement import FeatureManager +from featuremanagement import FeatureManager, TargetingContext from featuremanagement.azuremonitor import publish_telemetry, track_event @@ -21,7 +21,7 @@ script_directory = os.path.dirname(os.path.abspath(sys.argv[0])) -with open(script_directory + "/formatted_feature_flags.json", "r", encoding="utf-8") as f: +with open(script_directory + "/telemetry_samples.json", "r", encoding="utf-8") as f: feature_flags = json.load(f) # Initialize the feature manager with telemetry callback @@ -30,7 +30,7 @@ ) # Evaluate the feature flag for the user -print(feature_manager.get_variant("TestVariants", "Adam").configuration) +print(feature_manager.get_variant("VariantFeatureAlwaysOff", TargetingContext(user_id="Marsha", groups=["Group1"]) ).configuration) # Track an event -track_event("TestEvent", "Adam") +track_event("TestEvent", TargetingContext(user_id="Marsha", groups=["Group1"])) diff --git a/samples/telemetry_samples.json b/samples/telemetry_samples.json new file mode 100644 index 0000000..c9cbe52 --- /dev/null +++ b/samples/telemetry_samples.json @@ -0,0 +1,545 @@ +{ + "feature_management": { + "feature_flags": [ + { + "id": "OnTestFeature", + "enabled": true + }, + { + "id": "OffTestFeature", + "enabled": false + }, + { + "id": "ConditionalFeature", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "Test", + "parameters": { + "P1": "V1" + } + } + ] + } + }, + { + "id": "ContextualFeature", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "ContextualTest", + "parameters": { + "AllowedAccounts": [ + "abc" + ] + } + } + ] + } + }, + { + "id": "AnyFilterFeature", + "enabled": true, + "conditions": { + "requirement_type": "Any", + "client_filters": [ + { + "name": "Test", + "parameters": { + "Id": "1" + } + }, + { + "name": "Test", + "parameters": { + "Id": "2" + } + } + ] + } + }, + { + "id": "AllFilterFeature", + "enabled": true, + "conditions": { + "requirement_type": "All", + "client_filters": [ + { + "name": "Test", + "parameters": { + "Id": "1" + } + }, + { + "name": "Test", + "parameters": { + "Id": "2" + } + } + ] + } + }, + { + "id": "FeatureUsesFiltersWithDuplicatedAlias", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "DuplicatedFilterName" + }, + { + "name": "Percentage", + "parameters": { + "Value": 100 + } + } + ] + } + }, + { + "id": "TargetingTestFeature", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "Targeting", + "parameters": { + "Audience": { + "Users": [ + "Jeff", + "Alicia" + ], + "Groups": [ + { + "Name": "Ring0", + "RolloutPercentage": 100 + }, + { + "Name": "Ring1", + "RolloutPercentage": 50 + } + ], + "DefaultRolloutPercentage": 20 + } + } + } + ] + } + }, + { + "id": "TargetingTestFeatureWithExclusion", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "Targeting", + "parameters": { + "Audience": { + "Users": [ + "Jeff", + "Alicia" + ], + "Groups": [ + { + "Name": "Ring0", + "RolloutPercentage": 100 + }, + { + "Name": "Ring1", + "RolloutPercentage": 50 + } + ], + "DefaultRolloutPercentage": 20, + "Exclusion": { + "Users": [ + "Jeff" + ], + "Groups": [ + "Ring0", + "Ring2" + ] + } + } + } + } + ] + } + }, + { + "id": "CustomFilterFeature", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "CustomTargetingFilter", + "parameters": { + "Audience": { + "Users": [ + "Jeff" + ] + } + } + } + ] + } + }, + { + "id": "VariantFeaturePercentileOn", + "enabled": true, + "variants": [ + { + "name": "Big", + "status_override": "Disabled" + } + ], + "allocation": { + "percentile": [ + { + "variant": "Big", + "from": 0, + "to": 50 + } + ], + "seed": 1234 + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeaturePercentileOff", + "enabled": true, + "variants": [ + { + "name": "Big" + } + ], + "allocation": { + "percentile": [ + { + "variant": "Big", + "from": 0, + "to": 50 + } + ], + "seed": 12345 + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureAlwaysOff", + "enabled": false, + "variants": [ + { + "name": "Big" + } + ], + "allocation": { + "percentile": [ + { + "variant": "Big", + "from": 0, + "to": 100 + } + ], + "seed": 12345 + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureDefaultDisabled", + "enabled": false, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + } + ], + "allocation": { + "default_when_disabled": "Small" + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureDefaultEnabled", + "enabled": true, + "variants": [ + { + "name": "Medium", + "configuration_value": { + "Size": "450px", + "Color": "Purple" + } + }, + { + "name": "Small", + "configuration_value": "300px" + } + ], + "allocation": { + "default_when_enabled": "Medium", + "user": [ + { + "variant": "Small", + "users": [ + "Jeff" + ] + } + ] + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureUser", + "enabled": true, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + } + ], + "allocation": { + "user": [ + { + "variant": "Small", + "users": [ + "Marsha" + ] + } + ] + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureGroup", + "enabled": true, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + } + ], + "allocation": { + "group": [ + { + "variant": "Small", + "groups": [ + "Group1" + ] + } + ] + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureNoVariants", + "enabled": true, + "variants": [], + "allocation": { + "user": [ + { + "variant": "Small", + "users": [ + "Marsha" + ] + } + ] + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureNoAllocation", + "enabled": true, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + } + ], + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureIncorrectDefaultWhenEnabled", + "enabled": true, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + } + ], + "allocation": { + "default_when_enabled": "Foo" + }, + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureAlwaysOffNoAllocation", + "enabled": false, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + } + ], + "telemetry": { + "enabled": true + } + }, + { + "id": "VariantFeatureInvalidStatusOverride", + "enabled": true, + "variants": [ + { + "name": "Small", + "configuration_value": "300px", + "status_override": "InvalidValue" + } + ], + "allocation": { + "default_when_enabled": "Small" + } + }, + { + "id": "VariantFeatureInvalidFromTo", + "enabled": true, + "variants": [ + { + "name": "Small", + "configuration_value": "300px" + } + ], + "allocation": { + "percentile": [ + { + "variant": "Small", + "from": "Invalid", + "to": "Invalid" + } + ] + } + }, + { + "id": "VariantImplementationFeature", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "Targeting", + "parameters": { + "Audience": { + "Users": [ + "UserOmega", + "UserSigma", + "UserBeta" + ] + } + } + } + ] + }, + "variants": [ + { + "name": "AlgorithmBeta" + }, + { + "name": "Sigma", + "configuration_value": "AlgorithmSigma" + }, + { + "name": "Omega" + } + ], + "allocation": { + "user": [ + { + "variant": "AlgorithmBeta", + "users": [ + "UserBeta" + ] + }, + { + "variant": "Omega", + "users": [ + "UserOmega" + ] + }, + { + "variant": "Sigma", + "users": [ + "UserSigma" + ] + } + ] + } + }, + { + "id": "OnTelemetryTestFeature", + "enabled": true, + "telemetry": { + "enabled": true, + "metadata": { + "Tags.Tag1": "Tag1Value", + "Tags.Tag2": "Tag2Value", + "Etag": "EtagValue", + "Label": "LabelValue" + } + } + }, + { + "id": "OffTelemetryTestFeature", + "enabled": false, + "telemetry": { + "enabled": true + } + }, + { + "id": "ContextualFeatureWithVariant", + "enabled": true, + "conditions": { + "client_filters": [ + { + "name": "ContextualTest", + "parameters": { + "AllowedAccounts": [ + "abc" + ] + } + } + ] + }, + "variants": [ + { + "name": "Big" + }, + { + "name": "Small" + } + ], + "allocation": { + "default_when_enabled": "Big", + "default_when_disabled": "Small" + } + } + ] + } + } \ No newline at end of file From 6b3e2b90b601d4ccadf7d0a93010cf424ff25dd7 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Wed, 11 Sep 2024 13:30:03 -0700 Subject: [PATCH 02/11] Update _send_telemetry.py --- featuremanagement/azuremonitor/_send_telemetry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index 359bc2a..65fbae3 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -93,7 +93,6 @@ def _generate_allocation_id(feature): # Encode the first 15 bytes in base64 url allocation_id_hash = base64.urlsafe_b64encode(hash_digest[:15]).decode('utf-8') - breakpoint() return allocation_id_hash From c32fa20501c00b5239cf3d976e974c60ec935b11 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Wed, 11 Sep 2024 13:43:48 -0700 Subject: [PATCH 03/11] cleaning up code --- .../azuremonitor/_send_telemetry.py | 84 +++++++++---------- .../feature_variant_sample_with_telemetry.py | 8 +- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index 65fbae3..e377072 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -29,6 +29,7 @@ EVALUATION_EVENT_VERSION = "1.0.0" + def _generate_allocation_id(feature): """ Generates an allocation ID for the specified feature. @@ -38,61 +39,52 @@ def _generate_allocation_id(feature): :return: The allocation ID. """ - # Seed - allocation_id = "seed=" - if feature.allocation and feature.allocation._seed: - allocation_id += "{0}".format(feature.allocation._seed) - + allocation_id = "" allocated_variants = [] - # DefaultWhenEnabled - if feature.allocation and feature.allocation._default_when_enabled: - allocated_variants.append(feature.allocation._default_when_enabled) - - allocation_id += "\n" - allocation_id += "default_when_enabled=" - if feature.allocation and feature.allocation._default_when_enabled: - allocation_id += "{0}".format(feature.allocation._default_when_enabled) - - # Percentile - allocation_id += "\n" - allocation_id += "percentiles=" - - if feature.allocation and feature.allocation._percentile and len(feature.allocation._percentile) > 0: - percentile_allocations = feature.allocation._percentile - percentile_allocations = filter(lambda x: x.percentile_from != x.percentile_to, percentile_allocations) - percentile_allocations = sorted(percentile_allocations, key=lambda x: x.percentile_from) - - for percentile_allocation in percentile_allocations: - allocated_variants += percentile_allocation.variant - - variant_allocation_id = "" - for allocation in percentile_allocations: - variant_allocation_id = ";{0},{1},{2}".format(allocation.percentile_from, allocation.variant, allocation.percentile_to) - allocation_id += variant_allocation_id[1:] + if feature.allocation: + # Seed + allocation_id = f"seed={feature.allocation._seed or ''}" + + # DefaultWhenEnabled + if feature.allocation._default_when_enabled: + allocated_variants.append(feature.allocation._default_when_enabled) + + allocation_id += f"\ndefault_when_enabled={feature.allocation._default_when_enabled or ''}" + + # Percentile + allocation_id += "\npercentiles=" + + if feature.allocation._percentile: + percentile_allocations = sorted( + (x for x in feature.allocation._percentile if x.percentile_from != x.percentile_to), + key=lambda x: x.percentile_from, + ) + + allocated_variants.extend(pa.variant for pa in percentile_allocations) + + allocation_id += ";".join( + f"{pa.percentile_from},{pa.variant},{pa.percentile_to}" for pa in percentile_allocations + ) + else: + allocation_id = "seed=\ndefault_when_enabled=\npercentiles=" # Variants - allocation_id += "\n" - allocation_id += "variants=" - - if len(allocated_variants) > 0: - varients = feature._variants - varients = filter(lambda x: x.name in allocated_variants, varients) - varients = sorted(varients, key=lambda x: x.name) - variant_id = "" - for variant in varients: - variant_id = ";{0},{1}".format(variant.name, variant.configuration_value) - allocation_id += variant_id[1:] - - if feature.allocation and feature.allocation._seed and len(allocated_variants) == 0: + allocation_id += "\nvariants=" + + if allocated_variants: + variants = sorted((v for v in feature._variants if v.name in allocated_variants), key=lambda v: v.name) + allocation_id += ";".join(f"{v.name},{v.configuration_value}" for v in variants) + + if not allocated_variants and (feature.allocation or feature.allocation._seed): return None - + # 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 - allocation_id_hash = base64.urlsafe_b64encode(hash_digest[:15]).decode('utf-8') + allocation_id_hash = base64.urlsafe_b64encode(hash_digest[:15]).decode("utf-8") return allocation_id_hash @@ -137,7 +129,7 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: allocation_percentage = 0 if evaluation_event.feature.allocation._percentile: - + for allocation in evaluation_event.feature.allocation._percentile: if allocation.variant == evaluation_event.variant.name: allocation_percentage += allocation.percentile_to - allocation.percentile_from diff --git a/samples/feature_variant_sample_with_telemetry.py b/samples/feature_variant_sample_with_telemetry.py index 0e02cf5..cf1b0d4 100644 --- a/samples/feature_variant_sample_with_telemetry.py +++ b/samples/feature_variant_sample_with_telemetry.py @@ -7,7 +7,7 @@ import os import sys from random_filter import RandomFilter -from featuremanagement import FeatureManager, TargetingContext +from featuremanagement import FeatureManager from featuremanagement.azuremonitor import publish_telemetry, track_event @@ -21,7 +21,7 @@ script_directory = os.path.dirname(os.path.abspath(sys.argv[0])) -with open(script_directory + "/telemetry_samples.json", "r", encoding="utf-8") as f: +with open(script_directory + "/formatted_feature_flags.json", "r", encoding="utf-8") as f: feature_flags = json.load(f) # Initialize the feature manager with telemetry callback @@ -30,7 +30,7 @@ ) # Evaluate the feature flag for the user -print(feature_manager.get_variant("VariantFeatureAlwaysOff", TargetingContext(user_id="Marsha", groups=["Group1"]) ).configuration) +print(feature_manager.get_variant("TestVariants", "Adam").configuration) # Track an event -track_event("TestEvent", TargetingContext(user_id="Marsha", groups=["Group1"])) +track_event("TestEvent", "Adam") \ No newline at end of file From 83842287cdf992e9af20b10b9cd928ead2dbacde Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Wed, 11 Sep 2024 13:45:01 -0700 Subject: [PATCH 04/11] Removing temp files --- .../feature_variant_sample_with_telemetry.py | 2 +- samples/telemetry_samples.json | 545 ------------------ 2 files changed, 1 insertion(+), 546 deletions(-) delete mode 100644 samples/telemetry_samples.json diff --git a/samples/feature_variant_sample_with_telemetry.py b/samples/feature_variant_sample_with_telemetry.py index cf1b0d4..d877aff 100644 --- a/samples/feature_variant_sample_with_telemetry.py +++ b/samples/feature_variant_sample_with_telemetry.py @@ -33,4 +33,4 @@ print(feature_manager.get_variant("TestVariants", "Adam").configuration) # Track an event -track_event("TestEvent", "Adam") \ No newline at end of file +track_event("TestEvent", "Adam") diff --git a/samples/telemetry_samples.json b/samples/telemetry_samples.json deleted file mode 100644 index c9cbe52..0000000 --- a/samples/telemetry_samples.json +++ /dev/null @@ -1,545 +0,0 @@ -{ - "feature_management": { - "feature_flags": [ - { - "id": "OnTestFeature", - "enabled": true - }, - { - "id": "OffTestFeature", - "enabled": false - }, - { - "id": "ConditionalFeature", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "Test", - "parameters": { - "P1": "V1" - } - } - ] - } - }, - { - "id": "ContextualFeature", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "ContextualTest", - "parameters": { - "AllowedAccounts": [ - "abc" - ] - } - } - ] - } - }, - { - "id": "AnyFilterFeature", - "enabled": true, - "conditions": { - "requirement_type": "Any", - "client_filters": [ - { - "name": "Test", - "parameters": { - "Id": "1" - } - }, - { - "name": "Test", - "parameters": { - "Id": "2" - } - } - ] - } - }, - { - "id": "AllFilterFeature", - "enabled": true, - "conditions": { - "requirement_type": "All", - "client_filters": [ - { - "name": "Test", - "parameters": { - "Id": "1" - } - }, - { - "name": "Test", - "parameters": { - "Id": "2" - } - } - ] - } - }, - { - "id": "FeatureUsesFiltersWithDuplicatedAlias", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "DuplicatedFilterName" - }, - { - "name": "Percentage", - "parameters": { - "Value": 100 - } - } - ] - } - }, - { - "id": "TargetingTestFeature", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "Targeting", - "parameters": { - "Audience": { - "Users": [ - "Jeff", - "Alicia" - ], - "Groups": [ - { - "Name": "Ring0", - "RolloutPercentage": 100 - }, - { - "Name": "Ring1", - "RolloutPercentage": 50 - } - ], - "DefaultRolloutPercentage": 20 - } - } - } - ] - } - }, - { - "id": "TargetingTestFeatureWithExclusion", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "Targeting", - "parameters": { - "Audience": { - "Users": [ - "Jeff", - "Alicia" - ], - "Groups": [ - { - "Name": "Ring0", - "RolloutPercentage": 100 - }, - { - "Name": "Ring1", - "RolloutPercentage": 50 - } - ], - "DefaultRolloutPercentage": 20, - "Exclusion": { - "Users": [ - "Jeff" - ], - "Groups": [ - "Ring0", - "Ring2" - ] - } - } - } - } - ] - } - }, - { - "id": "CustomFilterFeature", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "CustomTargetingFilter", - "parameters": { - "Audience": { - "Users": [ - "Jeff" - ] - } - } - } - ] - } - }, - { - "id": "VariantFeaturePercentileOn", - "enabled": true, - "variants": [ - { - "name": "Big", - "status_override": "Disabled" - } - ], - "allocation": { - "percentile": [ - { - "variant": "Big", - "from": 0, - "to": 50 - } - ], - "seed": 1234 - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeaturePercentileOff", - "enabled": true, - "variants": [ - { - "name": "Big" - } - ], - "allocation": { - "percentile": [ - { - "variant": "Big", - "from": 0, - "to": 50 - } - ], - "seed": 12345 - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureAlwaysOff", - "enabled": false, - "variants": [ - { - "name": "Big" - } - ], - "allocation": { - "percentile": [ - { - "variant": "Big", - "from": 0, - "to": 100 - } - ], - "seed": 12345 - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureDefaultDisabled", - "enabled": false, - "variants": [ - { - "name": "Small", - "configuration_value": "300px" - } - ], - "allocation": { - "default_when_disabled": "Small" - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureDefaultEnabled", - "enabled": true, - "variants": [ - { - "name": "Medium", - "configuration_value": { - "Size": "450px", - "Color": "Purple" - } - }, - { - "name": "Small", - "configuration_value": "300px" - } - ], - "allocation": { - "default_when_enabled": "Medium", - "user": [ - { - "variant": "Small", - "users": [ - "Jeff" - ] - } - ] - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureUser", - "enabled": true, - "variants": [ - { - "name": "Small", - "configuration_value": "300px" - } - ], - "allocation": { - "user": [ - { - "variant": "Small", - "users": [ - "Marsha" - ] - } - ] - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureGroup", - "enabled": true, - "variants": [ - { - "name": "Small", - "configuration_value": "300px" - } - ], - "allocation": { - "group": [ - { - "variant": "Small", - "groups": [ - "Group1" - ] - } - ] - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureNoVariants", - "enabled": true, - "variants": [], - "allocation": { - "user": [ - { - "variant": "Small", - "users": [ - "Marsha" - ] - } - ] - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureNoAllocation", - "enabled": true, - "variants": [ - { - "name": "Small", - "configuration_value": "300px" - } - ], - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureIncorrectDefaultWhenEnabled", - "enabled": true, - "variants": [ - { - "name": "Small", - "configuration_value": "300px" - } - ], - "allocation": { - "default_when_enabled": "Foo" - }, - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureAlwaysOffNoAllocation", - "enabled": false, - "variants": [ - { - "name": "Small", - "configuration_value": "300px" - } - ], - "telemetry": { - "enabled": true - } - }, - { - "id": "VariantFeatureInvalidStatusOverride", - "enabled": true, - "variants": [ - { - "name": "Small", - "configuration_value": "300px", - "status_override": "InvalidValue" - } - ], - "allocation": { - "default_when_enabled": "Small" - } - }, - { - "id": "VariantFeatureInvalidFromTo", - "enabled": true, - "variants": [ - { - "name": "Small", - "configuration_value": "300px" - } - ], - "allocation": { - "percentile": [ - { - "variant": "Small", - "from": "Invalid", - "to": "Invalid" - } - ] - } - }, - { - "id": "VariantImplementationFeature", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "Targeting", - "parameters": { - "Audience": { - "Users": [ - "UserOmega", - "UserSigma", - "UserBeta" - ] - } - } - } - ] - }, - "variants": [ - { - "name": "AlgorithmBeta" - }, - { - "name": "Sigma", - "configuration_value": "AlgorithmSigma" - }, - { - "name": "Omega" - } - ], - "allocation": { - "user": [ - { - "variant": "AlgorithmBeta", - "users": [ - "UserBeta" - ] - }, - { - "variant": "Omega", - "users": [ - "UserOmega" - ] - }, - { - "variant": "Sigma", - "users": [ - "UserSigma" - ] - } - ] - } - }, - { - "id": "OnTelemetryTestFeature", - "enabled": true, - "telemetry": { - "enabled": true, - "metadata": { - "Tags.Tag1": "Tag1Value", - "Tags.Tag2": "Tag2Value", - "Etag": "EtagValue", - "Label": "LabelValue" - } - } - }, - { - "id": "OffTelemetryTestFeature", - "enabled": false, - "telemetry": { - "enabled": true - } - }, - { - "id": "ContextualFeatureWithVariant", - "enabled": true, - "conditions": { - "client_filters": [ - { - "name": "ContextualTest", - "parameters": { - "AllowedAccounts": [ - "abc" - ] - } - } - ] - }, - "variants": [ - { - "name": "Big" - }, - { - "name": "Small" - } - ], - "allocation": { - "default_when_enabled": "Big", - "default_when_disabled": "Small" - } - } - ] - } - } \ No newline at end of file From 799a29e82851552de0e50ff4af8ed844d4b5ddbf Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Thu, 12 Sep 2024 10:52:04 -0700 Subject: [PATCH 05/11] Moving Allocation ID to provider --- featuremanagement/_featuremanagerbase.py | 2 +- featuremanagement/_models/_allocation.py | 6 +- featuremanagement/_models/_feature_flag.py | 6 +- .../azuremonitor/_send_telemetry.py | 88 ++++--------------- 4 files changed, 22 insertions(+), 80 deletions(-) diff --git a/featuremanagement/_featuremanagerbase.py b/featuremanagement/_featuremanagerbase.py index c5751ef..b86bec0 100644 --- a/featuremanagement/_featuremanagerbase.py +++ b/featuremanagement/_featuremanagerbase.py @@ -177,7 +177,7 @@ def _assign_variant( elif feature.allocation.percentile: seed = feature.allocation.seed if not seed: - seed = "allocation\n" + feature.id + seed = "allocation\n" + feature.name context_id = targeting_context.user_id + "\n" + seed box: float = self._is_targeted(context_id) for percentile_allocation in feature.allocation.percentile: diff --git a/featuremanagement/_models/_allocation.py b/featuremanagement/_models/_allocation.py index de9e72e..2ce0fb6 100644 --- a/featuremanagement/_models/_allocation.py +++ b/featuremanagement/_models/_allocation.py @@ -103,7 +103,7 @@ class Allocation: Represents an allocation configuration for a feature flag. """ - def __init__(self: str) -> None: + def __init__(self) -> None: self._default_when_enabled = None self._default_when_disabled = None self._user: List[UserAllocation] = [] @@ -112,7 +112,7 @@ def __init__(self: str) -> None: self._seed = None @classmethod - def convert_from_json(cls, json: Dict[str, Any], feature_name: str) -> Optional["Allocation"]: + def convert_from_json(cls, json: Dict[str, Any]) -> Optional["Allocation"]: """ Convert a JSON object to Allocation. @@ -197,7 +197,7 @@ def percentile(self) -> List[PercentileAllocation]: return self._percentile @property - def seed(self) -> str: + def seed(self) -> Optional[str]: """ Get the seed for the allocation. diff --git a/featuremanagement/_models/_feature_flag.py b/featuremanagement/_models/_feature_flag.py index 387e6f8..e0665c1 100644 --- a/featuremanagement/_models/_feature_flag.py +++ b/featuremanagement/_models/_feature_flag.py @@ -51,9 +51,7 @@ def convert_from_json(cls, json_value: Mapping[str, Any]) -> "FeatureFlag": ) else: feature_flag._conditions = FeatureConditions() - feature_flag._allocation = Allocation.convert_from_json( - json_value.get(FEATURE_FLAG_ALLOCATION, None), feature_flag._id - ) + feature_flag._allocation = Allocation.convert_from_json(json_value.get(FEATURE_FLAG_ALLOCATION, None)) if FEATURE_FLAG_VARIANTS in json_value: variants: List[Mapping[str, Any]] = json_value.get(FEATURE_FLAG_VARIANTS, []) feature_flag._variants = [] @@ -66,7 +64,7 @@ def convert_from_json(cls, json_value: Mapping[str, Any]) -> "FeatureFlag": return feature_flag @property - def name(self) -> Optional[str]: + def name(self) -> str: """ Get the name of the feature flag. diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index e377072..7dda40a 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -4,10 +4,11 @@ # license information. # -------------------------------------------------------------------------- import logging +import json from typing import Dict, Optional -from .._models import VariantAssignmentReason, EvaluationEvent import hashlib import base64 +from .._models import VariantAssignmentReason, EvaluationEvent, FeatureFlag try: from azure.monitor.events.extension import track_event as azure_monitor_track_event # type: ignore @@ -29,65 +30,6 @@ EVALUATION_EVENT_VERSION = "1.0.0" - -def _generate_allocation_id(feature): - """ - Generates an allocation ID for the specified feature. - - :param FeatureFlag feature: The feature to generate an allocation ID for. - :rtype: str - :return: The allocation ID. - """ - - allocation_id = "" - allocated_variants = [] - - if feature.allocation: - # Seed - allocation_id = f"seed={feature.allocation._seed or ''}" - - # DefaultWhenEnabled - if feature.allocation._default_when_enabled: - allocated_variants.append(feature.allocation._default_when_enabled) - - allocation_id += f"\ndefault_when_enabled={feature.allocation._default_when_enabled or ''}" - - # Percentile - allocation_id += "\npercentiles=" - - if feature.allocation._percentile: - percentile_allocations = sorted( - (x for x in feature.allocation._percentile if x.percentile_from != x.percentile_to), - key=lambda x: x.percentile_from, - ) - - allocated_variants.extend(pa.variant for pa in percentile_allocations) - - allocation_id += ";".join( - f"{pa.percentile_from},{pa.variant},{pa.percentile_to}" for pa in percentile_allocations - ) - else: - allocation_id = "seed=\ndefault_when_enabled=\npercentiles=" - - # Variants - allocation_id += "\nvariants=" - - if allocated_variants: - variants = sorted((v for v in feature._variants if v.name in allocated_variants), key=lambda v: v.name) - allocation_id += ";".join(f"{v.name},{v.configuration_value}" for v in variants) - - if not allocated_variants and (feature.allocation or feature.allocation._seed): - return None - - # 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 - allocation_id_hash = base64.urlsafe_b64encode(hash_digest[:15]).decode("utf-8") - return allocation_id_hash - - def track_event(event_name: str, user: str, event_properties: Optional[Dict[str, Optional[str]]] = None) -> None: """ Tracks an event with the specified name and properties. @@ -114,8 +56,7 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: if not HAS_AZURE_MONITOR_EVENTS_EXTENSION: return event = {} - if evaluation_event.feature: - event[FEATURE_NAME] = evaluation_event.feature.name + event[FEATURE_NAME] = evaluation_event.feature.name event[ENABLED] = str(evaluation_event.enabled) event["Version"] = EVALUATION_EVENT_VERSION @@ -128,31 +69,34 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: if evaluation_event.reason == VariantAssignmentReason.DEFAULT_WHEN_ENABLED: allocation_percentage = 0 - if evaluation_event.feature.allocation._percentile: - - for allocation in evaluation_event.feature.allocation._percentile: - if allocation.variant == evaluation_event.variant.name: + if evaluation_event.feature.allocation and evaluation_event.feature.allocation.percentile: + for allocation in evaluation_event.feature.allocation.percentile: + if ( + evaluation_event.variant + and allocation.variant == evaluation_event.variant.name + and allocation.percentile_to + ): allocation_percentage += allocation.percentile_to - allocation.percentile_from event["VariantAssignmentPercentage"] = 100 - allocation_percentage elif evaluation_event.reason == VariantAssignmentReason.PERCENTILE: - if evaluation_event.feature.allocation._percentile: + if evaluation_event.feature.allocation and evaluation_event.feature.allocation.percentile: allocation_percentage = 0 - for allocation in evaluation_event.feature.allocation._percentile: + for allocation in evaluation_event.feature.allocation.percentile: if allocation.variant == evaluation_event.variant.name: allocation_percentage += allocation.percentile_to - allocation.percentile_from event["VariantAssignmentPercentage"] = allocation_percentage # DefaultWhenEnabled - if evaluation_event.feature.allocation._default_when_enabled: - event["DefaultWhenEnabled"] = evaluation_event.feature.allocation._default_when_enabled + if evaluation_event.feature.allocation.default_when_enabled: + event["DefaultWhenEnabled"] = evaluation_event.feature.allocation.default_when_enabled # AllocationId - allocation_id = _generate_allocation_id(evaluation_event.feature) + allocation_id = evaluation_event.feature.telemetry.get("metadata", {}).get("AllocationId") if allocation_id: event["AllocationId"] = allocation_id - if evaluation_event.feature and evaluation_event.feature.telemetry: + if evaluation_event.feature.telemetry: for metadata_key, metadata_value in evaluation_event.feature.telemetry.metadata.items(): if metadata_key not in event: event[metadata_key] = metadata_value From 43e5a797bac0d703c1ebb8ef4546cf4c177ed22d Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Thu, 12 Sep 2024 11:00:44 -0700 Subject: [PATCH 06/11] type check fixes --- .../azuremonitor/_send_telemetry.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index 7dda40a..d58c1ab 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -4,11 +4,8 @@ # license information. # -------------------------------------------------------------------------- import logging -import json from typing import Dict, Optional -import hashlib -import base64 -from .._models import VariantAssignmentReason, EvaluationEvent, FeatureFlag +from .._models import VariantAssignmentReason, EvaluationEvent try: from azure.monitor.events.extension import track_event as azure_monitor_track_event # type: ignore @@ -30,6 +27,7 @@ EVALUATION_EVENT_VERSION = "1.0.0" + def track_event(event_name: str, user: str, event_properties: Optional[Dict[str, Optional[str]]] = None) -> None: """ Tracks an event with the specified name and properties. @@ -56,6 +54,8 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: if not HAS_AZURE_MONITOR_EVENTS_EXTENSION: return event = {} + if not evaluation_event.feature: + return event[FEATURE_NAME] = evaluation_event.feature.name event[ENABLED] = str(evaluation_event.enabled) event["Version"] = EVALUATION_EVENT_VERSION @@ -78,21 +78,25 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: ): allocation_percentage += allocation.percentile_to - allocation.percentile_from - event["VariantAssignmentPercentage"] = 100 - allocation_percentage + event["VariantAssignmentPercentage"] = str(100 - allocation_percentage) elif evaluation_event.reason == VariantAssignmentReason.PERCENTILE: - if evaluation_event.feature.allocation and evaluation_event.feature.allocation.percentile: + if evaluation_event.feature.allocation and evaluation_event.feature.allocation.percentile: allocation_percentage = 0 for allocation in evaluation_event.feature.allocation.percentile: - if allocation.variant == evaluation_event.variant.name: + if ( + evaluation_event.variant + and allocation.variant == evaluation_event.variant.name + and allocation.percentile_to + ): allocation_percentage += allocation.percentile_to - allocation.percentile_from - event["VariantAssignmentPercentage"] = allocation_percentage + event["VariantAssignmentPercentage"] = str(allocation_percentage) # DefaultWhenEnabled - if evaluation_event.feature.allocation.default_when_enabled: + if evaluation_event.feature.allocation and evaluation_event.feature.allocation.default_when_enabled: event["DefaultWhenEnabled"] = evaluation_event.feature.allocation.default_when_enabled # AllocationId - allocation_id = evaluation_event.feature.telemetry.get("metadata", {}).get("AllocationId") + allocation_id = evaluation_event.feature.telemetry.metadata.get("AllocationId") if allocation_id: event["AllocationId"] = allocation_id From 1e7fd28bb67300c56b1d2da8270e73c0a38b012a Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Thu, 12 Sep 2024 11:02:55 -0700 Subject: [PATCH 07/11] Update _send_telemetry.py --- featuremanagement/azuremonitor/_send_telemetry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index d58c1ab..8531179 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -53,7 +53,7 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: """ if not HAS_AZURE_MONITOR_EVENTS_EXTENSION: return - event = {} + event: Dict[str, Optional[str]] = {} if not evaluation_event.feature: return event[FEATURE_NAME] = evaluation_event.feature.name From 3bb4ffff801bb9b4c2c99241d88972d8c4e88c70 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Fri, 27 Sep 2024 12:07:04 -0700 Subject: [PATCH 08/11] Update _featuremanagerbase.py --- featuremanagement/_featuremanagerbase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/featuremanagement/_featuremanagerbase.py b/featuremanagement/_featuremanagerbase.py index cc5a7bf..64732e5 100644 --- a/featuremanagement/_featuremanagerbase.py +++ b/featuremanagement/_featuremanagerbase.py @@ -174,7 +174,7 @@ def _assign_variant( if group in group_allocation.groups: evaluation_event.reason = VariantAssignmentReason.GROUP variant_name = group_allocation.variant - if not variant_name and feature.allocation.percentile: + if not variant_name and feature.allocation.percentile: seed = feature.allocation.seed if not seed: seed = "allocation\n" + feature.name From bcbc9e638444df81f28d40f6a94255b588aeb8e5 Mon Sep 17 00:00:00 2001 From: Matthew Metcalf Date: Fri, 11 Oct 2024 09:08:44 -0700 Subject: [PATCH 09/11] Update featuremanagement/azuremonitor/_send_telemetry.py Co-authored-by: Ross Grambo --- featuremanagement/azuremonitor/_send_telemetry.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/featuremanagement/azuremonitor/_send_telemetry.py b/featuremanagement/azuremonitor/_send_telemetry.py index 8531179..bd84fd7 100644 --- a/featuremanagement/azuremonitor/_send_telemetry.py +++ b/featuremanagement/azuremonitor/_send_telemetry.py @@ -95,11 +95,6 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None: if evaluation_event.feature.allocation and evaluation_event.feature.allocation.default_when_enabled: event["DefaultWhenEnabled"] = evaluation_event.feature.allocation.default_when_enabled - # AllocationId - allocation_id = evaluation_event.feature.telemetry.metadata.get("AllocationId") - if allocation_id: - event["AllocationId"] = allocation_id - if evaluation_event.feature.telemetry: for metadata_key, metadata_value in evaluation_event.feature.telemetry.metadata.items(): if metadata_key not in event: From aebbe4e672c1d2194ac13cba816bf432eca199ce Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Fri, 11 Oct 2024 09:17:31 -0700 Subject: [PATCH 10/11] trying to fix formatter --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 2eff0bb..0d33430 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,7 +1,7 @@ pytest pytest-cov pytest-asyncio -black +black<24.10.0 pylint mypy sphinx From dd2211147678b0af316f7e70b4ef83cde056a128 Mon Sep 17 00:00:00 2001 From: Matt Metcalf Date: Fri, 11 Oct 2024 09:22:36 -0700 Subject: [PATCH 11/11] trying to fix black --- .github/workflows/validate.yml | 2 +- dev_requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index ebbe669..1a3e75e 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -22,7 +22,7 @@ jobs: - name: Analysing the code with pylint run: | pylint featuremanagement - - uses: psf/black@stable + - uses: psf/black@24.8.0 - name: Run mypy run: | mypy featuremanagement diff --git a/dev_requirements.txt b/dev_requirements.txt index 0d33430..2eff0bb 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,7 +1,7 @@ pytest pytest-cov pytest-asyncio -black<24.10.0 +black pylint mypy sphinx