Skip to content
This repository was archived by the owner on Apr 5, 2025. It is now read-only.

Commit 56e7144

Browse files
authored
Merge pull request #13 from martijndegouw/feature/handle-rate-limiting
Add Rate limiting support.
2 parents 7cff2f1 + 394f60b commit 56e7144

File tree

2 files changed

+28
-9
lines changed

2 files changed

+28
-9
lines changed

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
INSTALL_REQUIRES = [
1717
'requests>= 2.18, < 3',
1818
'cached_property >= 1.5, < 2',
19-
'inflection == 0.4'
19+
'inflection == 0.4',
20+
'requests-ratelimiter == 0.7.0',
2021
]
2122

2223

src/bookstack/models.py

+26-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from json.decoder import JSONDecodeError
22
import string
33
import urllib
4+
import time
45

56
from cached_property import cached_property
67
import inflection
78
import requests
8-
9+
from requests_ratelimiter import LimiterSession
910

1011
API_PATH = 'api/docs.json/'
1112

@@ -19,12 +20,13 @@ def __init__(
1920
api_path=API_PATH
2021
):
2122
self.api_base_url = base_url
22-
self.token_id = None
23-
self.token_secret = None
23+
self.token_id = token_id
24+
self.token_secret = token_secret
2425
self._api_path = api_path
26+
self.rate_limit=180 # Bookstack default
2527

2628
self.available_api_methods = set()
27-
self._session = BaseURLSession(self.api_base_url)
29+
self._session = BaseURLSession(self.api_base_url, per_minute=self.rate_limit)
2830
self._session.auth = Auth(token_id, token_secret)
2931

3032
def generate_api_methods(self):
@@ -41,7 +43,16 @@ def generate_api_methods(self):
4143
self.available_api_methods.add(method_name)
4244

4345
def _get_api(self):
44-
return self._session.request('GET', self._api_path).json()
46+
r = self._session.request('GET', self._api_path)
47+
48+
rate_limit = int(r.headers.get('X-RateLimit-Limit', self.rate_limit))
49+
if rate_limit != self.rate_limit:
50+
# Create a new rate limited session with the correct rate limit
51+
self._session = BaseURLSession(self.api_base_url, per_minute=self.rate_limit)
52+
self._session.auth = Auth(self.token_id, self.token_secret)
53+
self.rate_limit = rate_limit
54+
55+
return r.json()
4556

4657
def _create_api_method(self, method_info):
4758
def request_method(*args, **kwargs):
@@ -85,15 +96,22 @@ def _get_response_content(response):
8596
return content
8697

8798

88-
class BaseURLSession(requests.Session):
99+
class BaseURLSession(LimiterSession):
89100
def __init__(self, base_url, *args, **kwargs):
90101
super().__init__(*args, **kwargs)
91102
self.base_url = base_url
92103

93104
def request(self, method, url_path, *args, **kwargs):
94105
url = urllib.parse.urljoin(self.base_url, url_path)
95-
96-
return super().request(method, url, *args, **kwargs)
106+
response = super().request(method, url, *args, **kwargs)
107+
108+
# Handle 'Too Many Attempts' response
109+
if response.status_code == 429:
110+
retry_after = int(response.headers.get('Retry-After', 60))
111+
time.sleep(retry_after)
112+
response = super().request(method, url, *args, **kwargs)
113+
114+
return response
97115

98116

99117
class Auth(requests.auth.AuthBase):

0 commit comments

Comments
 (0)