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..a42c5c361a --- /dev/null +++ b/frappe/custom/report/audit_system_hooks/audit_system_hooks.py @@ -0,0 +1,70 @@ +# Copyright (c) 2023, Frappe Technologies and contributors +# For license information, please see license.txt + +import frappe + + +def execute(filters=None): + 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": "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 + 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 "" + + v = delist(v) + + if isinstance(v, (dict, list)): + try: + return frappe.as_json(v) + except Exception: + pass + + return str(v) + + data = [] + for hook, values in hooks.items(): + 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)) + + data.append(row) + + return data + + +def delist(val): + if isinstance(val, list) and len(val) == 1: + return val[0] + return val 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")