Skip to content

Commit c62c501

Browse files
fix(python): Add exporter metadata to the evaluation context
Signed-off-by: Thomas Poignant <[email protected]>
1 parent 56c6775 commit c62c501

File tree

4 files changed

+75
-25
lines changed

4 files changed

+75
-25
lines changed

openfeature/providers/python-provider/gofeatureflag_python_provider/provider.py

+25-18
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import json
2-
from http import HTTPStatus
3-
from threading import Thread
4-
from typing import List, Optional, Type, Union
5-
from urllib.parse import urljoin
6-
72
import pylru
83
import urllib3
94
import websocket
5+
from gofeatureflag_python_provider.data_collector_hook import DataCollectorHook
6+
from gofeatureflag_python_provider.metadata import GoFeatureFlagMetadata
7+
from gofeatureflag_python_provider.options import BaseModel, GoFeatureFlagOptions
8+
from gofeatureflag_python_provider.request_flag_evaluation import (
9+
RequestFlagEvaluation,
10+
convert_evaluation_context,
11+
)
12+
from gofeatureflag_python_provider.response_flag_evaluation import (
13+
JsonType,
14+
ResponseFlagEvaluation,
15+
)
16+
from http import HTTPStatus
1017
from openfeature.evaluation_context import EvaluationContext
1118
from openfeature.exception import (
1219
ErrorCode,
@@ -17,21 +24,11 @@
1724
)
1825
from openfeature.flag_evaluation import FlagResolutionDetails, Reason
1926
from openfeature.hook import Hook
20-
from openfeature.provider.metadata import Metadata
2127
from openfeature.provider import AbstractProvider
28+
from openfeature.provider.metadata import Metadata
2229
from pydantic import PrivateAttr, ValidationError
23-
24-
from gofeatureflag_python_provider.data_collector_hook import DataCollectorHook
25-
from gofeatureflag_python_provider.metadata import GoFeatureFlagMetadata
26-
from gofeatureflag_python_provider.options import BaseModel, GoFeatureFlagOptions
27-
from gofeatureflag_python_provider.request_flag_evaluation import (
28-
RequestFlagEvaluation,
29-
convert_evaluation_context,
30-
)
31-
from gofeatureflag_python_provider.response_flag_evaluation import (
32-
JsonType,
33-
ResponseFlagEvaluation,
34-
)
30+
from threading import Thread
31+
from typing import List, Optional, Type, Union
3532

3633
AbstractProviderMetaclass = type(AbstractProvider)
3734
BaseModelMetaclass = type(BaseModel)
@@ -196,6 +193,16 @@ def generic_go_feature_flag_resolver(
196193
"/v1/feature/{}/eval".format(flag_key),
197194
)
198195

196+
# add exporter metadata to the context if it exists
197+
if self.options.exporter_metadata:
198+
goff_request.gofeatureflag[
199+
"exporterMetadata"
200+
] = self.options.exporter_metadata
201+
goff_request.gofeatureflag["exporterMetadata"]["openfeature"] = True
202+
goff_request.gofeatureflag["exporterMetadata"][
203+
"provider"
204+
] = "python"
205+
199206
response = self._http_client.request(
200207
method="POST",
201208
url=url,

openfeature/providers/python-provider/gofeatureflag_python_provider/request_flag_evaluation.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import hashlib
22
import json
3-
from typing import Optional, Any
4-
3+
from gofeatureflag_python_provider.options import BaseModel
54
from openfeature.evaluation_context import EvaluationContext
65
from openfeature.exception import (
76
TargetingKeyMissingError,
87
InvalidContextError,
98
)
109
from pydantic import SkipValidation
11-
12-
from gofeatureflag_python_provider.options import BaseModel
10+
from typing import Optional, Any, Dict
1311

1412

1513
class GoFeatureFlagEvaluationContext(BaseModel):
@@ -56,3 +54,4 @@ def convert_evaluation_context(
5654
class RequestFlagEvaluation(BaseModel):
5755
user: GoFeatureFlagEvaluationContext
5856
defaultValue: SkipValidation[Any] = None
57+
gofeatureflag: Optional[Dict] = {}

openfeature/providers/python-provider/poetry.lock

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openfeature/providers/python-provider/tests/test_gofeatureflag_python_provider.py

+44
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,50 @@ def test_url_parsing(mock_request):
689689
assert got == want
690690

691691

692+
@patch("urllib3.poolmanager.PoolManager.request")
693+
def test_should_call_evaluation_api_with_exporter_metadata(
694+
mock_request: Mock,
695+
):
696+
flag_key = "bool_targeting_match"
697+
default_value = False
698+
mock_request.side_effect = [
699+
Mock(
700+
status="200", data=_read_mock_file(flag_key)
701+
), # first call to get the flag
702+
Mock(status="200", data={}), # second call to send the data
703+
Mock(status="200", data={}),
704+
]
705+
goff_provider = GoFeatureFlagProvider(
706+
options=GoFeatureFlagOptions(
707+
endpoint="https://gofeatureflag.org/",
708+
data_flush_interval=100,
709+
disable_cache_invalidation=True,
710+
exporter_metadata={"version": "1.0.0", "name": "myapp", "id": 123},
711+
)
712+
)
713+
api.set_provider(goff_provider)
714+
client = api.get_client(domain="test-client")
715+
716+
client.get_boolean_details(
717+
flag_key=flag_key,
718+
default_value=default_value,
719+
evaluation_context=_default_evaluation_ctx,
720+
)
721+
722+
api.shutdown()
723+
got = json.loads(mock_request.call_args.kwargs["body"])["gofeatureflag"]
724+
want = {
725+
"exporterMetadata": {
726+
"version": "1.0.0",
727+
"name": "myapp",
728+
"id": 123,
729+
"provider": "python",
730+
"openfeature": True,
731+
},
732+
}
733+
assert got == want
734+
735+
692736
def _read_mock_file(flag_key: str) -> str:
693737
# This hacky if is here to make test run inside pycharm and from the root of the project
694738
if os.getcwd().endswith("/tests"):

0 commit comments

Comments
 (0)