Skip to content

Commit 22b09e8

Browse files
authored
Use urljoin, not string cat, for urls (#746)
* fix: use urljoin, not string cat, for urls * chore: update changelog
1 parent 3053341 commit 22b09e8

File tree

6 files changed

+242
-4
lines changed

6 files changed

+242
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Use urljoin to build hrefs [#746](https://github.com/stac-utils/pystac-client/pull/746)
13+
1014
## [v0.8.4] - 2024-10-16
1115

1216
### Added

pystac_client/_utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import urllib
12
import warnings
23
from typing import Any, Callable, Dict, Optional, Union
34

@@ -25,3 +26,12 @@ def call_modifier(
2526
"a function that returns 'None' or silence this warning.",
2627
IgnoredResultWarning,
2728
)
29+
30+
31+
def urljoin(href: str, name: str) -> str:
32+
"""Joins a path onto an existing href, respecting query strings, etc."""
33+
url = urllib.parse.urlparse(href)
34+
path = url.path
35+
if not path.endswith("/"):
36+
path += "/"
37+
return urllib.parse.urlunparse(url._replace(path=path + name))

pystac_client/client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from pystac.layout import APILayoutStrategy, HrefLayoutStrategy
2121
from requests import Request
2222

23-
from pystac_client._utils import Modifiable, call_modifier
23+
from pystac_client._utils import Modifiable, call_modifier, urljoin
2424
from pystac_client.collection_client import CollectionClient
2525
from pystac_client.collection_search import CollectionSearch
2626
from pystac_client.conformance import ConformanceClasses
@@ -786,11 +786,11 @@ def _collections_href(self, collection_id: Optional[str] = None) -> str:
786786
data_link = self.get_single_link("data")
787787
href = self._get_href("data", data_link, "collections")
788788
if collection_id is not None:
789-
return f"{href.rstrip('/')}/{collection_id}"
789+
return urljoin(href, collection_id)
790790
return href
791791

792792
def _get_collection_queryables_href(
793793
self, collection_id: Optional[str] = None
794794
) -> str:
795795
href = self._collections_href(collection_id)
796-
return f"{href.rstrip('/')}/{QUERYABLES_ENDPOINT}"
796+
return urljoin(href, QUERYABLES_ENDPOINT)

pystac_client/mixins.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import pystac
55

6+
from pystac_client._utils import urljoin
67
from pystac_client.conformance import ConformanceClasses
78
from pystac_client.exceptions import APIError
89
from pystac_client.stac_api_io import StacApiIO
@@ -25,7 +26,7 @@ def _get_href(self, rel: str, link: Optional[pystac.Link], endpoint: str) -> str
2526
href = link.absolute_href
2627
else:
2728
warnings.warn(MissingLink(rel, self.__class__.__name__), stacklevel=2)
28-
href = f"{self.self_href.rstrip('/')}/{endpoint}"
29+
href = urljoin(self.self_href, endpoint)
2930
return href
3031

3132

tests/cassettes/test_client/test_query_string_in_collections_url.yaml

Lines changed: 216 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,3 +791,10 @@ def test_fallback_strategy() -> None:
791791

792792
assert (item_root := item.get_single_link("root"))
793793
assert item_root.href == root_href
794+
795+
796+
@pytest.mark.vcr
797+
def test_query_string_in_collections_url() -> None:
798+
# https://github.com/stac-utils/pystac-client/issues/745
799+
client = Client.open("https://paituli.csc.fi/geoserver/ogc/stac/v1")
800+
client.get_collection("sentinel2-l2a")

0 commit comments

Comments
 (0)