From 8c221b6d9364128b734b5e49ca0846901089a962 Mon Sep 17 00:00:00 2001 From: Erwan Rouchet <rouchet@teklia.com> Date: Wed, 4 Dec 2024 12:47:08 +0000 Subject: [PATCH] Set a farm on export processes --- arkindex/process/serializers/imports.py | 27 ++++++- .../tests/process/test_export_process.py | 80 +++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/arkindex/process/serializers/imports.py b/arkindex/process/serializers/imports.py index 2806fa8fd5..a8e08198d2 100644 --- a/arkindex/process/serializers/imports.py +++ b/arkindex/process/serializers/imports.py @@ -415,14 +415,36 @@ class ExportProcessSerializer(ProcessDetailsSerializer): read_only=True, ) state = EnumField(State, required=False, read_only=True) + farm_id = serializers.PrimaryKeyRelatedField( + queryset=Farm.objects.all(), + source="farm", + write_only=True, + allow_null=True, + default=None, + ) + + def validate_farm_id(self, farm): + """ + Using the `default=` on the farm field would allow the default farm + to be used without having access rights to it, so we do the ACL checks + here during validation. We thus do not filter the field's queryset, + as we would be duplicating the ACL checks otherwise. + """ + if farm is None: + farm = get_default_farm() + + if farm and not farm.is_available(self.context["request"].user): + raise ValidationError(["You do not have access to this farm."]) + + return farm class Meta(ProcessDetailsSerializer.Meta): - model = Process fields = ProcessDetailsSerializer.Meta.fields + ( "export_id", "format", "selection", - "configuration" + "configuration", + "farm_id", ) read_only_fields = ProcessDetailsSerializer.Meta.read_only_fields + ( "element_name_contains", @@ -562,6 +584,7 @@ class ExportProcessSerializer(ProcessDetailsSerializer): corpus=corpus, creator=user, element=validated_data.get("element"), + farm=validated_data.get("farm"), ) # If using the user selection, create process elements if validated_data.get("selection"): diff --git a/arkindex/process/tests/process/test_export_process.py b/arkindex/process/tests/process/test_export_process.py index 0ddd0fda9b..3fd3d5b8eb 100644 --- a/arkindex/process/tests/process/test_export_process.py +++ b/arkindex/process/tests/process/test_export_process.py @@ -1,11 +1,13 @@ from datetime import datetime, timedelta, timezone from unittest.mock import call, patch +from uuid import uuid4 from django.test import override_settings from rest_framework import status from rest_framework.reverse import reverse from arkindex.documents.models import Corpus, CorpusExport, CorpusExportState +from arkindex.ponos.models import Farm from arkindex.process.models import ArkindexFeature, Process, ProcessMode, WorkerVersion from arkindex.project.tests import FixtureAPITestCase from arkindex.users.models import Role, User @@ -524,3 +526,81 @@ class TestExportProcess(FixtureAPITestCase): "order_by_name": False, "page_type": "page" }) + + def test_farm(self): + farm = Farm.objects.get() + self.client.force_login(self.user) + + with self.assertNumQueries(25): + response = self.client.post( + reverse("api:export-process", kwargs={"corpus_id": str(self.corpus.id)}), + { + "format": "pdf", + "export_id": str(self.complete_export.id), + "farm_id": str(farm.id), + }, + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + process = Process.objects.get(id=response.json()["id"]) + self.assertEqual(process.farm, farm) + + @patch("arkindex.process.serializers.imports.get_default_farm") + def test_default_farm(self, get_default_farm_mock): + farm = Farm.objects.get() + get_default_farm_mock.return_value = farm + self.client.force_login(self.user) + + with self.assertNumQueries(24): + response = self.client.post( + reverse("api:export-process", kwargs={"corpus_id": str(self.corpus.id)}), + { + "format": "pdf", + "export_id": str(self.complete_export.id), + }, + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + process = Process.objects.get(id=response.json()["id"]) + self.assertEqual(process.farm, farm) + + @patch("arkindex.ponos.models.Farm.is_available", return_value=False) + def test_farm_unavailable(self, is_available_mock): + farm = Farm.objects.get() + self.client.force_login(self.user) + + with self.assertNumQueries(5): + response = self.client.post( + reverse("api:export-process", kwargs={"corpus_id": str(self.corpus.id)}), + { + "format": "pdf", + "export_id": str(self.complete_export.id), + "farm_id": farm.id, + }, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + self.assertEqual(response.json(), { + "farm_id": ["You do not have access to this farm."], + }) + self.assertFalse(self.corpus.processes.filter(mode=ProcessMode.Export).exists()) + + def test_unknown_farm_id(self): + self.client.force_login(self.user) + + farm_id = str(uuid4()) + with self.assertNumQueries(5): + response = self.client.post( + reverse("api:export-process", kwargs={"corpus_id": str(self.corpus.id)}), + { + "format": "pdf", + "export_id": str(self.complete_export.id), + "farm_id": farm_id, + }, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + self.assertEqual(response.json(), { + "farm_id": [f'Invalid pk "{farm_id}" - object does not exist.'], + }) + self.assertFalse(self.corpus.processes.filter(mode=ProcessMode.Export).exists()) -- GitLab