Skip to content

Commit 6799788

Browse files
authored
feat: client identification headers (#342)
1 parent b46cd17 commit 6799788

File tree

7 files changed

+76
-20
lines changed

7 files changed

+76
-20
lines changed

UnleashClient/__init__.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
METRIC_LAST_SENT_TIME,
2222
REQUEST_RETRIES,
2323
REQUEST_TIMEOUT,
24+
SDK_NAME,
25+
SDK_VERSION,
2426
)
2527
from UnleashClient.events import UnleashEvent, UnleashEventType
2628
from UnleashClient.loader import load_features
@@ -211,12 +213,19 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
211213
if not self.is_initialized:
212214
# pylint: disable=no-else-raise
213215
try:
216+
headers = {
217+
**self.unleash_custom_headers,
218+
"x-unleash-connection-id": str(uuid.uuid4()),
219+
"x-unleash-appname": self.unleash_app_name,
220+
"x-unleash-sdk": f"{SDK_NAME}:{SDK_VERSION}",
221+
}
222+
214223
# Setup
215224
metrics_args = {
216225
"url": self.unleash_url,
217226
"app_name": self.unleash_app_name,
218227
"instance_id": self.unleash_instance_id,
219-
"custom_headers": self.unleash_custom_headers,
228+
"headers": headers,
220229
"custom_options": self.unleash_custom_options,
221230
"request_timeout": self.unleash_request_timeout,
222231
"engine": self.engine,
@@ -229,7 +238,7 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
229238
self.unleash_app_name,
230239
self.unleash_instance_id,
231240
self.unleash_metrics_interval,
232-
self.unleash_custom_headers,
241+
headers,
233242
self.unleash_custom_options,
234243
self.strategy_mapping,
235244
self.unleash_request_timeout,
@@ -240,7 +249,7 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
240249
"url": self.unleash_url,
241250
"app_name": self.unleash_app_name,
242251
"instance_id": self.unleash_instance_id,
243-
"custom_headers": self.unleash_custom_headers,
252+
"headers": headers,
244253
"custom_options": self.unleash_custom_options,
245254
"cache": self.cache,
246255
"engine": self.engine,

UnleashClient/api/features.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def get_feature_toggles(
1313
url: str,
1414
app_name: str,
1515
instance_id: str,
16-
custom_headers: dict,
16+
headers: dict,
1717
custom_options: dict,
1818
request_timeout: int,
1919
request_retries: int,
@@ -30,7 +30,7 @@ def get_feature_toggles(
3030
:param url:
3131
:param app_name:
3232
:param instance_id:
33-
:param custom_headers:
33+
:param headers:
3434
:param custom_options:
3535
:param request_timeout:
3636
:param request_retries:
@@ -41,10 +41,13 @@ def get_feature_toggles(
4141
try:
4242
LOGGER.info("Getting feature flag.")
4343

44-
headers = {"UNLEASH-APPNAME": app_name, "UNLEASH-INSTANCEID": instance_id}
44+
request_specific_headers = {
45+
"UNLEASH-APPNAME": app_name,
46+
"UNLEASH-INSTANCEID": instance_id,
47+
}
4548

4649
if cached_etag:
47-
headers["If-None-Match"] = cached_etag
50+
request_specific_headers["If-None-Match"] = cached_etag
4851

4952
base_url = f"{url}{FEATURES_URL}"
5053
base_params = {}
@@ -60,7 +63,7 @@ def get_feature_toggles(
6063
session.mount("http://", adapter)
6164
resp = session.get(
6265
base_url,
63-
headers={**custom_headers, **headers},
66+
headers={**headers, **request_specific_headers},
6467
params=base_params,
6568
timeout=request_timeout,
6669
**custom_options,

UnleashClient/api/metrics.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
def send_metrics(
1111
url: str,
1212
request_body: dict,
13-
custom_headers: dict,
13+
headers: dict,
1414
custom_options: dict,
1515
request_timeout: int,
1616
) -> bool:
@@ -22,7 +22,7 @@ def send_metrics(
2222
2323
:param url:
2424
:param request_body:
25-
:param custom_headers:
25+
:param headers:
2626
:param custom_options:
2727
:param request_timeout:
2828
:return: true if registration successful, false if registration unsuccessful or exception.
@@ -34,7 +34,7 @@ def send_metrics(
3434
resp = requests.post(
3535
url + METRICS_URL,
3636
data=json.dumps(request_body),
37-
headers={**custom_headers, **APPLICATION_HEADERS},
37+
headers={**headers, **APPLICATION_HEADERS},
3838
timeout=request_timeout,
3939
**custom_options,
4040
)

UnleashClient/api/register.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def register_client(
2222
app_name: str,
2323
instance_id: str,
2424
metrics_interval: int,
25-
custom_headers: dict,
25+
headers: dict,
2626
custom_options: dict,
2727
supported_strategies: dict,
2828
request_timeout: int,
@@ -38,7 +38,7 @@ def register_client(
3838
:param app_name:
3939
:param instance_id:
4040
:param metrics_interval:
41-
:param custom_headers:
41+
:param headers:
4242
:param custom_options:
4343
:param supported_strategies:
4444
:param request_timeout:
@@ -64,7 +64,7 @@ def register_client(
6464
resp = requests.post(
6565
url + REGISTER_URL,
6666
data=json.dumps(registration_request),
67-
headers={**custom_headers, **APPLICATION_HEADERS},
67+
headers={**headers, **APPLICATION_HEADERS},
6868
timeout=request_timeout,
6969
**custom_options,
7070
)

UnleashClient/periodic_tasks/fetch_and_load.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def fetch_and_load_features(
1313
url: str,
1414
app_name: str,
1515
instance_id: str,
16-
custom_headers: dict,
16+
headers: dict,
1717
custom_options: dict,
1818
cache: BaseCache,
1919
request_timeout: int,
@@ -25,7 +25,7 @@ def fetch_and_load_features(
2525
url,
2626
app_name,
2727
instance_id,
28-
custom_headers,
28+
headers,
2929
custom_options,
3030
request_timeout,
3131
request_retries,

UnleashClient/periodic_tasks/send_metrics.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def aggregate_and_send_metrics(
1212
url: str,
1313
app_name: str,
1414
instance_id: str,
15-
custom_headers: dict,
15+
headers: dict,
1616
custom_options: dict,
1717
request_timeout: int,
1818
engine: UnleashEngine,
@@ -30,8 +30,6 @@ def aggregate_and_send_metrics(
3030
}
3131

3232
if metrics_bucket:
33-
send_metrics(
34-
url, metrics_request, custom_headers, custom_options, request_timeout
35-
)
33+
send_metrics(url, metrics_request, headers, custom_options, request_timeout)
3634
else:
3735
LOGGER.debug("No feature flags with metrics, skipping metrics submission.")

tests/unit_tests/test_client.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,3 +1032,49 @@ def test_is_enabled_works_with_properties_field_in_the_context_root():
10321032

10331033
context = {"myContext": "1234"}
10341034
assert unleash_client.is_enabled("customContextToggle", context)
1035+
1036+
1037+
@responses.activate
1038+
def test_identification_headers_sent_and_consistent(unleash_client):
1039+
responses.add(responses.POST, URL + REGISTER_URL, json={}, status=202)
1040+
responses.add(
1041+
responses.GET, URL + FEATURES_URL, json=MOCK_FEATURE_RESPONSE, status=200
1042+
)
1043+
responses.add(responses.POST, URL + METRICS_URL, json={}, status=202)
1044+
unleash_client.initialize_client()
1045+
1046+
connection_id = responses.calls[0].request.headers["X-UNLEASH-CONNECTION-ID"]
1047+
app_name = responses.calls[0].request.headers["X-UNLEASH-APPNAME"]
1048+
sdk = responses.calls[0].request.headers["X-UNLEASH-SDK"]
1049+
1050+
for api_call in responses.calls:
1051+
assert api_call.request.headers["X-UNLEASH-CONNECTION-ID"] == connection_id
1052+
assert api_call.request.headers["X-UNLEASH-APPNAME"] == app_name
1053+
assert api_call.request.headers["X-UNLEASH-SDK"] == sdk
1054+
1055+
1056+
@responses.activate
1057+
def test_identification_headers_unique_connection_id():
1058+
responses.add(responses.POST, URL + REGISTER_URL, json={}, status=202)
1059+
responses.add(
1060+
responses.GET, URL + FEATURES_URL, json=MOCK_FEATURE_RESPONSE, status=200
1061+
)
1062+
responses.add(responses.POST, URL + METRICS_URL, json={}, status=202)
1063+
1064+
unleash_client = UnleashClient(
1065+
URL, APP_NAME, disable_metrics=True, disable_registration=True
1066+
)
1067+
unleash_client.initialize_client()
1068+
connection_id_first_client = responses.calls[0].request.headers[
1069+
"X-UNLEASH-CONNECTION-ID"
1070+
]
1071+
1072+
other_unleash_client = UnleashClient(
1073+
URL, APP_NAME, disable_metrics=True, disable_registration=True
1074+
)
1075+
other_unleash_client.initialize_client()
1076+
1077+
connection_id_second_client = responses.calls[1].request.headers[
1078+
"X-UNLEASH-CONNECTION-ID"
1079+
]
1080+
assert connection_id_first_client != connection_id_second_client

0 commit comments

Comments
 (0)