Skip to content

Commit 0c314ab

Browse files
authored
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>
1 parent f74eda0 commit 0c314ab

4 files changed

Lines changed: 16 additions & 41 deletions

File tree

openfeature/provider/in_memory_provider.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from openfeature._backports.strenum import StrEnum
55
from openfeature.evaluation_context import EvaluationContext
6-
from openfeature.exception import ErrorCode
6+
from openfeature.exception import FlagNotFoundError
77
from openfeature.flag_evaluation import FlagMetadata, FlagResolutionDetails, Reason
88
from openfeature.hook import Hook
99
from openfeature.provider.metadata import Metadata
@@ -26,7 +26,6 @@ class State(StrEnum):
2626
ENABLED = "ENABLED"
2727
DISABLED = "DISABLED"
2828

29-
flag_key: str
3029
default_variant: str
3130
variants: typing.Dict[str, T]
3231
flag_metadata: FlagMetadata = field(default_factory=dict)
@@ -74,52 +73,46 @@ def resolve_boolean_details(
7473
default_value: bool,
7574
evaluation_context: typing.Optional[EvaluationContext] = None,
7675
) -> FlagResolutionDetails[bool]:
77-
return self._resolve(flag_key, default_value, evaluation_context)
76+
return self._resolve(flag_key, evaluation_context)
7877

7978
def resolve_string_details(
8079
self,
8180
flag_key: str,
8281
default_value: str,
8382
evaluation_context: typing.Optional[EvaluationContext] = None,
8483
) -> FlagResolutionDetails[str]:
85-
return self._resolve(flag_key, default_value, evaluation_context)
84+
return self._resolve(flag_key, evaluation_context)
8685

8786
def resolve_integer_details(
8887
self,
8988
flag_key: str,
9089
default_value: int,
9190
evaluation_context: typing.Optional[EvaluationContext] = None,
9291
) -> FlagResolutionDetails[int]:
93-
return self._resolve(flag_key, default_value, evaluation_context)
92+
return self._resolve(flag_key, evaluation_context)
9493

9594
def resolve_float_details(
9695
self,
9796
flag_key: str,
9897
default_value: float,
9998
evaluation_context: typing.Optional[EvaluationContext] = None,
10099
) -> FlagResolutionDetails[float]:
101-
return self._resolve(flag_key, default_value, evaluation_context)
100+
return self._resolve(flag_key, evaluation_context)
102101

103102
def resolve_object_details(
104103
self,
105104
flag_key: str,
106105
default_value: typing.Union[dict, list],
107106
evaluation_context: typing.Optional[EvaluationContext] = None,
108107
) -> FlagResolutionDetails[typing.Union[dict, list]]:
109-
return self._resolve(flag_key, default_value, evaluation_context)
108+
return self._resolve(flag_key, evaluation_context)
110109

111110
def _resolve(
112111
self,
113112
flag_key: str,
114-
default_value: V,
115113
evaluation_context: typing.Optional[EvaluationContext],
116114
) -> FlagResolutionDetails[V]:
117115
flag = self._flags.get(flag_key)
118116
if flag is None:
119-
return FlagResolutionDetails(
120-
value=default_value,
121-
reason=Reason.ERROR,
122-
error_code=ErrorCode.FLAG_NOT_FOUND,
123-
error_message=f"Flag '{flag_key}' not found",
124-
)
117+
raise FlagNotFoundError(f"Flag '{flag_key}' not found")
125118
return flag.resolve(evaluation_context)

tests/features/data.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,30 @@ def context_func(flag: InMemoryFlag, evaluation_context: EvaluationContext):
2222

2323
IN_MEMORY_FLAGS = {
2424
"boolean-flag": InMemoryFlag(
25-
flag_key="boolean-flag",
2625
state=InMemoryFlag.State.ENABLED,
2726
default_variant="on",
2827
variants={"on": True, "off": False},
2928
context_evaluator=None,
3029
),
3130
"string-flag": InMemoryFlag(
32-
flag_key="string-flag",
3331
state=InMemoryFlag.State.ENABLED,
3432
default_variant="greeting",
3533
variants={"greeting": "hi", "parting": "bye"},
3634
context_evaluator=None,
3735
),
3836
"integer-flag": InMemoryFlag(
39-
flag_key="integer-flag",
4037
state=InMemoryFlag.State.ENABLED,
4138
default_variant="ten",
4239
variants={"one": 1, "ten": 10},
4340
context_evaluator=None,
4441
),
4542
"float-flag": InMemoryFlag(
46-
flag_key="float-flag",
4743
state=InMemoryFlag.State.ENABLED,
4844
default_variant="half",
4945
variants={"tenth": 0.1, "half": 0.5},
5046
context_evaluator=None,
5147
),
5248
"object-flag": InMemoryFlag(
53-
flag_key="object-flag",
5449
state=InMemoryFlag.State.ENABLED,
5550
default_variant="template",
5651
variants={
@@ -64,14 +59,12 @@ def context_func(flag: InMemoryFlag, evaluation_context: EvaluationContext):
6459
context_evaluator=None,
6560
),
6661
"context-aware": InMemoryFlag(
67-
flag_key="context-aware",
6862
state=InMemoryFlag.State.ENABLED,
6963
variants={"internal": "INTERNAL", "external": "EXTERNAL"},
7064
default_variant="external",
7165
context_evaluator=context_func,
7266
),
7367
"wrong-flag": InMemoryFlag(
74-
flag_key="wrong-flag",
7568
state="ENABLED",
7669
variants={"one": "uno", "two": "dos"},
7770
default_variant="one",

tests/provider/test_in_memory_provider.py

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import pytest
12
from numbers import Number
23

3-
from openfeature.exception import ErrorCode
4+
from openfeature.exception import FlagNotFoundError
45
from openfeature.flag_evaluation import FlagResolutionDetails, Reason
56
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
67

@@ -19,14 +20,9 @@ def test_should_handle_unknown_flags_correctly():
1920
# Given
2021
provider = InMemoryProvider({})
2122
# When
22-
flag = provider.resolve_boolean_details(flag_key="Key", default_value=True)
23+
with pytest.raises(FlagNotFoundError):
24+
provider.resolve_boolean_details(flag_key="Key", default_value=True)
2325
# Then
24-
assert flag is not None
25-
assert flag.value is True
26-
assert isinstance(flag.value, bool)
27-
assert flag.reason == Reason.ERROR
28-
assert flag.error_code == ErrorCode.FLAG_NOT_FOUND
29-
assert flag.error_message == "Flag 'Key' not found"
3026

3127

3228
def test_calls_context_evaluator_if_present():
@@ -40,7 +36,6 @@ def context_evaluator(flag: InMemoryFlag, evaluation_context: dict):
4036
provider = InMemoryProvider(
4137
{
4238
"Key": InMemoryFlag(
43-
"Key",
4439
"true",
4540
{"true": True, "false": False},
4641
context_evaluator=context_evaluator,
@@ -59,7 +54,7 @@ def context_evaluator(flag: InMemoryFlag, evaluation_context: dict):
5954
def test_should_resolve_boolean_flag_from_in_memory():
6055
# Given
6156
provider = InMemoryProvider(
62-
{"Key": InMemoryFlag("Key", "true", {"true": True, "false": False})}
57+
{"Key": InMemoryFlag("true", {"true": True, "false": False})}
6358
)
6459
# When
6560
flag = provider.resolve_boolean_details(flag_key="Key", default_value=False)
@@ -73,7 +68,7 @@ def test_should_resolve_boolean_flag_from_in_memory():
7368
def test_should_resolve_integer_flag_from_in_memory():
7469
# Given
7570
provider = InMemoryProvider(
76-
{"Key": InMemoryFlag("Key", "hundred", {"zero": 0, "hundred": 100})}
71+
{"Key": InMemoryFlag("hundred", {"zero": 0, "hundred": 100})}
7772
)
7873
# When
7974
flag = provider.resolve_integer_details(flag_key="Key", default_value=0)
@@ -87,7 +82,7 @@ def test_should_resolve_integer_flag_from_in_memory():
8782
def test_should_resolve_float_flag_from_in_memory():
8883
# Given
8984
provider = InMemoryProvider(
90-
{"Key": InMemoryFlag("Key", "ten", {"zero": 0.0, "ten": 10.23})}
85+
{"Key": InMemoryFlag("ten", {"zero": 0.0, "ten": 10.23})}
9186
)
9287
# When
9388
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():
10398
provider = InMemoryProvider(
10499
{
105100
"Key": InMemoryFlag(
106-
"Key",
107101
"stringVariant",
108102
{"defaultVariant": "Default", "stringVariant": "String"},
109103
)
@@ -121,11 +115,7 @@ def test_should_resolve_string_flag_from_in_memory():
121115
def test_should_resolve_list_flag_from_in_memory():
122116
# Given
123117
provider = InMemoryProvider(
124-
{
125-
"Key": InMemoryFlag(
126-
"Key", "twoItems", {"empty": [], "twoItems": ["item1", "item2"]}
127-
)
128-
}
118+
{"Key": InMemoryFlag("twoItems", {"empty": [], "twoItems": ["item1", "item2"]})}
129119
)
130120
# When
131121
flag = provider.resolve_object_details(flag_key="Key", default_value=[])
@@ -144,7 +134,7 @@ def test_should_resolve_object_flag_from_in_memory():
144134
"Boolean": True,
145135
}
146136
provider = InMemoryProvider(
147-
{"Key": InMemoryFlag("Key", "obj", {"obj": return_value, "empty": {}})}
137+
{"Key": InMemoryFlag("obj", {"obj": return_value, "empty": {}})}
148138
)
149139
# When
150140
flag = provider.resolve_object_details(flag_key="Key", default_value={})

tests/test_client.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ def test_should_pass_flag_metadata_from_resolution_to_evaluation_details():
103103
provider = InMemoryProvider(
104104
{
105105
"Key": InMemoryFlag(
106-
"Key",
107106
"true",
108107
{"true": True, "false": False},
109108
flag_metadata={"foo": "bar"},

0 commit comments

Comments
 (0)