Skip to content

Commit ada2073

Browse files
committed
feat: pass flag_metadata from resolution to evaluation details
Signed-off-by: Federico Bond <federicobond@gmail.com>
1 parent d41cea2 commit ada2073

File tree

5 files changed

+37
-5
lines changed

5 files changed

+37
-5
lines changed

openfeature/client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ def _create_provider_evaluation(
377377
flag_key=flag_key,
378378
value=resolution.value,
379379
variant=resolution.variant,
380+
flag_metadata=resolution.flag_metadata or {},
380381
reason=resolution.reason,
381382
error_code=resolution.error_code,
382383
error_message=resolution.error_message,

openfeature/flag_evaluation.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import typing
4+
from collections.abc import Mapping
45
from dataclasses import dataclass, field
56

67
from openfeature._backports.strenum import StrEnum
@@ -29,6 +30,8 @@ class Reason(StrEnum):
2930
UNKNOWN = "UNKNOWN"
3031

3132

33+
FlagMetadata = Mapping[str, typing.Any]
34+
3235
T = typing.TypeVar("T", covariant=True)
3336

3437

@@ -37,6 +40,7 @@ class FlagEvaluationDetails(typing.Generic[T]):
3740
flag_key: str
3841
value: T
3942
variant: typing.Optional[str] = None
43+
flag_metadata: FlagMetadata = field(default_factory=dict)
4044
reason: typing.Optional[Reason] = None
4145
error_code: typing.Optional[ErrorCode] = None
4246
error_message: typing.Optional[str] = None
@@ -58,4 +62,4 @@ class FlagResolutionDetails(typing.Generic[U]):
5862
error_message: typing.Optional[str] = None
5963
reason: typing.Optional[Reason] = None
6064
variant: typing.Optional[str] = None
61-
flag_metadata: typing.Optional[str] = None
65+
flag_metadata: FlagMetadata = field(default_factory=dict)

openfeature/provider/in_memory_provider.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import typing
2-
from dataclasses import dataclass
2+
from dataclasses import dataclass, field
33

44
from openfeature._backports.strenum import StrEnum
55
from openfeature.evaluation_context import EvaluationContext
66
from openfeature.exception import ErrorCode
7-
from openfeature.flag_evaluation import FlagResolutionDetails, Reason
7+
from openfeature.flag_evaluation import FlagMetadata, FlagResolutionDetails, Reason
88
from openfeature.hook import Hook
99
from openfeature.provider.metadata import Metadata
1010
from openfeature.provider.provider import AbstractProvider
@@ -29,6 +29,7 @@ class State(StrEnum):
2929
flag_key: str
3030
default_variant: str
3131
variants: typing.Dict[str, T]
32+
flag_metadata: FlagMetadata = field(default_factory=dict)
3233
state: State = State.ENABLED
3334
context_evaluator: typing.Optional[
3435
typing.Callable[["InMemoryFlag", EvaluationContext], FlagResolutionDetails[T]]
@@ -46,6 +47,7 @@ def resolve(
4647
value=self.variants[self.default_variant],
4748
reason=Reason.STATIC,
4849
variant=self.default_variant,
50+
flag_metadata=self.flag_metadata,
4951
)
5052

5153

tests/test_client.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from openfeature.exception import ErrorCode, OpenFeatureError
88
from openfeature.flag_evaluation import Reason
99
from openfeature.hook import Hook
10+
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
1011
from openfeature.provider.no_op_provider import NoOpProvider
1112

1213

@@ -97,6 +98,28 @@ def test_should_raise_exception_when_invalid_flag_type_provided(no_op_provider_c
9798
assert flag.reason == Reason.ERROR
9899

99100

101+
def test_should_pass_flag_metadata_from_resolution_to_evaluation_details():
102+
# Given
103+
provider = InMemoryProvider(
104+
{
105+
"Key": InMemoryFlag(
106+
"Key",
107+
"true",
108+
{"true": True, "false": False},
109+
flag_metadata={"foo": "bar"},
110+
)
111+
}
112+
)
113+
client = OpenFeatureClient("my-client", None, provider)
114+
115+
# When
116+
details = client.get_boolean_details(flag_key="Key", default_value=False)
117+
118+
# Then
119+
assert details is not None
120+
assert details.flag_metadata == {"foo": "bar"}
121+
122+
100123
def test_should_handle_a_generic_exception_thrown_by_a_provider(no_op_provider_client):
101124
# Given
102125
exception_hook = MagicMock(spec=Hook)

tests/test_flag_evaluation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
from openfeature.flag_evaluation import FlagEvaluationDetails, Reason
33

44

5-
def test_evaulation_details_reason_should_be_a_string():
5+
def test_evaluation_details_reason_should_be_a_string():
66
# Given
77
flag_key = "my-flag"
88
flag_value = 100
99
variant = "1-hundred"
10+
flag_metadata = {}
1011
reason = Reason.DEFAULT
1112
error_code = ErrorCode.GENERAL
1213
error_message = "message"
@@ -16,6 +17,7 @@ def test_evaulation_details_reason_should_be_a_string():
1617
flag_key,
1718
flag_value,
1819
variant,
20+
flag_metadata,
1921
reason,
2022
error_code,
2123
error_message,
@@ -30,7 +32,7 @@ def test_evaulation_details_reason_should_be_a_string():
3032
assert reason == flag_details.reason
3133

3234

33-
def test_evaulation_details_reason_should_be_a_string_when_set():
35+
def test_evaluation_details_reason_should_be_a_string_when_set():
3436
# Given
3537
flag_key = "my-flag"
3638
flag_value = 100

0 commit comments

Comments
 (0)