Skip to content

Commit 89e3499

Browse files
committed
enable bulk request for registration contributors POST/PATCH
1 parent f69a323 commit 89e3499

File tree

3 files changed

+215
-1
lines changed

3 files changed

+215
-1
lines changed

api/registrations/views.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def get_serializer_context(self):
275275
return context
276276

277277

278-
class RegistrationContributorsList(BaseContributorList, mixins.CreateModelMixin, RegistrationMixin, UserMixin):
278+
class RegistrationContributorsList(BaseContributorList, bulk_views.BulkUpdateJSONAPIView, bulk_views.ListBulkCreateJSONAPIView, mixins.CreateModelMixin, RegistrationMixin, UserMixin):
279279
"""See [documentation for this endpoint](https://developer.osf.io/#operation/registrations_contributors_list).
280280
"""
281281
view_category = 'registrations'
@@ -311,6 +311,20 @@ def get_default_queryset(self):
311311
node = self.get_resource()
312312
return node.contributor_set.all().prefetch_related('user__guids')
313313

314+
def get_queryset(self):
315+
queryset = self.get_queryset_from_request()
316+
if is_bulk_request(self.request):
317+
contrib_ids = []
318+
for item in self.request.data:
319+
try:
320+
contrib_ids.append(item['id'].split('-')[1])
321+
except AttributeError:
322+
raise ValidationError('Contributor identifier not provided.')
323+
except IndexError:
324+
raise ValidationError('Contributor identifier incorrectly formatted.')
325+
queryset = queryset.filter(user__guids___id__in=contrib_ids)
326+
return queryset
327+
314328
def get_serializer_context(self):
315329
context = super().get_serializer_context()
316330
context['resource'] = self.get_resource()

api_tests/draft_registrations/views/test_draft_registration_contributor_list.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,30 @@ def url_private(self, project_private):
330330
return '/{}draft_registrations/{}/contributors/?send_email=false'.format(
331331
API_BASE, project_private._id)
332332

333+
def test_bulk_post_new_contributors(
334+
self, app, user, project_public, payload_one, payload_two, url_public
335+
):
336+
res = app.post_json_api(
337+
url_public,
338+
{'data': [payload_one, payload_two]},
339+
auth=user.auth,
340+
bulk=True,
341+
)
342+
assert res.status_code == 201
343+
assert len(res.json['data']) == 2
344+
assert [
345+
res.json['data'][0]['attributes']['bibliographic'],
346+
res.json['data'][1]['attributes']['bibliographic'],
347+
] == [True, False]
348+
assert [
349+
res.json['data'][0]['attributes']['permission'],
350+
res.json['data'][1]['attributes']['permission'],
351+
] == [permissions.ADMIN, permissions.READ]
352+
assert res.content_type == 'application/vnd.api+json'
353+
354+
res = app.get(url_public, auth=user.auth)
355+
assert len(res.json['data']) == 3
356+
333357

334358
class TestDraftContributorBulkUpdated(DraftRegistrationCRUDTestCase, TestNodeContributorBulkUpdate):
335359

@@ -376,6 +400,32 @@ def url_private(self, project_private):
376400
return '/{}draft_registrations/{}/contributors/'.format(
377401
API_BASE, project_private._id)
378402

403+
def test_bulk_patch_existing_contributors(
404+
self, app, user, project_public, payload_public_one, payload_public_two, url_public
405+
):
406+
res = app.patch_json_api(
407+
url_public,
408+
{'data': [payload_public_one, payload_public_two]},
409+
auth=user.auth,
410+
bulk=True,
411+
)
412+
assert res.status_code == 200
413+
assert len(res.json['data']) == 2
414+
assert [
415+
res.json['data'][0]['attributes']['bibliographic'],
416+
res.json['data'][1]['attributes']['bibliographic'],
417+
] == [True, False]
418+
assert [
419+
res.json['data'][0]['attributes']['permission'],
420+
res.json['data'][1]['attributes']['permission'],
421+
] == [permissions.ADMIN, permissions.WRITE]
422+
423+
res = app.get(url_public, auth=user.auth)
424+
data = res.json['data']
425+
contrib_dict = {contrib['id']: contrib for contrib in data}
426+
assert contrib_dict[payload_public_one['id']]['attributes']['permission'] == permissions.ADMIN
427+
assert contrib_dict[payload_public_two['id']]['attributes']['permission'] == permissions.WRITE
428+
379429
class TestDraftRegistrationContributorBulkPartialUpdate(DraftRegistrationCRUDTestCase, TestNodeContributorBulkPartialUpdate):
380430
@pytest.fixture()
381431
def project_public(
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import pytest
2+
3+
from api.base.settings.defaults import API_BASE
4+
from osf_tests.factories import (
5+
AuthUserFactory,
6+
ProjectFactory,
7+
RegistrationFactory,
8+
)
9+
from osf.utils import permissions
10+
11+
12+
@pytest.fixture()
13+
def user():
14+
return AuthUserFactory()
15+
16+
17+
@pytest.mark.django_db
18+
class TestRegistrationContributorsList:
19+
20+
@pytest.fixture()
21+
def user_two(self):
22+
return AuthUserFactory()
23+
24+
@pytest.fixture()
25+
def user_three(self):
26+
return AuthUserFactory()
27+
28+
@pytest.fixture()
29+
def project_public(self, user):
30+
return ProjectFactory(
31+
title='Public Project',
32+
description='A public project',
33+
category='data',
34+
is_public=True,
35+
creator=user,
36+
)
37+
38+
@pytest.fixture()
39+
def registration_public(self, project_public, user):
40+
return RegistrationFactory(project=project_public, creator=user, is_public=True)
41+
42+
@pytest.fixture()
43+
def url_public(self, registration_public):
44+
return f'/{API_BASE}registrations/{registration_public._id}/contributors/?send_email=false'
45+
46+
@pytest.fixture()
47+
def payload_one(self, user_two):
48+
return {
49+
'type': 'contributors',
50+
'attributes': {'bibliographic': True, 'permission': permissions.ADMIN},
51+
'relationships': {'users': {'data': {'id': user_two._id, 'type': 'users'}}},
52+
}
53+
54+
@pytest.fixture()
55+
def payload_two(self, user_three):
56+
return {
57+
'type': 'contributors',
58+
'attributes': {'bibliographic': False, 'permission': permissions.READ},
59+
'relationships': {
60+
'users': {'data': {'id': user_three._id, 'type': 'users'}}
61+
},
62+
}
63+
64+
@pytest.fixture()
65+
def make_contrib_id(self):
66+
def contrib_id(registration_id, user_id):
67+
return f'{registration_id}-{user_id}'
68+
return contrib_id
69+
70+
@pytest.fixture()
71+
def registration_with_contributors(self, project_public, user, user_two, user_three):
72+
registration = RegistrationFactory(project=project_public, creator=user, is_public=True)
73+
74+
registration.add_contributor(
75+
user_two, permissions=permissions.READ, visible=True, save=True
76+
)
77+
registration.add_contributor(
78+
user_three, permissions=permissions.READ, visible=True, save=True
79+
)
80+
return registration
81+
82+
@pytest.fixture()
83+
def url_with_contributors(self, registration_with_contributors):
84+
return f'/{API_BASE}registrations/{registration_with_contributors._id}/contributors/'
85+
86+
@pytest.fixture()
87+
def update_payload_one(self, user_two, registration_with_contributors, make_contrib_id):
88+
return {
89+
'id': make_contrib_id(registration_with_contributors._id, user_two._id),
90+
'type': 'contributors',
91+
'attributes': {'bibliographic': True, 'permission': permissions.ADMIN},
92+
}
93+
94+
@pytest.fixture()
95+
def update_payload_two(self, user_three, registration_with_contributors, make_contrib_id):
96+
return {
97+
'id': make_contrib_id(registration_with_contributors._id, user_three._id),
98+
'type': 'contributors',
99+
'attributes': {'bibliographic': False, 'permission': permissions.WRITE},
100+
}
101+
102+
def test_bulk_post_new_contributors(
103+
self, app, user, registration_public, payload_one, payload_two, url_public
104+
):
105+
res = app.post_json_api(
106+
url_public,
107+
{'data': [payload_one, payload_two]},
108+
auth=user.auth,
109+
bulk=True,
110+
)
111+
assert res.status_code == 201
112+
assert len(res.json['data']) == 2
113+
assert [
114+
res.json['data'][0]['attributes']['bibliographic'],
115+
res.json['data'][1]['attributes']['bibliographic'],
116+
] == [True, False]
117+
assert [
118+
res.json['data'][0]['attributes']['permission'],
119+
res.json['data'][1]['attributes']['permission'],
120+
] == [permissions.ADMIN, permissions.READ]
121+
assert res.content_type == 'application/vnd.api+json'
122+
123+
res = app.get(url_public, auth=user.auth)
124+
assert len(res.json['data']) == 3
125+
126+
def test_bulk_patch_existing_contributors(
127+
self, app, user, registration_with_contributors, update_payload_one, update_payload_two, url_with_contributors
128+
):
129+
res = app.patch_json_api(
130+
url_with_contributors,
131+
{'data': [update_payload_one, update_payload_two]},
132+
auth=user.auth,
133+
bulk=True,
134+
)
135+
assert res.status_code == 200
136+
assert len(res.json['data']) == 2
137+
assert [
138+
res.json['data'][0]['attributes']['bibliographic'],
139+
res.json['data'][1]['attributes']['bibliographic'],
140+
] == [True, False]
141+
assert [
142+
res.json['data'][0]['attributes']['permission'],
143+
res.json['data'][1]['attributes']['permission'],
144+
] == [permissions.ADMIN, permissions.WRITE]
145+
146+
res = app.get(url_with_contributors, auth=user.auth)
147+
data = res.json['data']
148+
contrib_dict = {contrib['id']: contrib for contrib in data}
149+
assert contrib_dict[update_payload_one['id']]['attributes']['permission'] == permissions.ADMIN
150+
assert contrib_dict[update_payload_two['id']]['attributes']['permission'] == permissions.WRITE

0 commit comments

Comments
 (0)