Skip to content

Commit

Permalink
Extend API endpoint URL joining (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
djperrefort authored Jul 26, 2024
1 parent ec94513 commit 576210b
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
4 changes: 1 addition & 3 deletions keystone_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,7 @@ def retrieve_records(
The response from the API in JSON format
"""

url = endpoint.join_url(self.url)
if pk is not None:
url = urljoin(url, str(pk))
url = endpoint.join_url(self.url, pk)

try:
response = self.http_get(url, params=filters, timeout=timeout)
Expand Down
13 changes: 10 additions & 3 deletions keystone_client/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,31 @@

from dataclasses import dataclass, field
from urllib.parse import urljoin
from os import path


class Endpoint(str):
"""API endpoint agnostic th to baseAPI URL"""

def join_url(self, url: str) -> str:
def join_url(self, base: str, *append) -> str:
"""Join the endpoint with a base URL
This method returns URLs in a format that avoids trailing slash
redirects from the Keystone API.
Args:
url: The base URL
base: The base URL
*append: Partial paths to append onto the url
Returns:
The base URL join with the endpoint
"""

return urljoin(url, self).rstrip('/') + '/'
url = urljoin(base, self)
for partial_path in filter(lambda x: x is not None, append):
url = path.join(url, str(partial_path))

return url.rstrip('/') + '/'


@dataclass
Expand Down
54 changes: 48 additions & 6 deletions tests/schema/test_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,83 @@ class JoinUrl(TestCase):
"""Tests for the `join_url` method"""

def test_with_trailing_slash(self) -> None:
"""Test join_url with a base URL that has a trailing slash"""
"""Test `join_url` with a base URL that has a trailing slash"""

endpoint = Endpoint("authentication/new")
base_url = "https://api.example.com/"
expected_result = "https://api.example.com/authentication/new/"
self.assertEqual(expected_result, endpoint.join_url(base_url))

def test_without_trailing_slash(self) -> None:
"""Test join_url with a base URL that does not have a trailing slash"""
"""Test `join_url` with a base URL that does not have a trailing slash"""

endpoint = Endpoint("authentication/new")
base_url = "https://api.example.com"
expected_result = "https://api.example.com/authentication/new/"
self.assertEqual(expected_result, endpoint.join_url(base_url))

def test_with_endpoint_trailing_slash(self) -> None:
"""Test join_url with an endpoint that has a trailing slash"""
"""Test `join_url` with an endpoint that has a trailing slash"""

endpoint = Endpoint("authentication/new/")
base_url = "https://api.example.com"
expected_result = "https://api.example.com/authentication/new/"
self.assertEqual(expected_result, endpoint.join_url(base_url))

def test_without_endpoint_trailing_slash(self) -> None:
"""Test join_url with an endpoint that does not have a trailing slash"""
"""Test `join_url` with an endpoint that does not have a trailing slash"""

endpoint = Endpoint("authentication/new")
base_url = "https://api.example.com"
expected_result = "https://api.example.com/authentication/new/"
self.assertEqual(expected_result, endpoint.join_url(base_url))

def test_with_complete_url_as_endpoint(self) -> None:
"""Test join_url when the endpoint is a complete URL"""
def test_with_append_trailing_slash(self) -> None:
endpoint = Endpoint("authentication")
base_url = "https://api.example.com"
append_path = "new/"
expected_result = "https://api.example.com/authentication/new/"
self.assertEqual(expected_result, endpoint.join_url(base_url, append_path))

def test_without_append_trailing_slash(self) -> None:
endpoint = Endpoint("authentication")
base_url = "https://api.example.com"
append_path = "new"
expected_result = "https://api.example.com/authentication/new/"
self.assertEqual(expected_result, endpoint.join_url(base_url, append_path))

def test_with_mixed_trailing_slash_in_append(self) -> None:
"""Test `join_url` with mixed trailing slashes in append arguments"""

endpoint = Endpoint("authentication")
base_url = "https://api.example.com"
append_path1 = "new/"
append_path2 = "extra"
expected_result = "https://api.example.com/authentication/new/extra/"
self.assertEqual(expected_result, endpoint.join_url(base_url, append_path1, append_path2))

def test_complete_url_as_endpoint(self) -> None:
"""Test `join_url` when the endpoint is a complete URL"""

endpoint = Endpoint("https://anotherapi.com/authentication/new")
base_url = "https://api.example.com"
expected_result = "https://anotherapi.com/authentication/new/"
self.assertEqual(expected_result, endpoint.join_url(base_url))

def test_int_append_argument(self) -> None:
"""Test `join_url` with an `int` append argument"""

endpoint = Endpoint("authentication")
base_url = "https://api.example.com"
append_path = 123
expected_result = "https://api.example.com/authentication/123/"
self.assertEqual(expected_result, endpoint.join_url(base_url, str(append_path)))

def test_none_append_argument(self) -> None:
"""Test `join_url` with a `None` append argument"""

endpoint = Endpoint("authentication")
base_url = "https://api.example.com"
append_path = None
expected_result = "https://api.example.com/authentication/"
self.assertEqual(expected_result, endpoint.join_url(base_url, append_path))

0 comments on commit 576210b

Please sign in to comment.