From 5b937e0aba09ffebe538fac816c8506407c0b7bf Mon Sep 17 00:00:00 2001 From: Erwan Rouchet <rouchet@teklia.com> Date: Thu, 24 Aug 2023 07:26:15 +0000 Subject: [PATCH] Add export TTL setting --- arkindex/documents/api/export.py | 25 +++++++++++-------- arkindex/documents/tests/test_export.py | 7 ++++-- arkindex/project/config.py | 3 +++ arkindex/project/settings.py | 3 +++ .../tests/config_samples/defaults.yaml | 2 ++ .../project/tests/config_samples/errors.yaml | 2 ++ .../tests/config_samples/expected_errors.yaml | 2 ++ .../tests/config_samples/override.yaml | 2 ++ 8 files changed, 34 insertions(+), 12 deletions(-) diff --git a/arkindex/documents/api/export.py b/arkindex/documents/api/export.py index 220a8efe98..f5145b8bad 100644 --- a/arkindex/documents/api/export.py +++ b/arkindex/documents/api/export.py @@ -1,5 +1,8 @@ -from datetime import datetime, timedelta, timezone +from datetime import timedelta +from textwrap import dedent +from django.conf import settings +from django.utils import timezone from drf_spectacular.utils import extend_schema, extend_schema_view from rest_framework import serializers, status from rest_framework.exceptions import ValidationError @@ -12,9 +15,6 @@ from arkindex.project.mixins import CorpusACLMixin from arkindex.project.permissions import IsVerified from arkindex.users.models import Role -# Delay to generate a new export from a specific user -EXPORT_DELAY_HOURS = 6 - @extend_schema(tags=['exports']) @extend_schema_view( @@ -28,10 +28,15 @@ EXPORT_DELAY_HOURS = 6 post=extend_schema( operation_id='StartExport', request=None, - description=( - 'Start a corpus export job.\n' - f'A user must wait {EXPORT_DELAY_HOURS} hours before being able to generate a new export of the same corpus.\n\n' - 'Contributor access is required.' + description=dedent( + f""" + Start a corpus export job. + + A user must wait for {settings.EXPORT_TTL_SECONDS} seconds after the last successful import + before being able to generate a new export of the same corpus. + + Contributor access is required. + """ ), ) ) @@ -55,10 +60,10 @@ class CorpusExportAPIView(CorpusACLMixin, ListCreateAPIView): available_exports = corpus.exports.filter( state=CorpusExportState.Done, - created__gte=datetime.now(timezone.utc) - timedelta(hours=EXPORT_DELAY_HOURS) + created__gte=timezone.now() - timedelta(seconds=settings.EXPORT_TTL_SECONDS) ) if available_exports.exists(): - raise ValidationError(f'An export has already been made for this corpus in the last {EXPORT_DELAY_HOURS} hours.') + raise ValidationError(f'An export has already been made for this corpus in the last {settings.EXPORT_TTL_SECONDS} seconds.') export = corpus.exports.create(user=self.request.user) export.start() diff --git a/arkindex/documents/tests/test_export.py b/arkindex/documents/tests/test_export.py index 539cea687b..80611182a6 100644 --- a/arkindex/documents/tests/test_export.py +++ b/arkindex/documents/tests/test_export.py @@ -1,6 +1,7 @@ from datetime import datetime, timedelta, timezone from unittest.mock import call, patch +from django.test import override_settings from django.urls import reverse from rest_framework import status @@ -12,6 +13,7 @@ from arkindex.users.models import Role class TestExport(FixtureAPITestCase): @patch('arkindex.project.triggers.export.export_corpus.delay') + @override_settings(EXPORT_TTL_SECONDS=420) def test_start(self, delay_mock): self.client.force_login(self.superuser) response = self.client.post(reverse('api:corpus-export', kwargs={'pk': self.corpus.id})) @@ -81,11 +83,12 @@ class TestExport(FixtureAPITestCase): self.assertEqual(self.corpus.exports.count(), 1) self.assertFalse(delay_mock.called) + @override_settings(EXPORT_TTL_SECONDS=420) @patch('arkindex.project.triggers.export.export_corpus.delay') def test_start_recent_export(self, delay_mock): self.client.force_login(self.superuser) with patch('django.utils.timezone.now') as mock_now: - mock_now.return_value = datetime.now(timezone.utc) - timedelta(hours=2) + mock_now.return_value = datetime.now(timezone.utc) - timedelta(minutes=2) self.corpus.exports.create( user=self.user, state=CorpusExportState.Done, @@ -93,7 +96,7 @@ class TestExport(FixtureAPITestCase): response = self.client.post(reverse('api:corpus-export', kwargs={'pk': self.corpus.id})) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertListEqual(response.json(), ['An export has already been made for this corpus in the last 6 hours.']) + self.assertListEqual(response.json(), ['An export has already been made for this corpus in the last 420 seconds.']) self.assertEqual(self.corpus.exports.count(), 1) self.assertFalse(delay_mock.called) diff --git a/arkindex/project/config.py b/arkindex/project/config.py index b00c0ae3e0..c98d3211eb 100644 --- a/arkindex/project/config.py +++ b/arkindex/project/config.py @@ -114,6 +114,9 @@ def get_settings_parser(base_dir): email_parser.add_option('password', type=str) email_parser.add_option('error_report_recipients', type=str, many=True, default=[]) + export_parser = parser.add_subparser('export', default={}) + export_parser.add_option('ttl', type=int, default=21600) + static_parser = parser.add_subparser('static', default={}) static_parser.add_option('root_path', type=dir_path, default=None) static_parser.add_option('cdn_assets_url', type=str, default=None) diff --git a/arkindex/project/settings.py b/arkindex/project/settings.py index bae672ddc6..695ee75296 100644 --- a/arkindex/project/settings.py +++ b/arkindex/project/settings.py @@ -373,6 +373,9 @@ RQ = { # How many keys to delete at once inside a sorted set in Redis using a single ZREM command REDIS_ZREM_CHUNK_SIZE = 10000 +# How long before a corpus export can be run again after a successful one +EXPORT_TTL_SECONDS = conf['export']['ttl'] + LOGGING = { 'version': 1, 'filters': { diff --git a/arkindex/project/tests/config_samples/defaults.yaml b/arkindex/project/tests/config_samples/defaults.yaml index 2abdc4ffd7..1f2531b69c 100644 --- a/arkindex/project/tests/config_samples/defaults.yaml +++ b/arkindex/project/tests/config_samples/defaults.yaml @@ -33,6 +33,8 @@ doorbell: appkey: null id: null email: null +export: + ttl: 21600 features: search: false selection: true diff --git a/arkindex/project/tests/config_samples/errors.yaml b/arkindex/project/tests/config_samples/errors.yaml index e9228fc431..bee83a7c02 100644 --- a/arkindex/project/tests/config_samples/errors.yaml +++ b/arkindex/project/tests/config_samples/errors.yaml @@ -22,6 +22,8 @@ docker: here: have a dict email: host: 123 +export: + ttl: forever features: sv_cheats: 1 gitlab: diff --git a/arkindex/project/tests/config_samples/expected_errors.yaml b/arkindex/project/tests/config_samples/expected_errors.yaml index 648227fc7f..892ad86043 100644 --- a/arkindex/project/tests/config_samples/expected_errors.yaml +++ b/arkindex/project/tests/config_samples/expected_errors.yaml @@ -13,6 +13,8 @@ email: password: This option is required port: This option is required user: This option is required +export: + ttl: "invalid literal for int() with base 10: 'forever'" features: sv_cheats: This option does not exist ingest: diff --git a/arkindex/project/tests/config_samples/override.yaml b/arkindex/project/tests/config_samples/override.yaml index 3993e1e1f5..11d8487bd7 100644 --- a/arkindex/project/tests/config_samples/override.yaml +++ b/arkindex/project/tests/config_samples/override.yaml @@ -45,6 +45,8 @@ email: password: hunter2 port: 25 user: teklia@wanadoo.fr +export: + ttl: 123456 features: search: true selection: false -- GitLab