Skip to content

Commit

Permalink
feat: client identification headers (#342)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tymek authored Jan 29, 2025
1 parent b46cd17 commit 6799788
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 20 deletions.
15 changes: 12 additions & 3 deletions UnleashClient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
METRIC_LAST_SENT_TIME,
REQUEST_RETRIES,
REQUEST_TIMEOUT,
SDK_NAME,
SDK_VERSION,
)
from UnleashClient.events import UnleashEvent, UnleashEventType
from UnleashClient.loader import load_features
Expand Down Expand Up @@ -211,12 +213,19 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
if not self.is_initialized:
# pylint: disable=no-else-raise
try:
headers = {
**self.unleash_custom_headers,
"x-unleash-connection-id": str(uuid.uuid4()),
"x-unleash-appname": self.unleash_app_name,
"x-unleash-sdk": f"{SDK_NAME}:{SDK_VERSION}",
}

# Setup
metrics_args = {
"url": self.unleash_url,
"app_name": self.unleash_app_name,
"instance_id": self.unleash_instance_id,
"custom_headers": self.unleash_custom_headers,
"headers": headers,
"custom_options": self.unleash_custom_options,
"request_timeout": self.unleash_request_timeout,
"engine": self.engine,
Expand All @@ -229,7 +238,7 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
self.unleash_app_name,
self.unleash_instance_id,
self.unleash_metrics_interval,
self.unleash_custom_headers,
headers,
self.unleash_custom_options,
self.strategy_mapping,
self.unleash_request_timeout,
Expand All @@ -240,7 +249,7 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
"url": self.unleash_url,
"app_name": self.unleash_app_name,
"instance_id": self.unleash_instance_id,
"custom_headers": self.unleash_custom_headers,
"headers": headers,
"custom_options": self.unleash_custom_options,
"cache": self.cache,
"engine": self.engine,
Expand Down
13 changes: 8 additions & 5 deletions UnleashClient/api/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def get_feature_toggles(
url: str,
app_name: str,
instance_id: str,
custom_headers: dict,
headers: dict,
custom_options: dict,
request_timeout: int,
request_retries: int,
Expand All @@ -30,7 +30,7 @@ def get_feature_toggles(
:param url:
:param app_name:
:param instance_id:
:param custom_headers:
:param headers:
:param custom_options:
:param request_timeout:
:param request_retries:
Expand All @@ -41,10 +41,13 @@ def get_feature_toggles(
try:
LOGGER.info("Getting feature flag.")

headers = {"UNLEASH-APPNAME": app_name, "UNLEASH-INSTANCEID": instance_id}
request_specific_headers = {
"UNLEASH-APPNAME": app_name,
"UNLEASH-INSTANCEID": instance_id,
}

if cached_etag:
headers["If-None-Match"] = cached_etag
request_specific_headers["If-None-Match"] = cached_etag

base_url = f"{url}{FEATURES_URL}"
base_params = {}
Expand All @@ -60,7 +63,7 @@ def get_feature_toggles(
session.mount("http://", adapter)
resp = session.get(
base_url,
headers={**custom_headers, **headers},
headers={**headers, **request_specific_headers},
params=base_params,
timeout=request_timeout,
**custom_options,
Expand Down
6 changes: 3 additions & 3 deletions UnleashClient/api/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
def send_metrics(
url: str,
request_body: dict,
custom_headers: dict,
headers: dict,
custom_options: dict,
request_timeout: int,
) -> bool:
Expand All @@ -22,7 +22,7 @@ def send_metrics(
:param url:
:param request_body:
:param custom_headers:
:param headers:
:param custom_options:
:param request_timeout:
:return: true if registration successful, false if registration unsuccessful or exception.
Expand All @@ -34,7 +34,7 @@ def send_metrics(
resp = requests.post(
url + METRICS_URL,
data=json.dumps(request_body),
headers={**custom_headers, **APPLICATION_HEADERS},
headers={**headers, **APPLICATION_HEADERS},
timeout=request_timeout,
**custom_options,
)
Expand Down
6 changes: 3 additions & 3 deletions UnleashClient/api/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def register_client(
app_name: str,
instance_id: str,
metrics_interval: int,
custom_headers: dict,
headers: dict,
custom_options: dict,
supported_strategies: dict,
request_timeout: int,
Expand All @@ -38,7 +38,7 @@ def register_client(
:param app_name:
:param instance_id:
:param metrics_interval:
:param custom_headers:
:param headers:
:param custom_options:
:param supported_strategies:
:param request_timeout:
Expand All @@ -64,7 +64,7 @@ def register_client(
resp = requests.post(
url + REGISTER_URL,
data=json.dumps(registration_request),
headers={**custom_headers, **APPLICATION_HEADERS},
headers={**headers, **APPLICATION_HEADERS},
timeout=request_timeout,
**custom_options,
)
Expand Down
4 changes: 2 additions & 2 deletions UnleashClient/periodic_tasks/fetch_and_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def fetch_and_load_features(
url: str,
app_name: str,
instance_id: str,
custom_headers: dict,
headers: dict,
custom_options: dict,
cache: BaseCache,
request_timeout: int,
Expand All @@ -25,7 +25,7 @@ def fetch_and_load_features(
url,
app_name,
instance_id,
custom_headers,
headers,
custom_options,
request_timeout,
request_retries,
Expand Down
6 changes: 2 additions & 4 deletions UnleashClient/periodic_tasks/send_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def aggregate_and_send_metrics(
url: str,
app_name: str,
instance_id: str,
custom_headers: dict,
headers: dict,
custom_options: dict,
request_timeout: int,
engine: UnleashEngine,
Expand All @@ -30,8 +30,6 @@ def aggregate_and_send_metrics(
}

if metrics_bucket:
send_metrics(
url, metrics_request, custom_headers, custom_options, request_timeout
)
send_metrics(url, metrics_request, headers, custom_options, request_timeout)
else:
LOGGER.debug("No feature flags with metrics, skipping metrics submission.")
46 changes: 46 additions & 0 deletions tests/unit_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1032,3 +1032,49 @@ def test_is_enabled_works_with_properties_field_in_the_context_root():

context = {"myContext": "1234"}
assert unleash_client.is_enabled("customContextToggle", context)


@responses.activate
def test_identification_headers_sent_and_consistent(unleash_client):
responses.add(responses.POST, URL + REGISTER_URL, json={}, status=202)
responses.add(
responses.GET, URL + FEATURES_URL, json=MOCK_FEATURE_RESPONSE, status=200
)
responses.add(responses.POST, URL + METRICS_URL, json={}, status=202)
unleash_client.initialize_client()

connection_id = responses.calls[0].request.headers["X-UNLEASH-CONNECTION-ID"]
app_name = responses.calls[0].request.headers["X-UNLEASH-APPNAME"]
sdk = responses.calls[0].request.headers["X-UNLEASH-SDK"]

for api_call in responses.calls:
assert api_call.request.headers["X-UNLEASH-CONNECTION-ID"] == connection_id
assert api_call.request.headers["X-UNLEASH-APPNAME"] == app_name
assert api_call.request.headers["X-UNLEASH-SDK"] == sdk


@responses.activate
def test_identification_headers_unique_connection_id():
responses.add(responses.POST, URL + REGISTER_URL, json={}, status=202)
responses.add(
responses.GET, URL + FEATURES_URL, json=MOCK_FEATURE_RESPONSE, status=200
)
responses.add(responses.POST, URL + METRICS_URL, json={}, status=202)

unleash_client = UnleashClient(
URL, APP_NAME, disable_metrics=True, disable_registration=True
)
unleash_client.initialize_client()
connection_id_first_client = responses.calls[0].request.headers[
"X-UNLEASH-CONNECTION-ID"
]

other_unleash_client = UnleashClient(
URL, APP_NAME, disable_metrics=True, disable_registration=True
)
other_unleash_client.initialize_client()

connection_id_second_client = responses.calls[1].request.headers[
"X-UNLEASH-CONNECTION-ID"
]
assert connection_id_first_client != connection_id_second_client

0 comments on commit 6799788

Please sign in to comment.