Skip to content

Commit

Permalink
fix!: raise error if the flag wasn't found using the in-memory provid…
Browse files Browse the repository at this point in the history
…er (#228)

Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
  • Loading branch information
beeme1mr authored Nov 1, 2023
1 parent f74eda0 commit 0c314ab
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 41 deletions.
21 changes: 7 additions & 14 deletions openfeature/provider/in_memory_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from openfeature._backports.strenum import StrEnum
from openfeature.evaluation_context import EvaluationContext
from openfeature.exception import ErrorCode
from openfeature.exception import FlagNotFoundError
from openfeature.flag_evaluation import FlagMetadata, FlagResolutionDetails, Reason
from openfeature.hook import Hook
from openfeature.provider.metadata import Metadata
Expand All @@ -26,7 +26,6 @@ class State(StrEnum):
ENABLED = "ENABLED"
DISABLED = "DISABLED"

flag_key: str
default_variant: str
variants: typing.Dict[str, T]
flag_metadata: FlagMetadata = field(default_factory=dict)
Expand Down Expand Up @@ -74,52 +73,46 @@ def resolve_boolean_details(
default_value: bool,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[bool]:
return self._resolve(flag_key, default_value, evaluation_context)
return self._resolve(flag_key, evaluation_context)

def resolve_string_details(
self,
flag_key: str,
default_value: str,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[str]:
return self._resolve(flag_key, default_value, evaluation_context)
return self._resolve(flag_key, evaluation_context)

def resolve_integer_details(
self,
flag_key: str,
default_value: int,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[int]:
return self._resolve(flag_key, default_value, evaluation_context)
return self._resolve(flag_key, evaluation_context)

def resolve_float_details(
self,
flag_key: str,
default_value: float,
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[float]:
return self._resolve(flag_key, default_value, evaluation_context)
return self._resolve(flag_key, evaluation_context)

def resolve_object_details(
self,
flag_key: str,
default_value: typing.Union[dict, list],
evaluation_context: typing.Optional[EvaluationContext] = None,
) -> FlagResolutionDetails[typing.Union[dict, list]]:
return self._resolve(flag_key, default_value, evaluation_context)
return self._resolve(flag_key, evaluation_context)

def _resolve(
self,
flag_key: str,
default_value: V,
evaluation_context: typing.Optional[EvaluationContext],
) -> FlagResolutionDetails[V]:
flag = self._flags.get(flag_key)
if flag is None:
return FlagResolutionDetails(
value=default_value,
reason=Reason.ERROR,
error_code=ErrorCode.FLAG_NOT_FOUND,
error_message=f"Flag '{flag_key}' not found",
)
raise FlagNotFoundError(f"Flag '{flag_key}' not found")
return flag.resolve(evaluation_context)
7 changes: 0 additions & 7 deletions tests/features/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,30 @@ def context_func(flag: InMemoryFlag, evaluation_context: EvaluationContext):

IN_MEMORY_FLAGS = {
"boolean-flag": InMemoryFlag(
flag_key="boolean-flag",
state=InMemoryFlag.State.ENABLED,
default_variant="on",
variants={"on": True, "off": False},
context_evaluator=None,
),
"string-flag": InMemoryFlag(
flag_key="string-flag",
state=InMemoryFlag.State.ENABLED,
default_variant="greeting",
variants={"greeting": "hi", "parting": "bye"},
context_evaluator=None,
),
"integer-flag": InMemoryFlag(
flag_key="integer-flag",
state=InMemoryFlag.State.ENABLED,
default_variant="ten",
variants={"one": 1, "ten": 10},
context_evaluator=None,
),
"float-flag": InMemoryFlag(
flag_key="float-flag",
state=InMemoryFlag.State.ENABLED,
default_variant="half",
variants={"tenth": 0.1, "half": 0.5},
context_evaluator=None,
),
"object-flag": InMemoryFlag(
flag_key="object-flag",
state=InMemoryFlag.State.ENABLED,
default_variant="template",
variants={
Expand All @@ -64,14 +59,12 @@ def context_func(flag: InMemoryFlag, evaluation_context: EvaluationContext):
context_evaluator=None,
),
"context-aware": InMemoryFlag(
flag_key="context-aware",
state=InMemoryFlag.State.ENABLED,
variants={"internal": "INTERNAL", "external": "EXTERNAL"},
default_variant="external",
context_evaluator=context_func,
),
"wrong-flag": InMemoryFlag(
flag_key="wrong-flag",
state="ENABLED",
variants={"one": "uno", "two": "dos"},
default_variant="one",
Expand Down
28 changes: 9 additions & 19 deletions tests/provider/test_in_memory_provider.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from numbers import Number

from openfeature.exception import ErrorCode
from openfeature.exception import FlagNotFoundError
from openfeature.flag_evaluation import FlagResolutionDetails, Reason
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider

Expand All @@ -19,14 +20,9 @@ def test_should_handle_unknown_flags_correctly():
# Given
provider = InMemoryProvider({})
# When
flag = provider.resolve_boolean_details(flag_key="Key", default_value=True)
with pytest.raises(FlagNotFoundError):
provider.resolve_boolean_details(flag_key="Key", default_value=True)
# Then
assert flag is not None
assert flag.value is True
assert isinstance(flag.value, bool)
assert flag.reason == Reason.ERROR
assert flag.error_code == ErrorCode.FLAG_NOT_FOUND
assert flag.error_message == "Flag 'Key' not found"


def test_calls_context_evaluator_if_present():
Expand All @@ -40,7 +36,6 @@ def context_evaluator(flag: InMemoryFlag, evaluation_context: dict):
provider = InMemoryProvider(
{
"Key": InMemoryFlag(
"Key",
"true",
{"true": True, "false": False},
context_evaluator=context_evaluator,
Expand All @@ -59,7 +54,7 @@ def context_evaluator(flag: InMemoryFlag, evaluation_context: dict):
def test_should_resolve_boolean_flag_from_in_memory():
# Given
provider = InMemoryProvider(
{"Key": InMemoryFlag("Key", "true", {"true": True, "false": False})}
{"Key": InMemoryFlag("true", {"true": True, "false": False})}
)
# When
flag = provider.resolve_boolean_details(flag_key="Key", default_value=False)
Expand All @@ -73,7 +68,7 @@ def test_should_resolve_boolean_flag_from_in_memory():
def test_should_resolve_integer_flag_from_in_memory():
# Given
provider = InMemoryProvider(
{"Key": InMemoryFlag("Key", "hundred", {"zero": 0, "hundred": 100})}
{"Key": InMemoryFlag("hundred", {"zero": 0, "hundred": 100})}
)
# When
flag = provider.resolve_integer_details(flag_key="Key", default_value=0)
Expand All @@ -87,7 +82,7 @@ def test_should_resolve_integer_flag_from_in_memory():
def test_should_resolve_float_flag_from_in_memory():
# Given
provider = InMemoryProvider(
{"Key": InMemoryFlag("Key", "ten", {"zero": 0.0, "ten": 10.23})}
{"Key": InMemoryFlag("ten", {"zero": 0.0, "ten": 10.23})}
)
# When
flag = provider.resolve_float_details(flag_key="Key", default_value=0.0)
Expand All @@ -103,7 +98,6 @@ def test_should_resolve_string_flag_from_in_memory():
provider = InMemoryProvider(
{
"Key": InMemoryFlag(
"Key",
"stringVariant",
{"defaultVariant": "Default", "stringVariant": "String"},
)
Expand All @@ -121,11 +115,7 @@ def test_should_resolve_string_flag_from_in_memory():
def test_should_resolve_list_flag_from_in_memory():
# Given
provider = InMemoryProvider(
{
"Key": InMemoryFlag(
"Key", "twoItems", {"empty": [], "twoItems": ["item1", "item2"]}
)
}
{"Key": InMemoryFlag("twoItems", {"empty": [], "twoItems": ["item1", "item2"]})}
)
# When
flag = provider.resolve_object_details(flag_key="Key", default_value=[])
Expand All @@ -144,7 +134,7 @@ def test_should_resolve_object_flag_from_in_memory():
"Boolean": True,
}
provider = InMemoryProvider(
{"Key": InMemoryFlag("Key", "obj", {"obj": return_value, "empty": {}})}
{"Key": InMemoryFlag("obj", {"obj": return_value, "empty": {}})}
)
# When
flag = provider.resolve_object_details(flag_key="Key", default_value={})
Expand Down
1 change: 0 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def test_should_pass_flag_metadata_from_resolution_to_evaluation_details():
provider = InMemoryProvider(
{
"Key": InMemoryFlag(
"Key",
"true",
{"true": True, "false": False},
flag_metadata={"foo": "bar"},
Expand Down

0 comments on commit 0c314ab

Please sign in to comment.