Skip to content

Commit bc4a980

Browse files
committed
add data OutcomeArtifact nested Outcome primary data OutcomeArtifact to linked data registration nodes
1 parent 250582f commit bc4a980

8 files changed

+883
-598
lines changed

api/base/views.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,11 +624,15 @@ class BaseLinkedList(JSONAPIBaseView, generics.ListAPIView):
624624

625625
def get_queryset(self):
626626
auth = get_user_auth(self.request)
627+
from api.resources import annotations as resource_annotations
627628

628629
return (
629630
self.get_node().linked_nodes
631+
.annotate(**resource_annotations.make_open_practice_badge_annotations())
630632
.filter(is_deleted=False)
631-
.annotate(region=F('addons_osfstorage_node_settings__region___id'))
633+
.annotate(
634+
region=F('addons_osfstorage_node_settings__region___id'),
635+
)
632636
.exclude(region=None)
633637
.exclude(type='osf.collection', region=None)
634638
.can_view(user=auth.user, private_link=auth.private_link)

api/nodes/views.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,15 @@ def get_queryset(self):
11041104
node = self.get_node()
11051105
auth = get_user_auth(self.request)
11061106
node_relation_subquery = node._parents.filter(is_node_link=True).values_list('parent', flat=True)
1107-
return Registration.objects.filter(id__in=Subquery(node_relation_subquery), retraction__isnull=True).can_view(user=auth.user, private_link=auth.private_link)
1107+
return Registration.objects.filter(
1108+
id__in=Subquery(node_relation_subquery),
1109+
retraction__isnull=True,
1110+
).can_view(
1111+
user=auth.user,
1112+
private_link=auth.private_link,
1113+
).annotate(
1114+
**resource_annotations.make_open_practice_badge_annotations(),
1115+
)
11081116

11091117

11101118
class NodeFilesList(JSONAPIBaseView, generics.ListAPIView, WaterButlerMixin, ListFilterMixin, NodeMixin):

api_tests/nodes/views/test_node_linked_by_list.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import pytest
22
from framework.auth.core import Auth
3+
from osf.models import OutcomeArtifact, Outcome, Identifier
4+
from osf.models.outcome_artifacts import ArtifactTypes
35

46
from osf_tests.factories import ProjectFactory, AuthUserFactory, RegistrationFactory, WithdrawnRegistrationFactory
57
from api.base.settings.defaults import API_BASE
@@ -114,6 +116,43 @@ def test_linked_by_registrations_links_registrations(self, app, url_public, user
114116
assert len(res.json['data']) == 1
115117
assert res.json['data'][0]['id'] == registration._id
116118

119+
def test_linked_by_registrations_links_registrations_artifacts(self, app, url_public, user, project_public, project_private):
120+
res = app.get(url_public, auth=user.auth)
121+
assert len(res.json['data']) == 0
122+
123+
project_private.add_pointer(project_public, auth=Auth(user), save=True)
124+
# registration will have the same links as its model project
125+
registration = RegistrationFactory(project=project_private, creator=user)
126+
outcome = Outcome.objects.create()
127+
registration_doi = Identifier.objects.create(
128+
referent=registration,
129+
value='SOME_PROJECT_DOI',
130+
category='doi'
131+
)
132+
# Create the PRIMARY artifact for this registration, so the annotations can resolve
133+
OutcomeArtifact.objects.create(
134+
outcome=outcome,
135+
identifier=registration_doi,
136+
artifact_type=ArtifactTypes.PRIMARY,
137+
finalized=True,
138+
)
139+
# Now create the DATA artifact for the same outcome
140+
OutcomeArtifact.objects.create(
141+
outcome=outcome,
142+
identifier=registration_doi,
143+
artifact_type=ArtifactTypes.DATA,
144+
finalized=True,
145+
)
146+
147+
res = app.get(url_public, auth=user.auth)
148+
assert len(res.json['data']) == 1
149+
assert res.json['data'][0]['id'] == registration._id
150+
assert res.json['data'][0]['attributes']['has_data'] # here and true!
151+
assert res.json['data'][0]['attributes']['has_analytic_code'] is False # here and false!
152+
assert res.json['data'][0]['attributes']['has_materials'] is False
153+
assert res.json['data'][0]['attributes']['has_papers'] is False
154+
assert res.json['data'][0]['attributes']['has_supplements'] is False
155+
117156
def test_linked_by_registrations_doesnt_list_nodes(self, app, url_public, user, project_public, project_private):
118157
project_private.add_pointer(project_public, auth=Auth(user), save=True)
119158
project_public.reload()
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
import pytest
2+
3+
from api.base.settings.defaults import API_BASE
4+
from framework.auth.core import Auth
5+
from osf_tests.factories import (
6+
AuthUserFactory,
7+
OSFGroupFactory,
8+
RegistrationFactory,
9+
NodeRelationFactory,
10+
)
11+
from osf.utils.permissions import READ
12+
from rest_framework import exceptions
13+
from .utils import LinkedRegistrationsTestCase
14+
15+
16+
@pytest.mark.django_db
17+
class TestNodeLinkedRegistrationsRelationshipCreate(LinkedRegistrationsTestCase):
18+
19+
def make_payload(self, registration_id=None, deprecated_type=True):
20+
return {
21+
'data': [
22+
{
23+
'type': 'linked_registrations' if deprecated_type else 'registrations',
24+
'id': registration_id
25+
}
26+
]
27+
}
28+
29+
def make_request(self, app, node_id=None, auth=None, reg_id=None, expect_errors=False, version=None, deprecated_type=True):
30+
url = f'/{API_BASE}nodes/{node_id}/relationships/linked_registrations/'
31+
if version:
32+
url = f'{url}?version={version}'
33+
if auth:
34+
return app.post_json_api(
35+
url,
36+
self.make_payload(registration_id=reg_id, deprecated_type=deprecated_type),
37+
auth=auth,
38+
expect_errors=expect_errors
39+
)
40+
return app.post_json_api(
41+
url,
42+
self.make_payload(registration_id=reg_id, deprecated_type=deprecated_type),
43+
expect_errors=expect_errors
44+
)
45+
46+
def test_admin_contributor_can_create_linked_registrations_relationship(self, app, user_admin_contrib, node_private):
47+
registration = RegistrationFactory(is_public=True)
48+
res = self.make_request(
49+
app,
50+
node_id=node_private._id,
51+
reg_id=registration._id,
52+
auth=user_admin_contrib.auth
53+
)
54+
assert res.status_code == 201
55+
linked_registrations = [r['id'] for r in res.json['data']]
56+
assert registration._id in linked_registrations
57+
58+
def test_admin_contributor_can_create_linked_registrations_relationship_2_13(self, app, user_admin_contrib, node_private):
59+
registration = RegistrationFactory(is_public=True)
60+
res = self.make_request(
61+
app,
62+
node_id=node_private._id,
63+
reg_id=registration._id,
64+
auth=user_admin_contrib.auth,
65+
version='2.13',
66+
deprecated_type=False,
67+
)
68+
assert res.status_code == 201
69+
linked_registrations = [r['id'] for r in res.json['data']]
70+
assert registration._id in linked_registrations
71+
72+
def test_rw_contributor_can_create_linked_registrations_relationship(self, app, user_write_contrib, node_private):
73+
registration = RegistrationFactory(is_public=True)
74+
res = self.make_request(
75+
app,
76+
node_id=node_private._id,
77+
reg_id=registration._id,
78+
auth=user_write_contrib.auth
79+
)
80+
assert res.status_code == 201
81+
linked_registrations = [r['id'] for r in res.json['data']]
82+
assert registration._id in linked_registrations
83+
84+
def test_read_contributor_cannot_create_linked_registrations_relationship(
85+
self,
86+
app,
87+
user_read_contrib,
88+
node_private,
89+
):
90+
91+
registration = RegistrationFactory(is_public=True)
92+
res = self.make_request(
93+
app,
94+
node_id=node_private._id,
95+
reg_id=registration._id,
96+
auth=user_read_contrib.auth,
97+
expect_errors=True
98+
)
99+
assert res.status_code == 403
100+
assert res.json['errors'][0]['detail'] == exceptions.PermissionDenied.default_detail
101+
102+
def test_non_contributor_cannot_create_linked_registrations_relationship(
103+
self,
104+
app,
105+
user_non_contrib,
106+
node_private,
107+
):
108+
registration = RegistrationFactory(is_public=True)
109+
res = self.make_request(
110+
app,
111+
node_id=node_private._id,
112+
reg_id=registration._id,
113+
auth=user_non_contrib.auth,
114+
expect_errors=True
115+
)
116+
assert res.status_code == 403
117+
assert res.json['errors'][0]['detail'] == exceptions.PermissionDenied.default_detail
118+
119+
def test_read_osf_group_mem_cannot_create_linked_registrations_relationship(
120+
self,
121+
app,
122+
user_non_contrib,
123+
node_private,
124+
):
125+
group_mem = AuthUserFactory()
126+
group = OSFGroupFactory(creator=group_mem)
127+
node_private.add_osf_group(group, READ)
128+
registration = RegistrationFactory(is_public=True)
129+
res = self.make_request(
130+
app,
131+
node_id=node_private._id,
132+
reg_id=registration._id,
133+
auth=group_mem.auth,
134+
expect_errors=True
135+
)
136+
assert res.status_code == 403
137+
138+
def test_unauthenticated_user_cannot_create_linked_registrations_relationship(
139+
self,
140+
app,
141+
user_non_contrib,
142+
node_private,
143+
):
144+
registration = RegistrationFactory(is_public=True)
145+
res = self.make_request(
146+
app,
147+
node_id=node_private._id,
148+
reg_id=registration._id,
149+
expect_errors=True
150+
)
151+
assert res.status_code == 401
152+
assert res.json['errors'][0]['detail'] == exceptions.NotAuthenticated.default_detail
153+
154+
def test_cannot_create_linked_registrations_relationship_invalid_registration_guid(
155+
self,
156+
app,
157+
user_admin_contrib,
158+
node_private,
159+
):
160+
res = self.make_request(
161+
app,
162+
node_id=node_private._id,
163+
reg_id='abcde',
164+
auth=user_admin_contrib.auth,
165+
expect_errors=True
166+
)
167+
assert res.status_code == 404
168+
assert res.json['errors'][0]['detail'] == 'Node with id "abcde" was not found'
169+
170+
def test_cannot_create_linked_registration_relationship_to_private_registration_if_non_contributor(
171+
self,
172+
app,
173+
user_admin_contrib,
174+
node_private,
175+
):
176+
registration = RegistrationFactory()
177+
res = self.make_request(
178+
app,
179+
node_id=node_private._id,
180+
reg_id=registration._id,
181+
auth=user_admin_contrib.auth,
182+
expect_errors=True
183+
)
184+
assert res.status_code == 403
185+
assert res.json['errors'][0]['detail'] == exceptions.PermissionDenied.default_detail
186+
187+
def test_cannot_create_relationship_with_child_registration(self, app, user_admin_contrib, node_private):
188+
child_reg = RegistrationFactory(creator=user_admin_contrib)
189+
NodeRelationFactory(child=child_reg, parent=node_private)
190+
url = f'/{API_BASE}nodes/{node_private._id}/relationships/linked_registrations/'
191+
data = self.make_payload(registration_id=child_reg._id)
192+
res = app.post_json_api(url, data, auth=user_admin_contrib.auth, expect_errors=True)
193+
assert res.status_code == 400
194+
assert res.json['errors'][0]['detail'] == f'Target Node \'{child_reg._id}\' is already a child of \'{node_private._id}\'.'
195+
196+
def test_cannot_create_link_registration_to_itself(self, app, user_admin_contrib, node_private):
197+
res = self.make_request(
198+
app,
199+
node_id=node_private._id,
200+
reg_id=node_private._id,
201+
auth=user_admin_contrib.auth,
202+
expect_errors=True
203+
)
204+
assert res.status_code == 400
205+
assert res.json['errors'][0]['detail'] == f'Cannot link node \'{node_private._id}\' to itself.'
206+
207+
def test_create_linked_registrations_relationship_registration_already_in_linked_registrations_returns_no_content(
208+
self, app, registration, node_private, user_admin_contrib):
209+
res = self.make_request(
210+
app,
211+
node_id=node_private._id,
212+
reg_id=registration._id,
213+
auth=user_admin_contrib.auth
214+
)
215+
assert res.status_code == 204
216+
217+
def test_can_create_linked_registration_relationship_to_private_registration_if_admin(
218+
self, app, user_admin_contrib, node_private):
219+
registration = RegistrationFactory(creator=user_admin_contrib)
220+
res = self.make_request(
221+
app,
222+
node_id=node_private._id,
223+
reg_id=registration._id,
224+
auth=user_admin_contrib.auth
225+
)
226+
assert res.status_code == 201
227+
linked_registrations = [r['id'] for r in res.json['data']]
228+
assert registration._id in linked_registrations
229+
230+
def test_can_create_linked_registration_relationship_to_private_registration_if_rw(
231+
self, app, user_admin_contrib, node_private):
232+
registration = RegistrationFactory()
233+
registration.add_contributor(
234+
user_admin_contrib,
235+
auth=Auth(registration.creator)
236+
)
237+
registration.save()
238+
res = self.make_request(
239+
app=app,
240+
node_id=node_private._id,
241+
reg_id=registration._id,
242+
auth=user_admin_contrib.auth
243+
)
244+
assert res.status_code == 201
245+
linked_registrations = [r['id'] for r in res.json['data']]
246+
assert registration._id in linked_registrations
247+
248+
def test_can_create_linked_registration_relationship_to_private_registration_if_read_only(
249+
self, app, user_admin_contrib, node_private):
250+
registration = RegistrationFactory()
251+
registration.add_contributor(
252+
user_admin_contrib,
253+
auth=Auth(registration.creator),
254+
permissions=READ
255+
)
256+
registration.save()
257+
res = self.make_request(
258+
app=app,
259+
node_id=node_private._id,
260+
reg_id=registration._id,
261+
auth=user_admin_contrib.auth
262+
)
263+
assert res.status_code == 201
264+
linked_registrations = [r['id'] for r in res.json['data']]
265+
assert registration._id in linked_registrations

0 commit comments

Comments
 (0)