Skip to content

Commit 924c691

Browse files
fix(python): Fix URL formatting + authorized key (#2707)
* feat: Add api_key options to be able to use authentication when calling the relay proxy Signed-off-by: Thomas Poignant <[email protected]> * fix: formatting URL was broken Signed-off-by: Thomas Poignant <[email protected]> --------- Signed-off-by: Thomas Poignant <[email protected]>
1 parent 0fcbe58 commit 924c691

File tree

6 files changed

+56
-9
lines changed

6 files changed

+56
-9
lines changed

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,14 @@ def _collect_data(self):
109109
meta={"provider": "open-feature-python-sdk"},
110110
events=self._event_queue,
111111
)
112+
headers = {"Content-Type": "application/json"}
113+
if self._options.api_key:
114+
headers["Authorization"] = "Bearer {}".format(self._options.api_key)
115+
112116
response = self._http_client.request(
113117
method="POST",
114118
url=urljoin(str(self._options.endpoint), "/v1/data/collector"),
115-
headers={"Content-Type": "application/json"},
119+
headers=headers,
116120
body=goff_request.model_dump_json(),
117121
)
118122

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

+5
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,8 @@ class GoFeatureFlagOptions(BaseModel):
4545
# config changes.
4646
# default: false
4747
disable_cache_invalidation: typing.Optional[bool] = False
48+
49+
# api_key (optional) If the relay proxy is configured to authenticate the requests, you should provide
50+
# an API Key to the provider. Please ask the administrator of the relay proxy to provide an API Key.
51+
# Default: None
52+
api_key: typing.Optional[str] = None

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

+18-8
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,18 @@ def generic_go_feature_flag_resolver(
188188
response_body = self._cache[evaluation_context_hash]
189189
is_from_cache = True
190190
else:
191+
headers = {"Content-Type": "application/json"}
192+
if self.options.api_key is not None:
193+
headers["Authorization"] = "Bearer {}".format(self.options.api_key)
194+
url = "{}{}".format(
195+
str(self.options.endpoint).rstrip("/"),
196+
"/v1/feature/{}/eval".format(flag_key),
197+
)
198+
191199
response = self._http_client.request(
192200
method="POST",
193-
url=urljoin(
194-
str(self.options.endpoint),
195-
"/v1/feature/{}/eval".format(flag_key),
196-
),
197-
headers={"Content-Type": "application/json"},
201+
url=url,
202+
headers=headers,
198203
body=goff_request.model_dump_json(),
199204
)
200205

@@ -261,10 +266,15 @@ def _build_websocket_uri(self):
261266
_build_websocket_uri is a helper to build the websocket uri to connect to the GO Feature Flag relay proxy.
262267
:return: a string representing the websocket uri
263268
"""
264-
http_uri = urljoin(
265-
str(self.options.endpoint),
266-
"/ws/v1/flag/change",
269+
url = "/ws/v1/flag/change"
270+
if self.options.api_key is not None:
271+
url = "{}?apiKey={}".format(url, self.options.api_key)
272+
273+
http_uri = "{}{}".format(
274+
str(self.options.endpoint).rstrip("/"),
275+
url,
267276
)
277+
268278
http_uri = http_uri.replace("http", "ws")
269279
http_uri = http_uri.replace("https", "wss")
270280
return http_uri

openfeature/providers/python-provider/tests/docker-compose.yml

+1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ services:
99
- POLLINGINTERVAL=1000
1010
- RETRIEVER_KIND=file
1111
- RETRIEVER_PATH=/config.goff.yaml
12+
- AUTHORIZEDKEYS_EVALUATION=apikey1
1213
volumes:
1314
- ./config.goff.yaml:/config.goff.yaml

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

+26
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def _generic_test(
4040
endpoint="https://gofeatureflag.org/",
4141
data_flush_interval=100,
4242
disable_cache_invalidation=True,
43+
api_key="apikey1",
4344
),
4445
)
4546
api.set_provider(goff_provider)
@@ -610,6 +611,31 @@ def _create_hook_context():
610611
assert len(hook._event_queue) == 1
611612

612613

614+
@patch("urllib3.poolmanager.PoolManager.request")
615+
def test_url_parsing(mock_request):
616+
flag_key = "bool_targeting_match"
617+
mock_request.return_value = Mock(status="200", data=_read_mock_file(flag_key))
618+
default_value = False
619+
goff_provider = GoFeatureFlagProvider(
620+
options=GoFeatureFlagOptions(
621+
endpoint="https://gofeatureflag.org/ff/",
622+
data_flush_interval=100,
623+
disable_cache_invalidation=True,
624+
api_key="apikey1",
625+
),
626+
)
627+
api.set_provider(goff_provider)
628+
client = api.get_client(domain="test-client")
629+
t = client.get_boolean_details(
630+
flag_key=flag_key,
631+
default_value=default_value,
632+
evaluation_context=_default_evaluation_ctx,
633+
)
634+
got = mock_request.call_args[1]["url"]
635+
want = "https://gofeatureflag.org/ff/v1/feature/bool_targeting_match/eval"
636+
assert got == want
637+
638+
613639
def _read_mock_file(flag_key: str) -> str:
614640
# This hacky if is here to make test run inside pycharm and from the root of the project
615641
if os.getcwd().endswith("/tests"):

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

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def test_test_websocket_cache_invalidation(goff):
5050
endpoint=goff,
5151
data_flush_interval=100,
5252
disable_data_collection=True,
53+
api_key="apikey1",
5354
)
5455
)
5556
api.set_provider(goff_provider)

0 commit comments

Comments
 (0)