Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions queue_job/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ def convert_to_record(self, value, record):
class JobEncoder(json.JSONEncoder):
"""Encode Odoo recordsets so that we can later recompose them"""

def _get_record_context(self, obj):
return obj._job_prepare_context_before_enqueue()

def default(self, obj):
if isinstance(obj, models.BaseModel):
return {
Expand All @@ -77,6 +80,7 @@ def default(self, obj):
"ids": obj.ids,
"uid": obj.env.uid,
"su": obj.env.su,
"context": self._get_record_context(obj),
}
elif isinstance(obj, datetime):
return {"_type": "datetime_isoformat", "value": obj.isoformat()}
Expand Down
19 changes: 19 additions & 0 deletions queue_job/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,22 @@ def _job_store_values(self, job):
:return: dictionary for setting job values.
"""
return {}

@api.model
def _job_prepare_context_before_enqueue_keys(self):
"""Keys to keep in context of stored jobs
Empty by default for backward compatibility.
"""
# TODO: when migrating to 16.0, active the base context keys:
# return ("tz", "lang", "allowed_company_ids", "force_company", "active_test")
return ()

def _job_prepare_context_before_enqueue(self):
"""Return the context to store in the jobs
Can be used to keep only safe keys.
"""
return {
key: value
for key, value in self.env.context.items()
if key in self._job_prepare_context_before_enqueue_keys()
}
24 changes: 24 additions & 0 deletions queue_job/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,30 @@ Based on this configuration, we can tell that:
* retries 10 to 15 postponed 30 seconds later
* all subsequent retries postponed 5 minutes later

**Job Context**

The context of the recordset of the job, or any recordset passed in arguments of
a job, is transferred to the job according to an allow-list.

The default allow-list is empty for backward compatibility. The allow-list can
be customized in ``Base._job_prepare_context_before_enqueue_keys``.

Example:

.. code-block:: python

class Base(models.AbstractModel):

_inherit = "base"

@api.model
def _job_prepare_context_before_enqueue_keys(self):
"""Keys to keep in context of stored jobs

Empty by default for backward compatibility.
"""
return ("tz", "lang", "allowed_company_ids", "force_company", "active_test")

**Bypass jobs on running Odoo**

When you are developing (ie: connector modules) you might want
Expand Down
20 changes: 16 additions & 4 deletions queue_job/tests/test_json_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
class TestJson(common.TransactionCase):
def test_encoder_recordset(self):
demo_user = self.env.ref("base.user_demo")
partner = self.env(user=demo_user).ref("base.main_partner")
context = demo_user.context_get()
partner = self.env(user=demo_user, context=context).ref("base.main_partner")
value = partner
value_json = json.dumps(value, cls=JobEncoder)
expected = {
Expand All @@ -25,12 +26,15 @@ def test_encoder_recordset(self):
"model": "res.partner",
"ids": [partner.id],
"su": False,
# no allowed context by default, must be changed in 16.0
"context": {},
}
self.assertEqual(json.loads(value_json), expected)

def test_encoder_recordset_list(self):
demo_user = self.env.ref("base.user_demo")
partner = self.env(user=demo_user).ref("base.main_partner")
context = demo_user.context_get()
partner = self.env(user=demo_user, context=context).ref("base.main_partner")
value = ["a", 1, partner]
value_json = json.dumps(value, cls=JobEncoder)
expected = [
Expand All @@ -42,18 +46,23 @@ def test_encoder_recordset_list(self):
"model": "res.partner",
"ids": [partner.id],
"su": False,
# no allowed context by default, must be changed in 16.0
"context": {},
},
]
self.assertEqual(json.loads(value_json), expected)

def test_decoder_recordset(self):
demo_user = self.env.ref("base.user_demo")
context = demo_user.context_get()
partner = self.env(user=demo_user).ref("base.main_partner")
value_json = (
'{"_type": "odoo_recordset",'
'"model": "res.partner",'
'"su": false,'
'"ids": [%s],"uid": %s}' % (partner.id, demo_user.id)
'"ids": [%s],"uid": %s, '
'"context": {"tz": "%s", "lang": "%s"}}'
% (partner.id, demo_user.id, context["tz"], context["lang"])
)
expected = partner
value = json.loads(value_json, cls=JobDecoder, env=self.env)
Expand All @@ -62,13 +71,16 @@ def test_decoder_recordset(self):

def test_decoder_recordset_list(self):
demo_user = self.env.ref("base.user_demo")
context = demo_user.context_get()
partner = self.env(user=demo_user).ref("base.main_partner")
value_json = (
'["a", 1, '
'{"_type": "odoo_recordset",'
'"model": "res.partner",'
'"su": false,'
'"ids": [%s],"uid": %s}]' % (partner.id, demo_user.id)
'"ids": [%s],"uid": %s, '
'"context": {"tz": "%s", "lang": "%s"}}]'
% (partner.id, demo_user.id, context["tz"], context["lang"])
)
expected = ["a", 1, partner]
value = json.loads(value_json, cls=JobDecoder, env=self.env)
Expand Down
7 changes: 6 additions & 1 deletion test_queue_job/models/test_models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2016 Camptocamp SA
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

from odoo import fields, models
from odoo import api, fields, models

from odoo.addons.queue_job.delay import chain
from odoo.addons.queue_job.exception import RetryableJobError
Expand Down Expand Up @@ -37,6 +37,11 @@ class ModelTestQueueJob(models.Model):

name = fields.Char()

# to test the context is serialized/deserialized properly
@api.model
def _job_prepare_context_before_enqueue_keys(self):
return ("tz", "lang")

def testing_method(self, *args, **kwargs):
"""Method used for tests

Expand Down
32 changes: 32 additions & 0 deletions test_queue_job/tests/test_json_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# copyright 2022 Guewen Baconnier
# license lgpl-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

import json

from odoo.tests import common

# pylint: disable=odoo-addons-relative-import
# we are testing, we want to test as if we were an external consumer of the API
from odoo.addons.queue_job.fields import JobEncoder


class TestJsonField(common.TransactionCase):

# TODO: when migrating to 16.0, adapt the checks in queue_job/tests/test_json_field.py
# to verify the context keys are encoded and remove these
def test_encoder_recordset_store_context(self):
demo_user = self.env.ref("base.user_demo")
user_context = {"lang": "en_US", "tz": "Europe/Brussels"}
test_model = self.env(user=demo_user, context=user_context)["test.queue.job"]
value_json = json.dumps(test_model, cls=JobEncoder)
self.assertEqual(json.loads(value_json)["context"], user_context)

def test_encoder_recordset_context_filter_keys(self):
demo_user = self.env.ref("base.user_demo")
user_context = {"lang": "en_US", "tz": "Europe/Brussels"}
tampered_context = dict(user_context, foo=object())
test_model = self.env(user=demo_user, context=tampered_context)[
"test.queue.job"
]
value_json = json.dumps(test_model, cls=JobEncoder)
self.assertEqual(json.loads(value_json)["context"], user_context)