Commit 81215fb0 authored by Romain Courteaud's avatar Romain Courteaud

erp5_json_rpc_api: subclass OpenAPIService

parent 8af1e468
......@@ -43,69 +43,22 @@ if typing.TYPE_CHECKING:
import jsonschema
from AccessControl import ClassSecurityInfo
from zExceptions import NotFound
from zExceptions import Unauthorized
from zope.component import queryMultiAdapter
from zope.publisher.browser import BrowserView
from zope.publisher.interfaces import IPublishTraverse
from zope.publisher.interfaces.browser import IBrowserPublisher
from ZPublisher.BaseRequest import DefaultPublishTraverse
from ZPublisher.interfaces import UseTraversalDefault
import zope.component
import zope.interface
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.XMLObject import XMLObject
from erp5.component.document.JsonRpcAPITypeInformation import (
NoMethodForOperationError,
OpenAPIError,
ParameterValidationError,
SchemaDefinitionError,
byteify
)
from erp5.component.document.OpenAPIService import OpenAPIService
class IOpenAPIRequest(zope.interface.Interface): # pylint:disable=inherit-non-class
"""Marker interface to register error handler for Open API requests.
"""
@zope.component.adapter(Exception, IOpenAPIRequest)
class ErrorHandlerView(BrowserView):
"""On exception, delegate the rendering to OpenAPIService.handleException
"""
def __call__(self):
return self.__parent__.handleException(self.context, self.request)
zope.component.getGlobalSiteManager().registerAdapter(
ErrorHandlerView,
provided=zope.interface.Interface,
name=u'index.html',
)
@zope.interface.implementer(IPublishTraverse, IBrowserPublisher)
class OpenAPIWrapper(object):
"""Wrapper for traversal
"""
def __init__(self, context, request):
self.context = context
zope.interface.alsoProvides(request, IOpenAPIRequest)
# disable redirection to login page
def unauthorized():
raise Unauthorized()
request.response.unauthorized = unauthorized
def __getattr__(self, name):
return getattr(self.context, name)
def __getitem__(self, name):
return self.context[name]
def publishTraverse(self, request, name):
return self
def browserDefault(self, request):
return OpenAPIBrowserView(self.context, request), ()
class OpenAPIBrowserView(BrowserView):
......@@ -116,7 +69,7 @@ class OpenAPIBrowserView(BrowserView):
@zope.interface.implementer(IPublishTraverse)
class JsonRpcAPIService(XMLObject):
class JsonRpcAPIService(OpenAPIService):
add_permission = Permissions.AddPortalContent
# Declarative security
......@@ -134,51 +87,10 @@ class JsonRpcAPIService(XMLObject):
security.declareProtected(
Permissions.AccessContentsInformation, 'viewOpenAPIAsJson')
def viewOpenAPIAsJson(self):
"""Return the Open API as JSON, with the current endpoint added as first servers
"""
schema = self.getTypeInfo().getSchema()
schema.setdefault('servers', []).insert(
0, {
'url': self.absolute_url(),
'description': self.getDescription()
})
return json.dumps(schema)
def handleException(self, exception, request):
"""Default Exception handler, renders the exception as json (rfc7807)
but make it possible to customize error handling with a type based
method.
"""
method = self.getTypeBasedMethod('handleException')
if method:
return method(exception, request)
status = type(exception)
if isinstance(exception, OpenAPIError):
exception_info = {'type': exception.type, 'title': str(exception)}
if exception.status:
status = exception_info['status'] = exception.status
if exception.detail:
exception_info['detail'] = exception.detail
elif isinstance(exception, Unauthorized):
# intentionnaly do not leak information when something is unauthorized
exception_info = {
'type': 'unauthorized',
}
elif isinstance(exception, NotFound):
exception_info = {'type': 'not-found', 'title': str(exception)}
else:
exception_info = {
'type': 'unknown-error',
'title': '{}: {}'.format(type(exception).__name__, exception)
}
response = request.response
response.setHeader("Content-Type", "application/json")
response.setStatus(status, lock=True)
response.setBody(json.dumps(exception_info).encode(), lock=True)
raise NotImplementedError()
def getMatchingOperation(self, request):
# type: (HTTPRequest) -> Optional[OpenAPIOperation]
......@@ -355,21 +267,3 @@ class JsonRpcAPIService(XMLObject):
return result
response.setHeader("Content-Type", "application/json")
return json.dumps(result).encode()
def publishTraverse(self, request, name):
if request.method.upper() in ('PUT', 'DELETE'):
# don't use default traversal for PUT and DELETE methods, because they are
# handled as WebDAV before the hooks are called.
return OpenAPIWrapper(self, request)
adapter = DefaultPublishTraverse(self, request)
try:
obj = adapter.publishTraverse(request, name)
except (KeyError, AttributeError):
view = queryMultiAdapter((self, request), name=name)
if view is not None:
return view
return OpenAPIWrapper(self, request)
return obj
def __bobo_traverse__(self, request, name):
raise UseTraversalDefault
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