Skip to content

Commit

Permalink
Merge pull request #842 from onaio/840-handle-a-list-in-xfrom-viewset…
Browse files Browse the repository at this point in the history
…-POST

Not have to send a request for each user we share a form with.
  • Loading branch information
denniswambua authored Nov 28, 2016
2 parents 586b310 + c2b1c7a commit 4606d66
Show file tree
Hide file tree
Showing 19 changed files with 689 additions and 49 deletions.
14 changes: 10 additions & 4 deletions docs/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -930,12 +930,12 @@ Get list of public forms
<b>GET</b> /api/v1/forms/public
</pre>

Share a form with a specific user
----------------------------------
Share a form with a specific username or usernames
--------------------------------------------------

You can share a form with a specific user by `POST` a payload with
You can share a form with a specific username or a list of usernames using `POST` with a payload of

- ``username`` of the user you want to share the form with and
- ``username`` OR ``usernames`` of the usernames you want to share the form with, multiple usernames should be comma separated, and
- ``role`` you want the user to have on the form. Available roles are ``readonly``, ``dataentry``, ``editor``, ``manager``.

.. raw:: html
Expand All @@ -949,6 +949,12 @@ Example

curl -X POST -d '{"username": "alice", "role": "readonly"}' https://api.ona.io/api/v1/forms/123.json

Example
^^^^^^^
::

curl -X POST -d '{"usernames": "alice,bob,eve", "role": "readonly"}' https://api.ona.io/api/v1/forms/123.json

Response
^^^^^^^^
::
Expand Down
15 changes: 15 additions & 0 deletions docs/metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,21 @@ Link XForm or Dataview as a media example:
"url": "https://api.ona.io/api/v1/metadata/7121.json"
}

Create XForm meta permissions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Set meta permissions for a specific form by passing two roles that are pipe delimited.
First role indicates editor default role and the other is the dataentry default role.

Example
::


curl -X POST -F 'data_type=xform_meta_perms' -F 'xform=320' -F 'data_value="editor-minor|dataentryonly"' https://api.ona.io/api/v1/metadata.json

::

HTTP 201 CREATED


Delete Metadata
^^^^^^^^^^^^^^^^
Expand Down
92 changes: 87 additions & 5 deletions onadata/apps/api/tests/viewsets/test_data_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from onadata.libs.utils.logger_tools import create_instance
from onadata.apps.logger.models import Attachment
from onadata.apps.logger.models import Instance
from onadata.apps.main.models.meta_data import MetaData
from onadata.apps.logger.models.instance import InstanceHistory
from onadata.apps.logger.models import XForm
from onadata.libs.permissions import ReadOnlyRole, EditorRole, \
Expand Down Expand Up @@ -215,10 +216,10 @@ def _assign_user_role(self, user, role):
# share bob's project with alice and give alice an editor role
data = {'username': user.username, 'role': role.name}
request = self.factory.put('/', data=data, **self.extra)
project_view = ProjectViewSet.as_view({
xform_view = XFormViewSet.as_view({
'put': 'share'
})
response = project_view(request, pk=self.project.pk)
response = xform_view(request, pk=self.xform.pk)
self.assertEqual(response.status_code, 204)

self.assertTrue(
Expand Down Expand Up @@ -253,6 +254,18 @@ def test_returned_data_is_based_on_form_permissions(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 0)

data = {"start": 1, "limit": 4}
request = self.factory.get('/', data=data, **alices_extra)
response = view(request, pk=formid)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 0)

data = {"sort": 1}
request = self.factory.get('/', data=data, **alices_extra)
response = view(request, pk=formid)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 0)

self._assign_user_role(user_alice, EditorMinorRole)
# check that by default, alice can be able to access all the data

Expand Down Expand Up @@ -282,6 +295,18 @@ def test_returned_data_is_based_on_form_permissions(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 2)

data = {"start": 1, "limit": 1}
request = self.factory.get('/', data=data, **alices_extra)
response = view(request, pk=formid)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)

data = {"sort": 1}
request = self.factory.get('/', data=data, **alices_extra)
response = view(request, pk=formid)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 2)

self._assign_user_role(user_alice, EditorRole)

request = self.factory.get('/', **alices_extra)
Expand All @@ -296,6 +321,55 @@ def test_returned_data_is_based_on_form_permissions(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 4)

def test_xform_meta_permissions_not_affected_w_projects_perms(self):
# create a form and make submissions to it
self._make_submissions()
view = DataViewSet.as_view({'get': 'list'})
formid = self.xform.pk
request = self.factory.get('/', **self.extra)
response = view(request, pk=formid)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 4)

# create user alice
user_alice = self._create_user('alice', 'alice')
# create user profile and set require_auth to false for tests
profile, created = UserProfile.objects.get_or_create(user=user_alice)
profile.require_auth = False
profile.save()

data = {'username': user_alice.username, 'role': EditorRole.name}
request = self.factory.put('/', data=data, **self.extra)
project_view = ProjectViewSet.as_view({
'put': 'share'
})
response = project_view(request, pk=self.project.pk)
self.assertEqual(response.status_code, 204)

self.assertTrue(
EditorRole.user_has_role(user_alice, self.xform)
)
self._assign_user_role(user_alice, EditorMinorRole)
MetaData.xform_meta_permission(self.xform,
data_value='editor-minor|dataentry')

self.assertFalse(
EditorRole.user_has_role(user_alice, self.xform)
)

alices_extra = {
'HTTP_AUTHORIZATION': 'Token %s' % user_alice.auth_token.key
}

request = self.factory.get('/', **alices_extra)
response = view(request, pk=formid)
self.assertEqual(response.status_code, 200)

self.assertFalse(
EditorRole.user_has_role(user_alice, self.xform)
)
self.assertEqual(len(response.data), 0)

def test_data_entryonly_can_submit_but_not_view(self):
# create user alice
user_alice = self._create_user('alice', 'alice')
Expand Down Expand Up @@ -565,9 +639,17 @@ def test_data_start_limit_sort_json_field(self):
self.assertEqual(response.status_code, 200)
data = json.loads(''.join([c for c in response.streaming_content]))
self.assertEqual(len(data), 2)
self.assertEqual(sorted([i['_uuid'] for i in data]),
[u'5b2cc313-fc09-437e-8149-fcd32f695d41',
u'9c6f3468-cfda-46e8-84c1-75458e72805d'])

data = {
"page": 1,
"page_size": 3,
}
request = self.factory.get('/', data=data,
**self.extra)
response = view(request, pk=formid)
self.assertEqual(response.status_code, 200)
data = json.loads(''.join([c for c in response.streaming_content]))
self.assertEqual(len(data), 3)

def test_data_anon(self):
self._make_submissions()
Expand Down
121 changes: 121 additions & 0 deletions onadata/apps/api/tests/viewsets/test_metadata_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from onadata.apps.main.models.meta_data import MetaData
from onadata.libs.serializers.xform_serializer import XFormSerializer
from onadata.libs.serializers.metadata_serializer import UNIQUE_TOGETHER_ERROR
from onadata.libs.utils.common_tags import XFORM_META_PERMS
from onadata.libs.permissions import (EditorRole, EditorMinorRole,
DataEntryRole, DataEntryOnlyRole)


class TestMetaDataViewSet(TestAbstractViewSet):
Expand Down Expand Up @@ -434,3 +437,121 @@ def test_invalid_form_metadata(self):
self.assertEqual(response.status_code, 400)
self.assertEqual(response.data,
{'xform': ['XForm does not exist']})

def test_xform_meta_permission(self):
view = MetaDataViewSet.as_view({'post': 'create'})

data = {
'data_type': XFORM_META_PERMS,
'data_value': 'editor-minor|dataentry',
'xform': self.xform.pk
}
request = self.factory.post('/', data, **self.extra)
response = view(request)

self.assertEqual(response.status_code, 201)

meta = MetaData.xform_meta_permission(self.xform)
self.assertEqual(meta.data_value, response.data.get('data_value'))

data = {
'data_type': XFORM_META_PERMS,
'data_value': 'editor-minors|invalid_role',
'xform': self.xform.pk
}
request = self.factory.post('/', data, **self.extra)
response = view(request)

self.assertEqual(response.status_code, 400)
error = u"Format 'role'|'role' or Invalid role"
self.assertEqual(response.data, {'non_field_errors': [error]})

def test_role_update_xform_meta_perms(self):
alice_data = {'username': 'alice', 'email': '[email protected]'}
alice_profile = self._create_user_profile(alice_data)

EditorRole.add(alice_profile.user, self.xform)

view = MetaDataViewSet.as_view({
'post': 'create',
'put': 'update'
})

data = {
'data_type': XFORM_META_PERMS,
'data_value': 'editor-minor|dataentry',
'xform': self.xform.pk
}
request = self.factory.post('/', data, **self.extra)
response = view(request)

self.assertEqual(response.status_code, 201)

self.assertFalse(
EditorRole.user_has_role(alice_profile.user, self.xform))

self.assertTrue(
EditorMinorRole.user_has_role(alice_profile.user, self.xform))

meta = MetaData.xform_meta_permission(self.xform)

DataEntryRole.add(alice_profile.user, self.xform)

data = {
'data_type': XFORM_META_PERMS,
'data_value': 'editor|dataentry-only',
'xform': self.xform.pk
}
request = self.factory.put('/', data, **self.extra)
response = view(request, pk=meta.pk)

self.assertEqual(response.status_code, 200)

self.assertFalse(
DataEntryRole.user_has_role(alice_profile.user, self.xform))

self.assertTrue(
DataEntryOnlyRole.user_has_role(alice_profile.user, self.xform))

def test_xform_meta_perms_duplicates(self):
view = MetaDataViewSet.as_view({
'post': 'create',
'put': 'update'
})

ct = ContentType.objects.get_for_model(self.xform)

data = {
'data_type': XFORM_META_PERMS,
'data_value': 'editor-minor|dataentry',
'xform': self.xform.pk
}
request = self.factory.post('/', data, **self.extra)
response = view(request)

self.assertEqual(response.status_code, 201)

count = MetaData.objects.filter(data_type=XFORM_META_PERMS,
object_id=self.xform.pk,
content_type=ct.pk).count()

self.assertEqual(1, count)

data = {
'data_type': XFORM_META_PERMS,
'data_value': 'editor-minor|dataentry-only',
'xform': self.xform.pk
}
request = self.factory.post('/', data, **self.extra)
response = view(request)

self.assertEqual(response.status_code, 201)

count = MetaData.objects.filter(data_type=XFORM_META_PERMS,
object_id=self.xform.pk,
content_type=ct.pk).count()

self.assertEqual(1, count)

metadata = MetaData.xform_meta_permission(self.xform)
self.assertEqual(metadata.data_value, "editor-minor|dataentry-only")
60 changes: 54 additions & 6 deletions onadata/apps/api/tests/viewsets/test_project_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
TestAbstractViewSet
from onadata.apps.api.viewsets.project_viewset import ProjectViewSet
from onadata.libs.permissions import (
OwnerRole, ReadOnlyRole, ManagerRole, DataEntryRole, EditorRole,
ReadOnlyRoleNoDownload)
OwnerRole, ReadOnlyRole, ManagerRole, DataEntryRole, EditorMinorRole,
EditorRole, ReadOnlyRoleNoDownload, DataEntryMinorRole, DataEntryOnlyRole,
ROLES_ORDERED)
from onadata.libs.serializers.project_serializer import ProjectSerializer,\
BaseProjectSerializer
from onadata.libs import permissions as role
Expand Down Expand Up @@ -1910,14 +1911,61 @@ def test_two_dataviews_count(self):
# assert count
self.assertIn('data_views', response.data)
self.assertEqual(len(response.data['data_views']), 2)
self.assertEqual(response.data['data_views'][1]['count'], 4)
self.assertEqual(response.data['data_views'][0]['count'], 0)
count_one = response.data['data_views'][1]['count']
count_two = response.data['data_views'][0]['count']
self.assertEqual([count_one, count_two].sort(), [0, 4].sort())

request = self.factory.get('/', **self.extra)
response = view(request, pk=self.project.pk)

# assert count
self.assertIn('data_views', response.data)
self.assertTrue(len(response.data['data_views']) == 2)
self.assertTrue(response.data['data_views'][1]['count'] == 4)
self.assertTrue(response.data['data_views'][0]['count'] == 0)
count_one = response.data['data_views'][1]['count']
count_two = response.data['data_views'][0]['count']
self.assertEqual([count_one, count_two].sort(), [0, 4].sort())

def test_project_share_xform_meta_perms(self):
# create project and publish form to project
self._publish_xls_form_to_project()
alice_data = {'username': 'alice', 'email': '[email protected]'}
alice_profile = self._create_user_profile(alice_data)
projectid = self.project.pk

data_value = "editor-minor|dataentry"

MetaData.xform_meta_permission(self.xform, data_value=data_value)

for role_class in ROLES_ORDERED:
self.assertFalse(role_class.user_has_role(alice_profile.user,
self.project))

data = {'username': 'alice', 'role': role_class.name}
request = self.factory.post('/', data=data, **self.extra)

view = ProjectViewSet.as_view({
'post': 'share'
})
response = view(request, pk=projectid)

self.assertEqual(response.status_code, 204)

self.assertTrue(role_class.user_has_role(alice_profile.user,
self.project))

if role_class in [EditorRole, EditorMinorRole]:
self.assertFalse(
EditorRole.user_has_role(alice_profile.user, self.xform))
self.assertTrue(
EditorMinorRole.user_has_role(alice_profile.user,
self.xform))

elif role_class in [DataEntryRole, DataEntryMinorRole,
DataEntryOnlyRole]:
self.assertTrue(
DataEntryRole.user_has_role(alice_profile.user,
self.xform))

else:
self.assertTrue(
role_class.user_has_role(alice_profile.user, self.xform))
Loading

0 comments on commit 4606d66

Please sign in to comment.