From d015d3bc4585619f52b42448395d44403bce5e5a Mon Sep 17 00:00:00 2001 From: Erwan Rouchet <rouchet@teklia.com> Date: Thu, 25 May 2023 13:11:03 +0200 Subject: [PATCH] Allow RetrieveWorkerRun on a local process --- arkindex/process/api.py | 14 ++- arkindex/process/tests/test_workerruns.py | 126 ++++++++++++++++++++++ 2 files changed, 136 insertions(+), 4 deletions(-) diff --git a/arkindex/process/api.py b/arkindex/process/api.py index bfd49b13bb..6606b187c4 100644 --- a/arkindex/process/api.py +++ b/arkindex/process/api.py @@ -1304,7 +1304,10 @@ class WorkerRunDetails(CorpusACLMixin, RetrieveUpdateDestroyAPIView): def get_queryset(self): # Use default DB to avoid a race condition checking process workflow return WorkerRun.objects \ - .filter(process__corpus_id__isnull=False) \ + .filter( + ~Q(process__corpus_id=None) + | Q(process__creator_id=self.user.id, process__mode=ProcessMode.Local) + ) \ .using('default') \ .select_related('version__worker__type', 'configuration', 'process__workflow', 'process__corpus', 'version__revision__repo') @@ -1315,12 +1318,15 @@ class WorkerRunDetails(CorpusACLMixin, RetrieveUpdateDestroyAPIView): return context def check_object_permissions(self, request, worker_run): - if not self.has_admin_access(worker_run.process.corpus): + if worker_run.process.corpus_id and not self.has_admin_access(worker_run.process.corpus): raise PermissionDenied(detail='You do not have an admin access to the process project.') # Updating a worker run is not possible once the process is started - if request.method not in permissions.SAFE_METHODS and worker_run.process.workflow_id is not None: - raise ValidationError({'__all__': ["Cannot update a WorkerRun on a Process that has already started"]}) + if request.method not in permissions.SAFE_METHODS: + if worker_run.process.workflow_id is not None: + raise ValidationError({'__all__': ["Cannot update a WorkerRun on a Process that has already started"]}) + if worker_run.process.mode == ProcessMode.Local: + raise ValidationError({'__all__': ['Cannot update a WorkerRun on a local process']}) super().check_object_permissions(request, worker_run) diff --git a/arkindex/process/tests/test_workerruns.py b/arkindex/process/tests/test_workerruns.py index 3e50fd4835..9931783c60 100644 --- a/arkindex/process/tests/test_workerruns.py +++ b/arkindex/process/tests/test_workerruns.py @@ -657,6 +657,78 @@ class TestWorkerRuns(FixtureAPITestCase): } ]) + def test_retrieve_run_local(self): + """ + A user can retrieve a run on their own local process + """ + local_process = self.user.processes.get(mode=ProcessMode.Local) + run = local_process.worker_runs.create(version=self.version_1, parents=[]) + self.client.force_login(self.user) + + with self.assertNumQueries(5): + response = self.client.get( + reverse('api:worker-run-details', kwargs={'pk': str(run.id)}), + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + self.assertDictEqual(response.json(), { + 'id': str(run.id), + 'worker_version': { + 'id': str(self.version_1.id), + 'configuration': {'test': 42}, + 'docker_image': str(self.version_1.docker_image.id), + 'docker_image_iid': None, + 'docker_image_name': f'my_repo.fake/workers/worker/reco:{self.version_1.id}', + 'gpu_usage': 'disabled', + 'model_usage': False, + 'revision': { + 'id': str(self.version_1.revision.id), + 'author': 'Test user', + 'commit_url': 'http://my_repo.fake/workers/worker/commit/1337', + 'created': self.version_1.revision.created.isoformat().replace('+00:00', 'Z'), + 'hash': '1337', + 'message': 'My w0rk3r', + 'refs': [] + }, + 'state': 'available', + 'worker': { + 'id': str(self.worker_1.id), + 'name': 'Recognizer', + 'slug': 'reco', + 'type': 'recognizer' + } + }, + 'parents': [], + 'model_version': None, + 'configuration': None, + 'process': { + 'id': str(local_process.id), + 'activity_state': 'disabled', + 'corpus': None, + 'mode': 'local', + 'model_id': None, + 'name': None, + 'state': 'unscheduled', + 'test_folder_id': None, + 'train_folder_id': None, + 'validation_folder_id': None, + 'workflow': None + }, + }) + + def test_retrieve_run_local_only_current_user(self): + """ + A user cannot retrieve a run on another user's local process + """ + run = WorkerRun.objects.filter(process__creator=self.superuser, process__mode=ProcessMode.Local).first() + self.client.force_login(self.user) + + with self.assertNumQueries(3): + response = self.client.get( + reverse('api:worker-run-details', kwargs={'pk': str(run.id)}), + ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + def test_update_run_requires_id_and_parents(self): self.client.force_login(self.user) with self.assertNumQueries(7): @@ -710,6 +782,25 @@ class TestWorkerRuns(FixtureAPITestCase): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.json(), {'detail': 'You do not have an admin access to the process project.'}) + def test_update_run_local(self): + """ + A user cannot update a worker run on a local process + """ + local_process = self.user.processes.get(mode=ProcessMode.Local) + run = local_process.worker_runs.create(version=self.version_1, parents=[]) + self.client.force_login(self.user) + + with self.assertNumQueries(3): + response = self.client.put( + reverse('api:worker-run-details', kwargs={'pk': str(run.id)}), + data={ + 'parents': [] + } + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + self.assertEqual(response.json(), {'__all__': ['Cannot update a WorkerRun on a local process']}) + def test_update_run_invalid_id(self): rev_2 = self.repo.revisions.create( hash='2', @@ -1439,6 +1530,25 @@ class TestWorkerRuns(FixtureAPITestCase): ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + def test_partial_update_run_local(self): + """ + A user cannot update a worker run on a local process + """ + local_process = self.user.processes.get(mode=ProcessMode.Local) + run = local_process.worker_runs.create(version=self.version_1, parents=[]) + self.client.force_login(self.user) + + with self.assertNumQueries(3): + response = self.client.patch( + reverse('api:worker-run-details', kwargs={'pk': str(run.id)}), + data={ + 'parents': [] + } + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + self.assertEqual(response.json(), {'__all__': ['Cannot update a WorkerRun on a local process']}) + def test_partial_update_run_inexistant_parent(self): self.client.force_login(self.user) with self.assertNumQueries(7): @@ -2088,6 +2198,22 @@ class TestWorkerRuns(FixtureAPITestCase): with self.assertRaises(WorkerRun.DoesNotExist): self.run_1.refresh_from_db() + def test_delete_run_local(self): + """ + A user cannot delete a worker run on a local process + """ + local_process = self.user.processes.get(mode=ProcessMode.Local) + run = local_process.worker_runs.create(version=self.version_1, parents=[]) + self.client.force_login(self.user) + + with self.assertNumQueries(3): + response = self.client.delete( + reverse('api:worker-run-details', kwargs={'pk': str(run.id)}), + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + self.assertEqual(response.json(), {'__all__': ['Cannot update a WorkerRun on a local process']}) + def test_delete_run(self): self.client.force_login(self.user) response = self.client.delete( -- GitLab