Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
yapf==0.6.2
yapf==0.10.0
pytest==2.8.7
31 changes: 24 additions & 7 deletions staffjoy/resource.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import time
from datetime import datetime
from copy import copy
import requests

from staffjoy.config import config_from_env
from staffjoy.exceptions import UnauthorizedException, NotFoundException, BadRequestException

MICROSECONDS_PER_SECOND = 10**6


class Resource:
# Seconds to sleep between requests (bc of rate limits)
REQUEST_SLEEP = 0.5
# Slow each request to this (bc of rate limits)
REQUEST_TIME_MICROSECONDS = 0.3 * MICROSECONDS_PER_SECOND # 0.3 seconds

PATH = "" # URL path added to base, including route variables
ID_NAME = None # What is this ID called in the route of children?
Expand Down Expand Up @@ -71,10 +74,11 @@ def get_all(cls, parent=None, **params):
base_obj = cls(key=parent.key, route=route, config=parent.config)
"""Perform a read request against the resource"""

start = datetime.now()
r = requests.get(base_obj._url(),
auth=(base_obj.key, ""),
params=params)
time.sleep(cls.REQUEST_SLEEP)
cls._delay_for_ratelimits(start)

if r.status_code not in cls.TRUTHY_CODES:
return base_obj._handle_request_exception(r)
Expand Down Expand Up @@ -121,8 +125,9 @@ def _handle_request_exception(request):

def fetch(self):
"""Perform a read request against the resource"""
start = datetime.now()
r = requests.get(self._url(), auth=(self.key, ""))
time.sleep(self.REQUEST_SLEEP)
self._delay_for_ratelimits(start)

if r.status_code not in self.TRUTHY_CODES:
return self._handle_request_exception(r)
Expand All @@ -144,16 +149,18 @@ def _process_meta(self, response):
def delete(self):
"""Delete the object"""

start = datetime.now()
r = requests.delete(self._url(), auth=(self.key, ""))
time.sleep(self.REQUEST_SLEEP)
self._delay_for_ratelimits(start)

if r.status_code not in self.TRUTHY_CODES:
return self._handle_request_exception(r)

def patch(self, **kwargs):
"""Change attributes of the item"""
start = datetime.now()
r = requests.patch(self._url(), auth=(self.key, ""), data=kwargs)
time.sleep(self.REQUEST_SLEEP)
self._delay_for_ratelimits(start)

if r.status_code not in self.TRUTHY_CODES:
return self._handle_request_exception(r)
Expand All @@ -175,8 +182,9 @@ def create(cls, parent=None, **kwargs):

obj = cls(key=parent.key, route=route, config=parent.config)

start = datetime.now()
response = requests.post(obj._url(), auth=(obj.key, ""), data=kwargs)
time.sleep(cls.REQUEST_SLEEP)
cls._delay_for_ratelimits(start)

if response.status_code not in cls.TRUTHY_CODES:
return cls._handle_request_exception(response)
Expand All @@ -191,6 +199,15 @@ def create(cls, parent=None, **kwargs):
def get_id(self):
return self.data.get("id", self.route.get(self.ID_NAME))

@classmethod
def _delay_for_ratelimits(cls, start):
"""If request was shorter than max request time, delay"""
stop = datetime.now()
duration_microseconds = (stop - start).microseconds
if duration_microseconds < cls.REQUEST_TIME_MICROSECONDS:
time.sleep((cls.REQUEST_TIME_MICROSECONDS - duration_microseconds)
/ MICROSECONDS_PER_SECOND)

def __str__(self):
return "{} id {}".format(self.__class__.__name__,
self.route.get(self.ID_NAME))