Skip to content

Commit de85529

Browse files
committed
Add DraftsModifyApi
Changes to be committed: modified: biocompute/apis.py modified: biocompute/selectors.py modified: biocompute/services.py modified: biocompute/urls.py modified: prefix/selectors.py deleted: tests/test_apis/test_biocompute/objects_drafts_create.py deleted: tests/test_apis/test_biocompute/test_objects_drafts_create.py
1 parent 0ff6b2d commit de85529

File tree

7 files changed

+306
-293
lines changed

7 files changed

+306
-293
lines changed

biocompute/apis.py

Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
from rest_framework.response import Response
1515
from tests.fixtures.example_bco import BCO_000001
1616
from config.services import legacy_api_converter, response_constructor
17-
from biocompute.services import BcoDraftSerializer, bco_counter_increment
18-
from biocompute.selectors import retrieve_bco
17+
from biocompute.services import BcoDraftSerializer, bco_counter_increment, ModifyBcoDraftSerializer
18+
from biocompute.selectors import retrieve_bco, user_can_modify_bco
1919
from prefix.selectors import user_can_draft
2020

2121
hostname = settings.PUBLIC_HOSTNAME
@@ -66,11 +66,10 @@ class DraftsCreateApi(APIView):
6666
"""
6767

6868
permission_classes = [IsAuthenticated,]
69-
request_body = BCO_DRAFT_SCHEMA
7069

7170
@swagger_auto_schema(
7271
operation_id="api_objects_drafts_create",
73-
request_body=request_body,
72+
request_body=BCO_DRAFT_SCHEMA,
7473
responses={
7574
200: "All requests were accepted.",
7675
207: "Some requests failed and some succeeded. Each object submitted"
@@ -166,6 +165,138 @@ def post(self, request) -> Response:
166165
data=response_data
167166
)
168167

168+
class DraftsModifyApi(APIView):
169+
"""Modify BCO Draft [Bulk Enabled]
170+
171+
API endpoint for modifying BioCompute Object (BCO) drafts, with support
172+
for bulk operations.
173+
174+
This endpoint allows authenticated users to modify existing BCO drafts
175+
individually or in bulk by submitting a list of BCO drafts. The operation
176+
can be performed for one or more drafts in a single request. Each draft is
177+
validated and processed independently, allowing for mixed response
178+
statuses (HTTP_207_MULTI_STATUS) in the case of bulk submissions.
179+
"""
180+
181+
permission_classes = [IsAuthenticated,]
182+
183+
@swagger_auto_schema(
184+
operation_id="api_objects_drafts_modify",
185+
request_body=openapi.Schema(
186+
type=openapi.TYPE_ARRAY,
187+
title="Modify BCO Draft Schema",
188+
items=openapi.Schema(
189+
type=openapi.TYPE_OBJECT,
190+
required=[],
191+
properties={
192+
"authorized_users": openapi.Schema(
193+
type=openapi.TYPE_ARRAY,
194+
description="Users which can access the BCO draft.",
195+
items=openapi.Schema(type=openapi.TYPE_STRING, example="tester")
196+
),
197+
"contents": openapi.Schema(
198+
type=openapi.TYPE_OBJECT,
199+
description="Contents of the BCO.",
200+
example=BCO_000001
201+
),
202+
},
203+
),
204+
description="BCO Drafts to create.",
205+
),
206+
responses={
207+
200: "All requests were accepted.",
208+
207: "Some requests failed and some succeeded. Each object submitted"
209+
" will have it's own response object with it's own status"
210+
" code and message.\n",
211+
400: "All requests were rejected.",
212+
403: "Invalid token.",
213+
},
214+
tags=["BCO Management"],
215+
)
216+
217+
def post(self, request) -> Response:
218+
response_data = []
219+
requester = request.user
220+
data = request.data
221+
rejected_requests = False
222+
accepted_requests = False
223+
if 'POST_api_objects_drafts_modify' in request.data:
224+
data = legacy_api_converter(request.data)
225+
226+
for index, object in enumerate(data):
227+
response_id = object.get("object_id", index)
228+
modify_permitted = user_can_modify_bco(response_id, requester)
229+
230+
if modify_permitted is None:
231+
response_data.append(response_constructor(
232+
identifier=response_id,
233+
status = "NOT FOUND",
234+
code= 404,
235+
message= f"Invalid BCO: {response_id}.",
236+
))
237+
rejected_requests = True
238+
continue
239+
240+
if modify_permitted is False:
241+
response_data.append(response_constructor(
242+
identifier=response_id,
243+
status = "FORBIDDEN",
244+
code= 400,
245+
message= f"User, {requester}, does not have draft permissions"\
246+
+ f" for BCO {response_id}.",
247+
))
248+
rejected_requests = True
249+
continue
250+
251+
bco = ModifyBcoDraftSerializer(data=object)
252+
253+
if bco.is_valid():
254+
try:
255+
bco.update(bco.validated_data)
256+
response_data.append(response_constructor(
257+
identifier=response_id,
258+
status = "SUCCESS",
259+
code= 200,
260+
message= f"BCO {response_id} updated",
261+
))
262+
accepted_requests = True
263+
264+
except Exception as err:
265+
response_data.append(response_constructor(
266+
identifier=response_id,
267+
status = "SERVER ERROR",
268+
code= 500,
269+
message= f"BCO {response_id} failed",
270+
))
271+
272+
else:
273+
response_data.append(response_constructor(
274+
identifier=response_id,
275+
status = "REJECTED",
276+
code= 400,
277+
message= f"BCO {response_id} rejected",
278+
data=bco.errors
279+
))
280+
rejected_requests = True
281+
282+
if accepted_requests is False and rejected_requests == True:
283+
return Response(
284+
status=status.HTTP_400_BAD_REQUEST,
285+
data=response_data
286+
)
287+
288+
if accepted_requests is True and rejected_requests is True:
289+
return Response(
290+
status=status.HTTP_207_MULTI_STATUS,
291+
data=response_data
292+
)
293+
294+
if accepted_requests is True and rejected_requests is False:
295+
return Response(
296+
status=status.HTTP_200_OK,
297+
data=response_data
298+
)
299+
169300
class DraftRetrieveApi(APIView):
170301
"""Get a draft object
171302

biocompute/selectors.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,37 @@
88
from django.conf import settings
99
from django.contrib.auth. models import User
1010
from biocompute.models import Bco
11-
from prefix.selectors import user_can_view
11+
from prefix.selectors import user_can_view, user_can_modify
1212

13+
def user_can_modify_bco(object_id: str, user:User) -> bool:
14+
"""Modify BCO
15+
"""
16+
17+
try:
18+
bco_instance = Bco.objects.get(object_id=object_id)
19+
except Bco.DoesNotExist:
20+
return None
21+
if user in bco_instance.authorized_users.all():
22+
return True
23+
24+
prefix_name = object_id.split("/")[-2].split("_")[0]
25+
view_permission = user_can_modify(prefix_name, user)
26+
if view_permission is False:
27+
return False
28+
29+
return True
30+
1331
def retrieve_bco(bco_accession:str, user:User, bco_version:str=None) -> bool:
1432
"""Retrieve BCO
1533
1634
This function checks whether a given user has the permission to view a BCO
1735
identified by its accession number and, optionally, its version. It
1836
performs several checks:
1937
20-
1. Checks if the user has general 'view' permissions for the prefix
38+
1. Verifies if the BCO exists. If not, returns `None`.
39+
2. Checks if the user is explicitly authorized to view this specific BCO.
40+
3. Checks if the user has general 'view' permissions for the prefix
2141
associated with the BCO.
22-
2. Verifies if the BCO exists. If not, returns `None`.
23-
3. Checks if the user is explicitly authorized to view this specific BCO.
24-
2542
"""
2643

2744
hostname = settings.PUBLIC_HOSTNAME
@@ -31,11 +48,6 @@ def retrieve_bco(bco_accession:str, user:User, bco_version:str=None) -> bool:
3148
else:
3249
object_id = f"{hostname}/{bco_accession}/{bco_version}"
3350

34-
prefix_name = bco_accession.split("_")[0]
35-
view_permission = user_can_view(prefix_name, user)
36-
if view_permission is False:
37-
return False
38-
3951
try:
4052
bco_instance = Bco.objects.get(object_id=object_id)
4153
except Bco.DoesNotExist:
@@ -44,4 +56,9 @@ def retrieve_bco(bco_accession:str, user:User, bco_version:str=None) -> bool:
4456
if user in bco_instance.authorized_users.all():
4557
return bco_instance
4658

59+
prefix_name = bco_accession.split("_")[0]
60+
view_permission = user_can_view(prefix_name, user)
61+
if view_permission is False:
62+
return False
63+
4764
return bco_instance

0 commit comments

Comments
 (0)