Skip to content

Commit 5c27a31

Browse files
committed
Add TooManyRequestsError for HTTP 429 errors
Ref: AP-683
1 parent e0b6265 commit 5c27a31

4 files changed

Lines changed: 38 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,31 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.2.4]
9+
10+
### Added
11+
- `TooManyRequestsError` is raised by all API calls when the TIND API returns an HTTP 429 response.
12+
13+
814
## [0.2.3]
915

1016
### Added
1117
- parameter to client.write_search_results_to_file() to specify an output directory for the file, with fallback to default_storage_dir if not provided
1218
- also uses PATH
1319

20+
1421
## [0.2.2]
1522

1623
### Changed
1724
- build package and publish to pypi, using setuptools_scm for version management
1825

26+
1927
## [0.2.1]
2028

2129
### Changed
2230
- reconciling version agreement between the releases, this file, and the pyproject.toml
2331

32+
2433
## [0.2.0]
2534

2635
### Added

tests/test_api.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import requests_mock as req_mock # noqa: F401 — activates the requests_mock fixture
77

88
from tind_client.api import tind_download, tind_get
9-
from tind_client.errors import AuthorizationError
9+
from tind_client.errors import AuthorizationError, TooManyRequestsError
1010

1111
BASE_URL = "https://tind.example.edu"
1212
API_KEY = "test-api-key"
@@ -39,6 +39,13 @@ def test_tind_get_raises_on_401(requests_mock: req_mock.Mocker) -> None:
3939
tind_get("record/1/", api_key=API_KEY, api_url=BASE_URL)
4040

4141

42+
def test_tind_get_raises_on_429(requests_mock: req_mock.Mocker) -> None:
43+
"""Ensure tind_get raises TooManyRequestsError on HTTP 429."""
44+
requests_mock.get(f"{BASE_URL}/record/1/", status_code=429)
45+
with pytest.raises(TooManyRequestsError):
46+
tind_get("record/1/", api_key=API_KEY, api_url=BASE_URL)
47+
48+
4249
def test_tind_get_raises_on_500(requests_mock: req_mock.Mocker) -> None:
4350
"""tind_get propagates an HTTPError on HTTP 5xx."""
4451
requests_mock.get(f"{BASE_URL}/record/1/", status_code=500)
@@ -90,6 +97,14 @@ def test_tind_download_raises_on_401(requests_mock: req_mock.Mocker) -> None:
9097
tind_download(url, "/tmp", api_key=API_KEY)
9198

9299

100+
def test_tind_download_raises_on_429(requests_mock: req_mock.Mocker) -> None:
101+
"""Ensure tind_download raises TooManyRequestsError on HTTP 429."""
102+
url = f"{BASE_URL}/files/12345/download"
103+
requests_mock.get(url, status_code=429)
104+
with pytest.raises(TooManyRequestsError):
105+
tind_download(url, "/tmp", api_key=API_KEY)
106+
107+
93108
def test_tind_download_non_200_returns_empty(requests_mock: req_mock.Mocker) -> None:
94109
"""tind_download returns (status, '') for non-200, non-error responses."""
95110
url = f"{BASE_URL}/files/12345/download"

tind_client/api.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from typing import Tuple
88
import requests
99

10-
from .errors import AuthorizationError, TINDError
10+
from .errors import AuthorizationError, TINDError, TooManyRequestsError
1111

1212

1313
TIMEOUT: int = 30
@@ -39,6 +39,8 @@ def tind_get(
3939
:param dict|None params: Extra query parameters to send.
4040
For example, ``{'of': 'xm'}``.
4141
:raises AuthorizationError: If an invalid TIND API key is provided.
42+
:raises TooManyRequestsError: If the TIND server is overloaded with requests.
43+
:raises TINDError: If an internal server error occurs during request processing.
4244
:returns: A tuple of the HTTP status code and response text (if any).
4345
:rtype: Tuple[int, str]
4446
"""
@@ -54,6 +56,8 @@ def tind_get(
5456
)
5557
if resp.status_code == 401:
5658
raise AuthorizationError("Invalid TIND API key provided")
59+
if resp.status_code == 429:
60+
raise TooManyRequestsError("Enhance your calm")
5761
if resp.status_code >= 500:
5862
raise TINDError.from_json(resp.status_code, resp.text)
5963
return resp.status_code, resp.text
@@ -66,13 +70,17 @@ def tind_download(url: str, output_dir: str, api_key: str) -> Tuple[int, str]:
6670
:param str output_dir: The path to the directory in which to save the file.
6771
:param str api_key: The TIND API token.
6872
:raises AuthorizationError: If an invalid TIND API key is provided.
73+
:raises TooManyRequestsError: If the TIND server is overloaded with requests.
74+
:raises TINDError: If an internal server error occurs during request processing.
6975
:returns: A tuple of the HTTP status code and the path to the downloaded file (if successful).
7076
:rtype: Tuple[int, str]
7177
"""
7278
resp = requests.get(url, headers=_auth_header(api_key), timeout=TIMEOUT)
7379
status = resp.status_code
7480
if status == 401:
7581
raise AuthorizationError("Invalid TIND API key provided")
82+
if status == 429:
83+
raise TooManyRequestsError("Enhance your calm")
7684
if status >= 500:
7785
raise TINDError.from_json(status, resp.text)
7886
if status != 200:

tind_client/errors.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ class AuthorizationError(TINDError):
2828
"""Raised when authorization with the TIND API fails."""
2929

3030

31+
class TooManyRequestsError(TINDError):
32+
"""Raised when the TIND API has had too many requests in a short period."""
33+
34+
3135
class RecordNotFoundError(TINDError):
3236
"""Raised when a requested record or file is not found in TIND."""

0 commit comments

Comments
 (0)