Skip to content

Commit 417b00d

Browse files
committed
Merge remote-tracking branch 'cos/develop' into feature/azure-blob-storage
Futa asked nicely and said Gizmo would be sad if I didn't.
2 parents a166eb1 + 1dd3954 commit 417b00d

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

api/nodes/serializers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,12 @@ def update(self, instance, validated_data):
13011301
validated_data,
13021302
)
13031303

1304+
class NodeContributorsUpdateSerializer(ser.Serializer):
1305+
def update(self, instance, validated_data):
1306+
if project := instance.root:
1307+
instance.copy_contributors_from(project)
1308+
return instance
1309+
13041310

13051311
class NodeLinksSerializer(JSONAPISerializer):
13061312

api/nodes/views.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
DraftRegistrationDetailLegacySerializer,
110110
NodeContributorsSerializer,
111111
NodeContributorDetailSerializer,
112+
NodeContributorsUpdateSerializer,
112113
NodeInstitutionsRelationshipSerializer,
113114
NodeContributorsCreateSerializer,
114115
NodeViewOnlyLinkSerializer,
@@ -434,11 +435,16 @@ class NodeContributorsList(BaseContributorList, bulk_views.BulkUpdateJSONAPIView
434435
def get_resource(self):
435436
return self.get_node()
436437

438+
def get_object(self):
439+
return self.get_node()
440+
437441
# overrides ListBulkCreateJSONAPIView, BulkUpdateJSONAPIView, BulkDeleteJSONAPIView
438442
def get_serializer_class(self):
439443
"""
440444
Use NodeContributorDetailSerializer which requires 'id'
441445
"""
446+
if self.request.method == 'PATCH' and self.request.query_params.get('copy_contributors_from_parent_project'):
447+
return NodeContributorsUpdateSerializer
442448
if self.request.method == 'PUT' or self.request.method == 'PATCH' or self.request.method == 'DELETE':
443449
return NodeContributorDetailSerializer
444450
elif self.request.method == 'POST':
@@ -499,6 +505,18 @@ def get_serializer_context(self):
499505
context['default_email'] = 'default'
500506
return context
501507

508+
def patch(self, request, *args, **kwargs):
509+
"""
510+
Override the default patch behavior to handle the special case
511+
of updating contributors by copying contributors from the parent project.
512+
"""
513+
if request.query_params.get('copy_contributors_from_parent_project'):
514+
instance = self.get_object()
515+
serializer = self.get_serializer_class()(instance, data=request.data)
516+
serializer.is_valid(raise_exception=True)
517+
serializer.save()
518+
return Response(serializer.data, status=200)
519+
return super().patch(request, *args, **kwargs)
502520

503521
class NodeContributorDetail(BaseContributorDetail, generics.RetrieveUpdateDestroyAPIView, NodeMixin, UserMixin):
504522
"""The documentation for this endpoint can be found [here](https://developer.osf.io/#operation/nodes_contributors_read).

api_tests/nodes/views/test_node_contributors_detail_update.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,21 @@ def project(self, user, contrib):
3232
)
3333
return project
3434

35+
@pytest.fixture()
36+
def component_project_and_parent_project(self, user, contrib):
37+
# both need to be in same method to keep root same as parent
38+
parent_project = ProjectFactory(creator=user)
39+
parent_project.add_contributor(
40+
contrib,
41+
permissions=permissions.WRITE,
42+
visible=True,
43+
save=True
44+
)
45+
component_project = ProjectFactory(creator=user)
46+
component_project.root = parent_project
47+
component_project.save()
48+
return component_project, parent_project
49+
3550
@pytest.fixture()
3651
def url_creator(self, user, project):
3752
return f'/{API_BASE}nodes/{project._id}/contributors/{user._id}/'
@@ -414,3 +429,22 @@ def test_change_admin_self_with_other_admin(self, app, user, contrib, project, u
414429
attributes = res.json['data']['attributes']
415430
assert attributes['permission'] == permissions.WRITE
416431
assert project.get_permissions(user) == [permissions.READ, permissions.WRITE]
432+
433+
def test_update_component_project_with_parent_contributors(self, app, user, component_project_and_parent_project):
434+
def exists_unique_parent_contributors(parent_project, component_project):
435+
return parent_project.contributors.exclude(
436+
id__in=component_project.contributors.values_list('id', flat=True)
437+
).exists()
438+
component_project, parent_project = component_project_and_parent_project
439+
assert exists_unique_parent_contributors(parent_project, component_project)
440+
res = app.patch_json_api(
441+
f'/{API_BASE}nodes/{component_project._id}/contributors/?copy_contributors_from_parent_project=true',
442+
{
443+
'data': {
444+
'type': 3
445+
}
446+
},
447+
auth=user.auth
448+
)
449+
assert res.status_code == 200
450+
assert not exists_unique_parent_contributors(parent_project, component_project)

0 commit comments

Comments
 (0)