feat: v2 for message response strucutre

This commit is contained in:
Ankush Menat 2023-09-11 12:47:00 +05:30
parent 3cc2ca8fc7
commit 411819ef81
3 changed files with 57 additions and 21 deletions

View file

@ -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

View file

@ -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):

View file

@ -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