From 6b7bf5dcf27daae03f7c38c628c41d4488df9f8f Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 21 Jul 2023 10:16:45 +0100 Subject: [PATCH] Add feature flag API to Event --- bugsnag/event.py | 27 +++++++++++++- tests/test_event.py | 91 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/bugsnag/event.py b/bugsnag/event.py index 0d9e8351..990894c0 100644 --- a/bugsnag/event.py +++ b/bugsnag/event.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, List # noqa +from typing import Any, Dict, Optional, List, Union # noqa import linecache import logging import os @@ -14,6 +14,7 @@ from bugsnag.utils import fully_qualified_class_name as class_name from bugsnag.utils import FilterDict, package_version, SanitizingJSONEncoder from bugsnag.error import Error +from bugsnag.feature_flags import FeatureFlag, FeatureFlagDelegate __all__ = ('Event',) @@ -65,6 +66,7 @@ def __init__(self, exception: BaseException, config, request_config, self._breadcrumbs = [ deepcopy(breadcrumb) for breadcrumb in config.breadcrumbs ] + self._feature_flag_delegate = FeatureFlagDelegate() def get_config(key): return options.pop(key, getattr(self.config, key)) @@ -237,6 +239,26 @@ def add_tab(self, name, dictionary): self.metadata[name].update(dictionary) + @property + def feature_flags(self) -> List[FeatureFlag]: + return self._feature_flag_delegate.to_list() + + def add_feature_flag( + self, + name: Union[str, bytes], + variant: Union[None, str, bytes] = None + ) -> None: + self._feature_flag_delegate.add(name, variant) + + def add_feature_flags(self, feature_flags: List[FeatureFlag]) -> None: + self._feature_flag_delegate.merge(feature_flags) + + def clear_feature_flag(self, name: Union[str, bytes]) -> None: + self._feature_flag_delegate.remove(name) + + def clear_feature_flags(self) -> None: + self._feature_flag_delegate.clear() + def _generate_error_list( self, exception: BaseException, @@ -437,6 +459,7 @@ def _payload(self): "session": self.session, "breadcrumbs": [ breadcrumb.to_dict() for breadcrumb in self._breadcrumbs - ] + ], + "featureFlags": self._feature_flag_delegate.to_json() }] }) diff --git a/tests/test_event.py b/tests/test_event.py index 7eda818b..68bf126c 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -9,6 +9,7 @@ from bugsnag.breadcrumbs import Breadcrumb, BreadcrumbType from bugsnag.configuration import Configuration from bugsnag.event import Event +from bugsnag.feature_flags import FeatureFlag from tests import fixtures @@ -448,3 +449,93 @@ def test_breadcrumb_array_is_always_in_payload(self): payload = json.loads(event._payload()) assert payload['events'][0]['breadcrumbs'] == [] + + def test_feature_flags_can_be_added_individually(self): + config = Configuration() + event = self.event_class(Exception('oops'), config, {}) + + assert event.feature_flags == [] + + event.add_feature_flag('one') + event.add_feature_flag('two', 'a') + event.add_feature_flag('three', None) + + assert event.feature_flags == [ + FeatureFlag('one'), + FeatureFlag('two', 'a'), + FeatureFlag('three') + ] + + def test_feature_flags_can_be_added_in_bulk(self): + config = Configuration() + event = self.event_class(Exception('oops'), config, {}) + + assert event.feature_flags == [] + + event.add_feature_flags([ + FeatureFlag('a', '1'), + FeatureFlag('b'), + FeatureFlag('c', '3') + ]) + + assert event.feature_flags == [ + FeatureFlag('a', '1'), + FeatureFlag('b'), + FeatureFlag('c', '3') + ] + + def test_feature_flags_can_be_removed_individually(self): + config = Configuration() + event = self.event_class(Exception('oops'), config, {}) + + assert event.feature_flags == [] + + event.add_feature_flags([ + FeatureFlag('a', '1'), + FeatureFlag('b'), + FeatureFlag('c', '3') + ]) + + event.clear_feature_flag('b') + + assert event.feature_flags == [ + FeatureFlag('a', '1'), + FeatureFlag('c', '3') + ] + + def test_feature_flags_can_be_cleared(self): + config = Configuration() + event = self.event_class(Exception('oops'), config, {}) + + assert event.feature_flags == [] + + event.add_feature_flags([ + FeatureFlag('a', '1'), + FeatureFlag('b'), + FeatureFlag('c', '3') + ]) + + event.clear_feature_flags() + + assert event.feature_flags == [] + + def test_feature_flags_are_included_in_payload(self): + config = Configuration() + event = self.event_class(Exception('oops'), config, {}) + + assert event.feature_flags == [] + + event.add_feature_flags([ + FeatureFlag('a', '1'), + FeatureFlag('b'), + FeatureFlag('c', '3') + ]) + + payload = json.loads(event._payload()) + feature_flags = payload['events'][0]['featureFlags'] + + assert feature_flags == [ + {'name': 'a', 'variant': '1'}, + {'name': 'b'}, + {'name': 'c', 'variant': '3'} + ]