You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ch...@apache.org on 2019/12/30 06:36:57 UTC

[servicecomb-toolkit] 01/02: [SCB-1676] nested complex properties are not parsed and added to the component

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

chanjarster pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-toolkit.git

commit 638b269d34cd9e7e4e4c1fcb2177be1b63a31f3a
Author: kakulisen <18...@163.com>
AuthorDate: Mon Dec 23 11:45:44 2019 +0800

    [SCB-1676] nested complex properties are not parsed and added to the component
    
    Signed-off-by: kakulisen <18...@163.com>
---
 .../generator/annotation/ModelInterceptor.java     |   4 +-
 .../toolkit/generator/util/ModelConverter.java     |  60 ++++++-
 .../servicecomb/toolkit/generator/UtilsTest.java   | 197 ++++++++++++++++++++-
 .../generator/MultipartFileInterceptor.java        |   4 +-
 4 files changed, 260 insertions(+), 5 deletions(-)

diff --git a/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/annotation/ModelInterceptor.java b/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/annotation/ModelInterceptor.java
index 8415e1a..c6c8954 100644
--- a/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/annotation/ModelInterceptor.java
+++ b/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/annotation/ModelInterceptor.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.toolkit.generator.annotation;
 
+import java.lang.reflect.Type;
+
 import io.swagger.v3.oas.models.Components;
 import io.swagger.v3.oas.models.media.Schema;
 
@@ -24,5 +26,5 @@ public interface ModelInterceptor {
 
   int order();
 
-  Schema process(Class<?> cls, Components components);
+  Schema process(Type cls, Components components);
 }
diff --git a/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/util/ModelConverter.java b/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/util/ModelConverter.java
index 307863d..ae3f9e4 100644
--- a/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/util/ModelConverter.java
+++ b/oas-generator/oas-generator-core/src/main/java/org/apache/servicecomb/toolkit/generator/util/ModelConverter.java
@@ -17,10 +17,16 @@
 
 package org.apache.servicecomb.toolkit.generator.util;
 
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.ServiceLoader;
 
 import org.apache.servicecomb.toolkit.generator.annotation.ModelInterceptor;
@@ -37,6 +43,7 @@ import io.swagger.v3.core.util.PrimitiveType;
 import io.swagger.v3.core.util.RefUtils;
 import io.swagger.v3.oas.models.Components;
 import io.swagger.v3.oas.models.media.ArraySchema;
+import io.swagger.v3.oas.models.media.ObjectSchema;
 import io.swagger.v3.oas.models.media.Schema;
 
 public class ModelConverter {
@@ -64,11 +71,11 @@ public class ModelConverter {
     interceptorMgr.remove(interceptor);
   }
 
-  public static Schema getSchema(Class<?> cls) {
+  public static Schema getSchema(Type cls) {
     return getSchema(cls, null);
   }
 
-  public static Schema getSchema(Class<?> cls, Components components) {
+  public static Schema getSchema(Type cls, Components components) {
 
     for (ModelInterceptor interceptor : interceptorMgr) {
       Schema schema = interceptor.process(cls, components);
@@ -77,16 +84,42 @@ public class ModelConverter {
       }
     }
 
+    if (cls instanceof Class) {
+      Map<String, Type> beanProperties = getBeanProperties((Class) cls);
+
+      Optional.ofNullable(beanProperties)
+          .ifPresent(properties -> properties.forEach((name, type) ->
+              {
+                if (type instanceof ParameterizedType) {
+                  Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
+                  Arrays.stream(actualTypeArguments).forEach(arg -> getSchema(arg, components));
+                }
+
+                if (type instanceof Class) {
+                  getSchema(type, components);
+                }
+              })
+          );
+    }
+
     Schema schema = PrimitiveType.createProperty(cls);
     if (schema == null) {
       schema = context
           .resolve(new AnnotatedType(cls));
     }
 
+    if (schema == null) {
+      if (cls == List.class) {
+        schema = new ArraySchema();
+        ((ArraySchema) schema).setItems(new ObjectSchema());
+      }
+    }
+
     if (components == null) {
       return schema;
     }
 
+    // correct reference
     Schema refSchema = schema;
 
     if (shouldExtractRef(schema)) {
@@ -143,4 +176,27 @@ public class ModelConverter {
 
     return mapper;
   }
+
+  public static Map<String, Type> getBeanProperties(Class cls) {
+    if (cls.isPrimitive()) {
+      return null;
+    }
+    Method[] declaredMethods = cls.getDeclaredMethods();
+    Map<String, Type> beanProperties = new HashMap<>();
+
+    for (Method method : declaredMethods) {
+      if (method.getName().startsWith("get")) {
+        if (method.getReturnType() != null && method.getReturnType() != void.class) {
+          String propName = method.getName().substring(3);
+          try {
+            cls.getDeclaredMethod("set" + propName, method.getReturnType());
+            beanProperties.put(method.getName(), method.getGenericReturnType());
+          } catch (NoSuchMethodException e) {
+            continue;
+          }
+        }
+      }
+    }
+    return beanProperties;
+  }
 }
diff --git a/oas-generator/oas-generator-core/src/test/java/org/apache/servicecomb/toolkit/generator/UtilsTest.java b/oas-generator/oas-generator-core/src/test/java/org/apache/servicecomb/toolkit/generator/UtilsTest.java
index 1ef65d1..6aab6b9 100644
--- a/oas-generator/oas-generator-core/src/test/java/org/apache/servicecomb/toolkit/generator/UtilsTest.java
+++ b/oas-generator/oas-generator-core/src/test/java/org/apache/servicecomb/toolkit/generator/UtilsTest.java
@@ -21,6 +21,7 @@ import static org.mockito.Mockito.when;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
 import java.util.List;
 
 import org.apache.servicecomb.toolkit.generator.annotation.ModelInterceptor;
@@ -74,7 +75,7 @@ public class UtilsTest {
       }
 
       @Override
-      public Schema process(Class<?> cls, Components components) {
+      public Schema process(Type cls, Components components) {
         return new Schema().name("unknown");
       }
     };
@@ -83,6 +84,55 @@ public class UtilsTest {
     schema = ModelConverter.getSchema(ParameterClass.class);
     Assert.assertEquals("unknown", schema.getName());
     ModelConverter.unRegisterInterceptor(mockModelInterceptor);
+
+    Components component = new Components();
+    ModelConverter.getSchema(BeanClass.class, component);
+    Assert.assertNotNull(component.getSchemas().get("Value"));
+
+    Schema beanClass = component.getSchemas().get("BeanClass");
+    Assert.assertNotNull(beanClass);
+    Schema valueRef = (Schema) beanClass.getProperties().get("value");
+    Assert.assertEquals("#/components/schemas/Value", valueRef.get$ref());
+
+    Schema intVal = (Schema) beanClass.getProperties().get("intVal");
+    Assert.assertEquals("int32", intVal.getFormat());
+    Assert.assertEquals("integer", intVal.getType());
+
+    Schema intObj = (Schema) beanClass.getProperties().get("intObj");
+    Assert.assertEquals("int32", intObj.getFormat());
+    Assert.assertEquals("integer", intObj.getType());
+
+    Schema longVal = (Schema) beanClass.getProperties().get("longVal");
+    Assert.assertEquals("int64", longVal.getFormat());
+    Assert.assertEquals("integer", longVal.getType());
+
+    Schema longObj = (Schema) beanClass.getProperties().get("longObj");
+    Assert.assertEquals("int64", longObj.getFormat());
+    Assert.assertEquals("integer", longObj.getType());
+
+    Schema doubleVal = (Schema) beanClass.getProperties().get("doubleVal");
+    Assert.assertEquals("double", doubleVal.getFormat());
+    Assert.assertEquals("number", doubleVal.getType());
+
+    Schema doubleObj = (Schema) beanClass.getProperties().get("doubleObj");
+    Assert.assertEquals("double", doubleObj.getFormat());
+    Assert.assertEquals("number", doubleObj.getType());
+
+    Schema props = (Schema) beanClass.getProperties().get("props");
+    Assert.assertEquals(ArraySchema.class, props.getClass());
+    Assert.assertEquals("array", props.getType());
+    Assert.assertEquals("string", ((ArraySchema) props).getItems().getType());
+
+    Schema numbers = (Schema) beanClass.getProperties().get("numbers");
+    Assert.assertEquals(ArraySchema.class, numbers.getClass());
+    Assert.assertEquals("array", numbers.getType());
+    Assert.assertEquals("integer", ((ArraySchema) numbers).getItems().getType());
+    Assert.assertEquals("int32", ((ArraySchema) numbers).getItems().getFormat());
+
+    Schema values = (Schema) beanClass.getProperties().get("values");
+    Assert.assertEquals(ArraySchema.class, values.getClass());
+    Assert.assertEquals("array", values.getType());
+    Assert.assertEquals("#/components/schemas/Value", ((ArraySchema) values).getItems().get$ref());
   }
 
   @Test
@@ -99,4 +149,149 @@ public class UtilsTest {
     public void method(String param) {
     }
   }
+
+  class BeanClass {
+
+    private String name;
+
+    private int intVal;
+
+    private long longVal;
+
+    private double doubleVal;
+
+    private Integer intObj;
+
+    private Long longObj;
+
+    private Double doubleObj;
+
+    private Value value;
+
+    List<String> props;
+
+    List<Integer> numbers;
+
+    List<Value> values;
+
+    List list;
+
+    public List getList() {
+      return list;
+    }
+
+    public void setList(List list) {
+      this.list = list;
+    }
+
+    public int getIntVal() {
+      return intVal;
+    }
+
+    public void setIntVal(int intVal) {
+      this.intVal = intVal;
+    }
+
+    public long getLongVal() {
+      return longVal;
+    }
+
+    public void setLongVal(long longVal) {
+      this.longVal = longVal;
+    }
+
+    public double getDoubleVal() {
+      return doubleVal;
+    }
+
+    public void setDoubleVal(double doubleVal) {
+      this.doubleVal = doubleVal;
+    }
+
+    public Integer getIntObj() {
+      return intObj;
+    }
+
+    public void setIntObj(Integer intObj) {
+      this.intObj = intObj;
+    }
+
+    public Long getLongObj() {
+      return longObj;
+    }
+
+    public void setLongObj(Long longObj) {
+      this.longObj = longObj;
+    }
+
+    public Double getDoubleObj() {
+      return doubleObj;
+    }
+
+    public void setDoubleObj(Double doubleObj) {
+      this.doubleObj = doubleObj;
+    }
+
+    public List<Value> getValues() {
+      return values;
+    }
+
+    public void setValues(List<Value> values) {
+      this.values = values;
+    }
+
+    public List<Integer> getNumbers() {
+      return numbers;
+    }
+
+    public void setNumbers(List<Integer> numbers) {
+      this.numbers = numbers;
+    }
+
+    public List<String> getProps() {
+      return props;
+    }
+
+    public void setProps(List<String> props) {
+      this.props = props;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public void setName(String name) {
+      this.name = name;
+    }
+
+    public Value getValue() {
+      return value;
+    }
+
+    public void setValue(Value value) {
+      this.value = value;
+    }
+  }
+
+  class Value {
+    private String prop1;
+
+    private String prop2;
+
+    public String getProp1() {
+      return prop1;
+    }
+
+    public void setProp1(String prop1) {
+      this.prop1 = prop1;
+    }
+
+    public String getProp2() {
+      return prop2;
+    }
+
+    public void setProp2(String prop2) {
+      this.prop2 = prop2;
+    }
+  }
 }
diff --git a/oas-generator/oas-generator-spring/src/main/java/org/apache/servicecomb/toolkit/generator/MultipartFileInterceptor.java b/oas-generator/oas-generator-spring/src/main/java/org/apache/servicecomb/toolkit/generator/MultipartFileInterceptor.java
index 4857143..a5c2a92 100644
--- a/oas-generator/oas-generator-spring/src/main/java/org/apache/servicecomb/toolkit/generator/MultipartFileInterceptor.java
+++ b/oas-generator/oas-generator-spring/src/main/java/org/apache/servicecomb/toolkit/generator/MultipartFileInterceptor.java
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.toolkit.generator;
 
+import java.lang.reflect.Type;
+
 import org.apache.servicecomb.toolkit.generator.annotation.ModelInterceptor;
 import org.apache.servicecomb.toolkit.generator.util.ModelConverter;
 import org.springframework.web.multipart.MultipartFile;
@@ -33,7 +35,7 @@ public class MultipartFileInterceptor implements ModelInterceptor {
   }
 
   @Override
-  public Schema process(Class<?> cls, Components components) {
+  public Schema process(Type cls, Components components) {
 
     if (!MultipartFile.class.equals(cls)) {
       return null;