Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extends API joining functionality for API endpoints #36

Merged
merged 1 commit into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))
Loading