Commit 81c4f47b authored by Romain Courteaud's avatar Romain Courteaud

erp5_json_rpc_api: clone erp5_open_api

parent cee2573c
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>action_view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>2.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Actions</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/BaseType_viewAction</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>copy_roles</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>2.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Copy Roles</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/BaseType_viewCopyRoleListDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>definition_view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/BaseType_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>generate_python_scripts_for_operations</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Generate Python Scripts for Operations</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/OpenAPIType_viewGeneratePythonScriptForOperationsDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>open_api_view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.1</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Open API</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/OpenAPIType_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>operation_view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.2</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Open API Operations</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/OpenAPIType_viewOperationList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>role_view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>3.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Roles</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/BaseType_viewRole</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>translation_view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>4.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Translations</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/BaseType_viewTranslation</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>update_local_roles</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Update Local Roles</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/updateRoleMapping</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/BaseType_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
##############################################################################
# coding:utf-8
# Copyright (c) 2023 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import base64
import binascii
import json
import typing
import six
from six.moves.urllib.parse import unquote
if typing.TYPE_CHECKING:
from typing import Any, Callable, Optional
from erp5.component.document.OpenAPITypeInformation import OpenAPIOperation, OpenAPIParameter
from ZPublisher.HTTPRequest import HTTPRequest
_ = (
OpenAPIOperation, OpenAPIParameter, HTTPRequest, Any, Callable, Optional)
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.OpenAPITypeInformation import (
NoMethodForOperationError,
OpenAPIError,
ParameterValidationError,
SchemaDefinitionError,
)
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):
"""View to render Open API operation calls
"""
def __call__(self, *args, **kw):
return self.context.executeMethod(self.request)
@zope.interface.implementer(IPublishTraverse)
class OpenAPIService(XMLObject):
add_permission = Permissions.AddPortalContent
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Reference,
)
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)
def getMatchingOperation(self, request):
# type: (HTTPRequest) -> Optional[OpenAPIOperation]
# Compute the relative URL of the request path, by removing the
# relative URL of the Open API Service. This is tricky, because
# it may be in the acquisition context of a web section and the request
# might be using a virtual root with virtual paths.
# First, strip the left part of the URL corresponding to the "root"
web_section = self.getWebSectionValue()
root = web_section if web_section is not None else self.getPortalObject()
request_path_parts = [
unquote(part) for part in request['URL']
[1 + len(request.physicalPathToURL(root.getPhysicalPath())):].split('/')
]
# then strip everything corresponding to the "self" open api service.
# Here, unlike getPhysicalPath(), we don't use the inner acquistion,
# but keep the acquisition chain from this request traversal.
i = 0
for aq_parent in reversed(self.aq_chain[:self.aq_chain.index(root)]):
if aq_parent.id == request_path_parts[i]:
i += 1
else:
break
request_path_parts = request_path_parts[i:]
request_method = request.method.lower()
matched_operation = None
for operation in self.getTypeInfo().getOpenAPIOperationIterator():
if operation.request_method != request_method:
continue
operation_path_parts = operation.path.split('/')[1:]
if len(operation_path_parts) != len(request_path_parts):
continue
if operation_path_parts == request_path_parts:
# this is a concrete match, use this operation
request.other['traverse_subpath'] = request_path_parts
return operation
# look for a templated match
for operation_path_part, request_path_part in zip(
operation_path_parts,
request_path_parts,
):
if operation_path_part == request_path_part:
continue
elif operation_path_part[0] == '{' and operation_path_part[-1] == '}':
continue
# TODO: match paths like /report.{format}
else:
break
else:
# we had a match, but there might be a "better" match, so we keep looping.
# https://spec.openapis.org/oas/v3.1.0.html#patterned-fields :
# > When matching URLs, concrete (non-templated) paths would be matched before
# > their templated counterparts
matched_operation = operation
continue
request.other['traverse_subpath'] = request_path_parts
return matched_operation
def getMethodForOperation(self, operation):
# type: (OpenAPIOperation) -> Optional[Callable]
operation_id = operation.get('operationId')
if operation_id:
method = self._getTypeBasedMethod(operation_id)
if method is not None:
return method
raise NoMethodForOperationError(
'No method for operation {operation_id} {request_method} {path}'.format(
operation_id=operation.get('operationId', ''),
request_method=operation.request_method.upper(),
path=operation.path,
))
def extractParametersFromRequest(self, operation, request):
# type: (OpenAPIOperation, HTTPRequest) -> dict
parameter_dict = {}
for parameter in operation.getParameters():
parameter_dict[parameter['name']] = self.validateParameter(
'parameter `{}`'.format(parameter['name']),
parameter.getValue(request),
parameter,
parameter.getJSONSchema(),
)
requestBody = self.validateRequestBody(
operation.getRequestBodyValue(request),
operation.getRequestBodyJSONSchema(request),
)
if requestBody:
# we try to bind the request body as `body` parameter, but use alternate name
# if it's already used by a parameter
for body_arg in ('body', 'request_body', 'body_'):
if body_arg not in parameter_dict:
parameter_dict[body_arg] = requestBody
break
else:
raise SchemaDefinitionError('unable to bind requestBody')
return parameter_dict
security.declareProtected(
Permissions.AccessContentsInformation, 'validateParameter')
def validateParameter(
self, parameter_name, parameter_value, parameter, schema):
# type: (str, Any, dict, dict) -> Any
"""Validate the parameter (or request body), raising a ParameterValidationError
when the parameter is not valid according to the corresponding schema.
"""
if schema is not None:
if parameter_value is None and not parameter.get('required'):
return parameter_value
__traceback_info__ = (parameter_name, parameter_value, schema)
try:
jsonschema.validate(parameter_value, schema)
except jsonschema.ValidationError as e:
raise ParameterValidationError(
'Error validating {parameter_name}: {e}'.format(
parameter_name=parameter_name, e=e.message), str(e))
return parameter_value
security.declareProtected(
Permissions.AccessContentsInformation, 'validateRequestBody')
def validateRequestBody(self, parameter_value, schema):
# type: (str, dict) -> Any
"""Validate the request body raising a ParameterValidationError
when the parameter is not valid according to the corresponding schema.
"""
if schema is not None:
if schema.get('type') == 'string':
if schema.get('format') == 'base64':
try:
return base64.b64decode(parameter_value)
except (binascii.Error, TypeError) as e:
if isinstance(e, TypeError):
# BBB on python2 this raises a generic type error
# but we don't want to ignore potential TypeErrors
# on python3 here
if six.PY3:
raise
raise ParameterValidationError(
'Error validating request body: {e}'.format(e=str(e)))
elif schema.get('format') == 'binary':
return parameter_value or b''
return self.validateParameter(
'request body',
parameter_value,
{},
schema,
)
def executeMethod(self, request):
# type: (HTTPRequest) -> Any
operation = self.getMatchingOperation(request)
if operation is None:
raise NotFound()
method = self.getMethodForOperation(operation)
parameters = self.extractParametersFromRequest(operation, request)
result = method(**parameters)
response = request.RESPONSE
if response.getHeader('Content-Type'):
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
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>OpenAPIService</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.OpenAPIService</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
##############################################################################
#
# Copyright (c) 2023 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import collections
import json
import io
import itertools
import warnings
from six.moves import urllib
import typing
if typing.TYPE_CHECKING:
from ZPublisher.HTTPRequest import HTTPRequest
_ = (HTTPRequest, )
from AccessControl import ClassSecurityInfo
from AccessControl import ModuleSecurityInfo
import jsonpointer
import six
from Products.ERP5Type.ERP5Type import ERP5TypeInformation
from Products.ERP5Type import Permissions, PropertySheet
class OpenAPIError(Exception):
"""Base class for errors, the Open API Errors
"""
type = None
status = None
def __init__(self, title, detail=None):
super(OpenAPIError, self).__init__(title)
self.detail = detail
class ParameterValidationError(OpenAPIError):
"""Error while validating request parameter.
"""
type = "parameter-validation-error"
status = 400
class MissingParameterError(OpenAPIError):
"""Parameter is missing
"""
type = "missing-parameter-error"
status = 400
class NoMethodForOperationError(OpenAPIError):
"""The service does not define a method for this operation.
"""
type = "no-method-for-operation"
class SchemaDefinitionError(OpenAPIError):
"""Error in the definition of OpenAPI schema.
"""
type = "schema-definition-error"
ModuleSecurityInfo(__name__).declarePublic(
OpenAPIError.__name__,
ParameterValidationError.__name__,
MissingParameterError.__name__,
)
# On python2, make sure we use UTF-8 strings for the json schemas, so that we don't
# have ugly u' prefixes in the reprs. This also transforms the collections.OrderedDict
# to simple dicts, because the former also have an ugly representation.
# http://stackoverflow.com/a/13105359
if six.PY2:
def byteify(string):
if isinstance(string, dict):
return {
byteify(key): byteify(value)
for key, value in string.iteritems()
}
elif isinstance(string, list):
return [byteify(element) for element in string]
elif isinstance(string, tuple):
return tuple(byteify(element) for element in string)
elif isinstance(string, six.text_type):
return string.encode('utf-8')
else:
return string
else:
def byteify(x):
return x
class SchemaWithComponents(dict):
# A local JSON schema ( for a parameter or request body ) with access to
# global `components` to resolve $refs.
__allow_access_to_unprotected_subobjects__ = 1
def __init__(self, _schema, iterable):
dict.__init__(self, byteify(iterable))
self._schema = _schema
def __missing__(self, key):
if key == 'components':
return byteify(self._schema[key])
raise KeyError
class OpenAPIOperation(dict):
__allow_access_to_unprotected_subobjects__ = True
def __init__(self, _schema, _path, _request_method, _path_item, **kwargs):
dict.__init__(self, **kwargs)
self._schema = _schema
self.path = _path
self.request_method = _request_method
self._path_item = _path_item
def __str__(self):
return '<OpenAPIOperation {} {} {}>'.format(
self.get('operationId'),
self.request_method,
self.path,
)
def getParameters(self):
# type: () -> Iterable[OpenAPIParameter]
for parameter in itertools.chain(self._path_item.get('parameters', ()),
self.get('parameters', ())):
ref = parameter.get('$ref')
if ref and ref[0] == '#':
yield OpenAPIParameter(
self._schema, self.path,
**jsonpointer.resolve_pointer(self._schema, ref[1:]))
else:
yield OpenAPIParameter(self._schema, self.path, **parameter)
def getRequestBodyValue(self, request):
# type: (HTTPRequest) -> Any
request_content_type = request.getHeader('content-type')
request_body = request.get('BODY') or ''
if request_content_type == 'application/xml':
raise NotImplementedError
elif request_content_type == 'application/x-www-form-urlencoded':
request_body = dict(urllib.parse.parse_qsl(request_body, True))
elif request_content_type == 'application/json':
request_body = json.loads(request_body)
return request_body
def getRequestBodyJSONSchema(self, request):
# type: (HTTPRequest) -> Optional[dict]
"""Returns the schema for the request body, or None if no `requestBody` defined
"""
exact_request_content_type = request.getHeader('content-type')
wildcard_request_content_type = '%s/*' % ((exact_request_content_type or '').split('/')[0])
for request_content_type in exact_request_content_type, wildcard_request_content_type, '*/*':
# TODO there might be $ref ?
request_body_definition = self.get(
'requestBody', {'content': {}})['content'].get(request_content_type)
if request_body_definition:
return SchemaWithComponents(
self._schema, request_body_definition.get('schema', {}))
class OpenAPIParameter(dict):
__allow_access_to_unprotected_subobjects__ = True
def __init__(self, _schema, _path, **kwargs):
dict.__init__(self, **kwargs)
self._schema = _schema
self._path = _path
def _getType(self, local_schema):
"""Get the `type` key of a local schema, resolving refs in global schema if needed.
"""
if local_schema:
if 'type' in local_schema:
return local_schema['type']
if '$ref' in local_schema:
ref = local_schema['$ref']
if ref and ref[0] == '#':
return jsonpointer.resolve_pointer(
self._schema,
ref[1:],
{},
).get('type')
def getValue(self, request):
# type: (HTTPRequest) -> Any
"""Extract the value of this paremeter from request, returns
the value, after trying to cast it to expected type.
In case of errors during cast, the value is returned as is (typically as a str).
"""
def deserialize_array(param_name, param_value, style, explode):
# type: (str, str, str, bool) -> list[str]
if style == 'simple':
return param_value.split(',')
if style == 'label':
if param_value[0] != '.':
raise ValueError(param_name, param_value)
return param_value[1:].split('.' if explode else ',')
if style == 'matrix':
matrix_key = ";{param_name}=".format(param_name=param_name)
if not param_value.startswith(matrix_key):
raise ValueError(param_name, param_value)
return param_value[len(matrix_key):].split(
matrix_key if explode else ',')
if style == 'form':
return param_value.split(',')
if style == 'spaceDelimited':
return param_value.split(' ')
if style == 'pipeDelimited':
return param_value.split('|')
raise SchemaDefinitionError(
"Unsupported serialization style {style}".format(style=style))
def deserialize_primitive_type(param_name, param_value, style):
# type: (str, str, str) -> list[str]
if style == 'label':
if param_value[0] != '.':
raise ValueError(param_name, param_value)
return param_value[1:]
if style == 'matrix':
matrix_key = ";{param_name}=".format(param_name=param_name)
if not param_value.startswith(matrix_key):
raise ValueError(param_name, param_value)
return param_value[len(matrix_key):]
return param_value
param_name = self['name']
param_in = self['in']
if param_in == 'path':
param_value = None
template = '{%s}' % param_name
for path_element, request_path_element in zip(
self._path.split('/')[1:], request.other['traverse_subpath']):
if template in path_element:
param_value = request_path_element
break
style = self.get('style', 'simple')
explode = self.get('explode', False)
elif param_in == 'query':
style = self.get('style', 'form')
explode = self.get('explode', True)
param_value = []
# Note that we parsed "again" QUERY_STRING and do not look in request.form
# because request.form is set only for GET requests.
parsed_qsl = urllib.parse.parse_qsl(
request.environ['QUERY_STRING'], True)
for k, v in parsed_qsl:
if k == param_name:
param_value.append(v)
if len(param_value) == 1:
param_value = param_value[0]
elif param_in == 'header':
param_value = request.getHeader(param_name)
style = self.get('style', 'simple')
explode = self.get('explode', False)
elif param_in == 'cookie':
param_value = request.cookies.get(param_name)
style = self.get('style', 'form')
explode = self.get('explode', True)
if not param_value:
if self.get('required'):
raise MissingParameterError(param_name)
param_value = self.get('schema', {}).get('default', param_value)
else:
parameter_type = self._getType(self.get('schema'))
if parameter_type == 'array':
if not isinstance(param_value, list):
param_value = deserialize_array(
param_name,
param_value,
style,
explode,
)
if self._getType(self['schema'].get('items')) in ('integer', 'number'):
try:
param_value = [json.loads(item) for item in param_value]
except (TypeError, ValueError):
pass
else:
param_value = deserialize_primitive_type(
param_name, param_value, style)
if parameter_type in ('integer', 'number'):
try:
param_value = json.loads(param_value)
except (TypeError, ValueError):
pass
return param_value
def getJSONSchema(self):
"""Returns the schema for this parameter
"""
return SchemaWithComponents(self._schema, self.get('schema', {}))
class OpenAPITypeInformation(ERP5TypeInformation):
"""
"""
portal_type = 'Open API Type'
meta_type = 'ERP5 Open API Type'
# Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Data,
PropertySheet.TextDocument,
)
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
def getSchema(self):
text_content = self.getTextContent() or '{}'
if six.PY3:
text_content = text_content.encode()
stream = io.BytesIO(text_content)
if self.getContentType() == 'application/x-yaml':
try:
import yaml # pylint:disable=import-error
except ImportError:
warnings.warn(
ImportWarning, "yaml not available, falling back to json")
else:
# XXX PyYAML does not seem to keep the order of keys on py2
# ( https://gist.github.com/enaeseth/844388 could be a workaround )
return yaml.load(stream, yaml.SafeLoader)
if six.PY2:
return json.load(stream, object_pairs_hook=collections.OrderedDict)
else:
return json.load(stream)
security.declareProtected(
Permissions.AccessContentsInformation, 'getOpenAPIOperationIterator')
def getOpenAPIOperationIterator(self):
# type: () -> typing.Iterator[OpenAPIOperation]
"""Iterator over the operations defined in the schema
"""
schema = self.getSchema()
http_verbs = {
'get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'
}
def iter_path_item(path, path_item):
for request_method, operation in six.iteritems(path_item):
if request_method == '$ref':
if operation and operation[0] == '#':
# BBB py2 yield from
for _ in iter_path_item(
path,
jsonpointer.resolve_pointer(schema, operation[1:]),
):
yield _
if request_method in http_verbs:
yield OpenAPIOperation(
schema, path, request_method, path_item, **operation)
for path, path_item in six.iteritems(schema.get('paths', {})):
# BBB py2 yield from
for _ in iter_path_item(path, path_item):
yield _
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>OpenAPITypeInformation</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.OpenAPITypeInformation</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<allowed_content_type_list>
<portal_type id="Open API Type">
<item>Action Information</item>
<item>Role Information</item>
</portal_type>
<portal_type id="Types Tool">
<item>Open API Type</item>
</portal_type>
</allowed_content_type_list>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_property_domain_dict</string> </key>
<value>
<dictionary>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_meta_type</string> </key>
<value> <string>ERP5 Base Type</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addERP5TypeInformation</string> </value>
</item>
<item>
<key> <string>filter_content_types</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple>
<string>type_definition</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Open API Type</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value> <string>OpenAPIType_init</string> </value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple>
<string>id</string>
<string>title</string>
<string>description</string>
<string>reference</string>
<string>short_title</string>
</tuple>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>OpenAPITypeInformation</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_ui</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>short_title</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_ui</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>title</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<workflow_chain>
<chain>
<type>Open API Type</type>
<workflow>base_type_interaction_workflow, dynamic_class_generation_interaction_workflow</workflow>
</chain>
</workflow_chain>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_open_api</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>your_swagger_ui</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OpenAPIService_view</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>OpenAPIType_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Open API</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>gadget_url</string>
<string>js_sandbox</string>
<string>renderjs_extra</string>
<string>title</string>
<string>validator_field_id</string>
<string>validator_form_id</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_swagger_ui</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>
<item>
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>js_sandbox</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string>swagger-ui.gadget.html</string> </value>
</item>
<item>
<key> <string>js_sandbox</string> </key>
<value> <string>iframe</string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Swagger UI</string> </value>
</item>
<item>
<key> <string>validator_field_id</string> </key>
<value> <string>my_core_mode_text_content_validator</string> </value>
</item>
<item>
<key> <string>validator_form_id</string> </key>
<value> <string>erp5_core/Base_viewFieldLibrary</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(\'openapi_url\', \'%s/viewOpenAPIAsJson\' % context.absolute_url())]</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
import six
from Products.ERP5Type.Message import translateString
portal = context.getPortalObject()
skin_folder = portal.portal_skins[skin_folder_id]
type_id = context.getId().replace(' ', '')
for operation in context.getOpenAPIOperationIterator():
operation_id = operation.get('operationId')
if operation_id:
script_id = '{type_id}_{operation_id}'.format(
type_id=type_id, operation_id=operation_id)
if six.PY2:
script_id = script_id.encode()
if script_id not in skin_folder.objectIds():
skin_folder.manage_addProduct['ERP5'].addPythonScriptThroughZMI(
script_id)
python_script = skin_folder[script_id]
params = ', '.join(
[param['name'] for param in operation.getParameters()]
+ (['body'] if operation.get('requestBody') else []))
python_script.setParameterSignature(params)
python_script.setBody(
'''
"""{description}
{request_method} {path}
"""
'''.format(
description=operation.get('description') or '',
request_method=operation.request_method.upper(),
path=operation.path))
return context.Base_redirect(
form_id,
keep_items={
'portal_status_message': translateString('Python Scripts Generated'),
})
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>skin_folder_id, form_id=\'\', **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OpenAPIType_generatePythonScriptForOperations</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""Returns operations in a format suitable for a listbox's list method
"""
from ZTUtils import make_query
from Products.PythonScripts.standard import Object
portal = context.getPortalObject()
service = portal.newContent(portal_type=context.getId(), temp_object=True)
def makeOperation(method, **kw):
if method is not None:
kw['method_id'] = method.getId()
kw['absolute_url'] = method.absolute_url
kw['getObject'] = method.getObject
else:
kw['absolute_url'] = context.absolute_url
kw['getObject'] = context.getObject
def getListItemUrl(cname_id, selection_index, selection_name):
if method is None:
return None
return '{}/view?{}'.format(
method.absolute_url(),
make_query(
selection_index=selection_index, selection_name=selection_name))
kw['getListItemUrl'] = getListItemUrl
return Object(**kw)
operation_list = []
for operation in context.getOpenAPIOperationIterator():
operation_id = operation.get('operationId')
method = None
if operation_id:
method = service.getTypeBasedMethod(operation_id)
operation_list.append(
makeOperation(
method,
uid='new_{}'.format(operation_id),
operation_id=operation_id,
path=operation.path,
request_method=operation.request_method.upper(),
))
return operation_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Python Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>**kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OpenAPIType_getOperationList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
context.newContent(
portal_type='Action Information',
reference='view',
title='Open API',
action_type='object_view',
action='string:${object_url}/OpenAPIService_view',
)
context.setTextContent('{}')
context.setContentType('application/json')
context.setTypeClass('OpenAPIService')
context.setTypeWorkflowList(['edit_workflow', 'validation_workflow'])
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>*args, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OpenAPIType_init</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>my_text_content</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_content_type</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OpenAPIType_view</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>OpenAPIType_view</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Open API Type</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ListField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_content_type</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>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>unknown_selection</string> </key>
<value> <string>You selected an item that was not in the list.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>items</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>items</string> </key>
<value>
<list>
<tuple>
<string>JSON</string>
<string>application/json</string>
</tuple>
<tuple>
<string>YAML</string>
<string>application/x-yaml</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Content Type</string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>gadget_url</string>
<string>js_sandbox</string>
<string>renderjs_extra</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_text_content</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>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>js_sandbox</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</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></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_text_content</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string>monaco-editor.gadget.html</string> </value>
</item>
<item>
<key> <string>js_sandbox</string> </key>
<value> <string>iframe</string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Open API JSON</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(\'content_type\', context.getContentType()), (\'schema_url\', "%s/OpenAPIType_viewOpenAPIJSONSchema" % context.absolute_url())]</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>OpenAPIType_generatePythonScriptForOperations</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>your_skin_folder_id</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OpenAPIType_viewGeneratePythonScriptForOperationsDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>OpenAPIType_viewGeneratePythonScriptForOperationsDialog</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Generate Python Scripts for Operations</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StringField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>your_skin_folder_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>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>Too much input was given.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>input_type</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>input_type</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_maxwidth</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>input_type</string> </key>
<value> <string>text</string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Skin Folder ID</string> </value>
</item>
<item>
<key> <string>truncate</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
{
"id": "https://spec.openapis.org/oas/3.0/schema/2021-09-28",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "The description of OpenAPI v3.0.x documents, as defined by https://spec.openapis.org/oas/v3.0.3",
"type": "object",
"required": [
"openapi",
"info",
"paths"
],
"properties": {
"openapi": {
"type": "string",
"pattern": "^3\\.0\\.\\d(-.+)?$"
},
"info": {
"$ref": "#/definitions/Info"
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
},
"servers": {
"type": "array",
"items": {
"$ref": "#/definitions/Server"
}
},
"security": {
"type": "array",
"items": {
"$ref": "#/definitions/SecurityRequirement"
}
},
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/Tag"
},
"uniqueItems": true
},
"paths": {
"$ref": "#/definitions/Paths"
},
"components": {
"$ref": "#/definitions/Components"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"definitions": {
"Reference": {
"type": "object",
"required": [
"$ref"
],
"patternProperties": {
"^\\$ref$": {
"type": "string",
"format": "uri-reference"
}
}
},
"Info": {
"type": "object",
"required": [
"title",
"version"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"termsOfService": {
"type": "string",
"format": "uri-reference"
},
"contact": {
"$ref": "#/definitions/Contact"
},
"license": {
"$ref": "#/definitions/License"
},
"version": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Contact": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri-reference"
},
"email": {
"type": "string",
"format": "email"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"License": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri-reference"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Server": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string"
},
"description": {
"type": "string"
},
"variables": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ServerVariable"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ServerVariable": {
"type": "object",
"required": [
"default"
],
"properties": {
"enum": {
"type": "array",
"items": {
"type": "string"
}
},
"default": {
"type": "string"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Components": {
"type": "object",
"properties": {
"schemas": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"responses": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Response"
}
]
}
}
},
"parameters": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Parameter"
}
]
}
}
},
"examples": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Example"
}
]
}
}
},
"requestBodies": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/RequestBody"
}
]
}
}
},
"headers": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Header"
}
]
}
}
},
"securitySchemes": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/SecurityScheme"
}
]
}
}
},
"links": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Link"
}
]
}
}
},
"callbacks": {
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9\\.\\-_]+$": {
"oneOf": [
{
"$ref": "#/definitions/Reference"
},
{
"$ref": "#/definitions/Callback"
}
]
}
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Schema": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"multipleOf": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true
},
"maximum": {
"type": "number"
},
"exclusiveMaximum": {
"type": "boolean",
"default": false
},
"minimum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"default": false
},
"maxLength": {
"type": "integer",
"minimum": 0
},
"minLength": {
"type": "integer",
"minimum": 0,
"default": 0
},
"pattern": {
"type": "string",
"format": "regex"
},
"maxItems": {
"type": "integer",
"minimum": 0
},
"minItems": {
"type": "integer",
"minimum": 0,
"default": 0
},
"uniqueItems": {
"type": "boolean",
"default": false
},
"maxProperties": {
"type": "integer",
"minimum": 0
},
"minProperties": {
"type": "integer",
"minimum": 0,
"default": 0
},
"required": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"enum": {
"type": "array",
"items": {
},
"minItems": 1,
"uniqueItems": false
},
"type": {
"type": "string",
"enum": [
"array",
"boolean",
"integer",
"number",
"object",
"string"
]
},
"not": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"allOf": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"oneOf": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"anyOf": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"items": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"properties": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
},
{
"type": "boolean"
}
],
"default": true
},
"description": {
"type": "string"
},
"format": {
"type": "string"
},
"default": {
},
"nullable": {
"type": "boolean",
"default": false
},
"discriminator": {
"$ref": "#/definitions/Discriminator"
},
"readOnly": {
"type": "boolean",
"default": false
},
"writeOnly": {
"type": "boolean",
"default": false
},
"example": {
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
},
"deprecated": {
"type": "boolean",
"default": false
},
"xml": {
"$ref": "#/definitions/XML"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Discriminator": {
"type": "object",
"required": [
"propertyName"
],
"properties": {
"propertyName": {
"type": "string"
},
"mapping": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
},
"XML": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"namespace": {
"type": "string",
"format": "uri"
},
"prefix": {
"type": "string"
},
"attribute": {
"type": "boolean",
"default": false
},
"wrapped": {
"type": "boolean",
"default": false
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Response": {
"type": "object",
"required": [
"description"
],
"properties": {
"description": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Header"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
}
},
"links": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Link"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"MediaType": {
"type": "object",
"properties": {
"schema": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"example": {
},
"examples": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Example"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"encoding": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Encoding"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"allOf": [
{
"$ref": "#/definitions/ExampleXORExamples"
}
]
},
"Example": {
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"value": {
},
"externalValue": {
"type": "string",
"format": "uri-reference"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Header": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"required": {
"type": "boolean",
"default": false
},
"deprecated": {
"type": "boolean",
"default": false
},
"allowEmptyValue": {
"type": "boolean",
"default": false
},
"style": {
"type": "string",
"enum": [
"simple"
],
"default": "simple"
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"type": "boolean",
"default": false
},
"schema": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
},
"minProperties": 1,
"maxProperties": 1
},
"example": {
},
"examples": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Example"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"allOf": [
{
"$ref": "#/definitions/ExampleXORExamples"
},
{
"$ref": "#/definitions/SchemaXORContent"
}
]
},
"Paths": {
"type": "object",
"patternProperties": {
"^\\/": {
"$ref": "#/definitions/PathItem"
},
"^x-": {
}
},
"additionalProperties": false
},
"PathItem": {
"type": "object",
"properties": {
"$ref": {
"type": "string"
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"servers": {
"type": "array",
"items": {
"$ref": "#/definitions/Server"
}
},
"parameters": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Parameter"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"uniqueItems": true
}
},
"patternProperties": {
"^(get|put|post|delete|options|head|patch|trace)$": {
"$ref": "#/definitions/Operation"
},
"^x-": {
}
},
"additionalProperties": false
},
"Operation": {
"type": "object",
"required": [
"responses"
],
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
},
"operationId": {
"type": "string"
},
"parameters": {
"type": "array",
"items": {
"oneOf": [
{
"$ref": "#/definitions/Parameter"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"uniqueItems": true
},
"requestBody": {
"oneOf": [
{
"$ref": "#/definitions/RequestBody"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"responses": {
"$ref": "#/definitions/Responses"
},
"callbacks": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Callback"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"deprecated": {
"type": "boolean",
"default": false
},
"security": {
"type": "array",
"items": {
"$ref": "#/definitions/SecurityRequirement"
}
},
"servers": {
"type": "array",
"items": {
"$ref": "#/definitions/Server"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Responses": {
"type": "object",
"properties": {
"default": {
"oneOf": [
{
"$ref": "#/definitions/Response"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"patternProperties": {
"^[1-5](?:\\d{2}|XX)$": {
"oneOf": [
{
"$ref": "#/definitions/Response"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"^x-": {
}
},
"minProperties": 1,
"additionalProperties": false
},
"SecurityRequirement": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"Tag": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "#/definitions/ExternalDocumentation"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ExternalDocumentation": {
"type": "object",
"required": [
"url"
],
"properties": {
"description": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri-reference"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ExampleXORExamples": {
"description": "Example and examples are mutually exclusive",
"not": {
"required": [
"example",
"examples"
]
}
},
"SchemaXORContent": {
"description": "Schema and content are mutually exclusive, at least one is required",
"not": {
"required": [
"schema",
"content"
]
},
"oneOf": [
{
"required": [
"schema"
]
},
{
"required": [
"content"
],
"description": "Some properties are not allowed if content is present",
"allOf": [
{
"not": {
"required": [
"style"
]
}
},
{
"not": {
"required": [
"explode"
]
}
},
{
"not": {
"required": [
"allowReserved"
]
}
},
{
"not": {
"required": [
"example"
]
}
},
{
"not": {
"required": [
"examples"
]
}
}
]
}
]
},
"Parameter": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"in": {
"type": "string"
},
"description": {
"type": "string"
},
"required": {
"type": "boolean",
"default": false
},
"deprecated": {
"type": "boolean",
"default": false
},
"allowEmptyValue": {
"type": "boolean",
"default": false
},
"style": {
"type": "string"
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"type": "boolean",
"default": false
},
"schema": {
"oneOf": [
{
"$ref": "#/definitions/Schema"
},
{
"$ref": "#/definitions/Reference"
}
]
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
},
"minProperties": 1,
"maxProperties": 1
},
"example": {
},
"examples": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Example"
},
{
"$ref": "#/definitions/Reference"
}
]
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"required": [
"name",
"in"
],
"allOf": [
{
"$ref": "#/definitions/ExampleXORExamples"
},
{
"$ref": "#/definitions/SchemaXORContent"
},
{
"$ref": "#/definitions/ParameterLocation"
}
]
},
"ParameterLocation": {
"description": "Parameter location",
"oneOf": [
{
"description": "Parameter in path",
"required": [
"required"
],
"properties": {
"in": {
"enum": [
"path"
]
},
"style": {
"enum": [
"matrix",
"label",
"simple"
],
"default": "simple"
},
"required": {
"enum": [
true
]
}
}
},
{
"description": "Parameter in query",
"properties": {
"in": {
"enum": [
"query"
]
},
"style": {
"enum": [
"form",
"spaceDelimited",
"pipeDelimited",
"deepObject"
],
"default": "form"
}
}
},
{
"description": "Parameter in header",
"properties": {
"in": {
"enum": [
"header"
]
},
"style": {
"enum": [
"simple"
],
"default": "simple"
}
}
},
{
"description": "Parameter in cookie",
"properties": {
"in": {
"enum": [
"cookie"
]
},
"style": {
"enum": [
"form"
],
"default": "form"
}
}
}
]
},
"RequestBody": {
"type": "object",
"required": [
"content"
],
"properties": {
"description": {
"type": "string"
},
"content": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/MediaType"
}
},
"required": {
"type": "boolean",
"default": false
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"SecurityScheme": {
"oneOf": [
{
"$ref": "#/definitions/APIKeySecurityScheme"
},
{
"$ref": "#/definitions/HTTPSecurityScheme"
},
{
"$ref": "#/definitions/OAuth2SecurityScheme"
},
{
"$ref": "#/definitions/OpenIdConnectSecurityScheme"
}
]
},
"APIKeySecurityScheme": {
"type": "object",
"required": [
"type",
"name",
"in"
],
"properties": {
"type": {
"type": "string",
"enum": [
"apiKey"
]
},
"name": {
"type": "string"
},
"in": {
"type": "string",
"enum": [
"header",
"query",
"cookie"
]
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"HTTPSecurityScheme": {
"type": "object",
"required": [
"scheme",
"type"
],
"properties": {
"scheme": {
"type": "string"
},
"bearerFormat": {
"type": "string"
},
"description": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"http"
]
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"oneOf": [
{
"description": "Bearer",
"properties": {
"scheme": {
"type": "string",
"pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$"
}
}
},
{
"description": "Non Bearer",
"not": {
"required": [
"bearerFormat"
]
},
"properties": {
"scheme": {
"not": {
"type": "string",
"pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$"
}
}
}
}
]
},
"OAuth2SecurityScheme": {
"type": "object",
"required": [
"type",
"flows"
],
"properties": {
"type": {
"type": "string",
"enum": [
"oauth2"
]
},
"flows": {
"$ref": "#/definitions/OAuthFlows"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"OpenIdConnectSecurityScheme": {
"type": "object",
"required": [
"type",
"openIdConnectUrl"
],
"properties": {
"type": {
"type": "string",
"enum": [
"openIdConnect"
]
},
"openIdConnectUrl": {
"type": "string",
"format": "uri-reference"
},
"description": {
"type": "string"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"OAuthFlows": {
"type": "object",
"properties": {
"implicit": {
"$ref": "#/definitions/ImplicitOAuthFlow"
},
"password": {
"$ref": "#/definitions/PasswordOAuthFlow"
},
"clientCredentials": {
"$ref": "#/definitions/ClientCredentialsFlow"
},
"authorizationCode": {
"$ref": "#/definitions/AuthorizationCodeOAuthFlow"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ImplicitOAuthFlow": {
"type": "object",
"required": [
"authorizationUrl",
"scopes"
],
"properties": {
"authorizationUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"PasswordOAuthFlow": {
"type": "object",
"required": [
"tokenUrl",
"scopes"
],
"properties": {
"tokenUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"ClientCredentialsFlow": {
"type": "object",
"required": [
"tokenUrl",
"scopes"
],
"properties": {
"tokenUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"AuthorizationCodeOAuthFlow": {
"type": "object",
"required": [
"authorizationUrl",
"tokenUrl",
"scopes"
],
"properties": {
"authorizationUrl": {
"type": "string",
"format": "uri-reference"
},
"tokenUrl": {
"type": "string",
"format": "uri-reference"
},
"refreshUrl": {
"type": "string",
"format": "uri-reference"
},
"scopes": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false
},
"Link": {
"type": "object",
"properties": {
"operationId": {
"type": "string"
},
"operationRef": {
"type": "string",
"format": "uri-reference"
},
"parameters": {
"type": "object",
"additionalProperties": {
}
},
"requestBody": {
},
"description": {
"type": "string"
},
"server": {
"$ref": "#/definitions/Server"
}
},
"patternProperties": {
"^x-": {
}
},
"additionalProperties": false,
"not": {
"description": "Operation Id and Operation Ref are mutually exclusive",
"required": [
"operationId",
"operationRef"
]
}
},
"Callback": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/PathItem"
},
"patternProperties": {
"^x-": {
}
}
},
"Encoding": {
"type": "object",
"properties": {
"contentType": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"$ref": "#/definitions/Header"
},
{
"$ref": "#/definitions/Reference"
}
]
}
},
"style": {
"type": "string",
"enum": [
"form",
"spaceDelimited",
"pipeDelimited",
"deepObject"
]
},
"explode": {
"type": "boolean"
},
"allowReserved": {
"type": "boolean",
"default": false
}
},
"additionalProperties": false
}
}
}
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>OpenAPIType_viewOpenAPIJSONSchema</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/json</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>schema.json</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>listbox</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list>
<string>my_title</string>
</list>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>OpenAPIType_viewOperationList</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>OpenAPIType_view</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Open API Operations</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>columns</string>
<string>list_method</string>
<string>sort_columns</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox</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>columns</string> </key>
<value>
<list>
<tuple>
<string>request_method</string>
<string>Request Method</string>
</tuple>
<tuple>
<string>path</string>
<string>Request Path</string>
</tuple>
<tuple>
<string>operation_id</string>
<string>Operation ID</string>
</tuple>
<tuple>
<string>method_id</string>
<string>Method ID</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>list_method</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>sort_columns</string> </key>
<value>
<list>
<tuple>
<string>none</string>
<string>none</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Operations</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>OpenAPIType_getOperationList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>editable</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_title</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>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_dialog_mode_title</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="./rsvp.js"></script>
<script src="./renderjs.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="SwaggerUI" />
<title>SwaggerUI</title>
<link rel="stylesheet" href="./swagger-ui/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui/swagger-ui-bundle.js"></script>
<script src="./swagger-ui.gadget.js"></script>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>swagger-ui.gadget.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*jslint nomen: true, indent: 2 */
/*global window, rJS, SwaggerUIBundle */
(function (window, rJS, SwaggerUIBundle) {
'use strict';
rJS(window)
.declareMethod('render', function (options) {
var state_dict = {
key: options.key,
url: options.openapi_url
};
return this.changeState(state_dict);
})
.onStateChange(function (modification_dict) {
var queue = new RSVP.Queue();
if (modification_dict.hasOwnProperty('url')) {
SwaggerUIBundle({
url: modification_dict.url,
dom_id: '#swagger-ui'
});
}
return queue;
})
.declareMethod('getContent', function () {
return {};
});
})(window, rJS, window['SwaggerUIBundle']);
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>swagger-ui.gadget.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>swagger-ui</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>swagger-ui-bundle.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>swagger-ui-bundle.js</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>swagger-ui.css</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/css</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>swagger-ui.css</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
{
"openapi": "3.0.2",
"info": {
"title": "Swagger Petstore - OpenAPI 3.0",
"description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"email": "apiteam@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0.17"
},
"externalDocs": {
"description": "Find out more about Swagger",
"url": "http://swagger.io"
},
"servers": [
{
"url": "/api/v3"
}
],
"tags": [
{
"name": "pet",
"description": "Everything about your Pets",
"externalDocs": {
"description": "Find out more",
"url": "http://swagger.io"
}
},
{
"name": "store",
"description": "Access to Petstore orders",
"externalDocs": {
"description": "Find out more about our store",
"url": "http://swagger.io"
}
},
{
"name": "user",
"description": "Operations about user"
}
],
"paths": {
"/pet": {
"put": {
"tags": [
"pet"
],
"summary": "Update an existing pet",
"description": "Update an existing pet by Id",
"operationId": "updatePet",
"requestBody": {
"description": "Update an existent pet in the store",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
},
"405": {
"description": "Validation exception"
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
},
"post": {
"tags": [
"pet"
],
"summary": "Add a new pet to the store",
"description": "Add a new pet to the store",
"operationId": "addPet",
"requestBody": {
"description": "Create a new pet in the store",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"405": {
"description": "Invalid input"
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/pet/findByStatus": {
"get": {
"tags": [
"pet"
],
"summary": "Finds Pets by status",
"description": "Multiple status values can be provided with comma separated strings",
"operationId": "findPetsByStatus",
"parameters": [
{
"name": "status",
"in": "query",
"description": "Status values that need to be considered for filter",
"required": false,
"explode": true,
"schema": {
"type": "string",
"default": "available",
"enum": [
"available",
"pending",
"sold"
]
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/xml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
},
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
}
}
},
"400": {
"description": "Invalid status value"
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/pet/findByTags": {
"get": {
"tags": [
"pet"
],
"summary": "Finds Pets by tags",
"description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
"operationId": "findPetsByTags",
"parameters": [
{
"name": "tags",
"in": "query",
"description": "Tags to filter by",
"required": false,
"explode": true,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/xml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
},
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
}
}
},
"400": {
"description": "Invalid tag value"
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/pet/{petId}": {
"get": {
"tags": [
"pet"
],
"summary": "Find pet by ID",
"description": "Returns a single pet",
"operationId": "getPetById",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of pet to return",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
}
},
"security": [
{
"api_key": []
},
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
},
"post": {
"tags": [
"pet"
],
"summary": "Updates a pet in the store with form data",
"description": "",
"operationId": "updatePetWithForm",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of pet that needs to be updated",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "name",
"in": "query",
"description": "Name of pet that needs to be updated",
"schema": {
"type": "string"
}
},
{
"name": "status",
"in": "query",
"description": "Status of pet that needs to be updated",
"schema": {
"type": "string"
}
}
],
"responses": {
"405": {
"description": "Invalid input"
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
},
"delete": {
"tags": [
"pet"
],
"summary": "Deletes a pet",
"description": "",
"operationId": "deletePet",
"parameters": [
{
"name": "api_key",
"in": "header",
"description": "",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "petId",
"in": "path",
"description": "Pet id to delete",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"400": {
"description": "Invalid pet value"
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/pet/{petId}/uploadImage": {
"post": {
"tags": [
"pet"
],
"summary": "uploads an image",
"description": "",
"operationId": "uploadFile",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of pet to update",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "additionalMetadata",
"in": "query",
"description": "Additional Metadata",
"required": false,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponse"
}
}
}
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/store/inventory": {
"get": {
"tags": [
"store"
],
"summary": "Returns pet inventories by status",
"description": "Returns a map of status codes to quantities",
"operationId": "getInventory",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "int32"
}
}
}
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/store/order": {
"post": {
"tags": [
"store"
],
"summary": "Place an order for a pet",
"description": "Place a new order in the store",
"operationId": "placeOrder",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Order"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"405": {
"description": "Invalid input"
}
}
}
},
"/store/order/{orderId}": {
"get": {
"tags": [
"store"
],
"summary": "Find purchase order by ID",
"description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.",
"operationId": "getOrderById",
"parameters": [
{
"name": "orderId",
"in": "path",
"description": "ID of order that needs to be fetched",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Order"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
},
"delete": {
"tags": [
"store"
],
"summary": "Delete purchase order by ID",
"description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
"operationId": "deleteOrder",
"parameters": [
{
"name": "orderId",
"in": "path",
"description": "ID of the order that needs to be deleted",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
}
},
"/user": {
"post": {
"tags": [
"user"
],
"summary": "Create user",
"description": "This can only be done by the logged in user.",
"operationId": "createUser",
"requestBody": {
"description": "Created user object",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"responses": {
"default": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
},
"/user/createWithList": {
"post": {
"tags": [
"user"
],
"summary": "Creates list of users with given input array",
"description": "Creates list of users with given input array",
"operationId": "createUsersWithListInput",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
}
}
}
},
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/xml": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"default": {
"description": "successful operation"
}
}
}
},
"/user/login": {
"get": {
"tags": [
"user"
],
"summary": "Logs user into the system",
"description": "",
"operationId": "loginUser",
"parameters": [
{
"name": "username",
"in": "query",
"description": "The user name for login",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "password",
"in": "query",
"description": "The password for login in clear text",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"headers": {
"X-Rate-Limit": {
"description": "calls per hour allowed by the user",
"schema": {
"type": "integer",
"format": "int32"
}
},
"X-Expires-After": {
"description": "date in UTC when token expires",
"schema": {
"type": "string",
"format": "date-time"
}
}
},
"content": {
"application/xml": {
"schema": {
"type": "string"
}
},
"application/json": {
"schema": {
"type": "string"
}
}
}
},
"400": {
"description": "Invalid username/password supplied"
}
}
}
},
"/user/logout": {
"get": {
"tags": [
"user"
],
"summary": "Logs out current logged in user session",
"description": "",
"operationId": "logoutUser",
"parameters": [],
"responses": {
"default": {
"description": "successful operation"
}
}
}
},
"/user/{username}": {
"get": {
"tags": [
"user"
],
"summary": "Get user by user name",
"description": "",
"operationId": "getUserByName",
"parameters": [
{
"name": "username",
"in": "path",
"description": "The name that needs to be fetched. Use user1 for testing. ",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/xml": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"400": {
"description": "Invalid username supplied"
},
"404": {
"description": "User not found"
}
}
},
"put": {
"tags": [
"user"
],
"summary": "Update user",
"description": "This can only be done by the logged in user.",
"operationId": "updateUser",
"parameters": [
{
"name": "username",
"in": "path",
"description": "name that need to be deleted",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Update an existent user in the store",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"responses": {
"default": {
"description": "successful operation"
}
}
},
"delete": {
"tags": [
"user"
],
"summary": "Delete user",
"description": "This can only be done by the logged in user.",
"operationId": "deleteUser",
"parameters": [
{
"name": "username",
"in": "path",
"description": "The name that needs to be deleted",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"400": {
"description": "Invalid username supplied"
},
"404": {
"description": "User not found"
}
}
}
}
},
"components": {
"schemas": {
"Order": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 10
},
"petId": {
"type": "integer",
"format": "int64",
"example": 198772
},
"quantity": {
"type": "integer",
"format": "int32",
"example": 7
},
"shipDate": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"description": "Order Status",
"example": "approved",
"enum": [
"placed",
"approved",
"delivered"
]
},
"complete": {
"type": "boolean"
}
},
"xml": {
"name": "order"
}
},
"Customer": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 100000
},
"username": {
"type": "string",
"example": "fehguy"
},
"address": {
"type": "array",
"xml": {
"name": "addresses",
"wrapped": true
},
"items": {
"$ref": "#/components/schemas/Address"
}
}
},
"xml": {
"name": "customer"
}
},
"Address": {
"type": "object",
"properties": {
"street": {
"type": "string",
"example": "437 Lytton"
},
"city": {
"type": "string",
"example": "Palo Alto"
},
"state": {
"type": "string",
"example": "CA"
},
"zip": {
"type": "string",
"example": "94301"
}
},
"xml": {
"name": "address"
}
},
"Category": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 1
},
"name": {
"type": "string",
"example": "Dogs"
}
},
"xml": {
"name": "category"
}
},
"User": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 10
},
"username": {
"type": "string",
"example": "theUser"
},
"firstName": {
"type": "string",
"example": "John"
},
"lastName": {
"type": "string",
"example": "James"
},
"email": {
"type": "string",
"example": "john@email.com"
},
"password": {
"type": "string",
"example": "12345"
},
"phone": {
"type": "string",
"example": "12345"
},
"userStatus": {
"type": "integer",
"description": "User Status",
"format": "int32",
"example": 1
}
},
"xml": {
"name": "user"
}
},
"Tag": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
}
},
"xml": {
"name": "tag"
}
},
"Pet": {
"required": [
"name",
"photoUrls"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 10
},
"name": {
"type": "string",
"example": "doggie"
},
"category": {
"$ref": "#/components/schemas/Category"
},
"photoUrls": {
"type": "array",
"xml": {
"wrapped": true
},
"items": {
"type": "string",
"xml": {
"name": "photoUrl"
}
}
},
"tags": {
"type": "array",
"xml": {
"wrapped": true
},
"items": {
"$ref": "#/components/schemas/Tag"
}
},
"status": {
"type": "string",
"description": "pet status in the store",
"enum": [
"available",
"pending",
"sold"
]
}
},
"xml": {
"name": "pet"
}
},
"ApiResponse": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"type": {
"type": "string"
},
"message": {
"type": "string"
}
},
"xml": {
"name": "##default"
}
}
},
"requestBodies": {
"Pet": {
"description": "Pet object that needs to be added to the store",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"UserArray": {
"description": "List of user object",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
},
"securitySchemes": {
"petstore_auth": {
"type": "oauth2",
"flows": {
"implicit": {
"authorizationUrl": "https://petstore3.swagger.io/oauth/authorize",
"scopes": {
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
}
}
},
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
}
}
}
}
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>test-petstore-swagger.json</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/json</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
##############################################################################
# coding: utf-8
# Copyright (c) 2002-2023 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
import six
# pylint:disable=no-name-in-module
if six.PY2:
from base64 import encodestring as base64_encodebytes
else:
from base64 import encodebytes as base64_encodebytes
# pylint:enable=no-name-in-module
import io
import json
import unittest
try:
import yaml
except ImportError:
yaml = None
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
class OpenAPITestCase(ERP5TypeTestCase):
_type_id = NotImplemented # type: str
_open_api_schema = NotImplemented # type: str
_open_api_schema_content_type = 'application/json'
_public_api = True
def afterSetUp(self):
open_api_type = self.portal.portal_types.newContent(
portal_type='Open API Type',
id=self._type_id,
text_content=self._open_api_schema,
)
open_api_type.setContentType(self._open_api_schema_content_type, )
if self._public_api:
open_api_type.setTypeWorkflowList(['publication_workflow'])
web_service_tool_type = self.portal.portal_types['Web Service Tool']
web_service_tool_type.setTypeAllowedContentTypeList(
sorted(
set(web_service_tool_type.getTypeAllowedContentTypeList())
| set([self._type_id])))
self.tic()
self.connector = self.portal.portal_web_services.newContent(
portal_type=self._type_id,
title=self.id(),
)
if self._public_api:
self.connector.publish()
else:
self.connector.validate()
self.tic()
self._python_script_id_to_cleanup = []
def beforeTearDown(self):
self.abort()
self.tic()
if self.portal.portal_types.get(self._type_id):
self.portal.portal_types.manage_delObjects([self._type_id])
web_service_tool_type = self.portal.portal_types['Web Service Tool']
web_service_tool_type.setTypeAllowedContentTypeList(
sorted(
set(web_service_tool_type.getTypeAllowedContentTypeList())
- set([self._type_id])))
connector_id_list = [
c.getId() for c in self.portal.portal_web_services.contentValues(
portal_type=self._type_id)
]
if connector_id_list:
self.portal.portal_web_services.manage_delObjects(connector_id_list)
skin_folder = self.portal.portal_skins['custom']
if self._python_script_id_to_cleanup:
skin_folder.manage_delObjects(self._python_script_id_to_cleanup)
self.tic()
def addPythonScript(self, script_id, params, body):
skin_folder = self.portal.portal_skins['custom']
skin_folder.manage_addProduct['ERP5'].addPythonScriptThroughZMI(
id=script_id)
self._python_script_id_to_cleanup.append(script_id)
self.script = skin_folder.get(script_id)
self.script.setParameterSignature(params)
self.script.setBody(body)
self.tic()
self.portal.changeSkin(None)
class OpenAPIPetStoreTestCase(OpenAPITestCase):
_type_id = 'Test Pet Store Open API'
@property
def _open_api_schema(self):
return bytes(
self.portal.portal_skins.erp5_open_api['test-petstore-swagger.json'])
class TestOpenAPIConnectorView(OpenAPIPetStoreTestCase):
def test_view(self):
ret = self.publish(self.connector.getPath(), user='ERP5TypeTestCase')
self.assertEqual(ret.getStatus(), 200)
self.assertEqual(ret.getHeader('content-type'), 'text/html; charset=utf-8')
self.assertIn(b'<html', ret.getBody())
def test_viewOpenAPIAsJson(self):
ret = self.publish(
self.connector.getPath() + '/viewOpenAPIAsJson', user='ERP5TypeTestCase')
self.assertEqual(ret.getStatus(), 200)
body = json.load(io.BytesIO(ret.getBody()))
server_url = body['servers'][0]['url']
self.assertIn(self.connector.getPath(), server_url)
def test_erp5_document_methods(self):
self.connector.setTitle('Pet Store')
self.assertEqual(self.connector.getTitle(), 'Pet Store')
self.tic()
ret = self.publish(
self.connector.getPath() + '/getTitle', user='ERP5TypeTestCase')
self.assertEqual(ret.getStatus(), 200)
self.assertEqual(ret.getBody(), b'Pet Store')
def test_portal_skins_acquisition(self):
self.assertEqual(self.connector.OpenAPIService_view.getId(), 'OpenAPIService_view')
ret = self.publish(
self.connector.getPath() + '/OpenAPIService_view', user='ERP5TypeTestCase')
self.assertEqual(ret.getStatus(), 200)
self.assertNotIn(b'Error', ret.getBody())
def test_non_existing_attribute(self):
with self.assertRaises(AttributeError):
_ = self.connector.non_existing_attribute
ret = self.publish(
self.connector.getPath() + '/non_existing_attribute',
user='ERP5TypeTestCase')
self.assertEqual(ret.getStatus(), 404)
class TestOpenScriptGeneration(OpenAPIPetStoreTestCase):
def test_generate_scripts(self):
self.portal.portal_skins.manage_addProduct['OFSP'].manage_addFolder(
self.id())
skin_folder = self.portal.portal_skins[self.id()]
self.portal.portal_types[
'Test Pet Store Open API'].OpenAPIType_generatePythonScriptForOperations(
self.id())
self.assertEqual(
sorted(skin_folder.objectIds()), [
'TestPetStoreOpenAPI_addPet',
'TestPetStoreOpenAPI_createUser',
'TestPetStoreOpenAPI_createUsersWithListInput',
'TestPetStoreOpenAPI_deleteOrder',
'TestPetStoreOpenAPI_deletePet',
'TestPetStoreOpenAPI_deleteUser',
'TestPetStoreOpenAPI_findPetsByStatus',
'TestPetStoreOpenAPI_findPetsByTags',
'TestPetStoreOpenAPI_getInventory',
'TestPetStoreOpenAPI_getOrderById',
'TestPetStoreOpenAPI_getPetById',
'TestPetStoreOpenAPI_getUserByName',
'TestPetStoreOpenAPI_loginUser',
'TestPetStoreOpenAPI_logoutUser',
'TestPetStoreOpenAPI_placeOrder',
'TestPetStoreOpenAPI_updatePet',
'TestPetStoreOpenAPI_updatePetWithForm',
'TestPetStoreOpenAPI_updateUser',
'TestPetStoreOpenAPI_uploadFile',
])
self.assertEqual(
skin_folder.TestPetStoreOpenAPI_deletePet.getParameterSignature(),
'api_key, petId')
self.assertEqual(
skin_folder.TestPetStoreOpenAPI_createUser.getParameterSignature(),
'body')
self.assertEqual(
skin_folder.TestPetStoreOpenAPI_updateUser.getParameterSignature(),
'username, body')
self.assertEqual(
skin_folder.TestPetStoreOpenAPI_getInventory.getParameterSignature(), '')
self.assertEqual(
skin_folder.TestPetStoreOpenAPI_getInventory.getBody(),
'''"""Returns a map of status codes to quantities
GET /store/inventory
"""
''')
class TestOpenAPIServicePetController(OpenAPIPetStoreTestCase):
def test_add_pet(self):
self.addPythonScript(
'TestPetStoreOpenAPI_addPet',
'body',
'return "ok" if (body["id"] == 10 and body["name"] == "doggie") else repr(body)',
)
response = self.publish(
self.connector.getPath() + '/pet',
request_method='POST',
stdin=io.BytesIO(
json.dumps(
{
"category": {
"id": 1,
"name": "Dogs"
},
"status": "available",
"name": "doggie",
"tags": [{
"id": 0,
"name": "string"
}],
"photoUrls": ["string"],
"id": 10
}).encode()),
env={'CONTENT_TYPE': 'application/json'})
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_delete_pet(self):
self.addPythonScript(
'TestPetStoreOpenAPI_deletePet',
'**kw',
'return "ok" if kw == {"api_key": "key", "petId": 789} else repr(kw)',
)
response = self.publish(
self.connector.getPath() + '/pet/789',
request_method='DELETE',
env={'HTTP_API_KEY': "key"},
)
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_find_pets_by_status(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus',
'status',
'return "ok" if status == "available" else repr(status)',
)
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_find_pets_by_tags(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByTags',
'tags',
'return tags',
)
response = self.publish(
self.connector.getPath() + '/pet/findByTags?tags=a&tags=b')
self.assertEqual(response.getStatus(), 200)
self.assertEqual(response.getBody(), json.dumps(['a', 'b']).encode())
response = self.publish(
self.connector.getPath() + '/pet/findByTags?tags=tag_example')
self.assertEqual(response.getStatus(), 200)
self.assertEqual(response.getBody(), json.dumps(['tag_example']).encode())
def test_get_pet_by_id(self):
self.addPythonScript(
'TestPetStoreOpenAPI_getPetById',
'petId',
'return "ok" if petId == 789 else repr(petId)',
)
response = self.publish(self.connector.getPath() + '/pet/789')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_get_pet_by_id_traverse_subpath(self):
self.addPythonScript(
'TestPetStoreOpenAPI_getPetById',
'petId',
'return "ok" if traverse_subpath == ["pet", "789"] else repr(traverse_subpath)',
)
response = self.publish(self.connector.getPath() + '/pet/789')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_update_pet(self):
self.addPythonScript(
'TestPetStoreOpenAPI_updatePet',
'body',
'return "ok" if ('
' body["category"]["id"] == 1'
' and body["category"]["name"] == "Dogs"'
' and body["status"] == "available"'
' and body["tags"] == [{"id": 0, "name": "string"}]'
' and body["photoUrls"] == ["string"]'
' and body["id"] == 10'
' ) else repr(body)',
)
response = self.publish(
self.connector.getPath() + '/pet',
request_method='PUT',
stdin=io.BytesIO(
json.dumps(
{
"category": {
"id": 1,
"name": "Dogs"
},
"status": "available",
"name": "doggie",
"tags": [{
"id": 0,
"name": "string"
}],
"photoUrls": ["string"],
"id": 10
}).encode()),
env={'CONTENT_TYPE': 'application/json'})
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_update_pet_with_form(self):
self.addPythonScript(
'TestPetStoreOpenAPI_updatePetWithForm',
'petId, name, status',
'return "ok" if ('
'petId == 789'
' and name == "name"'
' and status == "status"'
') else repr((petId, name, status))',
)
response = self.publish(
self.connector.getPath() + '/pet/789?name=name&status=status',
request_method='POST',
env={'CONTENT_TYPE': 'application/x-www-form-urlencoded'})
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_upload_file(self):
self.addPythonScript(
'TestPetStoreOpenAPI_uploadFile',
'petId, body, additionalMetadata',
'return "ok" if ('
'petId == 789'
' and body == b"file content"'
' and additionalMetadata == "additional"'
') else repr((petId, body, additionalMetadata))',
)
response = self.publish(
self.connector.getPath()
+ '/pet/{petId}/uploadImage?additionalMetadata=additional'.format(
petId=789),
request_method='POST',
stdin=io.BytesIO(b'file content'),
env={"CONTENT_TYPE": 'application/octet-stream'})
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
@unittest.skipIf(yaml is None, 'PyYAML is required for yaml tests')
class TestOpenAPIServiceYaml(OpenAPITestCase):
_type_id = 'Test Open API YAML'
_open_api_schema_content_type = 'application/x-yaml'
_open_api_schema = '''
openapi: 3.0.3
info:
title: TestOpenAPIServiceYaml
version: 0.0.0
paths:
'/users/{user_id}':
get:
operationId: testGET
parameters:
- name: user_id
in: path
required: true
schema:
type: integer
'''
def test_get(self):
self.addPythonScript(
'TestOpenAPIYAML_testGET',
'user_id',
'return "ok" if user_id == 123 else repr(user_id)',
)
response = self.publish(self.connector.getPath() + '/users/123')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
class TestPathParameterSerialization(OpenAPITestCase):
_type_id = 'Test Open API Parameter Serialization'
@property
def _open_api_schema(self):
primitive_params = []
array_params = []
for style in ('simple', 'label', 'matrix'):
for explode in (True, False):
primitive_params.append(
{
'name': '{style}_{explode}'.format(style=style, explode=explode),
'in': 'path',
'schema': {
'type': 'integer'
},
'style': style,
'explode': explode
})
array_params.append(
{
'name': '{style}_{explode}'.format(style=style, explode=explode),
'in': 'path',
'schema': {
'type': 'array',
'items': {
'type': 'integer'
}
},
'style': style,
'explode': explode
})
return json.dumps(
{
'paths': {
'/primitive/{simple_False}/{simple_True}'
'/{label_False}/{label_True}'
'/{matrix_False}/{matrix_True}': {
'get': {
'operationId': 'testPrimitiveSerialization',
'parameters': primitive_params
}
},
'/array/{simple_False}/{simple_True}'
'/{label_False}/{label_True}'
'/{matrix_False}/{matrix_True}': {
'get': {
'operationId': 'testArraySerialization',
'parameters': array_params
}
}
}
})
def test_primitive_parameter_serialization(self):
self.addPythonScript(
'TestOpenAPIParameterSerialization_testPrimitiveSerialization',
'simple_False, simple_True, label_False, label_True, matrix_False, matrix_True',
'return "ok" if ('
'simple_False == 5'
' and simple_True == 5'
' and label_False == 5'
' and label_True == 5'
' and matrix_False == 5'
' and matrix_True == 5'
') else repr((simple_False, simple_True, label_False, label_True, matrix_False, matrix_True))',
)
response = self.publish(
self.connector.getPath() + '/primitive/5/5/.5/.5'
+ '/;matrix_False=5/;matrix_True=5')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_array_parameter_serialization(self):
self.addPythonScript(
'TestOpenAPIParameterSerialization_testArraySerialization',
'simple_False, simple_True, label_False, label_True, matrix_False, matrix_True',
'return "ok" if ('
'simple_False == [3,4,5]'
' and simple_True == [3,4,5]'
' and label_False == [3,4,5]'
' and label_True == [3,4,5]'
' and matrix_False == [3,4,5]'
' and matrix_True == [3,4,5]'
') else repr((simple_False, simple_True, label_False, label_True, matrix_False, matrix_True))',
)
response = self.publish(
self.connector.getPath() + '/array/3,4,5/3,4,5/.3,4,5/.3.4.5'
+ '/;matrix_False=3,4,5/;matrix_True=3;matrix_True=4;matrix_True=5',
handle_errors=False)
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
class TestQueryParameterSerialization(OpenAPITestCase):
_type_id = 'Test Open API Parameter Serialization'
@property
def _open_api_schema(self):
array_params = []
for style in ('form', 'spaceDelimited', 'pipeDelimited'):
for explode in (True, False):
array_params.append(
{
'name': '{style}_{explode}'.format(style=style, explode=explode),
'in': 'query',
'schema': {
'type': 'array',
'items': {
'type': 'integer'
}
},
'style': style,
'explode': explode
})
return json.dumps(
{
'paths': {
'/array': {
'get': {
'operationId': 'testArraySerialization',
'parameters': array_params
}
}
}
})
def test_array_parameter_serialization(self):
self.addPythonScript(
'TestOpenAPIParameterSerialization_testArraySerialization',
'form_False, form_True, spaceDelimited_False, spaceDelimited_True, '
'pipeDelimited_False, pipeDelimited_True',
'return "ok" if ('
'form_False == [3,4,5]'
' and form_True == [3,4,5]'
' and spaceDelimited_False == [3,4,5]'
' and spaceDelimited_True == [3,4,5]'
' and pipeDelimited_False == [3,4,5]'
' and pipeDelimited_True == [3,4,5]'
') else repr((form_False, form_True, spaceDelimited_False, spaceDelimited_True, '
'pipeDelimited_False, pipeDelimited_True))',
)
response = self.publish(
self.connector.getPath() + '/array?'
+ 'form_True=3&form_True=4&form_True=5&' + 'form_False=3,4,5&'
+ 'spaceDelimited_True=3&spaceDelimited_True=4&spaceDelimited_True=5&'
+ 'spaceDelimited_False=3%204%205&'
+ 'pipeDelimited_True=3&pipeDelimited_True=4&pipeDelimited_True=5&'
+ 'pipeDelimited_False=3|4|5')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
class TestOpenAPIParameterValidation(OpenAPIPetStoreTestCase):
def assertValidationError(self, response, title, detail=None):
body = json.loads(response.getBody())
self.assertEqual(body['type'], 'parameter-validation-error')
self.assertEqual(body['title'], title)
if detail:
self.assertEqual(body['detail'], detail)
self.assertEqual(response.getStatus(), 400)
def test_wrong_type_in_json_body(self):
self.addPythonScript('TestPetStoreOpenAPI_updatePet', 'body', '')
response = self.publish(
self.connector.getPath() + '/pet',
request_method='PUT',
stdin=io.BytesIO(
json.dumps(
{
"category": {
"id": 1,
"name": "Dogs"
},
"status": "available",
"name": "doggie",
"tags": [{
"id": 0,
"name": "string"
}],
"photoUrls": 123, # wrong type
"id": 10
}).encode()),
env={'CONTENT_TYPE': 'application/json'})
self.assertValidationError(
response,
"Error validating request body: 123 is not of type 'array'",
"""\
123 is not of type 'array'
Failed validating 'type' in schema['properties']['photoUrls']:
{'items': {'type': 'string', 'xml': {'name': 'photoUrl'}},
'type': 'array',
'xml': {'wrapped': True}}
On instance['photoUrls']:
123""",
)
def test_wrong_type_in_path_param(self):
self.addPythonScript('TestPetStoreOpenAPI_deletePet', 'petId', '')
response = self.publish(
self.connector.getPath() + '/pet/not_a_number', request_method='DELETE')
self.assertValidationError(
response,
"Error validating parameter `petId`: 'not_a_number' is not of type 'integer'",
"""\
'not_a_number' is not of type 'integer'
Failed validating 'type' in schema:
{'format': 'int64', 'type': 'integer'}
On instance:
'not_a_number'""",
)
def test_wrong_type_in_query_param(self):
self.addPythonScript('TestPetStoreOpenAPI_findPetsByStatus', 'status', '')
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=invalid_status')
self.assertValidationError(
response,
"Error validating parameter `status`: 'invalid_status' is not one of ['available', 'pending', 'sold']"
)
class TestOpenAPINonAsciiParameters(OpenAPIPetStoreTestCase):
def test_path_parameter(self):
self.addPythonScript(
'TestPetStoreOpenAPI_getUserByName',
'username',
'return "ok" if username == "hé" else repr(username)',
)
response = self.publish(self.connector.getPath() + '/user/hé')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_query_parameter(self):
self.addPythonScript(
'TestPetStoreOpenAPI_updatePetWithForm',
'petId, name, status',
'return "ok" if ('
'petId == 789'
' and name == "é"'
' and status == "à"'
') else repr((petId, name, status))',
)
response = self.publish(
self.connector.getPath() + '/pet/789?name=é&status=à',
request_method='POST')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
response = self.publish(
self.connector.getPath() + '/pet/789?name=é', request_method='POST')
self.assertEqual(response.getStatus(), 400)
def test_request_body(self):
# body is decoded as json, so strings are unicode on python2
self.addPythonScript(
'TestPetStoreOpenAPI_updatePet',
'body',
'# coding:utf-8\n'
'return "ok" if body["name"] == u"héhé" else repr(body)',
)
response = self.publish(
self.connector.getPath() + '/pet',
request_method='PUT',
stdin=io.BytesIO(
json.dumps(
{
"category": {
"id": 1,
"name": "Dogs"
},
"status": "available",
"name": "héhé",
"tags": [{
"id": 0,
"name": "héhé"
}],
"photoUrls": ["héhé"],
"id": 10
}).encode()),
env={'CONTENT_TYPE': 'application/json'})
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
response = self.publish(
self.connector.getPath() + '/pet',
request_method='PUT',
stdin=io.BytesIO(b'"h\\u00e9h\\u00e9"'),
env={'CONTENT_TYPE': 'application/json'})
self.assertEqual(response.getStatus(), 400)
class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase):
_type_id = 'Test Open API Common Parameters'
_open_api_schema = (
'''
{
"openapi": "3.0.3",
"info": {
"title": "TestOpenAPICommonParameters",
"version": "0.0.0"
},
"paths": {
'''
# https://swagger.io/docs/specification/describing-parameters/#common-for-path
'''
"/common-for-path": {
"parameters": [
{
"name": "a",
"in": "query",
"schema": {
"type": "number"
}
}
],
"get": {
"operationId": "testGET1",
"parameters": [
{
"name": "b",
"in": "query",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "ok"
}
}
}
},'''
# https://swagger.io/docs/specification/describing-parameters/#common-for-various-paths
'''
"/common-for-various-paths": {
"get": {
"operationId": "testGET2",
"parameters": [
{
"name": "b",
"in": "query",
"schema": {
'''
# here we also excercice $refs in parameter schemas
'''
"$ref": "#/components/schemas/custom-number"
}
},
{
"$ref": "#/components/parameters/c"
}
],
"responses": {
"200": {
"description": "ok"
}
}
}
},
'''
# https://spec.openapis.org/oas/v3.1.0#fixed-fields-6
# $refs: Allows for a referenced definition of this path item.
# The referenced structure MUST be in the form of a Path Item Object.
'''
"/alias": {
"$ref": "#/paths/~1common-for-path"
}
},
"components": {
"parameters": {
"c": {
"name": "c",
"in": "query",
"schema": {
"type": "number"
}
}
},
"schemas": {
"custom-number": {
"type": "number"
}
}
}
}
''')
def test_common_for_path(self):
self.addPythonScript(
'TestOpenAPICommonParameters_testGET1',
'a, b',
'return "ok" if (a == 1 and b == 2) else repr((a, b))',
)
response = self.publish(
self.connector.getPath() + '/common-for-path?a=1&b=2')
self.assertEqual(response.getBody(), b'"ok"')
def test_common_for_various_path(self):
self.addPythonScript(
'TestOpenAPICommonParameters_testGET2',
'b, c',
'return "ok" if (b == 2 and c == 3) else repr((b, c))',
)
response = self.publish(
self.connector.getPath() + '/common-for-various-paths?b=2&c=3')
self.assertEqual(response.getBody(), b'"ok"')
def test_alias(self):
self.addPythonScript(
'TestOpenAPICommonParameters_testGET1',
'a, b',
'return "ok" if (a == 1 and b == 2) else repr((a, b))',
)
response = self.publish(self.connector.getPath() + '/alias?a=1&b=2')
self.assertEqual(response.getBody(), b'"ok"')
def test_getOpenAPIOperationIterator(self):
# getOpenAPIOperationIterator iterates on the operations in order they are defined
# in the json and resolve parameters.
self.assertEqual(
[
(
operation.path, operation.request_method,
operation.get('operationId'),
[p['name'] for p in operation.getParameters()]) for operation in
self.portal.portal_types[self._type_id].getOpenAPIOperationIterator()
], [
('/common-for-path', 'get', 'testGET1', ['a', 'b']),
('/common-for-various-paths', 'get', 'testGET2', ['b', 'c']),
('/alias', 'get', 'testGET1', ['a', 'b'])
])
class TestOpenAPIMissingParameters(OpenAPIPetStoreTestCase):
_type_id = 'Test Open API Missing Parameters'
_open_api_schema = json.dumps(
{
'openapi': '3.0.3',
'info': {
'title': 'TestOpenAPIMissingParameters',
'version': '0.0.0'
},
'paths': {
'/query': {
'get': {
'operationId':
'testGETQuery',
'parameters': [
{
'name': 'user_id',
'in': 'query',
'required': True,
'schema': {
'type': 'integer'
}
}
]
}
},
'/query_with_default': {
'get': {
'operationId':
'testGETQueryWithDefault',
'parameters': [
{
'name': 'user_id',
'in': 'query',
'required': False,
'schema': {
'default': 123,
'type': 'integer'
}
}
]
}
}
}
})
def test_required_query(self):
self.addPythonScript(
'TestOpenAPIMissingParameters_testGETQuery',
'user_id=None',
'return user_id',
)
response = self.publish(self.connector.getPath() + '/query')
self.assertEqual(
json.loads(response.getBody()), {
"status": 400,
"type": "missing-parameter-error",
"title": "user_id"
})
response = self.publish(self.connector.getPath() + '/query?user_id=')
self.assertEqual(
json.loads(response.getBody()), {
"status": 400,
"type": "missing-parameter-error",
"title": "user_id"
})
response = self.publish(self.connector.getPath() + '/query?user_id')
self.assertEqual(
json.loads(response.getBody()), {
"status": 400,
"type": "missing-parameter-error",
"title": "user_id"
})
response = self.publish(self.connector.getPath() + '/query?user_id=123')
self.assertEqual(response.getBody(), b"123")
self.assertEqual(response.getStatus(), 200)
def test_required_query_with_default(self):
self.addPythonScript(
'TestOpenAPIMissingParameters_testGETQueryWithDefault',
'user_id=None',
'return user_id',
)
response = self.publish(
self.connector.getPath() + '/query_with_default?user_id=789')
self.assertEqual(response.getBody(), b"789")
self.assertEqual(response.getStatus(), 200)
response = self.publish(self.connector.getPath() + '/query_with_default')
self.assertEqual(response.getBody(), b"123")
self.assertEqual(response.getStatus(), 200)
class TestOpenAPIResponseContentType(OpenAPIPetStoreTestCase):
def test_default_content_type(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus',
'status',
'return {"a": "b"}',
)
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(response.getHeader('Content-type'), 'application/json')
self.assertEqual(response.getBody(), json.dumps({"a": "b"}).encode())
self.assertEqual(response.getStatus(), 200)
def test_content_type_set_by_script(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus',
'status',
'container.REQUEST.RESPONSE.setHeader("Content-Type", "text/html;charset=UTF-8")\n'
'return "<h1>hello</h1>"',
)
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(
response.getHeader('Content-type'), 'text/html;charset=UTF-8')
self.assertEqual(response.getBody(), b"<h1>hello</h1>")
self.assertEqual(response.getStatus(), 200)
class TestOpenAPIErrorHandling(OpenAPIPetStoreTestCase):
def test_default_error_handler(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus',
'status',
'1//0',
)
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(response.getStatus(), 500)
self.assertEqual(
response.getBody(),
json.dumps(
{
'type': 'unknown-error',
'title': 'ZeroDivisionError: integer division or modulo by zero'
}).encode())
def test_transaction_abort_on_error(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus',
'status',
'context.getPortalObject().setTitle("ooops")\n'
'1/0',
)
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available',
user='ERP5TypeTestCase')
self.assertEqual(response.getStatus(), 500)
self.assertNotEqual(self.portal.getTitle(), "ooops")
def test_no_method_for_operation(self):
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(
response.getBody(),
json.dumps(
{
'type':
'no-method-for-operation',
'title':
'No method for operation findPetsByStatus GET /pet/findByStatus'
}).encode())
self.assertEqual(response.getStatus(), 500)
def test_custom_error_handler(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus',
'status',
'1/0',
)
self.addPythonScript(
'TestPetStoreOpenAPI_handleException',
'exception, request',
'request.RESPONSE.setStatus(410, lock=True)\n'
'return "custom error" if isinstance(exception, ZeroDivisionError) else repr((exception, request))',
)
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(response.getBody(), b"custom error")
self.assertEqual(response.getStatus(), 410)
def test_custom_error(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus',
'status',
'from erp5.component.document.OpenAPITypeInformation import OpenAPIError\n'
'class CustomError(OpenAPIError):\n'
' type = "custom-error-type"\n'
' status = 417\n'
'raise CustomError("custom error title")',
)
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(
response.getBody(),
json.dumps(
{
'type': 'custom-error-type',
'title': "custom error title",
'status': 417
}).encode())
self.assertEqual(response.getStatus(), 417)
def test_unauthorized(self):
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus', 'status',
'context.setTitle("ooops")')
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(
response.getBody(),
json.dumps({
'type': 'unauthorized',
}).encode())
self.assertEqual(response.getStatus(), 401)
class TestRestrictedAPI(OpenAPIPetStoreTestCase):
_public_api = False
def test_unauthorized(self):
# the connector can not be traversed by anonymous user because the connector
# is not public in this test.
self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus', 'status',
'return "ok"')
response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available')
self.assertEqual(
response.getBody(),
json.dumps({
'type': 'unauthorized',
}).encode())
self.assertEqual(response.getStatus(), 401)
class TestPathParameterAndAcquisition(OpenAPIPetStoreTestCase):
"""Check that path parameters works even when a Zope OFS document with
same ID might be acquired.
"""
def afterSetUp(self):
super(TestPathParameterAndAcquisition, self).afterSetUp()
if '789' not in self.portal.portal_web_services.objectIds():
self.portal.portal_web_services.newContent(
id='789',
portal_type=self.portal.portal_web_services.allowedContentTypes()
[0].getId())
def test_get(self):
self.addPythonScript(
'TestPetStoreOpenAPI_getPetById',
'petId',
'return "ok" if petId == 789 else repr(petId)',
)
response = self.publish(self.connector.getPath() + '/pet/789')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
def test_put(self):
self.addPythonScript(
'TestPetStoreOpenAPI_updateUser',
'username',
'return "ok" if username == "789" else repr(username)',
)
response = self.publish(
self.connector.getPath() + '/user/789', request_method='PUT')
self.assertEqual(response.getBody(), b'"ok"')
self.assertEqual(response.getStatus(), 200)
class TestURLPathWithWebSiteAndVirtualHost(OpenAPIPetStoreTestCase):
def afterSetUp(self):
super(TestURLPathWithWebSiteAndVirtualHost, self).afterSetUp()
self.web_site = self.portal.web_site_module.newContent(
portal_type='Web Site')
self.web_site.publish()
self.web_section = self.web_site.newContent(portal_type='Web Section')
self.web_section.publish()
self.addPythonScript(
'TestPetStoreOpenAPI_getPetById',
'petId',
'return "ok" if petId == 789 else repr(petId)',
)
def test_virtual_host(self):
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/{}/pet/789'.format(
self.portal.getId(),
self.connector.getRelativeUrl(),
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/_vh_vh1/_vh_vh2/{}/pet/789'.format(
self.portal.getId(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
def test_web_site(self):
response = self.publish(
'{}/{}/pet/789'.format(
self.web_site.getPath(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
def test_web_site_virtual_host(self):
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/{}/pet/789'.format(
self.web_site.getPath(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/_vh_vh1/_vh_vh2/{}/pet/789'.format(
self.web_site.getPath(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/{}/{}/pet/789'.format(
self.portal.getId(),
self.web_site.getRelativeUrl(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
def test_web_section(self):
response = self.publish(
'{}/{}/pet/789'.format(
self.web_section.getPath(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
def test_web_section_virtual_host(self):
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/{}/pet/789'.format(
self.web_section.getPath(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/_vh_vh1/_vh_vh2/{}/pet/789'.format(
self.web_section.getPath(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/{}/{}/pet/789'.format(
self.portal.getId(),
self.web_section.getRelativeUrl(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
def test_connector_virtual_host(self):
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/pet/789'.format(
self.connector.getPath()
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/_vh_api/pet/789'.format(
self.connector.getPath()
))
self.assertEqual(response.getBody(), b'"ok"')
def test_acquisition_path(self):
response = self.publish(
'/{}/person_module/{}/pet/789'.format(
self.portal.getId(),
self.connector.getRelativeUrl(),
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/person_module/{}/pet/789'.format(
self.portal.getId(),
self.connector.getRelativeUrl(),
))
self.assertEqual(response.getBody(), b'"ok"')
response = self.publish(
'/VirtualHostBase/https/example.com:443/{}/VirtualHostRoot/_vh_vh1/_vh_vh2/person_module/{}/pet/789'.format(
self.portal.getId(),
self.connector.getRelativeUrl()
))
self.assertEqual(response.getBody(), b'"ok"')
class TestOpenAPIRequestBody(OpenAPITestCase):
_type_id = 'Test Open API Request Body'
_open_api_schema = json.dumps(
{
'openapi': '3.0.3',
'info': {
'title': 'TestOpenAPIRequestBody',
'version': '0.0.0'
},
'paths': {
'/post': {
'post': {
'operationId': 'testPostByContentType',
'requestBody': {
'content': {
'image/*': {
'schema': {
'type': 'string',
'format': 'binary',
}
},
'application/x-base64': {
'schema': {
'type': 'string',
'format': 'base64',
}
}
}
}
}
}
}
})
def test_request_body_content_encoding(self):
self.addPythonScript(
'TestOpenAPIRequestBody_testPostByContentType',
'body=None',
'container.REQUEST.RESPONSE.setHeader("Content-Type", "application/octet-stream")\n'
'return body',
)
response = self.publish(
self.connector.getPath() + '/post',
request_method='POST',
stdin=io.BytesIO(b'png file content'),
env={"CONTENT_TYPE": 'image/png'})
self.assertEqual(response.getBody(), b'png file content')
self.assertEqual(response.getStatus(), 200)
response = self.publish(
self.connector.getPath() + '/post',
request_method='POST',
stdin=io.BytesIO(base64_encodebytes(b'base64 file content')),
env={"CONTENT_TYPE": 'application/x-base64'})
self.assertEqual(response.getBody(), b'base64 file content')
self.assertEqual(response.getStatus(), 200)
response = self.publish(
self.connector.getPath() + '/post',
request_method='POST',
stdin=io.BytesIO(b'not base64'),
env={"CONTENT_TYPE": 'application/x-base64'})
self.assertEqual(response.getStatus(), 400)
body = json.loads(response.getBody())
self.assertEqual(body['type'], 'parameter-validation-error')
self.assertIn('Error validating request body:', body['title'])
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testOpenAPIService</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testOpenAPIService</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
erp5_monaco_editor
erp5_web_service
\ No newline at end of file
Framework for implementing web services from OpenAPI descriptions
\ No newline at end of file
Open API Type | action_view
Open API Type | copy_roles
Open API Type | definition_view
Open API Type | generate_python_scripts_for_operations
Open API Type | open_api_view
Open API Type | operation_view
Open API Type | role_view
Open API Type | translation_view
Open API Type | update_local_roles
Open API Type | view
\ No newline at end of file
document.erp5.OpenAPIService
document.erp5.OpenAPITypeInformation
\ No newline at end of file
Open API Type | Action Information
Open API Type | Role Information
Types Tool | Open API Type
\ No newline at end of file
Open API Type | base_type_interaction_workflow
Open API Type | dynamic_class_generation_interaction_workflow
\ No newline at end of file
erp5_open_api
\ No newline at end of file
test.erp5.testOpenAPIService
\ No newline at end of file
erp5_full_text_mroonga_catalog
erp5_web
\ No newline at end of file
erp5_json_rpc_api
\ No newline at end of file
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