From f3225da1ddb33050fedfa23bf4a5d5d407fc72aa Mon Sep 17 00:00:00 2001
From: Erwan Rouchet <rouchet@teklia.com>
Date: Thu, 13 Apr 2023 15:46:32 +0200
Subject: [PATCH] Support arbitrary job IDs in RetrieveJob and DestroyJob

---
 arkindex/project/api_v1.py        |  2 +-
 arkindex/users/serializers.py     |  2 +-
 arkindex/users/tests/test_jobs.py | 46 +++++++++++++++++++++++++++++++
 3 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/arkindex/project/api_v1.py b/arkindex/project/api_v1.py
index aadc80a50f..c2292c2ac5 100644
--- a/arkindex/project/api_v1.py
+++ b/arkindex/project/api_v1.py
@@ -332,7 +332,7 @@ api = [
 
     # Asynchronous jobs
     path('jobs/', JobList.as_view(), name='jobs-list'),
-    path('jobs/<uuid:pk>/', JobRetrieve.as_view(), name='jobs-retrieve'),
+    path('jobs/<path:pk>/', JobRetrieve.as_view(), name='jobs-retrieve'),
 
     # OpenAPI Schema
     path('openapi/', OpenApiSchemaView.as_view(), name='openapi-schema'),
diff --git a/arkindex/users/serializers.py b/arkindex/users/serializers.py
index 2deee821ba..b2112274be 100644
--- a/arkindex/users/serializers.py
+++ b/arkindex/users/serializers.py
@@ -230,7 +230,7 @@ class JobSerializer(serializers.Serializer):
     """
     Serializers a RQ job.
     """
-    id = serializers.UUIDField(read_only=True)
+    id = serializers.CharField(read_only=True)
     description = serializers.CharField(read_only=True)
     progress = serializers.FloatField(min_value=0, max_value=1, read_only=True, allow_null=True)
     status = serializers.SerializerMethodField()
diff --git a/arkindex/users/tests/test_jobs.py b/arkindex/users/tests/test_jobs.py
index 98e2c53bac..c06172b912 100644
--- a/arkindex/users/tests/test_jobs.py
+++ b/arkindex/users/tests/test_jobs.py
@@ -180,6 +180,34 @@ class TestJobs(FixtureAPITestCase):
         self.assertEqual(get_queue_mock().fetch_job.call_count, 1)
         self.assertEqual(get_queue_mock().fetch_job.call_args, call(self.user_mocked_job.id))
 
+    @patch('arkindex.users.api.get_queue')
+    def test_retrieve_custom_id(self, get_queue_mock):
+        """
+        Job IDs can be any string, including one with slashes, so we should allow any string in RetrieveJob
+        """
+        job = MockedJob(user_id=self.user.id, id='a/b/c/d')
+        get_queue_mock.return_value.fetch_job.return_value = job
+        self.client.force_login(self.user)
+
+        with self.assertNumQueries(2):
+            response = self.client.get(reverse('api:jobs-retrieve', kwargs={'pk': 'a/b/c/d'}))
+            self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+        self.assertDictEqual(response.json(), {
+            'id': 'a/b/c/d',
+            'status': 'queued',
+            'enqueued_at': '2020-01-01T13:37:42Z',
+            'started_at': None,
+            'ended_at': None,
+            'progress': None,
+            'description': 'something'
+        })
+
+        self.assertEqual(get_queue_mock.call_count, 1)
+        self.assertEqual(get_queue_mock.call_args, call('default'))
+        self.assertEqual(get_queue_mock().fetch_job.call_count, 1)
+        self.assertEqual(get_queue_mock().fetch_job.call_args, call('a/b/c/d'))
+
     @patch('arkindex.users.api.get_queue')
     def test_retrieve_not_found(self, get_queue_mock):
         get_queue_mock.return_value.fetch_job.return_value = None
@@ -300,3 +328,21 @@ class TestJobs(FixtureAPITestCase):
         self.assertEqual(get_queue_mock().fetch_job.call_count, 1)
         self.assertEqual(get_queue_mock().fetch_job.call_args, call(started_job.id))
         self.assertFalse(started_job.delete.called)
+
+    @patch('arkindex.users.api.get_queue')
+    def test_destroy_custom_id(self, get_queue_mock):
+        """
+        Job IDs can be any string, including one with slashes, so we should allow any string in DestroyJob
+        """
+        job = MockedJob(user_id=self.user.id, id='a/b/c/d')
+        get_queue_mock.return_value.fetch_job.return_value = job
+        self.client.force_login(self.user)
+
+        with self.assertNumQueries(2):
+            response = self.client.delete(reverse('api:jobs-retrieve', kwargs={'pk': 'a/b/c/d'}))
+            self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+        self.assertEqual(get_queue_mock.call_count, 1)
+        self.assertEqual(get_queue_mock().fetch_job.call_count, 1)
+        self.assertEqual(get_queue_mock().fetch_job.call_args, call('a/b/c/d'))
+        self.assertEqual(job.delete.call_count, 1)
-- 
GitLab