From 4d42d572b63ae9d87a451ab3594683a1daaeabb2 Mon Sep 17 00:00:00 2001
From: Titouan Soulard <titouan.soulard@rapid.space>
Date: Mon, 27 Nov 2023 13:03:58 -0100
Subject: [PATCH] erp5_action_information_api: support for OpenAPI
 hyperdocument

---
 .../api_openapi.xml                           | 81 -------------------
 .../document.erp5.ActionInformationAPI.py     |  3 +-
 .../ActionInformationAPI_api_openapi.py       | 12 +++
 .../ActionInformationAPI_api_openapi.xml      | 62 ++++++++++++++
 .../ActionInformationAPI_api_slap.py          |  2 +
 .../ActionInformationAPI_api_slap.xml         |  2 +-
 .../test.erp5.testActionInformationAPI.py     | 65 ++++++++++-----
 .../bt/template_action_path_list              |  1 -
 .../bt/test_dependency_list                   |  3 +-
 9 files changed, 127 insertions(+), 104 deletions(-)
 delete mode 100644 bt5/erp5_action_information_api/ActionTemplateItem/portal_types/Action%20Information%20API/api_openapi.xml
 create mode 100644 bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.py
 create mode 100644 bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.xml

diff --git a/bt5/erp5_action_information_api/ActionTemplateItem/portal_types/Action%20Information%20API/api_openapi.xml b/bt5/erp5_action_information_api/ActionTemplateItem/portal_types/Action%20Information%20API/api_openapi.xml
deleted file mode 100644
index 57d53660a5..0000000000
--- a/bt5/erp5_action_information_api/ActionTemplateItem/portal_types/Action%20Information%20API/api_openapi.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?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_api_type</string>
-              </tuple>
-            </value>
-        </item>
-        <item>
-            <key> <string>category</string> </key>
-            <value> <string>object_api_type</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>api_openapi</string> </value>
-        </item>
-        <item>
-            <key> <string>permissions</string> </key>
-            <value>
-              <tuple>
-                <string>View</string>
-              </tuple>
-            </value>
-        </item>
-        <item>
-            <key> <string>priority</string> </key>
-            <value> <float>1.0</float> </value>
-        </item>
-        <item>
-            <key> <string>title</string> </key>
-            <value> <string>OpenAPI</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}/ActionInformationAPI_api_openapi</string> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>
diff --git a/bt5/erp5_action_information_api/DocumentTemplateItem/portal_components/document.erp5.ActionInformationAPI.py b/bt5/erp5_action_information_api/DocumentTemplateItem/portal_components/document.erp5.ActionInformationAPI.py
index e6afc29ac3..49d6088511 100644
--- a/bt5/erp5_action_information_api/DocumentTemplateItem/portal_components/document.erp5.ActionInformationAPI.py
+++ b/bt5/erp5_action_information_api/DocumentTemplateItem/portal_components/document.erp5.ActionInformationAPI.py
@@ -120,8 +120,7 @@ class ActionInformationAPI(XMLObject):
   def handleRequest(self, request):
     portal = self.getPortalObject()
 
-    action_filter = self.getActionReferenceValue()
-    (hyperdocument, actions) = self.getTypeInfo().getDefaultViewFor(self, view=self.getApiTypeReference())(portal, action_filter, request.getURL())
+    (hyperdocument, actions) = self.getTypeInfo().getDefaultViewFor(self, view=self.getApiTypeReference())(self, portal, request)
 
     response = request.RESPONSE
     request_content_type = request.getHeader("content-type")
diff --git a/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.py b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.py
new file mode 100644
index 0000000000..e688c59a52
--- /dev/null
+++ b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.py
@@ -0,0 +1,12 @@
+import json
+
+schema = caller.getTypeInfo().getSchema()
+schema.setdefault("servers", []).insert(
+  0, {
+    "url": caller.absolute_url(),
+    "description": caller.getDescription()
+  })
+
+hyperdocument = json.dumps(schema)
+
+return (hyperdocument, {})
diff --git a/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.xml b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.xml
new file mode 100644
index 0000000000..ab1a20029b
--- /dev/null
+++ b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_openapi.xml
@@ -0,0 +1,62 @@
+<?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>caller, portal, request</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>ActionInformationAPI_api_openapi</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.py b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.py
index 2240568d12..353573d9d2 100644
--- a/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.py
+++ b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.py
@@ -1,8 +1,10 @@
 import json
 
+url = request.getURL()
 base_url_absolute = portal.portal_callables.absolute_url().strip()
 base_url_relative = portal.portal_callables.getPath().strip()
 
+action_filter = caller.getActionReferenceValue()
 raw_action_list = portal.portal_catalog(
   portal_type="Action Information",
   action_type__uid=action_filter.getUid()
diff --git a/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.xml b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.xml
index 415f328562..099d637f62 100644
--- a/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.xml
+++ b/bt5/erp5_action_information_api/SkinTemplateItem/portal_skins/erp5_action_information_api/ActionInformationAPI_api_slap.xml
@@ -50,7 +50,7 @@
         </item>
         <item>
             <key> <string>_params</string> </key>
-            <value> <string>portal, action_filter, url</string> </value>
+            <value> <string>caller, portal, request</string> </value>
         </item>
         <item>
             <key> <string>id</string> </key>
diff --git a/bt5/erp5_action_information_api/TestTemplateItem/portal_components/test.erp5.testActionInformationAPI.py b/bt5/erp5_action_information_api/TestTemplateItem/portal_components/test.erp5.testActionInformationAPI.py
index 3890c232d0..c06ad5ae57 100644
--- a/bt5/erp5_action_information_api/TestTemplateItem/portal_components/test.erp5.testActionInformationAPI.py
+++ b/bt5/erp5_action_information_api/TestTemplateItem/portal_components/test.erp5.testActionInformationAPI.py
@@ -58,22 +58,6 @@ class TestActionInformationAPI(ERP5TypeTestCase):
     self.tic()
     self.commit()
 
-    ai_type = self.portal.portal_catalog(
-      portal_type="Action Information",
-      action_type__uid=self.portal.portal_categories.action_type.object_api_type.getUid(),
-      reference="api_slap",
-    )[0]
-
-    self.web_service = self.portal.portal_web_services.newContent(
-      portal_type="Action Information API",
-    )
-    self.web_service.edit(
-      api_type=ai_type.getRelativeUrl(),
-      action_reference=self.object_api_value.getRelativeUrl(),
-    )
-    self.tic()
-    self.commit()
-
     portal_type_name = "Person"
     portal_type = self.portal.portal_types[portal_type_name]
     action_id = "Person_updateEmail"
@@ -113,6 +97,39 @@ return {"status": 200}""")
     self.tic()
     self.commit()
 
+  def setupAiApi(self):
+    ai_type = self.portal.portal_catalog(
+      portal_type="Action Information",
+      action_type__uid=self.portal.portal_categories.action_type.object_api_type.getUid(),
+      reference="api_slap",
+    )[0]
+
+    self.web_service = self.portal.portal_web_services.newContent(
+      portal_type="Action Information API",
+    )
+    self.web_service.edit(
+      api_type=ai_type.getRelativeUrl(),
+      action_reference=self.object_api_value.getRelativeUrl(),
+    )
+    self.tic()
+    self.commit()
+
+  def setupOpenApi(self):
+    ai_type = self.portal.portal_catalog(
+      portal_type="Action Information",
+      action_type__uid=self.portal.portal_categories.action_type.object_api_type.getUid(),
+      reference="api_openapi",
+    )[0]
+
+    self.web_service = self.portal.portal_web_services.newContent(
+      portal_type="Pet Store Open API",
+    )
+    self.web_service.edit(
+      api_type=ai_type.getRelativeUrl(),
+    )
+    self.tic()
+    self.commit()
+
   def loggedInRequest(self, path, method, content):
     return self.publish(
       self.web_service.getPath() + path,
@@ -124,7 +141,9 @@ return {"status": 200}""")
       user="ERP5TypeTestCase"
     )
 
-  def test_hyperdocument(self):
+  def test_hyperdocument_script(self):
+    self.setupAiApi()
+
     response = self.loggedInRequest("/api", "GET", {})
     response_json = json.loads(response.getBody())
     
@@ -133,7 +152,9 @@ return {"status": 200}""")
     self.assertTrue("Person_updateEmail/getInputJSONSchema" in response_json["links"][0]["$schemaRequest"])
     self.assertTrue("Person_updateEmail/getOutputJSONSchema" in response_json["links"][0]["$schemaResponse"])
 
-  def test_update(self):
+  def test_update_script(self):
+    self.setupAiApi()
+
     person = self.portal.person_module.newContent(
       id="%s_person" % self.current_id,
       portal_type="Person",
@@ -152,3 +173,11 @@ return {"status": 200}""")
     self.assertEqual(response.getBody(), json.dumps({ "status": 200 }, indent=2))
     self.assertEqual(response.getStatus(), 200)
     self.assertEqual(person.getDefaultEmailUrlString(), "alice@looking.glass")
+
+  def test_hyperdocument_openapi(self):
+    self.setupOpenApi()
+
+    response = self.loggedInRequest("/api", "GET", {})
+    response_json = json.loads(response.getBody())
+
+    self.assertEqual(response_json["openapi"], "3.0.2")
diff --git a/bt5/erp5_action_information_api/bt/template_action_path_list b/bt5/erp5_action_information_api/bt/template_action_path_list
index ddaa267cb7..0ad2e26bc4 100644
--- a/bt5/erp5_action_information_api/bt/template_action_path_list
+++ b/bt5/erp5_action_information_api/bt/template_action_path_list
@@ -1,3 +1,2 @@
-Action Information API | api_openapi
 Action Information API | api_slap
 Action Information API | view
\ No newline at end of file
diff --git a/bt5/erp5_action_information_api/bt/test_dependency_list b/bt5/erp5_action_information_api/bt/test_dependency_list
index bff77e32d8..309a46ea44 100644
--- a/bt5/erp5_action_information_api/bt/test_dependency_list
+++ b/bt5/erp5_action_information_api/bt/test_dependency_list
@@ -1 +1,2 @@
-erp5_full_text_mroonga_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
+erp5_open_api_ui_test
\ No newline at end of file
-- 
2.30.9