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

adoroszlai pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git

commit 86735210772b858654fb47becac5cb99af4c64bc
Author: Gabor Boros <6317425+g-bo...@users.noreply.github.com>
AuthorDate: Fri Apr 6 13:29:54 2018 +0200

    AMBARI-23436 - add SwaggerOverwriteNestedAPI annotation to handle
    special API cases
    
    Change-Id: I346de6f2d6f23eb7720b21bec2a4056a43df2a44
---
 .../annotations/SwaggerOverwriteNestedAPI.java     | 63 ++++++++++++++++++++++
 .../apache/ambari/swagger/AmbariSwaggerReader.java | 44 ++++++++++++---
 .../ambari/swagger/AmbariSwaggerReaderTest.java    | 43 +++++++++++++++
 3 files changed, 143 insertions(+), 7 deletions(-)

diff --git 
a/ambari-utility/src/main/java/org/apache/ambari/annotations/SwaggerOverwriteNestedAPI.java
 
b/ambari-utility/src/main/java/org/apache/ambari/annotations/SwaggerOverwriteNestedAPI.java
new file mode 100644
index 0000000..29e4024
--- /dev/null
+++ 
b/ambari-utility/src/main/java/org/apache/ambari/annotations/SwaggerOverwriteNestedAPI.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.ambari.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The {@link SwaggerOverwriteNestedAPI} is used to overwrite default values of
+ * {@link org.apache.ambari.swagger.NestedApiRecord} when {@link 
org.apache.ambari.swagger.AmbariSwaggerReader}
+ * processes nested API classes for Swagger annotations.
+ *
+ * It can be useful to overcome the limitations of multi-nested service 
endpoints or endpoints without Path
+ * parameters.
+ *
+ * Parent API and its properties are not validated.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface SwaggerOverwriteNestedAPI {
+
+    /**
+     * Class name of parent object
+     * @return
+     */
+    Class parentApi();
+
+    /**
+     * Parent API path, usually top-level API path starting with slash.
+     * @return
+     */
+    String parentApiPath();
+
+    /**
+     * Path annotation value of the method in parent class.
+     * @return
+     */
+    String parentMethodPath();
+
+    /**
+     * Array of Strings to provide path parameters. Only string types are 
supported as of now.
+     * @return
+     */
+    String[] pathParameters();
+}
diff --git 
a/ambari-utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java
 
b/ambari-utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java
index 1e0898d..4da9f2d 100644
--- 
a/ambari-utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java
+++ 
b/ambari-utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java
@@ -18,12 +18,14 @@
 package org.apache.ambari.swagger;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import javax.ws.rs.Path;
 
+import org.apache.ambari.annotations.SwaggerOverwriteNestedAPI;
 import org.apache.ambari.annotations.SwaggerPreferredParent;
 import org.apache.maven.plugin.logging.Log;
 import org.slf4j.Logger;
@@ -44,6 +46,7 @@ import io.swagger.models.Swagger;
 import io.swagger.models.Tag;
 import io.swagger.models.parameters.Parameter;
 import io.swagger.models.parameters.PathParameter;
+import io.swagger.models.properties.StringProperty;
 
 /**
  * Customized {@link 
com.github.kongchen.swagger.docgen.reader.ClassSwaggerReader} implementation to
@@ -95,6 +98,8 @@ public class AmbariSwaggerReader extends JaxrsReader {
             else {
               boolean skipAdd = false;
               Class<?> preferredParentClass = cls;
+              String parentApiPath;
+              String methodPathAsString = methodPath.value();
 
               // API is a nested API of multiple top level APIs
               if (nestedAPIs.containsKey(returnType)) {
@@ -128,9 +133,20 @@ public class AmbariSwaggerReader extends JaxrsReader {
                 nestedAPIs.remove(returnType);
               }
 
+              // API parent customization by @SwaggerOverwriteNestedAPI
+              SwaggerOverwriteNestedAPI swaggerOverwriteNestedAPI = 
AnnotationUtils.findAnnotation(returnType,
+                      SwaggerOverwriteNestedAPI.class);
+              if (null != swaggerOverwriteNestedAPI) {
+                preferredParentClass = swaggerOverwriteNestedAPI.parentApi();
+                parentApiPath = swaggerOverwriteNestedAPI.parentApiPath();
+                methodPathAsString = 
swaggerOverwriteNestedAPI.parentMethodPath();
+              } else {
+                parentApiPath = validateParentApiPath(preferredParentClass);
+              }
+
               logger.info("Registering nested API: {}", returnType);
-              NestedApiRecord nar = new NestedApiRecord(returnType, 
preferredParentClass,
-                      validateParentApiPath(preferredParentClass), method, 
methodPath.value());
+              NestedApiRecord nar = new NestedApiRecord(returnType, 
preferredParentClass, parentApiPath, method,
+                methodPathAsString);
               nestedAPIs.put(returnType, nar);
             }
           }
@@ -168,13 +184,27 @@ public class AmbariSwaggerReader extends JaxrsReader {
     NestedApiRecord nestedApiRecord = nestedAPIs.get(cls);
     if (null != nestedApiRecord) {
       logger.info("Processing nested API: {}", nestedApiRecord);
-      // Get the path parameters of the parent API method. All methods of the 
nested API class should include these
-      // parameters.
-      Operation operation = parseMethod(nestedApiRecord.parentMethod);
-      List<Parameter> pathParameters = ImmutableList.copyOf(
+      List<Parameter> pathParameters = new ArrayList<>();
+      SwaggerOverwriteNestedAPI swaggerOverwriteNestedAPI = 
AnnotationUtils.findAnnotation(nestedApiRecord.nestedApi,
+        SwaggerOverwriteNestedAPI.class);
+      if (null != swaggerOverwriteNestedAPI) {
+        logger.info("Will use path params from @SwaggerOverwriteNestedAPI: 
{}", (Object[]) swaggerOverwriteNestedAPI
+          .pathParameters());
+        for (String param : swaggerOverwriteNestedAPI.pathParameters()) {
+          PathParameter pathParam = new PathParameter();
+          pathParam.setName(param);
+          pathParam.setType(StringProperty.TYPE);
+          pathParameters.add(pathParam);
+        }
+      } else {
+        // Get the path parameters of the parent API method. All methods of 
the nested API class should include these
+        // parameters.
+        Operation operation = parseMethod(nestedApiRecord.parentMethod);
+        pathParameters = ImmutableList.copyOf(
           Collections2.filter(operation.getParameters(), 
Predicates.instanceOf(PathParameter.class)));
-      logger.info("Will copy path params from parent method: {}",
+        logger.info("Will copy path params from parent method: {}",
           Lists.transform(pathParameters, new ParameterToName()));
+      }
       return super.read(cls,
           joinPaths(nestedApiRecord.parentApiPath, 
nestedApiRecord.parentMethodPath, parentPath),
           parentMethod, readHidden,
diff --git 
a/ambari-utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java
 
b/ambari-utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java
index fbadafa..8ca519b 100644
--- 
a/ambari-utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java
+++ 
b/ambari-utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java
@@ -32,6 +32,7 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 
+import org.apache.ambari.annotations.SwaggerOverwriteNestedAPI;
 import org.apache.ambari.annotations.SwaggerPreferredParent;
 import org.apache.maven.plugin.logging.Log;
 import org.junit.Test;
@@ -145,6 +146,22 @@ public class AmbariSwaggerReaderTest {
   }
 
   /**
+   * Test nested API which uses {@link 
org.apache.ambari.annotations.SwaggerOverwriteNestedAPI} annotation.
+   * In this case we expect default values to be overwritten by the usage of 
the annotation.
+   */
+  @Test
+  public void swaggerNestedApisWithOverwrite() {
+    AmbariSwaggerReader asr = new AmbariSwaggerReader(null, 
createMock(Log.class));
+    Set<Class<?>> classes = new 
LinkedHashSet<>(Arrays.asList(NestedWithOverwrite.class, TopLevel4API.class));
+    Swagger swagger = asr.read(classes);
+    assertEquals(
+            ImmutableSet.of("/toplevel3/{foo}/bar/list", "/toplevel4/top"),
+            swagger.getPaths().keySet());
+    assertPathParamsExist(swagger, "/toplevel3/{foo}/bar/list", "foo");
+  }
+
+
+  /**
    * If an API is both top level (the class has a @Path annotation) and nested 
(class is a return type of an
    * API operation) then it should be treated as top level.
    */
@@ -235,6 +252,20 @@ abstract class YetAnotherTopLevelAPI {
 
 }
 
+@Path("/toplevel4")
+@Api(value = "Top Level 4", description = "Yet another top level API")
+abstract class TopLevel4API {
+
+  @GET
+  @Path("/top")
+  @ApiOperation(value = "list")
+  public abstract Response getList();
+
+  @Path("{param}/nested")
+  public abstract NestedWithOverwrite getNested(@ApiParam @PathParam(value = 
"param") String param);
+
+}
+
 @Api(value = "Nested", description = "A nested API")
 abstract class NestedAPI {
 
@@ -288,3 +319,15 @@ abstract class NestedWithBadPreferredParentAPI {
   public abstract Response getList();
 
 }
+
+@Api(value = "NestedWithOverWrite", description = "A nested API")
+@SwaggerOverwriteNestedAPI(parentApi = YetAnotherTopLevelAPI.class, 
parentApiPath = "/toplevel3", parentMethodPath =
+        "{foo}/bar", pathParameters = {"foo"})
+abstract class NestedWithOverwrite {
+
+  @GET
+  @Path("/list")
+  @ApiOperation(value = "list")
+  public abstract Response getList();
+
+}

-- 
To stop receiving notification emails like this one, please contact
adorosz...@apache.org.

Reply via email to