Commit 6f2014a7 authored by Titouan Soulard's avatar Titouan Soulard

erp5_api_style: rewrite most of `jIOWebSection`

parent b815419f
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Copyright (c) 2016 Nexedi SA and Contributors. All Rights Reserved. # Copyright (c) 2024 Nexedi SA and Contributors. All Rights Reserved.
# Cédric Le Ninivin <cedric.leninivin@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -28,90 +27,104 @@ ...@@ -28,90 +27,104 @@
############################################################################## ##############################################################################
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl import Unauthorized
from Acquisition import aq_inner from Acquisition import aq_inner
from OFS.Traversable import NotFound
from erp5.component.document.WebSection import WebSection from OFS.Traversable import NotFound, Unauthorized
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from zExceptions import HTTPClientError
from zLOG import LOG, INFO from erp5.component.mixin.DocumentExtensibleTraversableMixin import DocumentExtensibleTraversableMixin
from erp5.component.document.WebSection import WebSection
MARKER = [] MARKER = []
ALLOWED_MODES = ["put", "get", "post", "allDocs"]
class jIOError(object):
"""
Describes a JSON error, to avoid ERP5 default error pages.
Publishable but non-Persistent (since errors are logged in a separate module) and without Acquisition.
"""
def __init__(self, context, portal, request):
self.context = context
self.portal = portal
self.request = request
# Redefine an Unauthorized error to avoid Zope redirecting the user to the main ERP5 login form # Method not expected to be called without context, return Bad Request
class jIOUnauthorized(HTTPClientError): if not self.context:
errmsg = 'Unauthorized' self.context = {
status = 401 "error_code": 400,
"error_name": "BadRequest"
}
def __init__(self, underlyingError): self.context["text_content"] = request.get("BODY")
HTTPClientError.__init__(self)
self.underlyingError = underlyingError
# Used for debugging, especially in tests
def __str__(self): def __str__(self):
return str(self.underlyingError) return str(self.context)
def __bytes__(self): def __call__(self):
return bytes(self.underlyingError) portal = self.portal
response = self.request.response
def convertTojIOAPICall(function): response.setHeader("Content-Type", "application/json")
response.setStatus(self.context["error_code"], lock=True)
# Skin used to allow replacement and because Manager proxy role is needed
error_log = portal.ERP5Site_logApiErrorAndReturn(**self.context)
response.setBody(error_log, lock=True)
class jIOMethod(object):
""" """
Wrap the method to create a log entry for each invocation to the zope logger Represents one of the four possible jIO methods.
Publishable but non-Persistent and without Acquisition.
XXX: Acquisition might be suitable here
""" """
def wrapper(self, *args, **kwd):
""" def __init__(self, mode_name, web_section):
Log the call, and the result of the call super(jIOMethod, self).__init__()
""" self.mode_name = mode_name
#assert(self.REQUEST.REQUEST_METHOD == "POST") self.web_section = web_section
def __call__(self):
self.web_section.REQUEST.response.setHeader("Content-Type", "application/json")
try: try:
self.REQUEST.response.setHeader("Content-Type", "application/json") return self.web_section.ERP5Site_asjIOStyle(
retval = function(self, *args, **kwd) mode=self.mode_name,
except Unauthorized, e: text_content=self.web_section.REQUEST.get('BODY'),
body = self.ERP5Site_logApiErrorAndReturn( data_dict=None,
error_code="401",
error_message=str(e),
error_name="Unauthorized"
)
self.REQUEST.response.setBody(body, lock=True)
raise jIOUnauthorized(e)
except NotFound, e:
LOG('jIOWebSection', INFO, 'Converting NotFound to NotFound error mesage in JSON,',
error=True)
body = self.ERP5Site_logApiErrorAndReturn(
error_code="404",
error_message=str(e),
error_name="NotFound"
) )
self.REQUEST.response.setBody(body, lock=True) except Exception as e:
raise error_context = {
except: "error_code": 500,
LOG('jIOWebSection', INFO, 'Converting Error to InternalError message in JSON,', "error_message": str(e),
error=True) "error_name": "InternalError"
body = self.ERP5Site_logApiErrorAndReturn( }
error_code="500",
error_message="Internal Server Error", # This NotFound catches instance of objects not found inside API call
error_name="InternalError" if isinstance(e, NotFound):
) error_context["error_code"] = 404
self.REQUEST.response.setBody(body, lock=True) error_context["error_name"] = "NotFound"
raise elif isinstance(e, Unauthorized):
error_context["error_code"] = 403
return '%s' % retval error_context["error_name"] = "Unauthorized"
wrapper.__doc__ = function.__doc__ # Avoid informations leak when Unauthorized
return wrapper del error_context["error_message"]
return jIOError(error_context, self.web_section.getPortalObject(), self.web_section.REQUEST)
class jIOWebSection(WebSection): class jIOWebSection(WebSection):
""" portal_type = "jIO Web Section"
This Web Section is a wrapper to jIO to pass content in the body
"""
portal_type = 'jIO Web Section' # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
security.declareProtected(Permissions.AccessContentsInformation, 'getLayoutProperty') security.declareProtected(Permissions.AccessContentsInformation, "getLayoutProperty")
def getLayoutProperty(self, key, default=None): def getLayoutProperty(self, key, default=None):
""" """
A simple method to get a property of the current by A simple method to get a property of the current by
acquiring it from the current section or its parents. acquiring it from the current section or its parents.
""" """
section = aq_inner(self) section = aq_inner(self)
while section.getPortalType() in ('Web Section', 'Web Site', 'Static Web Section', 'Static Web Site', while section.getPortalType() in ('Web Section', 'Web Site', 'Static Web Section', 'Static Web Site',
...@@ -122,46 +135,23 @@ class jIOWebSection(WebSection): ...@@ -122,46 +135,23 @@ class jIOWebSection(WebSection):
section = section.aq_parent section = section.aq_parent
return default return default
@convertTojIOAPICall security.declareProtected(Permissions.View, '__bobo_traverse__')
def _asjIOStyle(self, mode, text_content="", data_dict=None): def __bobo_traverse__(self, request, name):
return self.ERP5Site_asjIOStyle( if name in ALLOWED_MODES:
mode=mode, return jIOMethod(name, self)
text_content=text_content,
data_dict=data_dict,
)
security.declareProtected(Permissions.View, 'get')
def get(self): #pylint:disable=arguments-differ
"""
Taken from WebSection Bobo Traverse, the difference is that
__bobo_traverse__ from DocumentExtensibleTraversableMixin is not called
"""
# Register current web site physical path for later URL generation
return self._asjIOStyle(mode="get", text_content=self.REQUEST.get('BODY'))
security.declareProtected(Permissions.View, 'post')
def post(self):
"""
Taken from WebSection Bobo Traverse, the difference is that
__bobo_traverse__ from DocumentExtensibleTraversableMixin is not called
"""
# Register current web site physical path for later URL generation
return self._asjIOStyle(mode="post", text_content=self.REQUEST.get('BODY'))
security.declareProtected(Permissions.View, 'put')
def put(self):
"""
Taken from WebSection Bobo Traverse, the difference is that
__bobo_traverse__ from DocumentExtensibleTraversableMixin is not called
"""
# Register current web site physical path for later URL generation
return self._asjIOStyle(mode="put", text_content=self.REQUEST.get('BODY'))
security.declareProtected(Permissions.View, 'allDocs') document = None
def allDocs(self): try:
""" # Inheritance as follows: jIOWebSection <| WebSection <| (Domain, DocumentExtensibleTraversableMixin)
Taken from WebSection Bobo Traverse, the difference is that # Use DocumentExtensibleTraversableMixin traversal to avoid ERP5 HTML 404 page.
__bobo_traverse__ from DocumentExtensibleTraversableMixin is not called document = DocumentExtensibleTraversableMixin.__bobo_traverse__(self, request, name)
""" # This NotFound catches objects not found during traversal
# Register current web site physical path for later URL generation except NotFound as e:
return self._asjIOStyle(mode="allDocs", text_content=self.REQUEST.get('BODY')) error_context = {
"error_code": 404,
"error_message": str(e),
"error_name": "NotFound"
}
document = jIOError(error_context, self.getPortalObject(), self.REQUEST)
return document
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