Skip to content

Commit b2e7a98

Browse files
authored
feat: new headers and payload ( connection id and interval) (#346)
1 parent 9fb8d44 commit b2e7a98

File tree

9 files changed

+127
-6
lines changed

9 files changed

+127
-6
lines changed

UnleashClient/__init__.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def __init__(
110110
self.unleash_app_name = app_name
111111
self.unleash_environment = environment
112112
self.unleash_instance_id = instance_id
113+
self._connection_id = str(uuid.uuid4())
113114
self.unleash_refresh_interval = refresh_interval
114115
self.unleash_request_timeout = request_timeout
115116
self.unleash_request_retries = request_retries
@@ -183,6 +184,18 @@ def __init__(
183184
engine=self.engine,
184185
)
185186

187+
@property
188+
def unleash_refresh_interval_str_millis(self) -> str:
189+
return str(self.unleash_refresh_interval * 1000)
190+
191+
@property
192+
def unleash_metrics_interval_str_millis(self) -> str:
193+
return str(self.unleash_metrics_interval * 1000)
194+
195+
@property
196+
def connection_id(self):
197+
return self._connection_id
198+
186199
def initialize_client(self, fetch_toggles: bool = True) -> None:
187200
"""
188201
Initializes client and starts communication with central unleash server(s).
@@ -213,19 +226,25 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
213226
if not self.is_initialized:
214227
# pylint: disable=no-else-raise
215228
try:
216-
headers = {
229+
base_headers = {
217230
**self.unleash_custom_headers,
218-
"unleash-connection-id": str(uuid.uuid4()),
231+
"unleash-connection-id": self.connection_id,
219232
"unleash-appname": self.unleash_app_name,
220233
"unleash-sdk": f"{SDK_NAME}:{SDK_VERSION}",
221234
}
222235

236+
metrics_headers = {
237+
**base_headers,
238+
"unleash-interval": self.unleash_metrics_interval_str_millis,
239+
}
240+
223241
# Setup
224242
metrics_args = {
225243
"url": self.unleash_url,
226244
"app_name": self.unleash_app_name,
245+
"connection_id": self.connection_id,
227246
"instance_id": self.unleash_instance_id,
228-
"headers": headers,
247+
"headers": metrics_headers,
229248
"custom_options": self.unleash_custom_options,
230249
"request_timeout": self.unleash_request_timeout,
231250
"engine": self.engine,
@@ -237,19 +256,25 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
237256
self.unleash_url,
238257
self.unleash_app_name,
239258
self.unleash_instance_id,
259+
self.connection_id,
240260
self.unleash_metrics_interval,
241-
headers,
261+
base_headers,
242262
self.unleash_custom_options,
243263
self.strategy_mapping,
244264
self.unleash_request_timeout,
245265
)
246266

247267
if fetch_toggles:
268+
fetch_headers = {
269+
**base_headers,
270+
"unleash-interval": self.unleash_refresh_interval_str_millis,
271+
}
272+
248273
job_args = {
249274
"url": self.unleash_url,
250275
"app_name": self.unleash_app_name,
251276
"instance_id": self.unleash_instance_id,
252-
"headers": headers,
277+
"headers": fetch_headers,
253278
"custom_options": self.unleash_custom_options,
254279
"cache": self.cache,
255280
"engine": self.engine,
@@ -277,7 +302,6 @@ def initialize_client(self, fetch_toggles: bool = True) -> None:
277302
executor=self.unleash_executor_name,
278303
kwargs=job_args,
279304
)
280-
281305
if not self.unleash_disable_metrics:
282306
self.metric_job = self.unleash_scheduler.add_job(
283307
aggregate_and_send_metrics,

UnleashClient/api/register.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def register_client(
2121
url: str,
2222
app_name: str,
2323
instance_id: str,
24+
connection_id: str,
2425
metrics_interval: int,
2526
headers: dict,
2627
custom_options: dict,
@@ -47,6 +48,7 @@ def register_client(
4748
registration_request = {
4849
"appName": app_name,
4950
"instanceId": instance_id,
51+
"connectionId": connection_id,
5052
"sdkVersion": f"{SDK_NAME}:{SDK_VERSION}",
5153
"strategies": [*supported_strategies],
5254
"started": datetime.now(timezone.utc).isoformat(),

UnleashClient/periodic_tasks/send_metrics.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def aggregate_and_send_metrics(
1212
url: str,
1313
app_name: str,
1414
instance_id: str,
15+
connection_id: str,
1516
headers: dict,
1617
custom_options: dict,
1718
request_timeout: int,
@@ -22,6 +23,7 @@ def aggregate_and_send_metrics(
2223
metrics_request = {
2324
"appName": app_name,
2425
"instanceId": instance_id,
26+
"connectionId": connection_id,
2527
"bucket": metrics_bucket,
2628
"platformName": python_implementation(),
2729
"platformVersion": python_version(),

tests/unit_tests/api/test_metrics.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import json
2+
13
import responses
24
from pytest import mark, param
35
from requests import ConnectionError
@@ -36,5 +38,9 @@ def test_send_metrics(payload, status, expected):
3638
URL, MOCK_METRICS_REQUEST, CUSTOM_HEADERS, CUSTOM_OPTIONS, REQUEST_TIMEOUT
3739
)
3840

41+
request = json.loads(responses.calls[0].request.body)
42+
3943
assert len(responses.calls) == 1
4044
assert expected(result)
45+
46+
assert request["connectionId"] == MOCK_METRICS_REQUEST.get("connectionId")

tests/unit_tests/api/test_register.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from tests.utilities.testing_constants import (
88
APP_NAME,
9+
CONNECTION_ID,
910
CUSTOM_HEADERS,
1011
CUSTOM_OPTIONS,
1112
INSTANCE_ID,
@@ -40,6 +41,7 @@ def test_register_client(payload, status, expected):
4041
URL,
4142
APP_NAME,
4243
INSTANCE_ID,
44+
CONNECTION_ID,
4345
METRICS_INTERVAL,
4446
CUSTOM_HEADERS,
4547
CUSTOM_OPTIONS,
@@ -59,6 +61,7 @@ def test_register_includes_metadata():
5961
URL,
6062
APP_NAME,
6163
INSTANCE_ID,
64+
CONNECTION_ID,
6265
METRICS_INTERVAL,
6366
CUSTOM_HEADERS,
6467
CUSTOM_OPTIONS,
@@ -71,5 +74,6 @@ def test_register_includes_metadata():
7174

7275
assert request["yggdrasilVersion"] is not None
7376
assert request["specVersion"] == CLIENT_SPEC_VERSION
77+
assert request["connectionId"] == CONNECTION_ID
7478
assert request["platformName"] is not None
7579
assert request["platformVersion"] is not None

tests/unit_tests/periodic/test_aggregate_and_send_metrics.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from tests.utilities.testing_constants import (
77
APP_NAME,
8+
CONNECTION_ID,
89
CUSTOM_HEADERS,
910
CUSTOM_OPTIONS,
1011
INSTANCE_ID,
@@ -31,6 +32,7 @@ def test_no_metrics():
3132
URL,
3233
APP_NAME,
3334
INSTANCE_ID,
35+
CONNECTION_ID,
3436
CUSTOM_HEADERS,
3537
CUSTOM_OPTIONS,
3638
REQUEST_TIMEOUT,
@@ -51,6 +53,7 @@ def test_metrics_metadata_is_sent():
5153
URL,
5254
APP_NAME,
5355
INSTANCE_ID,
56+
CONNECTION_ID,
5457
CUSTOM_HEADERS,
5558
CUSTOM_OPTIONS,
5659
REQUEST_TIMEOUT,

tests/unit_tests/test_client.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import json
12
import time
3+
import uuid
24
import warnings
35
from datetime import datetime, timezone
46
from pathlib import Path
@@ -1078,3 +1080,79 @@ def test_identification_headers_unique_connection_id():
10781080
"UNLEASH-CONNECTION-ID"
10791081
]
10801082
assert connection_id_first_client != connection_id_second_client
1083+
1084+
1085+
@responses.activate
1086+
def test_identification_values_are_passed_in():
1087+
responses.add(responses.POST, URL + REGISTER_URL, json={}, status=202)
1088+
responses.add(
1089+
responses.GET, URL + FEATURES_URL, json=MOCK_FEATURE_RESPONSE, status=200
1090+
)
1091+
responses.add(responses.POST, URL + METRICS_URL, json={}, status=202)
1092+
1093+
refresh_interval = 1
1094+
metrics_interval = 2
1095+
unleash_client = UnleashClient(
1096+
URL,
1097+
APP_NAME,
1098+
refresh_interval=refresh_interval,
1099+
metrics_interval=metrics_interval,
1100+
)
1101+
1102+
expected_refresh_interval = str(refresh_interval * 1000)
1103+
expected_metrics_interval = str(metrics_interval * 1000)
1104+
1105+
unleash_client.initialize_client()
1106+
register_request = responses.calls[0].request
1107+
register_body = json.loads(register_request.body)
1108+
1109+
assert "connectionId" in register_body, "Key missing: connectionId"
1110+
try:
1111+
uuid.UUID(register_body["connectionId"])
1112+
except ValueError:
1113+
assert False, "Invalid UUID format in connectionId"
1114+
1115+
assert (
1116+
"UNLEASH-CONNECTION-ID" in register_request.headers
1117+
), "Header missing: UNLEASH-CONNECTION-ID"
1118+
try:
1119+
uuid.UUID(register_request.headers["UNLEASH-CONNECTION-ID"])
1120+
except ValueError:
1121+
assert False, "Invalid UUID format in UNLEASH-CONNECTION-ID"
1122+
1123+
unleash_client.is_enabled("registerMetricsFlag")
1124+
1125+
features_request = responses.calls[1].request
1126+
1127+
assert features_request.headers["UNLEASH-INTERVAL"] == expected_refresh_interval
1128+
1129+
assert (
1130+
"UNLEASH-CONNECTION-ID" in features_request.headers
1131+
), "Header missing: UNLEASH-CONNECTION-ID"
1132+
1133+
try:
1134+
uuid.UUID(features_request.headers["UNLEASH-CONNECTION-ID"])
1135+
except ValueError:
1136+
assert False, "Invalid UUID format in UNLEASH-CONNECTION-ID"
1137+
1138+
time.sleep(3)
1139+
metrics_request = [
1140+
call for call in responses.calls if METRICS_URL in call.request.url
1141+
][0].request
1142+
metrics_body = json.loads(metrics_request.body)
1143+
1144+
assert metrics_request.headers["UNLEASH-INTERVAL"] == expected_metrics_interval
1145+
1146+
assert "connectionId" in metrics_body, "Key missing: connectionId"
1147+
try:
1148+
uuid.UUID(metrics_body["connectionId"])
1149+
except ValueError:
1150+
assert False, "Invalid UUID format in connectionId"
1151+
1152+
assert (
1153+
"UNLEASH-CONNECTION-ID" in metrics_request.headers
1154+
), "Header missing: UNLEASH-CONNECTION-ID"
1155+
try:
1156+
uuid.UUID(metrics_request.headers["UNLEASH-CONNECTION-ID"])
1157+
except ValueError:
1158+
assert False, "Invalid UUID format in UNLEASH-CONNECTION-ID"

tests/utilities/mocks/mock_metrics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
MOCK_METRICS_REQUEST = {
22
"appName": "appName",
33
"instanceId": "instanceId",
4+
"connectionId": "connectionId",
45
"bucket": {
56
"start": "2016-11-03T07:16:43.572Z",
67
"stop": "2016-11-03T07:16:53.572Z",

tests/utilities/testing_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
APP_NAME = "pytest"
55
ENVIRONMENT = "unit"
66
INSTANCE_ID = "123"
7+
CONNECTION_ID = "test-connection-id"
78
REFRESH_INTERVAL = 3
89
REFRESH_JITTER = None
910
METRICS_INTERVAL = 2

0 commit comments

Comments
 (0)