From 37bd04a7716e8a4dba5764c7c8e0465abfc568ff Mon Sep 17 00:00:00 2001 From: Iasmini Gomes Date: Thu, 23 May 2024 09:04:53 -0300 Subject: [PATCH 1/3] feat: use env var to limit eta task --- django_cloud_tasks/apps.py | 1 + django_cloud_tasks/tasks/task.py | 30 ++++++++++++------- .../tests/tests_tasks/tests_tasks.py | 10 +++++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/django_cloud_tasks/apps.py b/django_cloud_tasks/apps.py index 9736240..05331fe 100644 --- a/django_cloud_tasks/apps.py +++ b/django_cloud_tasks/apps.py @@ -30,6 +30,7 @@ def __init__(self, *args, **kwargs): self.delimiter = self._fetch_config(name="DELIMITER", default="--") self.eager = self._fetch_config(name="EAGER", default=False) self.tasks_url_name = self._fetch_config(name="URL_NAME", default="tasks-endpoint") + self.tasks_max_eta = self._fetch_config(name="MAXIMUM_ETA_TASK", default=60 * 60 * 24) # 1 day self.subscribers_url_name = self._fetch_config(name="SUBSCRIBERS_URL_NAME", default="subscriptions-endpoint") self.subscribers_max_retries = self._fetch_config(name="SUBSCRIBER_MAX_RETRIES", default=None) diff --git a/django_cloud_tasks/tasks/task.py b/django_cloud_tasks/tasks/task.py index d188fd6..c6d97e3 100644 --- a/django_cloud_tasks/tasks/task.py +++ b/django_cloud_tasks/tasks/task.py @@ -218,23 +218,33 @@ def asap(cls, **kwargs): @classmethod def later(cls, task_kwargs: dict, eta: int | timedelta | datetime, queue: str = None, headers: dict | None = None): + delay_in_seconds = cls._calculate_delay_in_seconds(eta) + cls._validate_delay(delay_in_seconds) + return cls.push( + task_kwargs=task_kwargs, + queue=queue, + headers=headers, + delay_in_seconds=delay_in_seconds, + ) + + @staticmethod + def _calculate_delay_in_seconds(eta: int | timedelta | datetime) -> int: if isinstance(eta, int): - delay_in_seconds = eta + return eta elif isinstance(eta, timedelta): - delay_in_seconds = eta.total_seconds() + return int(eta.total_seconds()) elif isinstance(eta, datetime): - delay_in_seconds = (eta - now()).total_seconds() + return int((eta - now()).total_seconds()) else: raise ValueError( - f"Unsupported schedule {eta} of type {eta.__class__.__name__}. " "Must be int, timedelta or datetime." + f"Unsupported schedule {eta} of type {eta.__class__.__name__}. Must be int, timedelta or datetime." ) - return cls.push( - task_kwargs=task_kwargs, - queue=queue, - headers=headers, - delay_in_seconds=delay_in_seconds, - ) + @staticmethod + def _validate_delay(delay_in_seconds: int): + max_eta_task = get_config("tasks_max_eta") + if max_eta_task is not None and delay_in_seconds > max_eta_task: + raise ValueError(f"Invalid delay time {delay_in_seconds}, maximum is {max_eta_task}") @classmethod def until(cls, task_kwargs: dict, max_eta: datetime, queue: str = None, headers: dict | None = None): diff --git a/sample_project/sample_app/tests/tests_tasks/tests_tasks.py b/sample_project/sample_app/tests/tests_tasks/tests_tasks.py index 109e414..ee6a664 100644 --- a/sample_project/sample_app/tests/tests_tasks/tests_tasks.py +++ b/sample_project/sample_app/tests/tests_tasks/tests_tasks.py @@ -224,6 +224,16 @@ def test_task_later_error(self): push.assert_not_called() + def test_task_later_delay_exceeds_maximum_eta(self): + task_kwargs = dict(price=30, quantity=4, discount=0.2) + excessive_delay = int(60 * 60 * 24 * 2) # 2 days + + with self.assertRaises(ValueError) as context: + tasks.CalculatePriceTask.later(eta=excessive_delay, task_kwargs=task_kwargs) + + max_eta_task = get_config("tasks_max_eta") + self.assertEqual(f"Invalid delay time {excessive_delay}, maximum is {max_eta_task}", str(context.exception)) + def test_singleton_client_on_task(self): # we have a singleton if it calls the same task twice with ( From 28984ade5e2da789e133159ee6d9cef666490079 Mon Sep 17 00:00:00 2001 From: Iasmini Gomes Date: Thu, 23 May 2024 09:05:07 -0300 Subject: [PATCH 2/3] build: bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d5063da..5a9cd8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "django-google-cloud-tasks" -version = "2.15.0" +version = "2.16.0" description = "Async Tasks with HTTP endpoints" authors = ["Joao Daher "] packages = [ From 4eff84862beeaddee3a15292cbab468bcfe8c02c Mon Sep 17 00:00:00 2001 From: Iasmini Gomes Date: Thu, 23 May 2024 15:04:24 -0300 Subject: [PATCH 3/3] docs: updated readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b2dbdbb..be2a878 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Additionally, you can configure with more settings or environment variables: - `DJANGO_CLOUD_TASKS_SUBSCRIBERS_URL_NAME`: Django URL-name that process Subscribers. We provide a view for that, but if you want to create your own, set this value. Default: `subscriptions-endpoint`. - `DJANGO_CLOUD_TASKS_PROPAGATED_HEADERS`: . Default: `["traceparent"]`. - `DJANGO_CLOUD_TASKS_PROPAGATED_HEADERS_KEY`: when propagating headers in PubSub, use a key to store the values in the Message data. Default: `_http_headers`. +- `DJANGO_CLOUD_TASKS_MAXIMUM_ETA_TASK`: maximum time in seconds to schedule a task in the future. Default: `86400`. ## On Demand Task