Skip to content

Commit 929a6ae

Browse files
authored
[ARC-3099] Propagate error details for invalid catalog search requests (#766)
* Propagate error details for invalid catalog search requests * Clean up and finalize
1 parent a1f8a7f commit 929a6ae

File tree

4 files changed

+48
-10
lines changed

4 files changed

+48
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ You can check your current version with the following command:
3030

3131
For more information, see [UP42 Python package description](https://pypi.org/project/up42-py/).
3232

33+
### 2.5.0a6
34+
**August 18, 2025**
35+
- Propagate request error message with `InvalidSearchRequest` when `Producer::search` fails.
36+
3337
### 2.5.0a5
3438
**August 15, 2025**
3539
- Fix how collection items are retrieved in `BulkDeletion`.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "up42-py"
3-
version = "2.5.0a5"
3+
version = "2.5.0a6"
44
description = "Python SDK for UP42, the geospatial marketplace and developer platform."
55
authors = ["UP42 GmbH <[email protected]>"]
66
license = "https://github.com/up42/up42-py/blob/master/LICENSE"

tests/test_glossary.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import geojson # type: ignore
55
import pytest
6+
import requests
67
import requests_mock as req_mock
78

89
from tests import constants, helpers
@@ -121,6 +122,7 @@ class TestProvider:
121122
description="description",
122123
roles=["PRODUCER", "HOST"],
123124
)
125+
search_url = f"{constants.API_HOST}/catalog/hosts/{HOST_NAME}/stac/search"
124126

125127
@pytest.mark.parametrize(
126128
"provider, is_host",
@@ -137,6 +139,28 @@ def test_fails_to_search_if_provider_is_not_host(self):
137139
with pytest.raises(glossary.InvalidHost):
138140
next(dataclasses.replace(self.provider, roles=["PRODUCER"]).search())
139141

142+
def test_fails_to_search_if_search_request_is_invalid(self, requests_mock: req_mock.Mocker):
143+
error_message = "invalid request"
144+
requests_mock.post(
145+
url=self.search_url,
146+
status_code=422,
147+
json={
148+
"data": {},
149+
"error": {"code": 422, "message": error_message, "details": "ignored"},
150+
},
151+
)
152+
with pytest.raises(glossary.InvalidSearchRequest, match=error_message):
153+
next(self.provider.search())
154+
155+
@pytest.mark.parametrize("error_code", [400, 401, 403, 500])
156+
def test_should_propagate_search_failures(self, error_code: int, requests_mock: req_mock.Mocker):
157+
requests_mock.post(
158+
url=self.search_url,
159+
status_code=error_code,
160+
)
161+
with pytest.raises(requests.HTTPError):
162+
next(self.provider.search())
163+
140164
@pytest.mark.parametrize("bbox", [None, BBOX])
141165
@pytest.mark.parametrize("intersects", [None, POLYGON])
142166
@pytest.mark.parametrize(
@@ -179,10 +203,9 @@ def test_should_search(
179203
if collections:
180204
search_params["collections"] = collections
181205

182-
search_url = f"{constants.API_HOST}/catalog/hosts/{self.provider.name}/stac/search"
183-
next_page_url = f"{search_url}/next"
206+
next_page_url = f"{self.search_url}/next"
184207
requests_mock.post(
185-
url=search_url,
208+
url=self.search_url,
186209
json={
187210
"type": "FeatureCollection",
188211
"features": [SCENE_FEATURE] * 3,

up42/glossary.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Any, Iterator, Literal, Optional, Union
44

55
import geojson # type: ignore
6+
import requests
67

78
from up42 import base, host, utils
89

@@ -65,6 +66,10 @@ class InvalidHost(ValueError):
6566
pass
6667

6768

69+
class InvalidSearchRequest(ValueError):
70+
pass
71+
72+
6873
@dataclasses.dataclass
6974
class Provider:
7075
session = base.Session()
@@ -112,12 +117,18 @@ def search(
112117
def get_pages():
113118
url = host.endpoint(f"/catalog/hosts/{self.name}/stac/search")
114119
while url:
115-
page: dict = self.session.post(url, json=payload).json()
116-
yield page["features"]
117-
url = next(
118-
(link["href"] for link in page["links"] if link["rel"] == "next"),
119-
None,
120-
)
120+
try:
121+
page: dict = self.session.post(url, json=payload).json()
122+
yield page["features"]
123+
url = next(
124+
(link["href"] for link in page["links"] if link["rel"] == "next"),
125+
None,
126+
)
127+
except requests.HTTPError as http_error:
128+
if http_error.response.status_code == 422:
129+
error = http_error.response.json()["error"]
130+
raise InvalidSearchRequest(error["message"]) from http_error
131+
raise http_error
121132

122133
for page in get_pages():
123134
for feature in page:

0 commit comments

Comments
 (0)