Skip to content

Commit 5af9666

Browse files
committed
[IMP] queue_job: prevent invalid change of job status
1 parent ceb7edf commit 5af9666

File tree

5 files changed

+76
-10
lines changed

5 files changed

+76
-10
lines changed

queue_job/models/queue_job.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from ..job import (
1818
CANCELLED,
1919
DONE,
20+
ENQUEUED,
2021
FAILED,
2122
PENDING,
2223
STARTED,
@@ -324,19 +325,24 @@ def _change_job_state(self, state, result=None):
324325
else:
325326
raise ValueError(f"State not supported: {state}")
326327

327-
def button_done(self):
328+
def button_done(self, __state_in=(WAIT_DEPENDENCIES, PENDING, ENQUEUED, FAILED)):
329+
# If job was set to STARTED or CANCELLED, do not set it to DONE
328330
result = _("Manually set to done by {}").format(self.env.user.name)
329-
self._change_job_state(DONE, result=result)
331+
records = self.filtered(lambda job_: job_.state in __state_in)
332+
records._change_job_state(DONE, result=result)
330333
return True
331334

332-
def button_cancelled(self):
335+
def button_cancelled(self, __state_in=(PENDING, ENQUEUED, FAILED)):
336+
# If job was set to DONE or WAIT_DEPENDENCIES, do not cancel it
333337
result = _("Cancelled by {}").format(self.env.user.name)
334-
self._change_job_state(CANCELLED, result=result)
338+
records = self.filtered(lambda job_: job_.state in __state_in)
339+
records._change_job_state(CANCELLED, result=result)
335340
return True
336341

337-
def requeue(self):
338-
jobs_to_requeue = self.filtered(lambda job_: job_.state != WAIT_DEPENDENCIES)
339-
jobs_to_requeue._change_job_state(PENDING)
342+
def requeue(self, __state_in=(FAILED, DONE, CANCELLED)):
343+
# If job is already in queue or started, do not requeue it
344+
records = self.filtered(lambda job_: job_.state in __state_in)
345+
records._change_job_state(PENDING)
340346
return True
341347

342348
def _message_post_on_failure(self):

queue_job/tests/test_wizards.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,60 @@ def test_03_done(self):
4646
wizard = self._wizard("queue.jobs.to.done")
4747
wizard.set_done()
4848
self.assertEqual(self.job.state, "done")
49+
50+
def test_04_requeue_forbidden(self):
51+
wizard = self._wizard("queue.requeue.job")
52+
53+
# State WAIT_DEPENDENCIES is not requeued
54+
self.job.state = "wait_dependencies"
55+
wizard.requeue()
56+
self.assertEqual(self.job.state, "wait_dependencies")
57+
58+
# State PENDING, ENQUEUED or STARTED are ignored too
59+
for test_state in ("pending", "enqueued", "started"):
60+
self.job.state = test_state
61+
wizard.requeue()
62+
self.assertEqual(self.job.state, test_state)
63+
64+
# States CANCELLED, DONE or FAILED will change status
65+
self.job.state = "cancelled"
66+
wizard.requeue()
67+
self.assertEqual(self.job.state, "pending")
68+
69+
def test_05_cancel_forbidden(self):
70+
wizard = self._wizard("queue.jobs.to.cancelled")
71+
72+
# State WAIT_DEPENDENCIES is not cancelled
73+
self.job.state = "wait_dependencies"
74+
wizard.set_cancelled()
75+
self.assertEqual(self.job.state, "wait_dependencies")
76+
77+
# State DONE is not cancelled
78+
self.job.state = "done"
79+
wizard.set_cancelled()
80+
self.assertEqual(self.job.state, "done")
81+
82+
# State PENDING, ENQUEUED or FAILED will be cancelled
83+
for test_state in ("pending", "enqueued"):
84+
self.job.state = test_state
85+
wizard.set_cancelled()
86+
self.assertEqual(self.job.state, "cancelled")
87+
88+
def test_06_done_forbidden(self):
89+
wizard = self._wizard("queue.jobs.to.done")
90+
91+
# State STARTED is not set DONE manually
92+
self.job.state = "started"
93+
wizard.set_done()
94+
self.assertEqual(self.job.state, "started")
95+
96+
# State CANCELLED is not cancelled
97+
self.job.state = "cancelled"
98+
wizard.set_done()
99+
self.assertEqual(self.job.state, "cancelled")
100+
101+
# State WAIT_DEPENDENCIES, PENDING, ENQUEUED or FAILED will be set to DONE
102+
for test_state in ("wait_dependencies", "pending", "enqueued"):
103+
self.job.state = test_state
104+
wizard.set_done()
105+
self.assertEqual(self.job.state, "done")

queue_job/wizards/queue_jobs_to_cancelled.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ class SetJobsToCancelled(models.TransientModel):
1010
_description = "Cancel all selected jobs"
1111

1212
def set_cancelled(self):
13-
jobs = self.job_ids.filtered(
14-
lambda x: x.state in ("pending", "failed", "enqueued")
15-
)
13+
# Only jobs with state PENDING, FAILED, ENQUEUED
14+
# will change to CANCELLED
15+
jobs = self.job_ids
1616
jobs.button_cancelled()
1717
return {"type": "ir.actions.act_window_close"}

queue_job/wizards/queue_jobs_to_done.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ class SetJobsToDone(models.TransientModel):
1010
_description = "Set all selected jobs to done"
1111

1212
def set_done(self):
13+
# Only jobs with state WAIT_DEPENDENCIES, PENDING, ENQUEUED or FAILED
14+
# will change to DONE
1315
jobs = self.job_ids
1416
jobs.button_done()
1517
return {"type": "ir.actions.act_window_close"}

queue_job/wizards/queue_requeue_job.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def _default_job_ids(self):
2020
)
2121

2222
def requeue(self):
23+
# Only jobs with state FAILED, DONE or CANCELLED will change to PENDING
2324
jobs = self.job_ids
2425
jobs.requeue()
2526
return {"type": "ir.actions.act_window_close"}

0 commit comments

Comments
 (0)