Skip to content
Open
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
33 changes: 19 additions & 14 deletions usability_webhooks/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,41 @@ def _call_function_api(self, model, vals, function):
return getattr(request.env["webhook.utils"], function)(model, vals)

def _create_api_logs(self, model, vals, function):
# Add logs
data_dict = {
"data": json.dumps(vals),
"model": model,
"route": f"/api/{function}",
"function_name": function,
}

ICP = request.env["ir.config_parameter"]
rollback_state_failed = ICP.sudo().get_param("webhook.rollback_state_failed")
rollback_except = ICP.sudo().get_param("webhook.rollback_except")

data_str = json.dumps(vals)
state = "draft"
res = {}

try:
res = self._call_function_api(model, vals, function)
state = "done" if res["is_success"] else "failed"
data_dict.update({"result": res, "state": state})
state = "done" if res.get("is_success") else "failed"
# Not success, rollback all data (if config in system parameter)
if not res["is_success"] and rollback_state_failed:
if not res.get("is_success") and rollback_state_failed:
request.env.cr.rollback()
except Exception:
res = {
"is_success": False,
"messages": traceback.format_exc(),
}
data_dict.update({"result": res, "state": "failed"})
state = "failed"
# Error from odoo exception,
# rollback all data (if config in system parameter)
if rollback_except:
request.env.cr.rollback()
if vals["is_create_log"]:
request.env["api.log"].create(data_dict)

if vals.get("is_create_log"):
log = request.env["api.log"].create(
{
"model": model,
"route": f"/api/{function}",
"function_name": function,
"state": state,
}
)
log._save_payload(data_str, json.dumps(res, ensure_ascii=False))
return res

def _set_create_logs(self, param, vals):
Expand Down
58 changes: 40 additions & 18 deletions usability_webhooks/models/api_log.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2023 Ecosoft Co., Ltd. (https://ecosoft.co.th)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

import base64
import json
import logging
from datetime import datetime, timedelta
Expand All @@ -17,18 +18,21 @@ class APILog(models.Model):
_rec_name = "id"
_order = "id desc"

data = fields.Text(tracking=True)
model = fields.Char(tracking=True)
route = fields.Char(tracking=True)
function_name = fields.Char(tracking=True)
result = fields.Text(tracking=True)
model = fields.Char()
route = fields.Char()
function_name = fields.Char()
log_type = fields.Selection(
selection=[
("send", "Send"),
("receive", "Receive"),
],
default="receive",
)
data_preview = fields.Text(string="Request Preview")
data_size = fields.Integer(string="Request Size")
result_preview = fields.Text(string="Response Preview")
result_size = fields.Integer(string="Response Size")
attachment_id = fields.Many2one(comodel_name="ir.attachment", string="Full Payload")
state = fields.Selection(
selection=[
("draft", "Draft"),
Expand All @@ -39,19 +43,28 @@ class APILog(models.Model):
tracking=True,
)

def action_call_api(self):
try:
func = getattr(self.env["webhook.utils"], self.function_name)
res = func(self.model, json.loads(self.data))
state = "done" if res["is_success"] else "failed"
self.write({"result": res, "state": state})
except Exception as e:
res = {
"is_success": False,
"messages": e,
}
self.write({"result": res, "state": "failed"})
return True
def _save_payload(self, data, result):
data = data or ""
result = result or ""

self.data_size = len(data)
self.data_preview = (data[:2000] + " ...") if len(data) > 2000 else data

self.result_size = len(result)
self.result_preview = (result[:2000] + " ...") if len(result) > 2000 else result

if len(data) > 2000 or len(result) > 2000:
payload = json.dumps({"data": data, "result": result}, indent=2)
attachment = self.env["ir.attachment"].create(
{
"name": f"api_log_{self.id}.json",
"datas": base64.b64encode(payload.encode("utf-8")),
"res_model": self._name,
"res_id": self.id,
"mimetype": "application/json",
}
)
self.attachment_id = attachment

@api.model
def autovacuum(self, days, chunk_size=None):
Expand Down Expand Up @@ -89,3 +102,12 @@ def autovacuum(self, days, chunk_size=None):

_logger.info("AUTOVACUUM - %s 'api.log' records deleted", nb_records)
return True

def action_view_full_log(self):
return {
"name": self.env._("Attachment"),
"type": "ir.actions.act_window",
"res_model": "ir.attachment",
"view_mode": "form",
"res_id": self.attachment_id.id,
}
16 changes: 1 addition & 15 deletions usability_webhooks/tests/test_webhook_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,21 +120,7 @@ def test_05_create_with_attachment(self):
self.assertTrue(attachment)
self.assertEqual(attachment.name, "test.txt")

def test_06_call_function(self):
"""Test calling model function via webhook"""
vals = {
"search_key": {
"id": self.test_log.id,
},
"payload": {
"method": "action_call_api",
"parameter": {},
},
}
result = self.webhook_utils.call_function(self.api_log_model, vals)
self.assertTrue(result["is_success"])

def test_07_invalid_search_key(self):
def test_06_invalid_search_key(self):
"""Test error handling for invalid search key"""
vals = {
"payload": {
Expand Down
26 changes: 15 additions & 11 deletions usability_webhooks/views/api_log.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<list>
<field name="create_date" optional="show" />
<field name="model" optional="show" />
<field name="data" optional="show" />
<field name="result" optional="show" />
<field name="data_preview" optional="show" />
<field name="result_preview" optional="show" />
<field name="log_type" optional="show" />
<field
name="state"
Expand All @@ -26,15 +26,19 @@
<field name="arch" type="xml">
<form>
<header>
<button
name="action_call_api"
string="Update API"
type="object"
class="oe_highlight"
/>
<field name="state" widget="statusbar" />
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button
name="action_view_full_log"
type="object"
icon="oi-search"
class="oe_stat_button"
string="Full Log"
invisible="not attachment_id"
/>
</div>
<group>
<group name="main_left">
<field name="model" />
Expand All @@ -48,12 +52,12 @@
</group>
<group string="Input">
<div colspan="2">
<field name="data" />
<field name="data_preview" />
</div>
</group>
<group string="Output">
<div colspan="2">
<field name="result" readonly="1" />
<field name="result_preview" />
</div>
</group>
</sheet>
Expand All @@ -66,7 +70,7 @@
<field name="model">api.log</field>
<field name="arch" type="xml">
<search>
<field name="data" string="Data" />
<field name="data_preview" string="Data Preview" />
<filter
name="state_draft"
string="Draft"
Expand Down