Skip to content

Commit

Permalink
fix: Move flag evaluation details to a dataclass (#27)
Browse files Browse the repository at this point in the history
* fix/flag-error-message: Move flag evaluation details to a dataclass and add error message to the class
Signed-off-by: Andrew Helsby <[email protected]>

Signed-off-by: Andrew Helsby <[email protected]>

* fix/flag-error-message: Rename flag_key in FlagEvaluationDetails to match spec
Signed-off-by: Andrew Helsby <[email protected]>

Signed-off-by: Andrew Helsby <[email protected]>

* fix/flag-error-message: Rename flag_key in provider.py to match spec
Signed-off-by: Andrew Helsby <[email protected]>

Signed-off-by: Andrew Helsby <[email protected]>

* fix/flag-error-message: Rename flag_key in open_feature_client.py
Signed-off-by: Andrew Helsby <[email protected]>

Signed-off-by: Andrew Helsby <[email protected]>

* fix/flag-error-message: Rename flag_key in open_feature_client.py
Signed-off-by: Andrew Helsby <[email protected]>

Signed-off-by: Andrew Helsby <[email protected]>

* fix/flag-error-message: handle error_message doesn't exist exception
Signed-off-by: Andrew Helsby <[email protected]>

Signed-off-by: Andrew Helsby <[email protected]>

Signed-off-by: Andrew Helsby <[email protected]>
  • Loading branch information
ajhelsby authored Oct 18, 2022
1 parent 7ef8667 commit b44224b
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 52 deletions.
23 changes: 10 additions & 13 deletions open_feature/flag_evaluation/flag_evaluation_details.py
Original file line number Diff line number Diff line change
@@ -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
58 changes: 35 additions & 23 deletions open_feature/open_feature_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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
Expand Down Expand Up @@ -40,119 +41,119 @@ def add_hooks(self, hooks: typing.List[Hook]):

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,
).value

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,
)

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,
).value

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,
)

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,
).value

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,
)

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,
).value

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,
Expand All @@ -161,7 +162,7 @@ def get_object_details(
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,
Expand All @@ -182,7 +183,7 @@ def evaluate_flag_details(
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,
Expand All @@ -207,7 +208,7 @@ def evaluate_flag_details(

flag_evaluation = self.create_provider_evaluation(
flag_type,
key,
flag_key,
default_value,
merged_context,
)
Expand All @@ -216,15 +217,26 @@ def evaluate_flag_details(

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:
Expand All @@ -233,7 +245,7 @@ def evaluate_flag_details(
def create_provider_evaluation(
self,
flag_type: FlagType,
key: str,
flag_key: str,
default_value: typing.Any,
evaluation_context: EvaluationContext = None,
) -> FlagEvaluationDetails:
Expand All @@ -248,7 +260,7 @@ def create_provider_evaluation(
provider
"""
args = (
key,
flag_key,
default_value,
evaluation_context,
)
Expand Down
16 changes: 8 additions & 8 deletions open_feature/provider/no_op_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,51 @@ def get_name(self) -> str:

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,
)

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,
)

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,
)

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,
Expand Down
8 changes: 4 additions & 4 deletions open_feature/provider/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def get_name(self) -> str:
@abstractmethod
def get_boolean_details(
self,
key: str,
flag_key: str,
default_value: bool,
evaluation_context: EvaluationContext = EvaluationContext(),
):
Expand All @@ -21,7 +21,7 @@ def get_boolean_details(
@abstractmethod
def get_string_details(
self,
key: str,
flag_key: str,
default_value: str,
evaluation_context: EvaluationContext = EvaluationContext(),
):
Expand All @@ -30,7 +30,7 @@ def get_string_details(
@abstractmethod
def get_number_details(
self,
key: str,
flag_key: str,
default_value: Number,
evaluation_context: EvaluationContext = EvaluationContext(),
):
Expand All @@ -39,7 +39,7 @@ def get_number_details(
@abstractmethod
def get_object_details(
self,
key: str,
flag_key: str,
default_value: dict,
evaluation_context: EvaluationContext = EvaluationContext(),
):
Expand Down
10 changes: 6 additions & 4 deletions tests/provider/test_no_op_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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"
Expand All @@ -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
Expand Down

0 comments on commit b44224b

Please sign in to comment.