You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2021/10/14 21:49:08 UTC

[tapestry-5] branch rest updated: TAP5-2696: generating OpenAPI for path and query parameters

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch rest
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git


The following commit(s) were added to refs/heads/rest by this push:
     new a1f8060  TAP5-2696: generating OpenAPI for path and query parameters
a1f8060 is described below

commit a1f8060d694a4a52c6e09925593a3076e4726555
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Thu Oct 14 18:48:51 2021 -0300

    TAP5-2696: generating OpenAPI for path and query parameters
---
 .../DefaultOpenApiDescriptionGenerator.java        | 96 ++++++++++++++++------
 .../app1/base/AbstractRestDemoPage.java            | 27 ++++++
 .../integration/app1/base/BaseRestDemoPage.java    | 17 +++-
 3 files changed, 112 insertions(+), 28 deletions(-)

diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultOpenApiDescriptionGenerator.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultOpenApiDescriptionGenerator.java
index c3ed3bb..89cf549 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultOpenApiDescriptionGenerator.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultOpenApiDescriptionGenerator.java
@@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.RequestParameter;
 import org.apache.tapestry5.annotations.StaticActivationContextValue;
 import org.apache.tapestry5.commons.Messages;
 import org.apache.tapestry5.http.services.BaseURLSource;
@@ -42,7 +43,6 @@ import org.apache.tapestry5.json.JSONObject;
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.services.ComponentClassResolver;
-import org.apache.tapestry5.services.ComponentSource;
 import org.apache.tapestry5.services.OpenApiDescriptionGenerator;
 import org.apache.tapestry5.services.PageRenderLinkSource;
 import org.apache.tapestry5.services.messages.ComponentMessagesSource;
@@ -78,8 +78,6 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen
     
     final private PageRenderLinkSource pageRenderLinkSource;
     
-    final private ComponentSource componentSource;
-    
     final private static String KEY_PREFIX = "openapi.";
     
     public DefaultOpenApiDescriptionGenerator(
@@ -89,8 +87,7 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen
             final ThreadLocale threadLocale,
             final PageSource pageSource,
             final ComponentClassResolver componentClassResolver,
-            final PageRenderLinkSource pageRenderLinkSource,
-            final ComponentSource componentSource) 
+            final PageRenderLinkSource pageRenderLinkSource) 
     {
         super();
         this.baseUrlSource = baseUrlSource;
@@ -100,7 +97,6 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen
         this.pageSource = pageSource;
         this.componentClassResolver = componentClassResolver;
         this.pageRenderLinkSource = pageRenderLinkSource;
-        this.componentSource = componentSource;
         messages = new ThreadLocal<>();
         failedPageNames = new HashSet<>();
     }
@@ -161,7 +157,7 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen
         JSONObject info = new JSONObject();
         putIfNotEmpty(info, "title", SymbolConstants.OPENAPI_TITLE);
         putIfNotEmpty(info, "description", SymbolConstants.OPENAPI_DESCRIPTION);
-        info.put("version", getValueFromSymbol(SymbolConstants.OPENAPI_APPLICATION_VERSION).orElse("?"));
+        info.put("version", getValueFromSymbolNoPrefix(SymbolConstants.OPENAPI_APPLICATION_VERSION).orElse("?"));
         documentation.put("info", info);
     }
     
@@ -250,28 +246,65 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen
         else
         {
             
-            final JSONObject methodDocumentation = new JSONObject();
+            final JSONObject methodDescription = new JSONObject();
             
-            putIfNotEmpty(methodDocumentation, "summary", getValue(method, uri, httpMethod, "summary"));
-            putIfNotEmpty(methodDocumentation, "description", getValue(method, uri, httpMethod, "description"));
+            putIfNotEmpty(methodDescription, "summary", getValue(method, uri, httpMethod, "summary"));
+            putIfNotEmpty(methodDescription, "description", getValue(method, uri, httpMethod, "description"));
             
             JSONArray methodTags = new JSONArray();
             methodTags.add(tagName);
-            methodDocumentation.put("tags", methodTags);
-            
-            JSONObject responses = new JSONObject();
-            JSONObject defaultResponse = new JSONObject();
-            int statusCode = httpMethod.equals("post") ? 
-                    HttpServletResponse.SC_CREATED : HttpServletResponse.SC_OK;
-            putIfNotEmpty(defaultResponse, "description", getValue(method, uri, httpMethod, statusCode));
-            responses.put(String.valueOf(statusCode), defaultResponse);
+            methodDescription.put("tags", methodTags);
             
-            methodDocumentation.put("responses", responses);
+            processResponses(method, uri, httpMethod, methodDescription);
+
+            processParameters(method, uri, httpMethod, methodDescription);
             
-            path.put(httpMethod, methodDocumentation);
+            path.put(httpMethod, methodDescription);
+        }
+    }
+
+    private void processParameters(Method method, final String uri, final String httpMethod, final JSONObject methodDescription) {
+        JSONArray parametersAsJsonArray = new JSONArray();
+        for (Parameter parameter : method.getParameters())
+        {
+            final JSONObject parameterDescription = new JSONObject();
+            if (!isIgnored(parameter) && 
+                    parameter.getAnnotation(StaticActivationContextValue.class) == null)
+            {
+                parameterDescription.put("in", "path");
+            }
+            else if (parameter.getAnnotation(RequestParameter.class) != null)
+            {
+                parameterDescription.put("in", "query");
+            }
+            if (!parameterDescription.isEmpty())
+            {
+                Optional<String> parameterName = getValue(method, uri, httpMethod, parameter, "name");
+                parameterDescription.put("name", parameterName.orElse(parameter.getName()));
+                getValue(method, uri, httpMethod, parameter, "description")
+                    .ifPresent((v) -> parameterDescription.put("description", v));
+                
+                parametersAsJsonArray.add(parameterDescription);
+            }
+        }
+        
+        if (!parametersAsJsonArray.isEmpty())
+        {
+            methodDescription.put("parameters", parametersAsJsonArray);
         }
     }
 
+    private void processResponses(Method method, final String uri, final String httpMethod, final JSONObject methodDescription) {
+        JSONObject responses = new JSONObject();
+        JSONObject defaultResponse = new JSONObject();
+        int statusCode = httpMethod.equals("post") ? 
+                HttpServletResponse.SC_CREATED : HttpServletResponse.SC_OK;
+        putIfNotEmpty(defaultResponse, "description", getValue(method, uri, httpMethod, statusCode));
+        responses.put(String.valueOf(statusCode), defaultResponse);
+
+        methodDescription.put("responses", responses);
+    }
+    
     private void addElementsIfNotPresent(JSONArray accumulator, JSONArray array) 
     {
         if (array != null)
@@ -317,21 +350,31 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen
     {
         return getValue(method, path + "." + httpMethod + "." + property, true);
     }
+
+    public Optional<String> getValue(Method method, String path, String httpMethod, Parameter parameter, String property) 
+    {
+        return getValue(method, path, httpMethod, "parameter." + parameter.getName(), property);
+    }
     
     public Optional<String> getValue(Method method, String path, String httpMethod, int statusCode) 
     {
-        Optional<String> value = getValue(method, path + "." + httpMethod + ".response." + String.valueOf(statusCode), true);
+        return getValue(method, path, httpMethod, "response", String.valueOf(statusCode));
+    }
+
+    public Optional<String> getValue(Method method, String path, String httpMethod, String middle, String propertyName) 
+    {
+        Optional<String> value = getValue(method, path + "." + httpMethod + "." + middle + "." + String.valueOf(propertyName), true);
         if (!value.isPresent())
         {
-            value = getValue(method, httpMethod + ".response." + String.valueOf(statusCode), false);
+            value = getValue(method, httpMethod + "." + middle + "." + propertyName, false);
         }
         if (!value.isPresent())
         {
-            value = getValue(method, "response." + String.valueOf(statusCode), false);
+            value = getValue(method, middle + "." + propertyName, false);
         }
         if (!value.isPresent())
         {
-            value = getValue("response." + String.valueOf(statusCode));
+            value = getValue(middle + "." + propertyName);
         }
         return value;
     }
@@ -490,8 +533,11 @@ public class DefaultOpenApiDescriptionGenerator implements OpenApiDescriptionGen
     
     private Optional<String> getValueFromSymbol(String key)
     {
+        return getValueFromSymbolNoPrefix("tapestry." + key);
+    }
+
+    private Optional<String> getValueFromSymbolNoPrefix(final String symbol) {
         String value;
-        final String symbol = "tapestry." + key;
         logSymbolLookup(symbol);
         try
         {
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/AbstractRestDemoPage.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/AbstractRestDemoPage.java
new file mode 100644
index 0000000..ca419a5
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/AbstractRestDemoPage.java
@@ -0,0 +1,27 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package org.apache.tapestry5.integration.app1.base;
+
+import org.apache.tapestry5.EventConstants;
+import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.StaticActivationContextValue;
+import org.apache.tapestry5.util.TextStreamResponse;
+
+public class AbstractRestDemoPage {
+    
+    @OnEvent(EventConstants.HTTP_GET)
+    protected Object abstractSuperclassEndpoint(@StaticActivationContextValue("abstract") String abstractValue)
+    {
+        return new TextStreamResponse("text/plain", abstractValue);
+    }
+
+}
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java
index 19fa7a7..20fbc4b 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/BaseRestDemoPage.java
@@ -13,11 +13,12 @@ package org.apache.tapestry5.integration.app1.base;
 
 import org.apache.tapestry5.EventConstants;
 import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.RequestParameter;
 import org.apache.tapestry5.annotations.StaticActivationContextValue;
 import org.apache.tapestry5.http.services.Response;
 import org.apache.tapestry5.util.TextStreamResponse;
 
-public class BaseRestDemoPage {
+public class BaseRestDemoPage extends AbstractRestDemoPage {
     
     public static final String EXTRA_HTTP_HEADER = "X-Event";
     
@@ -38,9 +39,19 @@ public class BaseRestDemoPage {
     }
     
     @OnEvent(EventConstants.HTTP_GET)
-    protected Object superclassEndpoint(@StaticActivationContextValue("superclassEndpoint") String parameter)
+    protected Object superclassEndpoint(@StaticActivationContextValue("superclassEndpoint") String pathParameter)
     {
-        return new TextStreamResponse("text/plain", parameter);
+        return new TextStreamResponse("text/plain", pathParameter);
+    }
+    
+    @OnEvent(EventConstants.HTTP_GET)
+    protected Object withParameters(
+            @StaticActivationContextValue("parametersTest") String staticParameter,
+            @RequestParameter(value = "fromQueryString", allowBlank = true) String queryParameter,
+            String pathParameter)
+    {
+        return new TextStreamResponse("text/plain", 
+                String.join(":", staticParameter, pathParameter, queryParameter));
     }
     
 }