fix!: raise error if the flag wasn't found using the in-memory provider (#228)
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
This commit is contained in:
parent
f74eda06bd
commit
0c314ab77c
|
|
@ -3,7 +3,7 @@ from dataclasses import dataclass, field
|
|||
|
||||
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
|
||||
|
|
@ -26,7 +26,6 @@ class InMemoryFlag(typing.Generic[T]):
|
|||
ENABLED = "ENABLED"
|
||||
DISABLED = "DISABLED"
|
||||
|
||||
flag_key: str
|
||||
default_variant: str
|
||||
variants: typing.Dict[str, T]
|
||||
flag_metadata: FlagMetadata = field(default_factory=dict)
|
||||
|
|
@ -74,7 +73,7 @@ class InMemoryProvider(AbstractProvider):
|
|||
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,
|
||||
|
|
@ -82,7 +81,7 @@ class InMemoryProvider(AbstractProvider):
|
|||
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,
|
||||
|
|
@ -90,7 +89,7 @@ class InMemoryProvider(AbstractProvider):
|
|||
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,
|
||||
|
|
@ -98,7 +97,7 @@ class InMemoryProvider(AbstractProvider):
|
|||
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,
|
||||
|
|
@ -106,20 +105,14 @@ class InMemoryProvider(AbstractProvider):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -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={
|
||||
|
|
@ -64,14 +59,12 @@ IN_MEMORY_FLAGS = {
|
|||
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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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():
|
||||
|
|
@ -40,7 +36,6 @@ def test_calls_context_evaluator_if_present():
|
|||
provider = InMemoryProvider(
|
||||
{
|
||||
"Key": InMemoryFlag(
|
||||
"Key",
|
||||
"true",
|
||||
{"true": True, "false": False},
|
||||
context_evaluator=context_evaluator,
|
||||
|
|
@ -59,7 +54,7 @@ def test_calls_context_evaluator_if_present():
|
|||
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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -103,7 +98,6 @@ def test_should_resolve_string_flag_from_in_memory():
|
|||
provider = InMemoryProvider(
|
||||
{
|
||||
"Key": InMemoryFlag(
|
||||
"Key",
|
||||
"stringVariant",
|
||||
{"defaultVariant": "Default", "stringVariant": "String"},
|
||||
)
|
||||
|
|
@ -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=[])
|
||||
|
|
@ -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={})
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
Loading…
Reference in New Issue