Tower: upload cetmix_tower_webhook 16.0.1.0.5 (via marketplace)

This commit is contained in:
2026-04-27 08:44:58 +00:00
parent 1a43c797c3
commit 8eb03de70b

View File

@@ -0,0 +1,201 @@
# Copyright (C) 2025 Cetmix OÜ
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, fields, models
from odoo.exceptions import ValidationError
from odoo.tools.safe_eval import safe_eval
class CxTowerWebhookEvalMixin(models.AbstractModel):
_name = "cx.tower.webhook.eval.mixin"
_inherit = [
"cx.tower.template.mixin",
"cx.tower.key.mixin",
"cx.tower.yaml.mixin",
"cx.tower.reference.mixin",
]
_description = "Eval context/code helper for Cetmix Tower Webhook"
code_help = fields.Html(
compute="_compute_code_help",
default=lambda self: self._default_eval_code_help(),
compute_sudo=True,
)
code = fields.Text(
default=lambda self: self._default_eval_code(),
required=True,
)
@classmethod
def _get_depends_fields(cls):
"""Add code to the depends fields."""
return ["code"]
def _compute_code_help(self):
"""
Compute code help
"""
self.code_help = self._default_eval_code_help()
def _default_eval_code_help(self):
"""
Return the default code help text for webhook or authenticator.
We use default because the computation method for this field
would not be triggered before this record is saved. And we need
to show the value instantly.
Returns:
str: HTML-formatted help string containing available objects and libraries.
"""
available_libraries = self._get_python_eval_odoo_objects()
available_libraries.update(self._get_python_eval_libraries())
help_text_fragments = []
for key, value in available_libraries.items():
if key == "server":
# Server is not available in the webhook/authenticator eval context
continue
help_text_fragments.append(f"<li><code>{key}</code>: {value['help']}</li>")
help_text = "<ul>" + "".join(help_text_fragments) + "</ul>"
return f"{self._get_default_python_eval_code_help()}{help_text}"
def _get_python_eval_odoo_objects(self, **kwargs):
"""
Return Odoo objects available in the eval context.
Args:
**kwargs: Optional context values.
Returns:
dict: Mapping of object names to their import values and help.
"""
return self.env["cx.tower.command"]._get_python_command_odoo_objects()
def _get_python_eval_libraries(self):
"""
Return Python libraries available in the eval context.
Returns:
dict: Mapping of library names to their import values and help.
"""
return self.env["cx.tower.command"]._get_python_command_libraries()
def _get_default_python_eval_code_help(self):
"""
Return the default help text for eval code.
Returns:
str: Help text.
"""
return ""
def _default_eval_code(self):
"""
Return the default code for webhook or authenticator.
Returns:
str: Default Python code.
"""
return ""
def _prepare_webhook_eval_context(self, context_extra=None, default_result=None):
"""
Build the evaluation context for webhook or authenticator
safe_eval.
Args:
context_extra (dict): Additional context variables
(payload, headers, etc).
default_result (dict): Default value for the 'result' variable.
Returns:
dict: Prepared eval context.
"""
context_extra = context_extra or {}
# Get the Odoo objects first
imports = self._get_python_eval_odoo_objects(**context_extra)
# Update with the libraries
imports.update(self._get_python_eval_libraries())
eval_context = {key: value["import"] for key, value in imports.items()}
# Remove server from eval context
eval_context.pop("server", None)
# Set default result
default_result = default_result or {}
eval_context["result"] = default_result.copy()
return eval_context
def _run_webhook_eval_code(self, code, **kwargs):
"""
Helper to execute user code safely. Returns the 'result' variable from context.
Args:
code (str): User code to run
kwargs:
key (dict): Extra keys for secret parser
context_extra (dict): Extra context variables (payload, headers, etc)
default_result (dict): Default value for the 'result' variable
Returns:
dict: The 'result' variable from context
"""
eval_context = self._prepare_webhook_eval_context(**kwargs)
if not code:
# if code is empty, return the default result
return eval_context["result"]
# prepare the code for evaluation
code_and_secrets = self.env["cx.tower.key"]._parse_code_and_return_key_values(
code, pythonic_mode=True, **kwargs.get("key", {})
)
secrets = code_and_secrets.get("key_values")
webhook_code = code_and_secrets["code"]
code = self.env["cx.tower.key"]._parse_code(
webhook_code, pythonic_mode=True, **kwargs.get("key", {})
)
# execute user code
safe_eval(
code,
eval_context,
mode="exec",
nocopy=True,
)
result = eval_context["result"]
return self._parse_eval_code_result(result, secrets=secrets, **kwargs)
def _parse_eval_code_result(self, result, secrets=None, **kwargs):
"""
Post-processes the result returned from webhook/authenticator eval code.
If 'secrets' are provided, all occurrences of secret values in the
'message' field of result will be replaced with a spoiler string to
prevent sensitive information leakage.
Args:
result (dict): The dict returned from the executed eval code,
expected to have at least a 'message' key.
secrets (dict, optional): A mapping of secret key-value
pairs used for replacement in 'message'.
Returns:
dict: The processed result with secrets masked in the 'message'
field, if applicable.
"""
if not isinstance(result, dict):
raise ValidationError(
_("Webhook/Authenticator code error: result is not a dict")
)
if secrets and isinstance(result.get("message"), str):
result["message"] = self.env["cx.tower.key"]._replace_with_spoiler(
result["message"], secrets
)
return result