Commit c2b3b1de authored by Romain Courteaud's avatar Romain Courteaud

erp5_json_rpc_api: test custom error handler

parent f11f8892
......@@ -41,8 +41,8 @@ if typing.TYPE_CHECKING:
OpenAPIOperation, OpenAPIParameter, HTTPRequest, Any, Callable, Optional)
# import jsonschema
from AccessControl import ClassSecurityInfo
from zExceptions import NotFound, MethodNotAllowed, BadRequest
from AccessControl import ClassSecurityInfo, ModuleSecurityInfo
from zExceptions import NotFound
from zope.publisher.browser import BrowserView
from zope.publisher.interfaces import IPublishTraverse
import zope.component
......@@ -51,11 +51,44 @@ import zope.interface
from Products.ERP5Type import Permissions, PropertySheet
from erp5.component.document.OpenAPITypeInformation import (
byteify
byteify,
OpenAPIError
)
from erp5.component.document.OpenAPIService import OpenAPIService
import jsonschema
class JsonRpcAPIError(OpenAPIError):
pass
class JsonRpcAPINotParsableJsonContent(JsonRpcAPIError):
type = "not-parsable-json-content"
status = 400
class JsonRpcAPINotJsonDictContent(JsonRpcAPIError):
type = "not-json-object-content"
status = 400
class JsonRpcAPIInvalidJsonDictContent(JsonRpcAPIError):
type = "invalid-json-object-content"
status = 400
class JsonRpcAPINotAllowedHttpMethod(JsonRpcAPIError):
type = "not-allowed-http-method"
status = 405
class JsonRpcAPIBadContentType(JsonRpcAPIError):
type = "unexpected-media-type"
status = 415
ModuleSecurityInfo(__name__).declarePublic(
JsonRpcAPIError.__name__,
JsonRpcAPINotParsableJsonContent.__name__,
JsonRpcAPINotJsonDictContent.__name__,
JsonRpcAPIInvalidJsonDictContent.__name__,
JsonRpcAPINotAllowedHttpMethod.__name__,
JsonRpcAPIBadContentType.__name__,
)
class OpenAPIBrowserView(BrowserView):
......@@ -139,11 +172,14 @@ class JsonRpcAPIService(OpenAPIService):
if matched_operation is None:
raise NotFound(request_path)
if 'application/json' not in request.getHeader('Content-Type', ''):
raise BadRequest('Request Content-Type must be application/json')
content_type = request.getHeader('Content-Type', '')
if 'application/json' not in content_type:
raise JsonRpcAPIBadContentType(
'Request Content-Type must be "application/json", not "%s"' % content_type
)
if (request_method != 'post'):
raise MethodNotAllowed('Only HTTP POST accepted')
raise JsonRpcAPINotAllowedHttpMethod('Only HTTP POST accepted')
"""
for operation in self.getTypeInfo().getOpenAPIOperationIterator():
......@@ -272,6 +308,28 @@ class JsonRpcAPIService(OpenAPIService):
schema,
)
'''
def handleException(self, exception, request):
if isinstance(exception, JsonRpcAPIError):
# Prevent catching all exceptions in the script
# to prevent returning wrong content in case of bugs...
script_id = self.getErrorHandlerScriptId()
if script_id:
try:
script_result = getattr(self, script_id)(exception)
except Exception as e:
exception = e
else:
# If the script returns something, consider the exception
# has explicitely handled
if script_result:
response = request.response
response.setBody(json.dumps(script_result).encode())#, lock=True)
response.setHeader("Content-Type", "application/json")
response.setStatus(exception.status)#, lock=True)
return
# ... but if really needed, developer is still able to use the type based method
return super(JsonRpcAPIService, self).handleException(exception, request)
def executeMethod(self, request):
# type: (HTTPRequest) -> Any
operation = self.getMatchingOperation(request)
......@@ -285,11 +343,14 @@ class JsonRpcAPIService(OpenAPIService):
try:
json_data = byteify(json.loads(request.get('BODY')))
except BaseException as e:
raise BadRequest(str(e))
raise JsonRpcAPINotParsableJsonContent(str(e))
if not isinstance(json_data, dict):
raise BadRequest("Did not received a JSON Object")
raise JsonRpcAPINotJsonDictContent("Did not received a JSON Object")
result = method(json_data=json_data, list_error=True)#**parameters)
try:
result = method(json_data=json_data, list_error=False)#**parameters)
except jsonschema.exceptions.ValidationError as e:
raise JsonRpcAPIInvalidJsonDictContent(str(e))
response = request.RESPONSE
if response.getHeader('Content-Type'):
return result
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/string</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>error_handler_script_id_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>range</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>storage_id</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -65,6 +65,7 @@
<value>
<list>
<string>my_json_form_list</string>
<string>my_error_handler_script_id</string>
</list>
</value>
</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>description</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_error_handler_script_id</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>The name of a document in ERP5</string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Title</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment