You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2021/09/07 08:12:45 UTC

[servicecomb-java-chassis] branch master updated: [SCB-2331] swagger generator support NotBlank and NotEmpty annotations (#2554)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 68e5228  [SCB-2331] swagger generator support NotBlank and NotEmpty annotations (#2554)
68e5228 is described below

commit 68e52289206aaf1c36981b60335df05fa64df302
Author: Alne <84...@users.noreply.github.com>
AuthorDate: Tue Sep 7 16:12:13 2021 +0800

    [SCB-2331] swagger generator support NotBlank and NotEmpty annotations (#2554)
---
 .../client/validation/ValidationServiceClient.java |  2 +-
 .../generator/core/AbstractOperationGenerator.java | 47 +++++++++++++----
 .../annotation/ApiOperationProcessorTest.java      | 61 ++++++++++++++++++++++
 3 files changed, 98 insertions(+), 12 deletions(-)

diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java
index 84fa8d0..9616b48 100644
--- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java
+++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.java
@@ -79,7 +79,7 @@ public class ValidationServiceClient {
     } catch (InvocationException e) {
       TestMgr.check(400, e.getStatus().getStatusCode());
       TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase());
-      TestMgr.check(e.getErrorData().toString().contains("propertyPath=queryValidate.name"), true);
+      TestMgr.check(e.getErrorData().toString().contains("Parameter is not valid for operation"), true);
     }
   }
 }
diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java
index 3eabb68..56734d0 100644
--- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java
+++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java
@@ -26,14 +26,8 @@ import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.pos
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
+import java.util.*;
 import java.util.Map.Entry;
-import java.util.Set;
 
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.MediaType;
@@ -101,6 +95,8 @@ public abstract class AbstractOperationGenerator implements OperationGenerator {
   // 如果Response中不存在对应的header,则会将这些header补充进去
   protected Map<String, Property> methodResponseHeaders = new LinkedHashMap<>();
 
+  private static List<String> NOT_NULL_ANNOTATIONS = Arrays.asList("NotBlank", "NotEmpty");
+
   public AbstractOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) {
     this.swaggerGenerator = swaggerGenerator;
     this.swagger = swaggerGenerator.getSwagger();
@@ -250,12 +246,12 @@ public abstract class AbstractOperationGenerator implements OperationGenerator {
   }
 
   protected boolean isAggregatedParameter(ParameterGenerator parameterGenerator,
-      java.lang.reflect.Parameter methodParameter) {
+                                          java.lang.reflect.Parameter methodParameter) {
     return false;
   }
 
   protected void extractAggregatedParameterGenerators(Map<String, List<Annotation>> methodAnnotationMap,
-      java.lang.reflect.Parameter methodParameter) {
+                                                      java.lang.reflect.Parameter methodParameter) {
     JavaType javaType = TypeFactory.defaultInstance().constructType(methodParameter.getParameterizedType());
     BeanDescription beanDescription = Json.mapper().getSerializationConfig().introspect(javaType);
     for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) {
@@ -298,7 +294,7 @@ public abstract class AbstractOperationGenerator implements OperationGenerator {
   }
 
   private void addMethodAnnotationByParameterName(Map<String, List<Annotation>> methodAnnotations, String name,
-      Annotation annotation) {
+                                                  Annotation annotation) {
     if (StringUtils.isEmpty(name)) {
       throw new IllegalStateException(String.format("%s.name should not be empty. method=%s:%s",
           annotation.annotationType().getSimpleName(),
@@ -364,7 +360,7 @@ public abstract class AbstractOperationGenerator implements OperationGenerator {
   }
 
   protected void fillParameter(Swagger swagger, Parameter parameter, String parameterName, JavaType type,
-      List<Annotation> annotations) {
+                               List<Annotation> annotations) {
     for (Annotation annotation : annotations) {
       ParameterProcessor<Parameter, Annotation> processor = findParameterProcessors(annotation.annotationType());
       if (processor != null) {
@@ -383,6 +379,11 @@ public abstract class AbstractOperationGenerator implements OperationGenerator {
 
     if (parameter instanceof AbstractSerializableParameter) {
       io.swagger.util.ParameterProcessor.applyAnnotations(swagger, parameter, type, annotations);
+      annotations.stream().forEach(annotation -> {
+        if (NOT_NULL_ANNOTATIONS.contains(annotation.annotationType().getSimpleName())){
+          parameter.setRequired(true);
+        }
+      });
       return;
     }
 
@@ -405,9 +406,33 @@ public abstract class AbstractOperationGenerator implements OperationGenerator {
       }
     }
 
+    // swagger 2.0 do not support NotBlank and NotEmpty annotations, fix it
+    if (((JavaType)type).getBindings().getTypeParameters().isEmpty()){
+      convertAnnotationProperty(((JavaType)type).getRawClass());
+    } else {
+      ((JavaType)type).getBindings().getTypeParameters().stream().
+          forEach(javaType -> convertAnnotationProperty(javaType.getRawClass()));
+    }
+
     mergeBodyParameter((BodyParameter) parameter, newBodyParameter);
   }
 
+  private void convertAnnotationProperty(Class<?> beanClass) {
+    Map<String, Model> definitions = swagger.getDefinitions();
+    if (definitions == null) {
+      return;
+    }
+    Model model = definitions.get(beanClass.getSimpleName());
+    Arrays.stream(beanClass.getDeclaredFields()).forEach(field -> {
+      boolean requireItem = Arrays.stream(field.getAnnotations()).anyMatch(annotation ->
+          NOT_NULL_ANNOTATIONS.contains(annotation.annotationType().getSimpleName()));
+      if (requireItem) {
+        model.getProperties().get(field.getName()).setRequired(true);
+      }
+    });
+  }
+
+
   private void mergeBodyParameter(BodyParameter bodyParameter, BodyParameter fromBodyParameter) {
     if (fromBodyParameter.getExamples() != null) {
       bodyParameter.setExamples(fromBodyParameter.getExamples());
diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiOperationProcessorTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiOperationProcessorTest.java
index 0aa1491..ae7bc19 100644
--- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiOperationProcessorTest.java
+++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiOperationProcessorTest.java
@@ -20,9 +20,14 @@ package org.apache.servicecomb.swagger.generator.core.processor.annotation;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
 import javax.ws.rs.core.MediaType;
 
+import io.swagger.models.properties.Property;
 import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation;
 import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations;
 import org.hamcrest.Matchers;
@@ -30,6 +35,9 @@ import org.junit.AfterClass;
 import org.junit.Test;
 
 import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.Map;
 
 public class ApiOperationProcessorTest {
   static SwaggerOperations swaggerOperations = SwaggerOperations.generate(TestClass.class);
@@ -64,6 +72,48 @@ public class ApiOperationProcessorTest {
     public String testBlankMediaType(String input) {
       return input;
     }
+
+    @ApiOperation(value = "testBodyParam")
+    public String testBodyParam(@RequestBody TestBodyBean user) {
+      return user.toString();
+    }
+  }
+
+
+  private static class TestBodyBean {
+
+    @NotBlank
+    private String age;
+
+    @NotNull
+    private String name;
+
+    @NotEmpty
+    private String sexes;
+
+    public String getAge() {
+      return age;
+    }
+
+    public void setAge(String age) {
+      this.age = age;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public void setName(String name) {
+      this.name = name;
+    }
+
+    public String getSexes() {
+      return sexes;
+    }
+
+    public void setSexes(String sexes) {
+      this.sexes = sexes;
+    }
   }
 
   @Test
@@ -99,4 +149,15 @@ public class ApiOperationProcessorTest {
     assertThat(swaggerOperation.getOperation().getConsumes(), Matchers.contains(MediaType.TEXT_HTML));
     assertThat(swaggerOperation.getOperation().getProduces(), Matchers.contains(MediaType.TEXT_HTML));
   }
+
+
+  @Test
+  public void testBodyParam() {
+    SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBodyParam");
+    Map<String, Property> properties = swaggerOperation.getSwagger().getDefinitions().get("TestBodyBean").getProperties();
+    assertTrue("Support NotBlank annotation", properties.get("age").getRequired());
+    assertTrue("Support NotEmpty annotation", properties.get("sexes").getRequired());
+    assertTrue("Original support NotNull annotation", properties.get("name").getRequired());
+  }
+
 }