Initial commit: OCA Technical packages (595 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:03 +02:00
commit 2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions

View file

@ -0,0 +1 @@
from . import service

View file

@ -0,0 +1,225 @@
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
# @author Guewen Baconnier <guewen.baconnier@camptocamp.com>
# @author Simone Orsi <simahawk@gmail.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import json
import logging
import traceback
from psycopg2.errors import OperationalError
from werkzeug.urls import url_encode, url_join
from odoo import exceptions, registry
from odoo.http import Response, request
from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY
from odoo.addons.base_rest.http import JSONEncoder
from odoo.addons.component.core import AbstractComponent
from ..exceptions import (
RESTServiceDispatchException,
RESTServiceMissingErrorException,
RESTServiceUserErrorException,
RESTServiceValidationErrorException,
)
_logger = logging.getLogger(__name__)
def json_dump(data):
"""Encode data to JSON as we like."""
return json.dumps(data, cls=JSONEncoder, indent=4, sort_keys=True, default=str)
class BaseRESTService(AbstractComponent):
_inherit = "base.rest.service"
# can be overridden to enable logging of requests to DB
_log_calls_in_db = False
def dispatch(self, method_name, *args, params=None):
if not self._db_logging_active(method_name):
return super().dispatch(method_name, *args, params=params)
return self._dispatch_with_db_logging(method_name, *args, params=params)
def _dispatch_with_db_logging(self, method_name, *args, params=None):
try:
with self.env.cr.savepoint():
result = super().dispatch(method_name, *args, params=params)
except exceptions.MissingError as orig_exception:
self._dispatch_exception(
method_name,
RESTServiceMissingErrorException,
orig_exception,
*args,
params=params,
)
except exceptions.ValidationError as orig_exception:
self._dispatch_exception(
method_name,
RESTServiceValidationErrorException,
orig_exception,
*args,
params=params,
)
except exceptions.UserError as orig_exception:
self._dispatch_exception(
method_name,
RESTServiceUserErrorException,
orig_exception,
*args,
params=params,
)
except Exception as orig_exception:
self._dispatch_exception(
method_name,
RESTServiceDispatchException,
orig_exception,
*args,
params=params,
)
self._log_dispatch_success(method_name, result, *args, params)
return result
def _log_dispatch_success(self, method_name, result, *args, params=None):
try:
with self.env.cr.savepoint():
log_entry = self._log_call_in_db(
self.env, request, method_name, *args, params, result=result
)
if log_entry and not isinstance(result, Response):
log_entry_url = self._get_log_entry_url(log_entry)
result["log_entry_url"] = log_entry_url
except Exception as e:
_logger.exception("Rest Log Error Creation: %s", e)
def _dispatch_exception(
self, method_name, exception_klass, orig_exception, *args, params=None
):
exc_msg, log_entry_url = None, None # in case it fails below
try:
exc_msg = self._get_exception_message(orig_exception)
tb = traceback.format_exc()
with registry(self.env.cr.dbname).cursor() as cr:
log_entry = self._log_call_in_db(
self.env(cr=cr),
request,
method_name,
*args,
params=params,
traceback=tb,
orig_exception=orig_exception,
)
log_entry_url = self._get_log_entry_url(log_entry)
except Exception as e:
_logger.exception("Rest Log Error Creation: %s", e)
# let the OperationalError bubble up to the retrying mechanism
# We can't wrap the OperationalError because we want to let it
# bubble up to the retrying mechanism, it will be handled by
# the default handler at the end of the chain.
if (
isinstance(orig_exception, OperationalError)
and orig_exception.pgcode in PG_CONCURRENCY_ERRORS_TO_RETRY
):
raise orig_exception
raise exception_klass(exc_msg, log_entry_url) from orig_exception
def _get_exception_message(self, exception):
return exception.args and exception.args[0] or str(exception)
def _get_log_entry_url(self, entry):
base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url")
url_params = {
"action": self.env.ref("rest_log.action_rest_log").id,
"view_type": "form",
"model": entry._name,
"id": entry.id,
}
url = "/web?#%s" % url_encode(url_params)
return url_join(base_url, url)
@property
def _log_call_header_strip(self):
return ("Cookie", "Api-Key")
def _log_call_in_db_values(self, _request, *args, params=None, **kw):
httprequest = _request.httprequest
headers = self._log_call_sanitize_headers(dict(httprequest.headers or []))
params = dict(params or {})
if args:
params.update(args=args)
params = self._log_call_sanitize_params(params)
error, exception_name, exception_message = self._log_call_prepare_error(**kw)
result, state = self._log_call_prepare_result(kw.get("result"))
collection = self.work.collection
return {
"collection": collection._name,
"collection_id": collection.id,
"request_url": httprequest.url,
"request_method": httprequest.method,
"params": params,
"headers": headers,
"result": result,
"error": error,
"exception_name": exception_name,
"exception_message": exception_message,
"state": state,
}
def _log_call_prepare_result(self, result):
# NB: ``result`` might be an object of class ``odoo.http.Response``,
# for example when you try to download a file. In this case, we need to
# handle it properly, without the assumption that ``result`` is a dict.
if isinstance(result, Response):
status_code = result.status_code
result = {
"status": status_code,
"headers": self._log_call_sanitize_headers(dict(result.headers or [])),
}
state = "success" if status_code in range(200, 300) else "failed"
else:
state = "success" if result else "failed"
return result, state
def _log_call_prepare_error(self, traceback=None, orig_exception=None, **kw):
exception_name = None
exception_message = None
if orig_exception:
exception_name = orig_exception.__class__.__name__
if hasattr(orig_exception, "__module__"):
exception_name = orig_exception.__module__ + "." + exception_name
exception_message = self._get_exception_message(orig_exception)
return traceback, exception_name, exception_message
_log_call_in_db_keys_to_serialize = ("params", "headers", "result")
def _log_call_in_db(self, env, _request, method_name, *args, params=None, **kw):
values = self._log_call_in_db_values(_request, *args, params=params, **kw)
for k in self._log_call_in_db_keys_to_serialize:
values[k] = json_dump(values[k])
enabled_states = self._get_matching_active_conf(method_name)
if not values or enabled_states and values["state"] not in enabled_states:
return
return env["rest.log"].sudo().create(values)
def _log_call_sanitize_params(self, params: dict) -> dict:
if "password" in params:
params["password"] = "<redacted>"
return params
def _log_call_sanitize_headers(self, headers: dict) -> dict:
for header_key in self._log_call_header_strip:
if header_key in headers:
headers[header_key] = "<redacted>"
return headers
def _db_logging_active(self, method_name):
enabled = self._log_calls_in_db
if not enabled:
enabled = bool(self._get_matching_active_conf(method_name))
return request and enabled and self.env["rest.log"].logging_active()
def _get_matching_active_conf(self, method_name):
return self.env["rest.log"]._get_matching_active_conf(
self._collection, self._usage, method_name
)