Skip to content

Commit f977331

Browse files
authored
Merge pull request #95 from mansimarkaur/CRUD_methods_groups
Added CRUD methods for Groups
2 parents 60a494a + 80747a8 commit f977331

File tree

4 files changed

+208
-12
lines changed

4 files changed

+208
-12
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This document describes changes between each past release.
77
6.3.0 (unreleased)
88
==================
99

10-
- Nothing changed yet.
10+
- Added CRUD methods for the endpoint group. (#95)
1111

1212

1313
6.2.1 (2016-09-08)

README.rst

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Kinto python client
1111
:target: https://coveralls.io/r/Kinto/kinto-http.py
1212

1313

14-
Kinto is a service that allows to store and synchronize arbitrary data,
14+
Kinto is a service that allows users to store and synchronize arbitrary data,
1515
attached to a user account. Its primary interface is HTTP.
1616

1717
*kinto-http* is a Python library that eases the interactions with
@@ -33,9 +33,9 @@ Usage
3333
.. note::
3434

3535
Operations are always performed directly on the server, and no
36-
synchronisation features are implemented yet.
36+
synchronisation features have been implemented yet.
3737

38-
- The first version of this API doesn't cache any access nor provide any
38+
- The first version of this API doesn't cache any access nor provides any
3939
refresh mechanism. If you want to be sure you have the latest data available,
4040
issue another call.
4141

@@ -122,6 +122,24 @@ If no specific bucket name is provided, the "default" bucket is used.
122122
# Or even every writable buckets.
123123
client.delete_buckets()
124124
125+
Groups
126+
------
127+
128+
A group associates a name to a list of principals. It is useful in order to handle permissions.
129+
130+
.. code-block:: python
131+
132+
client.create_group('receipts', bucket='payments', data={'members': ['blah', 'foo']})
133+
134+
# Or get an existing one.
135+
group = client.get_group('receipts', bucket='payments')
136+
137+
# To delete an existing group.
138+
client.delete_group('receipts', bucket='payments')
139+
140+
# Or all groups in a bucket.
141+
client.delete_groups(bucket='payments')
142+
125143
126144
Collections
127145
-----------
@@ -133,7 +151,7 @@ A collection is where records are stored.
133151
client.create_collection('receipts', bucket='payments')
134152
135153
# Or get an existing one.
136-
client.get_collection('receipts', bucket='payments')
154+
collection = client.get_collection('receipts', bucket='payments')
137155
138156
# To delete an existing collection.
139157
client.delete_collection('receipts', bucket='payments')
@@ -211,12 +229,21 @@ For instance to give access to "leplatrem" to a specific record, you would do:
211229
Get or create
212230
-------------
213231

214-
In some cases, you might want to create a bucket, collection or record only if
232+
In some cases, you might want to create a bucket, collection, group or record only if
215233
it doesn't exist already. To do so, you can pass the ``if_not_exists=True``
216234
to the ``create_*`` methods::
217235

218236
client.create_bucket('bucket', if_not_exists=True)
219237

238+
Delete
239+
------
240+
241+
In some cases, you might want to delete a bucket, collection, group or record only if
242+
it exists already. To do so, you can pass the ``if_exists=True``
243+
to the ``delete_*`` methods::
244+
245+
client.delete_bucket('bucket', if_exists=True)
246+
220247
Overwriting existing objects
221248
----------------------------
222249

kinto_http/__init__.py

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class Endpoints(object):
3232
'batch': '{root}/batch',
3333
'buckets': '{root}/buckets',
3434
'bucket': '{root}/buckets/{bucket}',
35+
'groups': '{root}/buckets/{bucket}/groups',
36+
'group': '{root}/buckets/{bucket}/groups/{group}',
3537
'collections': '{root}/buckets/{bucket}/collections',
3638
'collection': '{root}/buckets/{bucket}/collections/{collection}',
3739
'records': '{root}/buckets/{bucket}/collections/{collection}/records', # NOQA
@@ -97,7 +99,7 @@ def batch(self, **kwargs):
9799
batch_session.send()
98100
batch_session.reset()
99101

100-
def get_endpoint(self, name, bucket=None, collection=None, id=None):
102+
def get_endpoint(self, name, bucket=None, group=None, collection=None, id=None):
101103
"""Return the endpoint with named parameters.
102104
103105
Please always use the method as if it was defined like this:
@@ -112,6 +114,7 @@ def get_endpoint(self, name, bucket=None, collection=None, id=None):
112114
kwargs = {
113115
'bucket': bucket or self._bucket_name,
114116
'collection': collection or self._collection_name,
117+
'group': group,
115118
'id': id
116119
}
117120
return self.endpoints.get(name, **kwargs)
@@ -160,13 +163,15 @@ def _create_if_not_exists(self, resource, **kwargs):
160163
# The exception contains the existing record in details.existing
161164
# but it's not enough as we also need to return the permissions.
162165
get_kwargs = {}
163-
if resource in('bucket', 'collection', 'record'):
166+
if resource in('bucket', 'group', 'collection', 'record'):
164167
get_kwargs['bucket'] = kwargs['bucket']
165-
if resource in ('collection', 'record'):
168+
if resource == 'group':
169+
get_kwargs['group'] = kwargs['group']
170+
elif resource in ('collection', 'record'):
166171
get_kwargs['collection'] = kwargs['collection']
167-
if resource == 'record':
168-
_id = kwargs.get('id') or kwargs['data']['id']
169-
get_kwargs['id'] = _id
172+
if resource == 'record':
173+
_id = kwargs.get('id') or kwargs['data']['id']
174+
get_kwargs['id'] = _id
170175

171176
get_method = getattr(self, 'get_%s' % resource)
172177
return get_method(**get_kwargs)
@@ -259,6 +264,85 @@ def delete_buckets(self, safe=True, if_match=None, if_exists=False):
259264
resp, _ = self.session.request('delete', endpoint, headers=headers)
260265
return resp['data']
261266

267+
# Groups
268+
269+
def get_groups(self, bucket=None):
270+
endpoint = self.get_endpoint('groups', bucket=bucket)
271+
return self._paginated(endpoint)
272+
273+
def create_group(self, group, bucket=None,
274+
data=None, permissions=None,
275+
safe=True, if_not_exists=False):
276+
if if_not_exists:
277+
return self._create_if_not_exists('group',
278+
group=group,
279+
bucket=bucket,
280+
data=data,
281+
permissions=permissions,
282+
safe=safe)
283+
headers = DO_NOT_OVERWRITE if safe else None
284+
endpoint = self.get_endpoint('group',
285+
bucket=bucket,
286+
group=group)
287+
try:
288+
resp, _ = self.session.request('put', endpoint, data=data,
289+
permissions=permissions,
290+
headers=headers)
291+
except KintoException as e:
292+
if e.response.status_code == 403:
293+
msg = ("Unauthorized. Please check that the bucket exists and "
294+
"that you have the permission to create or write on "
295+
"this group.")
296+
e = KintoException(msg, e)
297+
raise e
298+
299+
return resp
300+
301+
def update_group(self, group, data=None, bucket=None,
302+
permissions=None, method='put',
303+
safe=True, if_match=None):
304+
endpoint = self.get_endpoint('group',
305+
bucket=bucket,
306+
group=group)
307+
headers = self._get_cache_headers(safe, data, if_match)
308+
resp, _ = self.session.request(method, endpoint, data=data,
309+
permissions=permissions,
310+
headers=headers)
311+
return resp
312+
313+
def patch_group(self, *args, **kwargs):
314+
kwargs['method'] = 'patch'
315+
return self.update_group(*args, **kwargs)
316+
317+
def get_group(self, group, bucket=None):
318+
endpoint = self.get_endpoint('group',
319+
bucket=bucket,
320+
group=group)
321+
resp, _ = self.session.request('get', endpoint)
322+
return resp
323+
324+
def delete_group(self, group, bucket=None,
325+
safe=True, if_match=None,
326+
if_exists=False):
327+
if if_exists:
328+
return self._delete_if_exists('group',
329+
group=group,
330+
bucket=bucket,
331+
safe=safe,
332+
if_match=if_match)
333+
endpoint = self.get_endpoint('group',
334+
bucket=bucket,
335+
group=group)
336+
headers = self._get_cache_headers(safe, if_match=if_match)
337+
resp, _ = self.session.request('delete', endpoint, headers=headers)
338+
return resp['data']
339+
340+
def delete_groups(self, bucket=None, safe=True, if_match=None):
341+
endpoint = self.get_endpoint('groups', bucket=bucket)
342+
headers = self._get_cache_headers(safe, if_match=if_match)
343+
resp, _ = self.session.request('delete', endpoint, headers=headers)
344+
return resp['data']
345+
262346
# Collections
263347

264348
def get_collections(self, bucket=None):

kinto_http/tests/functional.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,82 @@ def test_bucket_save(self):
104104
bucket = self.client.get_bucket('mozilla')
105105
assert 'alexis' in bucket['permissions']['write']
106106

107+
def test_group_creation(self):
108+
self.client.create_bucket('mozilla')
109+
self.client.create_group(
110+
'payments', bucket='mozilla',
111+
data={'members': ['blah', ]},
112+
permissions={'write': ['blah', ]})
113+
# Test retrieval of a group gets the permissions as well.
114+
group = self.client.get_group('payments', bucket='mozilla')
115+
assert 'blah' in group['permissions']['write']
116+
117+
def test_group_creation_if_not_exists(self):
118+
self.client.create_bucket('mozilla')
119+
self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
120+
self.client.create_group(
121+
'payments', bucket='mozilla',
122+
data={'members': ['blah', ]},
123+
permissions={'write': ['blah', ]},
124+
if_not_exists=True)
125+
126+
def test_group_creation_if_bucket_does_not_exist(self):
127+
with pytest.raises(KintoException):
128+
self.client.create_group(
129+
'payments', bucket='mozilla',
130+
data={'members': ['blah', ]})
131+
self.client.create_group(
132+
'payments', bucket='mozilla',
133+
data={'members': ['blah', ]},
134+
if_not_exists=True)
135+
136+
def test_group_update(self):
137+
self.client.create_bucket('mozilla')
138+
group = self.client.create_group(
139+
'payments', bucket='mozilla',
140+
data={'members': ['blah', ]},
141+
if_not_exists=True)
142+
assert group['data']['members'][0] == 'blah'
143+
group = self.client.update_group(
144+
data={'members': ['blah', 'foo']},
145+
group='payments', bucket='mozilla')
146+
self.assertEquals(group['data']['members'][1], 'foo')
147+
148+
def test_group_list(self):
149+
self.client.create_bucket('mozilla')
150+
self.client.create_group('receipts', bucket='mozilla', data={'members': ['blah', ]})
151+
self.client.create_group('assets', bucket='mozilla', data={'members': ['blah', ]})
152+
# The returned groups should be strings.
153+
groups = self.client.get_groups('mozilla')
154+
self.assertEquals(2, len(groups))
155+
self.assertEquals(set([coll['id'] for coll in groups]),
156+
set(['receipts', 'assets']))
157+
158+
def test_group_deletion(self):
159+
self.client.create_bucket('mozilla')
160+
self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
161+
self.client.delete_group('payments', bucket='mozilla')
162+
assert len(self.client.get_groups(bucket='mozilla')) == 0
163+
164+
def test_group_deletion_if_exists(self):
165+
self.client.create_bucket('mozilla')
166+
self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
167+
self.client.delete_group('payments', bucket='mozilla')
168+
self.client.delete_group('payments', bucket='mozilla', if_exists=True)
169+
170+
def test_group_deletion_can_still_raise_errors(self):
171+
error = KintoException("An error occured")
172+
with mock.patch.object(self.client.session, 'request', side_effect=error):
173+
with pytest.raises(KintoException):
174+
self.client.delete_group('payments', bucket='mozilla', if_exists=True)
175+
176+
def test_groups_deletion(self):
177+
self.client.create_bucket('mozilla')
178+
self.client.create_group('amo', bucket='mozilla', data={'members': ['blah', ]})
179+
self.client.create_group('blocklist', bucket='mozilla', data={'members': ['blah', ]})
180+
self.client.delete_groups(bucket='mozilla')
181+
assert len(self.client.get_groups(bucket='mozilla')) == 0
182+
107183
def test_collection_creation(self):
108184
self.client.create_bucket('mozilla')
109185
self.client.create_collection(
@@ -301,6 +377,15 @@ def test_bucket_sharing(self):
301377
auth=alice_credentials)
302378
alice_client.get_bucket('shared-bucket')
303379

380+
def test_updating_data_on_a_group(self):
381+
client = Client(server_url=self.server_url, auth=self.auth,
382+
bucket='mozilla')
383+
client.create_bucket()
384+
client.create_group('payments', data={'members': []})
385+
client.patch_group('payments', data={'secret': 'psssssst!'})
386+
group = client.get_group('payments')
387+
assert group['data']['secret'] == 'psssssst!'
388+
304389
def test_updating_data_on_a_collection(self):
305390
client = Client(server_url=self.server_url, auth=self.auth,
306391
bucket='mozilla', collection='payments')

0 commit comments

Comments
 (0)