feat: add in a doctype to optionally track API Requests (#32622)

This commit is contained in:
Akhil Narang 2025-06-06 13:22:25 +05:30 committed by GitHub
parent d5518fde79
commit 1a3602f715
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 156 additions and 2 deletions

View file

@ -41,6 +41,17 @@ def handle(request: Request):
`DELETE` will delete
"""
if frappe.get_system_settings("log_api_requests"):
doc = frappe.get_doc(
{
"doctype": "API Request Log",
"path": request.path,
"user": frappe.session.user,
"method": request.method,
}
)
doc.deferred_insert()
try:
endpoint, arguments = API_URL_MAP.bind_to_environ(request.environ).match()
except NotFound: # Wrap 404 - backward compatiblity

View file

@ -0,0 +1,8 @@
// Copyright (c) 2025, Frappe Technologies and contributors
// For license information, please see license.txt
// frappe.ui.form.on("API Request Log", {
// refresh(frm) {
// },
// });

View file

@ -0,0 +1,62 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2025-05-21 16:51:56.070193",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"path",
"method",
"user"
],
"fields": [
{
"fieldname": "path",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Path"
},
{
"fieldname": "user",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "User",
"options": "User"
},
{
"fieldname": "method",
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Method"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-05-21 17:09:55.054044",
"modified_by": "Administrator",
"module": "Core",
"name": "API Request Log",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View file

@ -0,0 +1,28 @@
# Copyright (c) 2025, Frappe Technologies and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
class APIRequestLog(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
method: DF.Data | None
path: DF.Data | None
user: DF.Link | None
# end: auto-generated types
@staticmethod
def clear_old_logs(days: int = 90):
from frappe.query_builder import Interval
from frappe.query_builder.functions import Now
table = frappe.qb.DocType("API Request Log")
frappe.db.delete(table, filters=(table.creation < (Now() - Interval(days=days))))

View file

@ -0,0 +1,29 @@
# Copyright (c) 2025, Frappe Technologies and Contributors
# See license.txt
# import frappe
from frappe.tests import IntegrationTestCase, UnitTestCase
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class UnitTestAPIRequestLog(UnitTestCase):
"""
Unit tests for APIRequestLog.
Use this class for testing individual functions and methods.
"""
pass
class IntegrationTestAPIRequestLog(IntegrationTestCase):
"""
Integration tests for APIRequestLog.
Use this class for testing interactions between multiple components.
"""
pass

View file

@ -155,6 +155,7 @@ LOG_DOCTYPES = [
"Email Queue Recipient",
"Error Log",
"OAuth Bearer Token",
"API Request Log",
]

View file

@ -104,7 +104,9 @@
"allow_error_traceback",
"enable_telemetry",
"search_section",
"link_field_results_limit"
"link_field_results_limit",
"api_logging_section",
"log_api_requests"
],
"fields": [
{
@ -707,12 +709,23 @@
"fieldname": "max_report_rows",
"fieldtype": "Int",
"label": "Max Report Rows"
},
{
"fieldname": "api_logging_section",
"fieldtype": "Section Break",
"label": "API Logging"
},
{
"default": "0",
"fieldname": "log_api_requests",
"fieldtype": "Check",
"label": "Log API Requests"
}
],
"icon": "fa fa-cog",
"issingle": 1,
"links": [],
"modified": "2025-05-19 14:17:40.748786",
"modified": "2025-05-21 17:03:23.310169",
"modified_by": "Administrator",
"module": "Core",
"name": "System Settings",

View file

@ -66,6 +66,7 @@ class SystemSettings(Document):
language: DF.Link
lifespan_qrcode_image: DF.Int
link_field_results_limit: DF.Int
log_api_requests: DF.Check
login_with_email_link: DF.Check
login_with_email_link_expiry: DF.Int
logout_on_password_reset: DF.Check

View file

@ -569,6 +569,7 @@ default_log_clearing_doctypes = {
"Activity Log": 90,
"Route History": 90,
"OAuth Bearer Token": 30,
"API Request Log": 90,
}
# These keys will not be erased when doing frappe.clear_cache()