From 360e53323c976d65e74323e963e573e028f4dba5 Mon Sep 17 00:00:00 2001 From: "Philip I. Thomas" Date: Sat, 2 Apr 2016 16:00:03 -0700 Subject: [PATCH 1/5] Prevent rate limiting --- README.md | 5 ++++- staffjoy/config.py | 1 + staffjoy/resource.py | 9 +++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e8be083..88077bb 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Authentication keys are currently tied to an individual user's account. To issue To get your organization ID, look at the URL path when you go to the Manager app while logged in. +## Rate Limits + +This client sleeps for .5 seconds after every request. Thus, in a single thread, requests are limited to 120 per minute. This is done to avoid rate limiting. Staffjoy's API currently rate limits to 300 requests per second across keys and IPs. Thus, by using this library, you should never encounter a rate limit (assuming one executing thread per IP address). + ## Updates If you use this library, please subscribe to the [Staffjoy API Updates Google Group](https://groups.google.com/forum/#!forum/staffjoy-api-updates) for important notifications about changes and deprecations. @@ -62,4 +66,3 @@ loc.delete() ``` - diff --git a/staffjoy/config.py b/staffjoy/config.py index 4b09018..514058f 100644 --- a/staffjoy/config.py +++ b/staffjoy/config.py @@ -5,6 +5,7 @@ class DefaultConfig: ENV = "prod" LOG_LEVEL = logging.INFO BASE = "https://www.staffjoy.com/api/v2/" + REQUEST_SLEEP = 0.5 class StageConfig(DefaultConfig): diff --git a/staffjoy/resource.py b/staffjoy/resource.py index 87438c5..e826036 100644 --- a/staffjoy/resource.py +++ b/staffjoy/resource.py @@ -1,4 +1,5 @@ import requests +import time from copy import copy from .config import config_from_env @@ -70,6 +71,7 @@ def get_all(cls, parent=None, **params): r = requests.get(base_obj._url(), auth=(base_obj.key, ""), params=params) + time.sleep(config.REQUEST_SLEEP) if r.status_code not in cls.TRUTHY_CODES: return base_obj._handle_request_exception(r) @@ -117,6 +119,8 @@ def _handle_request_exception(request): def fetch(self): """Perform a read request against the resource""" r = requests.get(self._url(), auth=(self.key, "")) + time.sleep(config.REQUEST_SLEEP) + if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -138,12 +142,16 @@ def delete(self): """Delete the object""" r = requests.delete(self._url(), auth=(self.key, "")) + time.sleep(config.REQUEST_SLEEP) + if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) def patch(self, **kwargs): """Change attributes of the item""" r = requests.patch(self._url(), auth=(self.key, ""), data=kwargs) + time.sleep(config.REQUEST_SLEEP) + if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -165,6 +173,7 @@ def create(cls, parent=None, **kwargs): obj = cls(key=parent.key, route=route, config=parent.config) response = requests.post(obj._url(), auth=(obj.key, ""), data=kwargs) + time.sleep(config.REQUEST_SLEEP) if response.status_code not in cls.TRUTHY_CODES: return cls._handle_request_exception(response) From 4b197d3837fdd6639d1bf7341a311bc78f606063 Mon Sep 17 00:00:00 2001 From: "Philip I. Thomas" Date: Sat, 2 Apr 2016 16:06:25 -0700 Subject: [PATCH 2/5] This is why we have testing --- staffjoy/resource.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/staffjoy/resource.py b/staffjoy/resource.py index e826036..53a945e 100644 --- a/staffjoy/resource.py +++ b/staffjoy/resource.py @@ -71,7 +71,7 @@ def get_all(cls, parent=None, **params): r = requests.get(base_obj._url(), auth=(base_obj.key, ""), params=params) - time.sleep(config.REQUEST_SLEEP) + time.sleep(self.config.REQUEST_SLEEP) if r.status_code not in cls.TRUTHY_CODES: return base_obj._handle_request_exception(r) @@ -119,7 +119,7 @@ def _handle_request_exception(request): def fetch(self): """Perform a read request against the resource""" r = requests.get(self._url(), auth=(self.key, "")) - time.sleep(config.REQUEST_SLEEP) + time.sleep(self.config.REQUEST_SLEEP) if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -142,7 +142,7 @@ def delete(self): """Delete the object""" r = requests.delete(self._url(), auth=(self.key, "")) - time.sleep(config.REQUEST_SLEEP) + time.sleep(self.config.REQUEST_SLEEP) if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -150,7 +150,7 @@ def delete(self): def patch(self, **kwargs): """Change attributes of the item""" r = requests.patch(self._url(), auth=(self.key, ""), data=kwargs) - time.sleep(config.REQUEST_SLEEP) + time.sleep(self.config.REQUEST_SLEEP) if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -173,7 +173,7 @@ def create(cls, parent=None, **kwargs): obj = cls(key=parent.key, route=route, config=parent.config) response = requests.post(obj._url(), auth=(obj.key, ""), data=kwargs) - time.sleep(config.REQUEST_SLEEP) + time.sleep(self.config.REQUEST_SLEEP) if response.status_code not in cls.TRUTHY_CODES: return cls._handle_request_exception(response) From a75fcbe6b822506fe7780c1c1857247d3eb92f10 Mon Sep 17 00:00:00 2001 From: "Philip I. Thomas" Date: Sat, 2 Apr 2016 16:07:30 -0700 Subject: [PATCH 3/5] version bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d896206..2d44b61 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = "0.13" +version = "0.14" setup(name="staffjoy", packages=find_packages(), version=version, From db606c33054b6a038ed3fa922453d3139f3fb39e Mon Sep 17 00:00:00 2001 From: "Philip I. Thomas" Date: Sat, 2 Apr 2016 16:11:57 -0700 Subject: [PATCH 4/5] Fix for class methods --- staffjoy/resource.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/staffjoy/resource.py b/staffjoy/resource.py index 53a945e..80be980 100644 --- a/staffjoy/resource.py +++ b/staffjoy/resource.py @@ -7,6 +7,9 @@ class Resource: + # Seconds to sleep between requests (bc of rate limits) + REQUEST_SLEEP = 0.5 + PATH = "" # URL path added to base, including route variables ID_NAME = None # What is this ID called in the route of children? META_ENVELOPES = [] # Metadata keys for what to unpack from response @@ -71,7 +74,7 @@ def get_all(cls, parent=None, **params): r = requests.get(base_obj._url(), auth=(base_obj.key, ""), params=params) - time.sleep(self.config.REQUEST_SLEEP) + time.sleep(self.REQUEST_SLEEP) if r.status_code not in cls.TRUTHY_CODES: return base_obj._handle_request_exception(r) @@ -119,7 +122,7 @@ def _handle_request_exception(request): def fetch(self): """Perform a read request against the resource""" r = requests.get(self._url(), auth=(self.key, "")) - time.sleep(self.config.REQUEST_SLEEP) + time.sleep(self.REQUEST_SLEEP) if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -142,7 +145,7 @@ def delete(self): """Delete the object""" r = requests.delete(self._url(), auth=(self.key, "")) - time.sleep(self.config.REQUEST_SLEEP) + time.sleep(self.REQUEST_SLEEP) if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -150,7 +153,7 @@ def delete(self): def patch(self, **kwargs): """Change attributes of the item""" r = requests.patch(self._url(), auth=(self.key, ""), data=kwargs) - time.sleep(self.config.REQUEST_SLEEP) + time.sleep(self.REQUEST_SLEEP) if r.status_code not in self.TRUTHY_CODES: return self._handle_request_exception(r) @@ -173,7 +176,7 @@ def create(cls, parent=None, **kwargs): obj = cls(key=parent.key, route=route, config=parent.config) response = requests.post(obj._url(), auth=(obj.key, ""), data=kwargs) - time.sleep(self.config.REQUEST_SLEEP) + time.sleep(self.REQUEST_SLEEP) if response.status_code not in cls.TRUTHY_CODES: return cls._handle_request_exception(response) From 87f0e815c14ea187df82e4f343fdddf642f8be9b Mon Sep 17 00:00:00 2001 From: "Philip I. Thomas" Date: Sat, 2 Apr 2016 16:16:11 -0700 Subject: [PATCH 5/5] oops - some are instantiated --- staffjoy/resource.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/staffjoy/resource.py b/staffjoy/resource.py index 80be980..d66ac6e 100644 --- a/staffjoy/resource.py +++ b/staffjoy/resource.py @@ -74,7 +74,7 @@ def get_all(cls, parent=None, **params): r = requests.get(base_obj._url(), auth=(base_obj.key, ""), params=params) - time.sleep(self.REQUEST_SLEEP) + time.sleep(cls.REQUEST_SLEEP) if r.status_code not in cls.TRUTHY_CODES: return base_obj._handle_request_exception(r) @@ -176,7 +176,7 @@ def create(cls, parent=None, **kwargs): obj = cls(key=parent.key, route=route, config=parent.config) response = requests.post(obj._url(), auth=(obj.key, ""), data=kwargs) - time.sleep(self.REQUEST_SLEEP) + time.sleep(cls.REQUEST_SLEEP) if response.status_code not in cls.TRUTHY_CODES: return cls._handle_request_exception(response)