feat: v2 for message response strucutre
This commit is contained in:
parent
3cc2ca8fc7
commit
411819ef81
3 changed files with 57 additions and 21 deletions
|
|
@ -1,5 +1,7 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
from enum import Enum
|
||||
|
||||
from werkzeug.exceptions import NotFound
|
||||
from werkzeug.routing import Map, Submount
|
||||
from werkzeug.wrappers import Request, Response
|
||||
|
|
@ -10,6 +12,11 @@ from frappe import _
|
|||
from frappe.utils.response import build_response
|
||||
|
||||
|
||||
class ApiVersion(str, Enum):
|
||||
V1 = "v1"
|
||||
V2 = "v2"
|
||||
|
||||
|
||||
def handle(request: Request):
|
||||
"""
|
||||
Entry point for `/api` methods.
|
||||
|
|
@ -56,9 +63,18 @@ API_URL_MAP = Map(
|
|||
[
|
||||
# V1 routes
|
||||
Submount("/api", v1_rules),
|
||||
Submount("/api/v1", v1_rules),
|
||||
Submount("/api/v2", v2_rules),
|
||||
Submount(f"/api/{ApiVersion.V1.value}", v1_rules),
|
||||
Submount(f"/api/{ApiVersion.V2.value}", v2_rules),
|
||||
],
|
||||
strict_slashes=False, # Allows skipping trailing slashes
|
||||
merge_slashes=False,
|
||||
)
|
||||
|
||||
|
||||
def get_api_version() -> ApiVersion | None:
|
||||
if not frappe.request or not frappe.request.path.startswith("/api"):
|
||||
return
|
||||
|
||||
if frappe.request.path.startswith(f"/api/{ApiVersion.V2.value}"):
|
||||
return ApiVersion.V2
|
||||
return ApiVersion.V1
|
||||
|
|
|
|||
|
|
@ -455,21 +455,23 @@ class TestMethodAPIV2(FrappeAPITestCase):
|
|||
def test_logs(self):
|
||||
method = "frappe.tests.test_api.test"
|
||||
|
||||
def get_message(resp, msg_type):
|
||||
return frappe.parse_json(frappe.parse_json(frappe.parse_json(resp.json)[msg_type])[0])
|
||||
expected_message = "Failed v2"
|
||||
response = self.get(
|
||||
self.method_path(method), {"sid": self.sid, "message": expected_message}
|
||||
).json
|
||||
|
||||
expected_message = "Failed"
|
||||
response = self.get(self.method_path(method), {"sid": self.sid, "message": expected_message})
|
||||
self.assertEqual(get_message(response, "_server_messages").message, expected_message)
|
||||
self.assertIsInstance(response["messages"], list)
|
||||
self.assertEqual(response["messages"][0]["message"], expected_message)
|
||||
|
||||
# Cause handled failured
|
||||
with suppress_stdout():
|
||||
response = self.get(
|
||||
self.method_path(method), {"sid": self.sid, "message": expected_message, "fail": True}
|
||||
)
|
||||
self.assertEqual(get_message(response, "_server_messages").message, expected_message)
|
||||
self.assertEqual(response.json["exc_type"], "ValidationError")
|
||||
self.assertIn("Traceback", response.json["exc"])
|
||||
).json
|
||||
self.assertIsInstance(response["errors"], list)
|
||||
self.assertEqual(response["errors"][0]["message"], expected_message)
|
||||
self.assertEqual(response["errors"][0]["type"], "ValidationError")
|
||||
self.assertIn("Traceback", response["errors"][0]["exception"])
|
||||
|
||||
# Cause handled failured
|
||||
with suppress_stdout():
|
||||
|
|
@ -477,9 +479,10 @@ class TestMethodAPIV2(FrappeAPITestCase):
|
|||
self.method_path(method),
|
||||
{"sid": self.sid, "message": expected_message, "fail": True, "handled": False},
|
||||
)
|
||||
self.assertNotIn("_server_messages", response.json)
|
||||
self.assertIn("ZeroDivisionError", response.json["exception"]) # WHY?
|
||||
self.assertIn("Traceback", response.json["exc"])
|
||||
|
||||
self.assertIsInstance(response["errors"], list)
|
||||
self.assertEqual(response["errors"][0]["type"], "ZeroDivisionError")
|
||||
self.assertIn("Traceback", response["errors"][0]["exception"])
|
||||
|
||||
|
||||
class TestDocTypeAPIV2(FrappeAPITestCase):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import decimal
|
|||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
from urllib.parse import quote
|
||||
|
||||
import werkzeug.utils
|
||||
|
|
@ -21,7 +21,7 @@ import frappe.sessions
|
|||
import frappe.utils
|
||||
from frappe import _
|
||||
from frappe.core.doctype.access_log.access_log import make_access_log
|
||||
from frappe.utils import cint, format_timedelta
|
||||
from frappe.utils import format_timedelta
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.core.doctype.file.file import File
|
||||
|
|
@ -99,6 +99,7 @@ def as_raw():
|
|||
|
||||
def as_json():
|
||||
make_logs()
|
||||
|
||||
response = Response()
|
||||
if frappe.local.response.http_status_code:
|
||||
response.status_code = frappe.local.response["http_status_code"]
|
||||
|
|
@ -125,13 +126,22 @@ def as_binary():
|
|||
return response
|
||||
|
||||
|
||||
def make_logs(response=None):
|
||||
# TODO: v2 API
|
||||
def make_logs():
|
||||
"""make strings for msgprint and errprint"""
|
||||
|
||||
from frappe.api import ApiVersion, get_api_version
|
||||
|
||||
match get_api_version():
|
||||
case ApiVersion.V1:
|
||||
_make_logs_v1()
|
||||
case ApiVersion.V2:
|
||||
_make_logs_v2()
|
||||
|
||||
|
||||
def _make_logs_v1():
|
||||
from frappe.utils.error import guess_exception_source
|
||||
|
||||
if not response:
|
||||
response = frappe.local.response
|
||||
response = frappe.local.response
|
||||
|
||||
if frappe.error_log:
|
||||
if source := guess_exception_source(frappe.local.error_log and frappe.local.error_log[0]["exc"]):
|
||||
|
|
@ -143,13 +153,20 @@ def make_logs(response=None):
|
|||
[frappe.utils.cstr(d) for d in frappe.local.message_log]
|
||||
)
|
||||
|
||||
if frappe.debug_log and frappe.conf.get("logging") or False:
|
||||
if frappe.debug_log and frappe.conf.get("logging"):
|
||||
response["_debug_messages"] = json.dumps(frappe.local.debug_log)
|
||||
|
||||
if frappe.flags.error_message:
|
||||
response["_error_message"] = frappe.flags.error_message
|
||||
|
||||
|
||||
def _make_logs_v2():
|
||||
response = frappe.local.response
|
||||
|
||||
if frappe.local.message_log:
|
||||
response["messages"] = [frappe.parse_json(l) for l in frappe.local.message_log]
|
||||
|
||||
|
||||
def json_handler(obj):
|
||||
"""serialize non-serializable data for json"""
|
||||
from collections.abc import Iterable
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue