Skip to content
This repository has been archived by the owner on Mar 28, 2019. It is now read-only.

Commit

Permalink
Merge pull request #12 from mozilla-services/support-if-unmodified-an…
Browse files Browse the repository at this point in the history
…d-if-modified-headers

Let pass some additionnal headers and params to requests.
  • Loading branch information
Natim committed Sep 23, 2015
2 parents eff71c8 + fbc6594 commit 259fcdb
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 34 deletions.
53 changes: 23 additions & 30 deletions syncclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,17 @@ def __init__(self, bid_assertion=None, client_state=None,
'key': credentials['key']
})

def _request(self, method, url, *args, **kwargs):
def _request(self, method, url, **kwargs):
"""Utility to request an endpoint with the correct authentication
setup, raises on errors and returns the JSON.
"""
url = self.api_endpoint.rstrip('/') + '/' + url.lstrip('/')
self.raw_resp = requests.request(method, url,
auth=self.auth, *args, **kwargs)
self.raw_resp = requests.request(method, url, auth=self.auth, **kwargs)
self.raw_resp.raise_for_status()
return self.raw_resp.json()

def info_collections(self, if_modified_since=None,
if_unmodified_since=None):
def info_collections(self, **kwargs):
"""
Returns an object mapping collection names associated with the account
to the last-modified time for each collection.
Expand All @@ -122,42 +120,41 @@ def info_collections(self, if_modified_since=None,
with an expired token, so that clients can check for server-side
changes before fetching an updated token from the Token Server.
"""
return self._request('get', '/info/collections')
return self._request('get', '/info/collections', **kwargs)

def info_quota(self):
def info_quota(self, **kwargs):
"""
Returns a two-item list giving the user's current usage and quota
(in KB). The second item will be null if the server does not enforce
quotas.
Note that usage numbers may be approximate.
"""
return self._request('get', '/info/quota')
return self._request('get', '/info/quota', **kwargs)

def get_collection_usage(self):
def get_collection_usage(self, **kwargs):
"""
Returns an object mapping collection names associated with the account
to the data volume used for each collection (in KB).
Note that these results may be very expensive as it calculates more
detailed and accurate usage information than the info_quota method.
"""
return self._request('get', '/info/collection_usage')
return self._request('get', '/info/collection_usage', **kwargs)

def get_collection_counts(self):
def get_collection_counts(self, **kwargs):
"""
Returns an object mapping collection names associated with the
account to the total number of items in each collection.
"""
return self._request('get', '/info/collection_counts')
return self._request('get', '/info/collection_counts', **kwargs)

def delete_all_records(self):
def delete_all_records(self, **kwargs):
"""Deletes all records for the user."""
return self._request('delete', '/')
return self._request('delete', '/', **kwargs)

def get_records(self, collection, full=True, ids=None, newer=None,
limit=None, offset=None, sort=None, if_modified_since=None,
if_unmodified_since=None):
limit=None, offset=None, sort=None, **kwargs):
"""
Returns a list of the BSOs contained in a collection. For example:
Expand Down Expand Up @@ -193,7 +190,7 @@ def get_records(self, collection, full=True, ids=None, newer=None,
"newest" - orders by last-modified time, largest first
"index" - orders by the sortindex, highest weight first
"""
params = {}
params = kwargs.pop('params', {})
if full:
params['full'] = True
if ids is not None:
Expand All @@ -208,21 +205,21 @@ def get_records(self, collection, full=True, ids=None, newer=None,
params['sort'] = sort

return self._request('get', '/storage/%s' % collection.lower(),
params=params)
params=params, **kwargs)

def get_record(self, collection, record_id):
def get_record(self, collection, record_id, **kwargs):
"""Returns the BSO in the collection corresponding to the requested id.
"""
return self._request('get', '/storage/%s/%s' % (collection.lower(),
record_id))
record_id), **kwargs)

def delete_record(self, collection, record_id):
def delete_record(self, collection, record_id, **kwargs):
"""Deletes the BSO at the given location.
"""
return self._request('delete', '/storage/%s/%s' % (collection.lower(),
record_id))
return self._request('delete', '/storage/%s/%s' % (
collection.lower(), record_id), **kwargs)

def put_record(self, collection, record, if_unmodified_since=None):
def put_record(self, collection, record, **kwargs):
"""
Creates or updates a specific BSO within a collection.
The passed record must be a python object containing new data for the
Expand All @@ -238,10 +235,6 @@ def put_record(self, collection, record, if_unmodified_since=None):
If the target BSO does not exist, then fields that are not provided in
the python object will be set to their default value by the server.
:param if_unmodified_since:
Avoid overwriting the data if it has been changed since the client
fetched it.
Successful responses will return the new last-modified time for the
collection.
Expand All @@ -251,9 +244,9 @@ def put_record(self, collection, record, if_unmodified_since=None):
record = record.copy()
record_id = record.pop('id')
return self._request('put', '/storage/%s/%s' % (
collection.lower(), record_id), json=record)
collection.lower(), record_id), json=record, **kwargs)

def post_records(self, collection, records):
def post_records(self, collection, records, **kwargs):
"""
Takes a list of BSOs in the request body and iterates over them,
effectively doing a series of individual PUTs with the same timestamp.
Expand Down
54 changes: 50 additions & 4 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,27 +240,50 @@ def test_info_collection(self):
self.client.info_collections()
self.client._request.assert_called_with('get', '/info/collections')

def test_info_collection_can_receive_requests_parameters(self):
self.client.info_collections(headers=mock.sentinel.headers)
self.client._request.assert_called_with('get', '/info/collections',
headers=mock.sentinel.headers)

def test_info_quota(self):
self.client.info_quota()
self.client._request.assert_called_with('get', '/info/quota')

def test_info_quota_can_receive_requests_parameters(self):
self.client.info_quota(headers=mock.sentinel.headers)
self.client._request.assert_called_with('get', '/info/quota',
headers=mock.sentinel.headers)

def test_collection_usage(self):
self.client.get_collection_usage()
self.client._request.assert_called_with(
'get',
'/info/collection_usage')
'get', '/info/collection_usage')

def test_collection_usage_can_receive_requests_parameters(self):
self.client.get_collection_usage(headers=mock.sentinel.headers)
self.client._request.assert_called_with(
'get', '/info/collection_usage', headers=mock.sentinel.headers)

def test_collection_counts(self):
self.client.get_collection_counts()
self.client._request.assert_called_with(
'get',
'/info/collection_counts')
'get', '/info/collection_counts')

def test_collection_counts_can_receive_requests_parameters(self):
self.client.get_collection_counts(headers=mock.sentinel.headers)
self.client._request.assert_called_with(
'get', '/info/collection_counts', headers=mock.sentinel.headers)

def test_delete_all_records(self):
self.client.delete_all_records()
self.client._request.assert_called_with(
'delete', '/')

def test_delete_all_records_can_receive_requests_parameters(self):
self.client.delete_all_records(headers=mock.sentinel.headers)
self.client._request.assert_called_with(
'delete', '/', headers=mock.sentinel.headers)

def test_get_records_sets_full_by_default(self):
self.client.get_records('mycollection')
self.client._request.assert_called_with(
Expand Down Expand Up @@ -326,18 +349,41 @@ def test_get_record(self):
self.client._request.assert_called_with(
'get', '/storage/mycollection/1234')

def test_get_record_can_receive_requests_parameters(self):
self.client.get_record('myCollection', 1234,
headers=mock.sentinel.headers)
self.client._request.assert_called_with(
'get', '/storage/mycollection/1234',
headers=mock.sentinel.headers)

def test_delete_record(self):
self.client.delete_record('myCollection', 1234)
self.client._request.assert_called_with(
'delete', '/storage/mycollection/1234')

def test_delete_record_can_receive_requests_parameters(self):
self.client.delete_record('myCollection', 1234,
headers=mock.sentinel.headers)
self.client._request.assert_called_with(
'delete', '/storage/mycollection/1234',
headers=mock.sentinel.headers)

def test_put_record(self):
record = {'id': 1234, 'foo': 'bar'}
self.client.put_record('myCollection', record)
self.client._request.assert_called_with(
'put', '/storage/mycollection/1234',
json={'foo': 'bar'})

def test_put_record_can_receive_requests_parameters(self):
record = {'id': 1234, 'foo': 'bar'}
self.client.put_record('myCollection', record,
headers=mock.sentinel.headers)
self.client._request.assert_called_with(
'put', '/storage/mycollection/1234',
json={'foo': 'bar'},
headers=mock.sentinel.headers)

def test_put_record_doesnt_modify_the_passed_object(self):
record = {'id': 1234, 'foo': 'bar'}
self.client.put_record('myCollection', record)
Expand Down

0 comments on commit 259fcdb

Please sign in to comment.