Skip to content

Commit 73f3d76

Browse files
authored
Add async request query functionality (#438)
* add utility for querying which async requests are running * add tests * fix docs * rename function
1 parent 5fd4f94 commit 73f3d76

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

Diff for: sentinelhub/api/process.py

+34-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
"""
22
Implementation of `Sentinel Hub Process API interface <https://docs.sentinel-hub.com/api/latest/api/process/>`__.
33
"""
4-
from typing import Any, Dict, List, Optional, Tuple, Union
4+
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
55

6+
import requests
7+
8+
from sentinelhub.exceptions import DownloadFailedException
9+
10+
from ..config import SHConfig
611
from ..constants import MimeType
712
from ..download import SentinelHubDownloadClient
813
from ..geometry import BBox, Geometry
@@ -29,7 +34,7 @@ def __init__(
2934
geometry: Optional[Geometry] = None,
3035
size: Optional[Tuple[int, int]] = None,
3136
resolution: Optional[Tuple[float, float]] = None,
32-
**kwargs: Any
37+
**kwargs: Any,
3338
):
3439
"""
3540
For details of certain parameters check the
@@ -163,7 +168,7 @@ def __init__(
163168
geometry: Optional[Geometry] = None,
164169
size: Optional[Tuple[int, int]] = None,
165170
resolution: Optional[Tuple[float, float]] = None,
166-
**kwargs: Any
171+
**kwargs: Any,
167172
):
168173
"""
169174
For details of certain parameters check the
@@ -283,3 +288,29 @@ def output(
283288
_update_other_args(request_output, other_args)
284289

285290
return request_output
291+
292+
293+
def get_async_running_status(ids: Iterable[str], config: Optional[SHConfig] = None) -> Dict[str, bool]:
294+
"""Returns a mapping that describes which requests are running.
295+
296+
:param ids: A collection of async request IDs.
297+
:param config: A custom instance of config class to override parameters from the saved configuration.
298+
:return: A mapping that specifies whether a process is running for each of the IDs.
299+
"""
300+
config = config or SHConfig()
301+
client = SentinelHubDownloadClient(config=config)
302+
result = {}
303+
for request_id in ids:
304+
try:
305+
client.get_json_dict(f"{config.sh_base_url}/api/v1/async/process/{request_id}", use_session=True)
306+
# A successful request means it's running
307+
result[request_id] = True
308+
except DownloadFailedException as exception:
309+
code_not_found = requests.status_codes.codes.NOT_FOUND
310+
# A 404 means it's not running
311+
if exception.request_exception and exception.request_exception.response.status_code == code_not_found:
312+
result[request_id] = False
313+
else:
314+
raise exception from exception
315+
316+
return result

Diff for: tests/api/test_process_async.py

+26-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
import datetime as dt
44

55
import pytest
6+
import requests
67
from requests_mock import Mocker
78

8-
from sentinelhub import CRS, BBox, DataCollection, MimeType, SentinelHubRequest
9-
from sentinelhub.api.process import AsyncProcessRequest
9+
from sentinelhub import CRS, BBox, DataCollection, MimeType, SentinelHubRequest, SHConfig
10+
from sentinelhub.api.process import AsyncProcessRequest, get_async_running_status
1011

1112
pytestmark = pytest.mark.sh_integration
1213

1314

14-
def test_async_process_request_response(requests_mock: Mocker) -> None:
15+
def test_async_process_request_response(requests_mock: Mocker, config: SHConfig) -> None:
1516
"""A test that mocks the response of the async process request."""
1617
evalscript = "some evalscript"
1718
time_interval = dt.date(year=2020, month=6, day=1), dt.date(year=2020, month=6, day=10)
@@ -29,6 +30,7 @@ def test_async_process_request_response(requests_mock: Mocker) -> None:
2930
],
3031
delivery=AsyncProcessRequest.s3_specification(url="s3_my_bucket", access_key="foo", secret_access_key="bar"),
3132
bbox=bbox,
33+
config=config,
3234
)
3335

3436
requests_mock.post("/oauth/token", real_http=True)
@@ -38,3 +40,24 @@ def test_async_process_request_response(requests_mock: Mocker) -> None:
3840
)
3941

4042
assert request.get_data() == [{"id": "beep", "status": "RUNNING"}]
43+
44+
45+
def test_which_async_requests_running(requests_mock: Mocker, config: SHConfig) -> None:
46+
running_ids = ["a", "c", "d"]
47+
nonrunning_ids = ["b", "e"]
48+
49+
requests_mock.post("/oauth/token", real_http=True)
50+
for request_id in running_ids:
51+
requests_mock.get(
52+
f"/api/v1/async/process/{request_id}",
53+
[{"json": {"id": request_id, "status": "RUNNING"}}],
54+
)
55+
for request_id in nonrunning_ids:
56+
response_404 = requests.Response()
57+
response_404.status_code = 404
58+
requests_mock.get(
59+
f"/api/v1/async/process/{request_id}", exc=requests.exceptions.HTTPError(response=response_404)
60+
)
61+
62+
results = get_async_running_status("abcde", config=config)
63+
assert results == {"a": True, "c": True, "d": True, "b": False, "e": False}

0 commit comments

Comments
 (0)