From 4d08a989f54a158a0765bcd7c7b134c6846b0f29 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 25 Jan 2023 15:03:59 +0530 Subject: [PATCH 1/2] feat: Audit hooks report --- frappe/custom/report/__init__.py | 0 .../report/audit_system_hooks/__init__.py | 0 .../audit_system_hooks/audit_system_hooks.js | 7 +++ .../audit_system_hooks.json | 27 +++++++++ .../audit_system_hooks/audit_system_hooks.py | 60 +++++++++++++++++++ .../test_audit_system_hooks.py | 17 ++++++ 6 files changed, 111 insertions(+) create mode 100644 frappe/custom/report/__init__.py create mode 100644 frappe/custom/report/audit_system_hooks/__init__.py create mode 100644 frappe/custom/report/audit_system_hooks/audit_system_hooks.js create mode 100644 frappe/custom/report/audit_system_hooks/audit_system_hooks.json create mode 100644 frappe/custom/report/audit_system_hooks/audit_system_hooks.py create mode 100644 frappe/custom/report/audit_system_hooks/test_audit_system_hooks.py diff --git a/frappe/custom/report/__init__.py b/frappe/custom/report/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/custom/report/audit_system_hooks/__init__.py b/frappe/custom/report/audit_system_hooks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/custom/report/audit_system_hooks/audit_system_hooks.js b/frappe/custom/report/audit_system_hooks/audit_system_hooks.js new file mode 100644 index 0000000000..a78464f3da --- /dev/null +++ b/frappe/custom/report/audit_system_hooks/audit_system_hooks.js @@ -0,0 +1,7 @@ +// Copyright (c) 2023, Frappe Technologies and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Audit System Hooks"] = { + filters: [], +}; diff --git a/frappe/custom/report/audit_system_hooks/audit_system_hooks.json b/frappe/custom/report/audit_system_hooks/audit_system_hooks.json new file mode 100644 index 0000000000..d9ea86f07f --- /dev/null +++ b/frappe/custom/report/audit_system_hooks/audit_system_hooks.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2023-01-25 15:02:21.896117", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2023-01-25 15:03:31.263337", + "modified_by": "Administrator", + "module": "Custom", + "name": "Audit System Hooks", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "System Settings", + "report_name": "Audit System Hooks", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/frappe/custom/report/audit_system_hooks/audit_system_hooks.py b/frappe/custom/report/audit_system_hooks/audit_system_hooks.py new file mode 100644 index 0000000000..8dab192a14 --- /dev/null +++ b/frappe/custom/report/audit_system_hooks/audit_system_hooks.py @@ -0,0 +1,60 @@ +# Copyright (c) 2023, Frappe Technologies and contributors +# For license information, please see license.txt + +import frappe + + +def execute(filters=None): + frappe.only_for("System Manager") + + return get_columns(), get_data() + + +def get_columns(): + values_field_type = "Data" # TODO: better text wrapping in reportview + columns = [ + {"label": "Hook name", "fieldname": "hook_name", "fieldtype": "Data", "width": 200}, + {"label": "Hook key (optional)", "fieldname": "key", "fieldtype": "Data", "width": 200}, + {"label": "Hook Values", "fieldname": "hook_values", "fieldtype": values_field_type}, + ] + + # Each app is shown in order as a column + installed_apps = frappe.get_installed_apps(_ensure_on_bench=True) + columns += [ + {"label": app, "fieldname": app, "fieldtype": values_field_type} for app in installed_apps + ] + + return columns + + +def get_data(): + hooks = frappe.get_hooks() + installed_apps = frappe.get_installed_apps(_ensure_on_bench=True) + + def fmt_hook_values(v): + """Improve readability by discarding falsy values and removing containers when only 1 + value is in container""" + if not v: + return "" + + if isinstance(v, list) and len(v) == 1: + return str(v[0]) + + if isinstance(v, (dict, list)): + try: + return frappe.as_json(v) + except Exception: + pass + + return str(v) + + data = [] + for hook, values in hooks.items(): + row = {"hook_name": hook, "hook_values": fmt_hook_values(values)} + + for app in installed_apps: + row[app] = fmt_hook_values(frappe.get_hooks(hook, app_name=app)) + + data.append(row) + + return data diff --git a/frappe/custom/report/audit_system_hooks/test_audit_system_hooks.py b/frappe/custom/report/audit_system_hooks/test_audit_system_hooks.py new file mode 100644 index 0000000000..cd3edffc77 --- /dev/null +++ b/frappe/custom/report/audit_system_hooks/test_audit_system_hooks.py @@ -0,0 +1,17 @@ +# Copyright (c) 2022, Frappe Technologies and contributors +# For license information, please see license.txt + + +from frappe.custom.report.audit_system_hooks.audit_system_hooks import execute +from frappe.tests.utils import FrappeTestCase + + +class TestAuditSystemHooksReport(FrappeTestCase): + def test_basic_query(self): + _, data = execute() + for row in data: + if row.get("hook_name") == "app_name": + self.assertEqual(row.get("hook_values"), "frappe") + break + else: + self.fail("Failed to generate hooks report") From 80dcf0b1788088bb1d469766deeabcd85bc0d7dc Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 29 Jan 2023 20:09:45 +0530 Subject: [PATCH 2/2] feat: Split dict hooks to separate lines --- .../audit_system_hooks/audit_system_hooks.py | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/frappe/custom/report/audit_system_hooks/audit_system_hooks.py b/frappe/custom/report/audit_system_hooks/audit_system_hooks.py index 8dab192a14..a42c5c361a 100644 --- a/frappe/custom/report/audit_system_hooks/audit_system_hooks.py +++ b/frappe/custom/report/audit_system_hooks/audit_system_hooks.py @@ -5,8 +5,6 @@ import frappe def execute(filters=None): - frappe.only_for("System Manager") - return get_columns(), get_data() @@ -14,8 +12,8 @@ def get_columns(): values_field_type = "Data" # TODO: better text wrapping in reportview columns = [ {"label": "Hook name", "fieldname": "hook_name", "fieldtype": "Data", "width": 200}, - {"label": "Hook key (optional)", "fieldname": "key", "fieldtype": "Data", "width": 200}, - {"label": "Hook Values", "fieldname": "hook_values", "fieldtype": values_field_type}, + {"label": "Hook key (optional)", "fieldname": "hook_key", "fieldtype": "Data", "width": 200}, + {"label": "Hook Values (resolved)", "fieldname": "hook_values", "fieldtype": values_field_type}, ] # Each app is shown in order as a column @@ -37,8 +35,7 @@ def get_data(): if not v: return "" - if isinstance(v, list) and len(v) == 1: - return str(v[0]) + v = delist(v) if isinstance(v, (dict, list)): try: @@ -50,11 +47,24 @@ def get_data(): data = [] for hook, values in hooks.items(): - row = {"hook_name": hook, "hook_values": fmt_hook_values(values)} + if isinstance(values, dict): + for k, v in values.items(): + row = {"hook_name": hook, "hook_key": fmt_hook_values(k), "hook_values": fmt_hook_values(v)} + for app in installed_apps: + if app_hooks := delist(frappe.get_hooks(hook, app_name=app)): + row[app] = fmt_hook_values(app_hooks.get(k)) + data.append(row) + else: + row = {"hook_name": hook, "hook_values": fmt_hook_values(values)} + for app in installed_apps: + row[app] = fmt_hook_values(frappe.get_hooks(hook, app_name=app)) - for app in installed_apps: - row[app] = fmt_hook_values(frappe.get_hooks(hook, app_name=app)) - - data.append(row) + data.append(row) return data + + +def delist(val): + if isinstance(val, list) and len(val) == 1: + return val[0] + return val