Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Release History

## 2.0.0 (11/19/2024)

* GA release of Feature Management supporting Feature Variants.
* GA release of Telemetry support.
* `publish_telemetry` sends: feature name, enabled, telemetry version, reason, variant name, targeting id, and additionally provided metadata.
* `publish_telemetry` can be added as a callback when creating `FeatureManager` with `on_feature_evaluated=publish_telemetry`
& `track_event` a custom event logger that adds the targeting id to the event.

## 2.0.0b3 (11/14/2024)

* Fixes a bug where no allocation reason is set if a user is allocated to exactly 100.
Expand Down
2 changes: 1 addition & 1 deletion featuremanagement/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
# license information.
# -------------------------------------------------------------------------

VERSION = "2.0.0b3"
VERSION = "2.0.0"
23 changes: 2 additions & 21 deletions featuremanagement/azuremonitor/_send_telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# --------------------------------------------------------------------------
import logging
from typing import Dict, Optional
from .._models import VariantAssignmentReason, EvaluationEvent
from .._models import EvaluationEvent

try:
from azure.monitor.events.extension import track_event as azure_monitor_track_event # type: ignore
Expand All @@ -25,7 +25,7 @@

EVENT_NAME = "FeatureEvaluation"

EVALUATION_EVENT_VERSION = "1.1.0"
EVALUATION_EVENT_VERSION = "1.0.0"


def track_event(event_name: str, user: str, event_properties: Optional[Dict[str, Optional[str]]] = None) -> None:
Expand Down Expand Up @@ -75,25 +75,6 @@ def publish_telemetry(evaluation_event: EvaluationEvent) -> None:
if variant:
event[VARIANT] = variant.name

# VariantAllocationPercentage
allocation_percentage = 0
if reason == VariantAssignmentReason.DEFAULT_WHEN_ENABLED:
event["VariantAssignmentPercentage"] = str(100)
if feature.allocation:
for allocation in feature.allocation.percentile:
allocation_percentage += allocation.percentile_to - allocation.percentile_from
event["VariantAssignmentPercentage"] = str(100 - allocation_percentage)
elif reason == VariantAssignmentReason.PERCENTILE:
if feature.allocation and feature.allocation.percentile:
for allocation in feature.allocation.percentile:
if variant and allocation.variant == variant.name:
allocation_percentage += allocation.percentile_to - allocation.percentile_from
event["VariantAssignmentPercentage"] = str(allocation_percentage)

# DefaultWhenEnabled
if feature.allocation and feature.allocation.default_when_enabled:
event["DefaultWhenEnabled"] = feature.allocation.default_when_enabled

if feature.telemetry:
for metadata_key, metadata_value in feature.telemetry.metadata.items():
if metadata_key not in event:
Expand Down
15 changes: 0 additions & 15 deletions tests/test_send_telemetry_appinsights.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ def test_send_telemetry_appinsights(self):
"ETag": "cmwBRcIAq1jUyKL3Kj8bvf9jtxBrFg-R-ayExStMC90",
"FeatureFlagReference": "fake-store-uri/kv/.appconfig.featureflag/TestFeature",
"FeatureFlagId": "fake-feature-flag-id",
"AllocationId": "fake-allocation-id",
},
},
}
Expand All @@ -46,16 +45,12 @@ def test_send_telemetry_appinsights(self):
assert mock_track_event.call_args[0][1]["Enabled"] == "True"
assert mock_track_event.call_args[0][1]["TargetingId"] == "test_user"
assert mock_track_event.call_args[0][1]["Variant"] == "TestVariant"
assert mock_track_event.call_args[0][1]["VariantAssignmentReason"] == "DefaultWhenDisabled"
assert "VariantAssignmentPercentage" not in mock_track_event.call_args[0][1]
assert mock_track_event.call_args[0][1]["ETag"] == "cmwBRcIAq1jUyKL3Kj8bvf9jtxBrFg-R-ayExStMC90"
assert (
mock_track_event.call_args[0][1]["FeatureFlagReference"]
== "fake-store-uri/kv/.appconfig.featureflag/TestFeature"
)
assert mock_track_event.call_args[0][1]["FeatureFlagId"] == "fake-feature-flag-id"
assert mock_track_event.call_args[0][1]["AllocationId"] == "fake-allocation-id"
assert "DefaultWhenEnabled" not in mock_track_event.call_args[0][1]

def test_send_telemetry_appinsights_no_user(self):
feature_flag = FeatureFlag.convert_from_json({"id": "TestFeature"})
Expand All @@ -78,8 +73,6 @@ def test_send_telemetry_appinsights_no_user(self):
assert "TargetingId" not in mock_track_event.call_args[0][1]
assert mock_track_event.call_args[0][1]["Variant"] == "TestVariant"
assert mock_track_event.call_args[0][1]["VariantAssignmentReason"] == "DefaultWhenDisabled"
assert "VariantAssignmentPercentage" not in mock_track_event.call_args[0][1]
assert "DefaultWhenEnabled" not in mock_track_event.call_args[0][1]

def test_send_telemetry_appinsights_no_variant(self):
feature_flag = FeatureFlag.convert_from_json({"id": "TestFeature"})
Expand Down Expand Up @@ -143,9 +136,6 @@ def test_send_telemetry_appinsights_default_when_enabled(self):
assert mock_track_event.call_args[0][1]["TargetingId"] == "test_user"
assert mock_track_event.call_args[0][1]["Variant"] == "big"
assert mock_track_event.call_args[0][1]["VariantAssignmentReason"] == "DefaultWhenEnabled"
assert mock_track_event.call_args[0][1]["VariantAssignmentPercentage"] == "25"
assert "DefaultWhenEnabled" in mock_track_event.call_args[0][1]
assert mock_track_event.call_args[0][1]["DefaultWhenEnabled"] == "big"

def test_send_telemetry_appinsights_default_when_enabled_no_percentile(self):
feature_flag = FeatureFlag.convert_from_json(
Expand Down Expand Up @@ -176,9 +166,6 @@ def test_send_telemetry_appinsights_default_when_enabled_no_percentile(self):
assert mock_track_event.call_args[0][1]["TargetingId"] == "test_user"
assert mock_track_event.call_args[0][1]["Variant"] == "big"
assert mock_track_event.call_args[0][1]["VariantAssignmentReason"] == "DefaultWhenEnabled"
assert mock_track_event.call_args[0][1]["VariantAssignmentPercentage"] == "100"
assert "DefaultWhenEnabled" in mock_track_event.call_args[0][1]
assert mock_track_event.call_args[0][1]["DefaultWhenEnabled"] == "big"

def test_send_telemetry_appinsights_allocation(self):
feature_flag = FeatureFlag.convert_from_json(
Expand Down Expand Up @@ -209,5 +196,3 @@ def test_send_telemetry_appinsights_allocation(self):
assert mock_track_event.call_args[0][1]["TargetingId"] == "test_user"
assert mock_track_event.call_args[0][1]["Variant"] == "big"
assert mock_track_event.call_args[0][1]["VariantAssignmentReason"] == "Percentile"
assert mock_track_event.call_args[0][1]["VariantAssignmentPercentage"] == "25"
assert "DefaultWhenEnabled" not in mock_track_event.call_args[0][1]
Loading