Skip to content

Commit

Permalink
Merge pull request #87 from City-of-Helsinki/feature-document-batch-list
Browse files Browse the repository at this point in the history
ATV-176 Document batch list
  • Loading branch information
AntiAki-M authored Feb 29, 2024
2 parents ce54f5b + d754b72 commit a42e791
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 a42e791

Please sign in to comment.