Skip to content

Commit

Permalink
Merge pull request #95 from mansimarkaur/CRUD_methods_groups
Browse files Browse the repository at this point in the history
Added CRUD methods for Groups
  • Loading branch information
glasserc authored Sep 15, 2016
2 parents 60a494a + 80747a8 commit f977331
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 12 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This document describes changes between each past release.
6.3.0 (unreleased)
==================

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


6.2.1 (2016-09-08)
Expand Down
37 changes: 32 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Kinto python client
:target: https://coveralls.io/r/Kinto/kinto-http.py


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

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

Operations are always performed directly on the server, and no
synchronisation features are implemented yet.
synchronisation features have been implemented yet.

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

Expand Down Expand Up @@ -122,6 +122,24 @@ If no specific bucket name is provided, the "default" bucket is used.
# Or even every writable buckets.
client.delete_buckets()
Groups
------

A group associates a name to a list of principals. It is useful in order to handle permissions.

.. code-block:: python
client.create_group('receipts', bucket='payments', data={'members': ['blah', 'foo']})
# Or get an existing one.
group = client.get_group('receipts', bucket='payments')
# To delete an existing group.
client.delete_group('receipts', bucket='payments')
# Or all groups in a bucket.
client.delete_groups(bucket='payments')
Collections
-----------
Expand All @@ -133,7 +151,7 @@ A collection is where records are stored.
client.create_collection('receipts', bucket='payments')
# Or get an existing one.
client.get_collection('receipts', bucket='payments')
collection = client.get_collection('receipts', bucket='payments')
# To delete an existing collection.
client.delete_collection('receipts', bucket='payments')
Expand Down Expand Up @@ -211,12 +229,21 @@ For instance to give access to "leplatrem" to a specific record, you would do:
Get or create
-------------

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

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

Delete
------

In some cases, you might want to delete a bucket, collection, group or record only if
it exists already. To do so, you can pass the ``if_exists=True``
to the ``delete_*`` methods::

client.delete_bucket('bucket', if_exists=True)

Overwriting existing objects
----------------------------

Expand Down
96 changes: 90 additions & 6 deletions kinto_http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class Endpoints(object):
'batch': '{root}/batch',
'buckets': '{root}/buckets',
'bucket': '{root}/buckets/{bucket}',
'groups': '{root}/buckets/{bucket}/groups',
'group': '{root}/buckets/{bucket}/groups/{group}',
'collections': '{root}/buckets/{bucket}/collections',
'collection': '{root}/buckets/{bucket}/collections/{collection}',
'records': '{root}/buckets/{bucket}/collections/{collection}/records', # NOQA
Expand Down Expand Up @@ -97,7 +99,7 @@ def batch(self, **kwargs):
batch_session.send()
batch_session.reset()

def get_endpoint(self, name, bucket=None, collection=None, id=None):
def get_endpoint(self, name, bucket=None, group=None, collection=None, id=None):
"""Return the endpoint with named parameters.
Please always use the method as if it was defined like this:
Expand All @@ -112,6 +114,7 @@ def get_endpoint(self, name, bucket=None, collection=None, id=None):
kwargs = {
'bucket': bucket or self._bucket_name,
'collection': collection or self._collection_name,
'group': group,
'id': id
}
return self.endpoints.get(name, **kwargs)
Expand Down Expand Up @@ -160,13 +163,15 @@ def _create_if_not_exists(self, resource, **kwargs):
# The exception contains the existing record in details.existing
# but it's not enough as we also need to return the permissions.
get_kwargs = {}
if resource in('bucket', 'collection', 'record'):
if resource in('bucket', 'group', 'collection', 'record'):
get_kwargs['bucket'] = kwargs['bucket']
if resource in ('collection', 'record'):
if resource == 'group':
get_kwargs['group'] = kwargs['group']
elif resource in ('collection', 'record'):
get_kwargs['collection'] = kwargs['collection']
if resource == 'record':
_id = kwargs.get('id') or kwargs['data']['id']
get_kwargs['id'] = _id
if resource == 'record':
_id = kwargs.get('id') or kwargs['data']['id']
get_kwargs['id'] = _id

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

# Groups

def get_groups(self, bucket=None):
endpoint = self.get_endpoint('groups', bucket=bucket)
return self._paginated(endpoint)

def create_group(self, group, bucket=None,
data=None, permissions=None,
safe=True, if_not_exists=False):
if if_not_exists:
return self._create_if_not_exists('group',
group=group,
bucket=bucket,
data=data,
permissions=permissions,
safe=safe)
headers = DO_NOT_OVERWRITE if safe else None
endpoint = self.get_endpoint('group',
bucket=bucket,
group=group)
try:
resp, _ = self.session.request('put', endpoint, data=data,
permissions=permissions,
headers=headers)
except KintoException as e:
if e.response.status_code == 403:
msg = ("Unauthorized. Please check that the bucket exists and "
"that you have the permission to create or write on "
"this group.")
e = KintoException(msg, e)
raise e

return resp

def update_group(self, group, data=None, bucket=None,
permissions=None, method='put',
safe=True, if_match=None):
endpoint = self.get_endpoint('group',
bucket=bucket,
group=group)
headers = self._get_cache_headers(safe, data, if_match)
resp, _ = self.session.request(method, endpoint, data=data,
permissions=permissions,
headers=headers)
return resp

def patch_group(self, *args, **kwargs):
kwargs['method'] = 'patch'
return self.update_group(*args, **kwargs)

def get_group(self, group, bucket=None):
endpoint = self.get_endpoint('group',
bucket=bucket,
group=group)
resp, _ = self.session.request('get', endpoint)
return resp

def delete_group(self, group, bucket=None,
safe=True, if_match=None,
if_exists=False):
if if_exists:
return self._delete_if_exists('group',
group=group,
bucket=bucket,
safe=safe,
if_match=if_match)
endpoint = self.get_endpoint('group',
bucket=bucket,
group=group)
headers = self._get_cache_headers(safe, if_match=if_match)
resp, _ = self.session.request('delete', endpoint, headers=headers)
return resp['data']

def delete_groups(self, bucket=None, safe=True, if_match=None):
endpoint = self.get_endpoint('groups', bucket=bucket)
headers = self._get_cache_headers(safe, if_match=if_match)
resp, _ = self.session.request('delete', endpoint, headers=headers)
return resp['data']

# Collections

def get_collections(self, bucket=None):
Expand Down
85 changes: 85 additions & 0 deletions kinto_http/tests/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,82 @@ def test_bucket_save(self):
bucket = self.client.get_bucket('mozilla')
assert 'alexis' in bucket['permissions']['write']

def test_group_creation(self):
self.client.create_bucket('mozilla')
self.client.create_group(
'payments', bucket='mozilla',
data={'members': ['blah', ]},
permissions={'write': ['blah', ]})
# Test retrieval of a group gets the permissions as well.
group = self.client.get_group('payments', bucket='mozilla')
assert 'blah' in group['permissions']['write']

def test_group_creation_if_not_exists(self):
self.client.create_bucket('mozilla')
self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
self.client.create_group(
'payments', bucket='mozilla',
data={'members': ['blah', ]},
permissions={'write': ['blah', ]},
if_not_exists=True)

def test_group_creation_if_bucket_does_not_exist(self):
with pytest.raises(KintoException):
self.client.create_group(
'payments', bucket='mozilla',
data={'members': ['blah', ]})
self.client.create_group(
'payments', bucket='mozilla',
data={'members': ['blah', ]},
if_not_exists=True)

def test_group_update(self):
self.client.create_bucket('mozilla')
group = self.client.create_group(
'payments', bucket='mozilla',
data={'members': ['blah', ]},
if_not_exists=True)
assert group['data']['members'][0] == 'blah'
group = self.client.update_group(
data={'members': ['blah', 'foo']},
group='payments', bucket='mozilla')
self.assertEquals(group['data']['members'][1], 'foo')

def test_group_list(self):
self.client.create_bucket('mozilla')
self.client.create_group('receipts', bucket='mozilla', data={'members': ['blah', ]})
self.client.create_group('assets', bucket='mozilla', data={'members': ['blah', ]})
# The returned groups should be strings.
groups = self.client.get_groups('mozilla')
self.assertEquals(2, len(groups))
self.assertEquals(set([coll['id'] for coll in groups]),
set(['receipts', 'assets']))

def test_group_deletion(self):
self.client.create_bucket('mozilla')
self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
self.client.delete_group('payments', bucket='mozilla')
assert len(self.client.get_groups(bucket='mozilla')) == 0

def test_group_deletion_if_exists(self):
self.client.create_bucket('mozilla')
self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
self.client.delete_group('payments', bucket='mozilla')
self.client.delete_group('payments', bucket='mozilla', if_exists=True)

def test_group_deletion_can_still_raise_errors(self):
error = KintoException("An error occured")
with mock.patch.object(self.client.session, 'request', side_effect=error):
with pytest.raises(KintoException):
self.client.delete_group('payments', bucket='mozilla', if_exists=True)

def test_groups_deletion(self):
self.client.create_bucket('mozilla')
self.client.create_group('amo', bucket='mozilla', data={'members': ['blah', ]})
self.client.create_group('blocklist', bucket='mozilla', data={'members': ['blah', ]})
self.client.delete_groups(bucket='mozilla')
assert len(self.client.get_groups(bucket='mozilla')) == 0

def test_collection_creation(self):
self.client.create_bucket('mozilla')
self.client.create_collection(
Expand Down Expand Up @@ -301,6 +377,15 @@ def test_bucket_sharing(self):
auth=alice_credentials)
alice_client.get_bucket('shared-bucket')

def test_updating_data_on_a_group(self):
client = Client(server_url=self.server_url, auth=self.auth,
bucket='mozilla')
client.create_bucket()
client.create_group('payments', data={'members': []})
client.patch_group('payments', data={'secret': 'psssssst!'})
group = client.get_group('payments')
assert group['data']['secret'] == 'psssssst!'

def test_updating_data_on_a_collection(self):
client = Client(server_url=self.server_url, auth=self.auth,
bucket='mozilla', collection='payments')
Expand Down

0 comments on commit f977331

Please sign in to comment.