Skip to content

Commit

Permalink
ATV-176 Document batch list
Browse files Browse the repository at this point in the history
Add endpoint for fetching multiple documents per id
Add tests for endpoint
Update test snapshots
Update docs
  • Loading branch information
AntiAki-M committed Feb 29, 2024
1 parent ce54f5b commit d754b72
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 8 deletions.
31 changes: 31 additions & 0 deletions documents/api/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,37 @@ def _base_500_response(custom_message: str = None) -> OpenApiResponse:
},
examples=[example_document, example_error],
),
"batch_list": extend_schema(
summary="Fetch multiple documents by IDs",
description="This endpoint allows a service to fetch multiple documents by their IDs",
# TODO: Uncomment when organization features are implemented
# Replace the previous line with the following
# "* Authenticated users are allowed access to the document if they are the owner of the document "
# "or the document is owned by an organization and the user has permission to act on behalf "
# "of that organization.",
request=serializers.JSONField(),
responses={
(status.HTTP_200_OK, "application/json"): OpenApiResponse(
response=DocumentSerializer,
description="The document/s was found and contents are returned as JSON.",
),
(status.HTTP_400_BAD_REQUEST, "application/json"): _base_400_response(),
status.HTTP_401_UNAUTHORIZED: _base_401_response(),
status.HTTP_403_FORBIDDEN: OpenApiResponse(
description="The authenticated user lacks the proper permissions to access the document. "
"Depending on the requested document, "
"either the user does not belong to the admin group of the service which owns the document, "
"the user does not own the document."
# TODO: Uncomment when organization features are implemented
# " or the user does not have permission to act on behalf "
# "of the organization which owns the document."
),
status.HTTP_404_NOT_FOUND: OpenApiResponse(
description="No document was found with `documentId`.",
),
status.HTTP_500_INTERNAL_SERVER_ERROR: _base_500_response(),
},
),
"create": extend_schema(
summary="Store a new document and its attachments",
description="Store a new document and its attachments.\n\n"
Expand Down
22 changes: 20 additions & 2 deletions documents/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
from drf_spectacular.utils import extend_schema, extend_schema_view
from helusers.utils import uuid_to_username
from rest_framework import filters, status
from rest_framework.decorators import action
from rest_framework.exceptions import (
MethodNotAllowed,
NotAuthenticated,
NotFound,
PermissionDenied,
)
from rest_framework.parsers import FileUploadParser, MultiPartParser
from rest_framework.parsers import FileUploadParser, JSONParser, MultiPartParser
from rest_framework.response import Response
from rest_framework.utils import json
from rest_framework_extensions.mixins import NestedViewSetMixin
Expand Down Expand Up @@ -426,7 +427,7 @@ def update(self, request, *args, **kwargs):

@extend_schema_view(**document_viewset_docs)
class DocumentViewSet(AuditLoggingModelViewSet):
parser_classes = [MultiPartParser]
parser_classes = [MultiPartParser, JSONParser]
serializer_class = DocumentSerializer
# Filtering/sorting
filter_backends = [
Expand All @@ -446,6 +447,23 @@ def get_queryset(self):
service_api_key = get_service_api_key_from_request(self.request)
return get_document_queryset(user, service, service_api_key)

@action(detail=False, methods=["POST"], url_path="batch-list")
def batch_list(self, request, *args, **kwargs):
data = request.data
if not isinstance(data, dict):
raise InvalidFieldException(detail="Data is not a json object.")
document_ids = data.get("document_ids")
if not document_ids:
raise InvalidFieldException(detail="Field 'document_ids' is required")
if not isinstance(document_ids, list):
raise InvalidFieldException(
detail="Field 'document_ids' should be a list of UUIDs"
)
queryset = self.get_queryset().filter(id__in=document_ids)
with self.record_action():
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)

Expand Down
6 changes: 3 additions & 3 deletions documents/tests/snapshots/snap_test_api_patch_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"id": "2d2b7a36-a306-4e35-990f-13aea04263ff",
"locked_after": None,
"metadata": {"created_by": "alex", "testing": True},
"service": "service 155",
"service": "service 160",
"status": {
"timestamp": "2021-06-30T12:00:00+03:00",
"value": "handled",
Expand Down Expand Up @@ -59,7 +59,7 @@
"id": "2d2b7a36-a306-4e35-990f-13aea04263ff",
"locked_after": None,
"metadata": {"created_by": "alex", "testing": True},
"service": "service 161",
"service": "service 166",
"status": {
"timestamp": "2021-06-30T12:00:00+03:00",
"value": "handled",
Expand Down Expand Up @@ -94,7 +94,7 @@
"id": "2d2b7a36-a306-4e35-990f-13aea04263ff",
"locked_after": None,
"metadata": {"created_by": "alex", "testing": True},
"service": "service 163",
"service": "service 168",
"status": {
"timestamp": "2021-06-30T12:00:00+03:00",
"value": "handled",
Expand Down
6 changes: 3 additions & 3 deletions documents/tests/snapshots/snap_test_api_retrieve_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"id": "485af718-d9d1-46b9-ad7b-33ea054126e3",
"locked_after": None,
"metadata": {},
"service": "service 207",
"service": "service 212",
"status": {
"timestamp": "2020-06-01T03:00:00+03:00",
"value": "testing",
Expand Down Expand Up @@ -47,7 +47,7 @@
"id": "485af718-d9d1-46b9-ad7b-33ea054126e3",
"locked_after": None,
"metadata": {},
"service": "service 210",
"service": "service 215",
"status": {
"timestamp": "2020-06-01T03:00:00+03:00",
"value": "testing",
Expand Down Expand Up @@ -75,7 +75,7 @@
"id": "485af718-d9d1-46b9-ad7b-33ea054126e3",
"locked_after": None,
"metadata": {},
"service": "service 209",
"service": "service 214",
"status": {
"timestamp": "2020-06-01T03:00:00+03:00",
"value": "testing",
Expand Down
46 changes: 46 additions & 0 deletions documents/tests/test_api_list_documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,52 @@ def test_gdpr_api_list_user(user, service):
assert response.status_code == status.HTTP_403_FORBIDDEN


def test_document_batch_list_service_api_key(service_api_client, user):
data = {**VALID_DOCUMENT_DATA, "user_id": user.uuid}
other_service = ServiceFactory()
document_ids = []
for _i in range(0, 2):
response = service_api_client.post(
reverse("documents-list"), data, format="multipart"
)
assert response.status_code == status.HTTP_201_CREATED
document_ids.append(response.json()["id"])

other_document_id = DocumentFactory(service=other_service, user=user).id

assert Document.objects.count() == 3
response = service_api_client.post(
reverse("documents-batch-list"),
data={"document_ids": document_ids},
format="json",
)
assert response.status_code == status.HTTP_200_OK
response_ids = [x["id"] for x in response.json()]
assert len(response_ids) == 2
assert str(other_document_id) not in response_ids


def test_document_batch_list_user(user, service):
api_client = get_user_service_client(user, service)
other_service = ServiceFactory()
other_user = UserFactory()
d1 = DocumentFactory(service=service, user=user, deletable=True)
d2 = DocumentFactory(service=other_service, user=user, deletable=False)
d3 = DocumentFactory(service=other_service, user=other_user, deletable=False)

assert Document.objects.count() == 3

response = api_client.post(
reverse("documents-batch-list"),
data={"document_ids": [d1.id, d2.id, d3.id]},
format="json",
)
assert response.status_code == status.HTTP_200_OK
response_ids = [x["id"] for x in response.json()]
assert len(response_ids) == 1
assert [str(d1.id)] == response_ids


@pytest.mark.parametrize(
"param,value",
[
Expand Down

0 comments on commit d754b72

Please sign in to comment.