diff --git a/UnleashClient/api/features.py b/UnleashClient/api/features.py index 0cf97e43..bf7ac540 100644 --- a/UnleashClient/api/features.py +++ b/UnleashClient/api/features.py @@ -4,6 +4,7 @@ from requests.adapters import HTTPAdapter from urllib3 import Retry +from UnleashClient.api.urls import build_normalized_url from UnleashClient.constants import FEATURES_URL from UnleashClient.utils import LOGGER, log_resp_info @@ -49,7 +50,7 @@ def get_feature_toggles( if cached_etag: request_specific_headers["If-None-Match"] = cached_etag - base_url = f"{url}{FEATURES_URL}" + base_url = build_normalized_url(url, FEATURES_URL) base_params = {} if project: diff --git a/UnleashClient/api/metrics.py b/UnleashClient/api/metrics.py index 4a94c4d2..6f877a5c 100644 --- a/UnleashClient/api/metrics.py +++ b/UnleashClient/api/metrics.py @@ -2,6 +2,7 @@ import requests +from UnleashClient.api.urls import build_normalized_url from UnleashClient.constants import APPLICATION_HEADERS, METRICS_URL from UnleashClient.utils import LOGGER, log_resp_info @@ -32,7 +33,7 @@ def send_metrics( LOGGER.info("unleash metrics information: %s", request_body) resp = requests.post( - url + METRICS_URL, + build_normalized_url(url, METRICS_URL), data=json.dumps(request_body), headers={**headers, **APPLICATION_HEADERS}, timeout=request_timeout, diff --git a/UnleashClient/api/register.py b/UnleashClient/api/register.py index 0bd0195e..0ed6b288 100644 --- a/UnleashClient/api/register.py +++ b/UnleashClient/api/register.py @@ -7,6 +7,7 @@ import yggdrasil_engine from requests.exceptions import InvalidHeader, InvalidSchema, InvalidURL, MissingSchema +from UnleashClient.api.urls import build_normalized_url from UnleashClient.constants import ( APPLICATION_HEADERS, CLIENT_SPEC_VERSION, @@ -72,7 +73,7 @@ def register_client( LOGGER.info("Registration request information: %s", registration_request) resp = requests.post( - url + REGISTER_URL, + build_normalized_url(url, REGISTER_URL), data=json.dumps(registration_request), headers={**headers, **APPLICATION_HEADERS}, timeout=request_timeout, diff --git a/UnleashClient/api/urls.py b/UnleashClient/api/urls.py new file mode 100644 index 00000000..0af03305 --- /dev/null +++ b/UnleashClient/api/urls.py @@ -0,0 +1,2 @@ +def build_normalized_url(url: str, path: str) -> str: + return f"{url.rstrip('/')}{path}" diff --git a/tests/unit_tests/api/test_feature.py b/tests/unit_tests/api/test_feature.py index a10bd237..38a10b4c 100644 --- a/tests/unit_tests/api/test_feature.py +++ b/tests/unit_tests/api/test_feature.py @@ -158,3 +158,29 @@ def test_get_feature_toggle_retries(): assert len(responses.calls) == 2 assert len(json.loads(result)["features"]) == 1 assert etag == ETAG_VALUE + + +@responses.activate +def test_get_feature_toggle_strips_trailing_slash_from_url(): + responses.add( + responses.GET, + FULL_FEATURE_URL, + json=MOCK_FEATURE_RESPONSE, + status=200, + headers={"etag": ETAG_VALUE}, + ) + + (result, etag) = get_feature_toggles( + f"{URL}/", + APP_NAME, + INSTANCE_ID, + CUSTOM_HEADERS, + CUSTOM_OPTIONS, + REQUEST_TIMEOUT, + REQUEST_RETRIES, + ) + + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == FULL_FEATURE_URL + assert json.loads(result)["version"] == 1 + assert etag == ETAG_VALUE diff --git a/tests/unit_tests/api/test_metrics.py b/tests/unit_tests/api/test_metrics.py index 35ff98d1..626ebddd 100644 --- a/tests/unit_tests/api/test_metrics.py +++ b/tests/unit_tests/api/test_metrics.py @@ -44,3 +44,20 @@ def test_send_metrics(payload, status, expected): assert expected(result) assert request["connectionId"] == MOCK_METRICS_REQUEST.get("connectionId") + + +@responses.activate +def test_send_metrics_strips_trailing_slash_from_url(): + responses.add(responses.POST, FULL_METRICS_URL, json={}, status=202) + + result = send_metrics( + f"{URL}/", + MOCK_METRICS_REQUEST, + CUSTOM_HEADERS, + CUSTOM_OPTIONS, + REQUEST_TIMEOUT, + ) + + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == FULL_METRICS_URL + assert result is True diff --git a/tests/unit_tests/api/test_register.py b/tests/unit_tests/api/test_register.py index c977c6d0..7ede1f3c 100644 --- a/tests/unit_tests/api/test_register.py +++ b/tests/unit_tests/api/test_register.py @@ -123,3 +123,24 @@ def test_register_omits_sdk_flavor_when_unset(): request = json.loads(responses.calls[0].request.body) assert "sdkFlavor" not in request assert "sdkFlavorVersion" not in request + + +@responses.activate +def test_register_client_strips_trailing_slash_from_url(): + responses.add(responses.POST, FULL_REGISTER_URL, json={}, status=202) + + result = register_client( + f"{URL}/", + APP_NAME, + INSTANCE_ID, + CONNECTION_ID, + METRICS_INTERVAL, + CUSTOM_HEADERS, + CUSTOM_OPTIONS, + {}, + REQUEST_TIMEOUT, + ) + + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == FULL_REGISTER_URL + assert result is True