From b44224be0ddaf3745d031d6e7caea19f41322cf1 Mon Sep 17 00:00:00 2001 From: Andrew Helsby Date: Tue, 18 Oct 2022 15:23:24 +0400 Subject: [PATCH] fix: Move flag evaluation details to a dataclass (#27) * fix/flag-error-message: Move flag evaluation details to a dataclass and add error message to the class Signed-off-by: Andrew Helsby Signed-off-by: Andrew Helsby * fix/flag-error-message: Rename flag_key in FlagEvaluationDetails to match spec Signed-off-by: Andrew Helsby Signed-off-by: Andrew Helsby * fix/flag-error-message: Rename flag_key in provider.py to match spec Signed-off-by: Andrew Helsby Signed-off-by: Andrew Helsby * fix/flag-error-message: Rename flag_key in open_feature_client.py Signed-off-by: Andrew Helsby Signed-off-by: Andrew Helsby * fix/flag-error-message: Rename flag_key in open_feature_client.py Signed-off-by: Andrew Helsby Signed-off-by: Andrew Helsby * fix/flag-error-message: handle error_message doesn't exist exception Signed-off-by: Andrew Helsby Signed-off-by: Andrew Helsby Signed-off-by: Andrew Helsby --- .../flag_evaluation_details.py | 23 ++++---- open_feature/open_feature_client.py | 58 +++++++++++-------- open_feature/provider/no_op_provider.py | 16 ++--- open_feature/provider/provider.py | 8 +-- tests/provider/test_no_op_provider.py | 10 ++-- 5 files changed, 63 insertions(+), 52 deletions(-) diff --git a/open_feature/flag_evaluation/flag_evaluation_details.py b/open_feature/flag_evaluation/flag_evaluation_details.py index 4deb9b1..0caf4c0 100644 --- a/open_feature/flag_evaluation/flag_evaluation_details.py +++ b/open_feature/flag_evaluation/flag_evaluation_details.py @@ -1,18 +1,15 @@ +import typing +from dataclasses import dataclass + from open_feature.flag_evaluation.error_code import ErrorCode from open_feature.flag_evaluation.reason import Reason +@dataclass class FlagEvaluationDetails: - def __init__( - self, - key: str, - value, - reason: Reason, - error_code: ErrorCode = None, - variant=None, - ): - self.key = key - self.value = value - self.reason = reason - self.error_code = error_code - self.variant = variant + flag_key: str + value: typing.Any + variant: str = None + reason: Reason = None + error_code: ErrorCode = None + error_message: str = None diff --git a/open_feature/open_feature_client.py b/open_feature/open_feature_client.py index 83c0b5f..a685f4b 100644 --- a/open_feature/open_feature_client.py +++ b/open_feature/open_feature_client.py @@ -4,6 +4,7 @@ from numbers import Number from open_feature.evaluation_context.evaluation_context import EvaluationContext from open_feature.exception.exceptions import GeneralError +from open_feature.flag_evaluation.error_code import ErrorCode from open_feature.flag_evaluation.flag_evaluation_details import FlagEvaluationDetails from open_feature.flag_evaluation.flag_type import FlagType from open_feature.flag_evaluation.reason import Reason @@ -40,14 +41,14 @@ class OpenFeatureClient: def get_boolean_value( self, - key: str, + flag_key: str, default_value: bool, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> bool: return self.evaluate_flag_details( FlagType.BOOLEAN, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -55,14 +56,14 @@ class OpenFeatureClient: def get_boolean_details( self, - key: str, + flag_key: str, default_value: bool, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> FlagEvaluationDetails: return self.evaluate_flag_details( FlagType.BOOLEAN, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -70,14 +71,14 @@ class OpenFeatureClient: def get_string_value( self, - key: str, + flag_key: str, default_value: str, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> str: return self.evaluate_flag_details( FlagType.STRING, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -85,14 +86,14 @@ class OpenFeatureClient: def get_string_details( self, - key: str, + flag_key: str, default_value: str, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> FlagEvaluationDetails: return self.evaluate_flag_details( FlagType.STRING, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -100,14 +101,14 @@ class OpenFeatureClient: def get_number_value( self, - key: str, + flag_key: str, default_value: Number, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> Number: return self.evaluate_flag_details( FlagType.NUMBER, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -115,14 +116,14 @@ class OpenFeatureClient: def get_number_details( self, - key: str, + flag_key: str, default_value: Number, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> FlagEvaluationDetails: return self.evaluate_flag_details( FlagType.NUMBER, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -130,14 +131,14 @@ class OpenFeatureClient: def get_object_value( self, - key: str, + flag_key: str, default_value: dict, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> dict: return self.evaluate_flag_details( FlagType.OBJECT, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -145,14 +146,14 @@ class OpenFeatureClient: def get_object_details( self, - key: str, + flag_key: str, default_value: dict, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, ) -> FlagEvaluationDetails: return self.evaluate_flag_details( FlagType.OBJECT, - key, + flag_key, default_value, evaluation_context, flag_evaluation_options, @@ -161,7 +162,7 @@ class OpenFeatureClient: def evaluate_flag_details( self, flag_type: FlagType, - key: str, + flag_key: str, default_value: typing.Any, evaluation_context: EvaluationContext = None, flag_evaluation_options: typing.Any = None, @@ -182,7 +183,7 @@ class OpenFeatureClient: evaluation_context = EvaluationContext() hook_context = HookContext( - flag_key=key, + flag_key=flag_key, flag_type=flag_type, default_value=default_value, evaluation_context=evaluation_context, @@ -207,7 +208,7 @@ class OpenFeatureClient: flag_evaluation = self.create_provider_evaluation( flag_type, - key, + flag_key, default_value, merged_context, ) @@ -216,15 +217,26 @@ class OpenFeatureClient: return flag_evaluation + except OpenFeatureError as e: # noqa + error_hooks(flag_type, hook_context, e, merged_hooks, None) + return FlagEvaluationDetails( + flag_key=flag_key, + value=default_value, + reason=Reason.ERROR, + error_code=e.error_code, + error_message=e.error_message, + ) # Catch any type of exception here since the user can provide any exception # in the error hooks except Exception as e: # noqa error_hooks(flag_type, hook_context, e, merged_hooks, None) + error_message = getattr(e, "error_message", str(e)) return FlagEvaluationDetails( - key=key, + flag_key=flag_key, value=default_value, reason=Reason.ERROR, - error_code=e.error_message, + error_code=ErrorCode.GENERAL, + error_message=error_message, ) finally: @@ -233,7 +245,7 @@ class OpenFeatureClient: def create_provider_evaluation( self, flag_type: FlagType, - key: str, + flag_key: str, default_value: typing.Any, evaluation_context: EvaluationContext = None, ) -> FlagEvaluationDetails: @@ -248,7 +260,7 @@ class OpenFeatureClient: provider """ args = ( - key, + flag_key, default_value, evaluation_context, ) diff --git a/open_feature/provider/no_op_provider.py b/open_feature/provider/no_op_provider.py index d91395e..515c447 100644 --- a/open_feature/provider/no_op_provider.py +++ b/open_feature/provider/no_op_provider.py @@ -14,12 +14,12 @@ class NoOpProvider(AbstractProvider): def get_boolean_details( self, - key: str, + flag_key: str, default_value: bool, evaluation_context: EvaluationContext = None, ): return FlagEvaluationDetails( - key=key, + flag_key=flag_key, value=default_value, reason=Reason.DEFAULT, variant=PASSED_IN_DEFAULT, @@ -27,12 +27,12 @@ class NoOpProvider(AbstractProvider): def get_string_details( self, - key: str, + flag_key: str, default_value: str, evaluation_context: EvaluationContext = None, ): return FlagEvaluationDetails( - key=key, + flag_key=flag_key, value=default_value, reason=Reason.DEFAULT, variant=PASSED_IN_DEFAULT, @@ -40,12 +40,12 @@ class NoOpProvider(AbstractProvider): def get_number_details( self, - key: str, + flag_key: str, default_value: Number, evaluation_context: EvaluationContext = None, ): return FlagEvaluationDetails( - key=key, + flag_key=flag_key, value=default_value, reason=Reason.DEFAULT, variant=PASSED_IN_DEFAULT, @@ -53,12 +53,12 @@ class NoOpProvider(AbstractProvider): def get_object_details( self, - key: str, + flag_key: str, default_value: dict, evaluation_context: EvaluationContext = None, ): return FlagEvaluationDetails( - key=key, + flag_key=flag_key, value=default_value, reason=Reason.DEFAULT, variant=PASSED_IN_DEFAULT, diff --git a/open_feature/provider/provider.py b/open_feature/provider/provider.py index f305703..10f3738 100644 --- a/open_feature/provider/provider.py +++ b/open_feature/provider/provider.py @@ -12,7 +12,7 @@ class AbstractProvider: @abstractmethod def get_boolean_details( self, - key: str, + flag_key: str, default_value: bool, evaluation_context: EvaluationContext = EvaluationContext(), ): @@ -21,7 +21,7 @@ class AbstractProvider: @abstractmethod def get_string_details( self, - key: str, + flag_key: str, default_value: str, evaluation_context: EvaluationContext = EvaluationContext(), ): @@ -30,7 +30,7 @@ class AbstractProvider: @abstractmethod def get_number_details( self, - key: str, + flag_key: str, default_value: Number, evaluation_context: EvaluationContext = EvaluationContext(), ): @@ -39,7 +39,7 @@ class AbstractProvider: @abstractmethod def get_object_details( self, - key: str, + flag_key: str, default_value: dict, evaluation_context: EvaluationContext = EvaluationContext(), ): diff --git a/tests/provider/test_no_op_provider.py b/tests/provider/test_no_op_provider.py index a493f8e..7b23714 100644 --- a/tests/provider/test_no_op_provider.py +++ b/tests/provider/test_no_op_provider.py @@ -13,7 +13,7 @@ def setup(): def test_should_get_boolean_flag_from_no_op(no_op_provider_client): # Given # When - flag = no_op_provider_client.get_boolean_details(key="Key", default_value=True) + flag = no_op_provider_client.get_boolean_details(flag_key="Key", default_value=True) # Then assert flag is not None assert flag.value @@ -23,7 +23,7 @@ def test_should_get_boolean_flag_from_no_op(no_op_provider_client): def test_should_get_number_flag_from_no_op(no_op_provider_client): # Given # When - flag = no_op_provider_client.get_number_details(key="Key", default_value=100) + flag = no_op_provider_client.get_number_details(flag_key="Key", default_value=100) # Then assert flag is not None assert flag.value == 100 @@ -33,7 +33,9 @@ def test_should_get_number_flag_from_no_op(no_op_provider_client): def test_should_get_string_flag_from_no_op(no_op_provider_client): # Given # When - flag = no_op_provider_client.get_string_details(key="Key", default_value="String") + flag = no_op_provider_client.get_string_details( + flag_key="Key", default_value="String" + ) # Then assert flag is not None assert flag.value == "String" @@ -49,7 +51,7 @@ def test_should_get_object_flag_from_no_op(no_op_provider_client): } # When flag = no_op_provider_client.get_string_details( - key="Key", default_value=return_value + flag_key="Key", default_value=return_value ) # Then assert flag is not None