wujimin closed pull request #497: [JAV-579]support strArray URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/497
This is a PR merged from a forked repository. As GitHub hides the original diff on merge, it is displayed below for the sake of provenance: As this is a foreign pull request (from a fork), the diff is supplied below (as it won't show otherwise due to GitHub magic): diff --git a/README.md b/README.md index 954cdd48d..b52064882 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [](https://www.apache.org/licenses/LICENSE-2.0.html) Apache ServiceComb (incubating) Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features - ## Quick Start Provider service: diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.java index 60f0a70cc..b363aad2d 100644 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.java +++ b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.java @@ -18,6 +18,9 @@ package io.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -26,10 +29,33 @@ import io.servicecomb.common.rest.codec.RestClientRequest; import io.swagger.models.parameters.Parameter; +import io.swagger.models.parameters.QueryParameter; public class QueryProcessorCreator implements ParamValueProcessorCreator { + public static final String PARAMTYPE = "query"; + public QueryProcessorCreator() { + ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); + } + + @Override + public ParamValueProcessor create(Parameter parameter, Type genericParamType) { + JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); + + String collectionFormat = ((QueryParameter) parameter).getCollectionFormat(); + + if (collectionFormat == null) { + return new QueryProcessor(parameter.getName(), targetType); + } + if (collectionFormat.equals("csv")) { + return new CsvQueryProcessor(parameter.getName(), targetType); + } + + return new QueryProcessor(parameter.getName(), targetType); + } + + public static class QueryProcessor extends AbstractParamProcessor { public QueryProcessor(String paramPath, JavaType targetType) { super(paramPath, targetType); @@ -58,13 +84,39 @@ public String getProcessorType() { } } - public QueryProcessorCreator() { - ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); - } + public static class CsvQueryProcessor extends AbstractParamProcessor { - @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); - return new QueryProcessor(parameter.getName(), targetType); + + public CsvQueryProcessor(String paramPath, JavaType targetType) { + super(paramPath, targetType); + } + + @Override + public Object getValue(HttpServletRequest request) throws Exception { + Object value = null; + + if (targetType.isContainerType()) { + String[] strs = request.getParameterValues(paramPath); + List<String> asList = new ArrayList<>(); + for (String str : strs) { + asList.addAll(Arrays.asList(str.split(","))); + } + value = asList.toArray(); + } else { + value = request.getParameter(paramPath); + } + + return convertValue(value, targetType); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + + } + + @Override + public String getProcessorType() { + return PARAMTYPE; + } } -} +} \ No newline at end of file diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestCsvQueryProcessor.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestCsvQueryProcessor.java new file mode 100644 index 000000000..6956b9b1e --- /dev/null +++ b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestCsvQueryProcessor.java @@ -0,0 +1,72 @@ +/* + * 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 io.servicecomb.common.rest.codec.param; + +import javax.servlet.http.HttpServletRequest; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; + +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.servicecomb.common.rest.codec.param.QueryProcessorCreator.CsvQueryProcessor; +import mockit.Expectations; +import mockit.Mocked; + +public class TestCsvQueryProcessor { + @Mocked + HttpServletRequest request; + + private ParamValueProcessor createProcessor(String name, Class<?> type) { + return new CsvQueryProcessor(name, TypeFactory.defaultInstance().constructType(type)); + } + + @Test + public void testGetValueNormal() throws Exception { + new Expectations() { + { + request.getParameter("name"); + result = "v1,v2,v3"; + } + }; + + ParamValueProcessor processor = createProcessor("name", String.class); + Object value = processor.getValue(request); + Assert.assertEquals(value,"v1,v2,v3"); + } + + @Test + public void testGetValueContainerType() throws Exception { + new Expectations() { + { + request.getParameterValues("name"); + result = new String[] {"v1,v2,v3"}; + } + }; + + ParamValueProcessor processor = createProcessor("name", String[].class); + String[] value = (String[]) processor.getValue(request); + Assert.assertThat(value, Matchers.arrayContaining("v1","v2","v3")); + } + + @Test + public void testGetProcessorType() { + ParamValueProcessor processor = createProcessor("name", String.class); + Assert.assertEquals("query", processor.getProcessorType()); + } +} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java index 02f4f3c70..7200a3365 100644 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java +++ b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.java @@ -20,6 +20,7 @@ import org.junit.Assert; import org.junit.Test; +import io.servicecomb.common.rest.codec.param.QueryProcessorCreator.CsvQueryProcessor; import io.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; import io.swagger.models.parameters.Parameter; import io.swagger.models.parameters.QueryParameter; @@ -31,9 +32,23 @@ public void testCreate() { ParamValueProcessorCreatorManager.INSTANCE.findValue(QueryProcessorCreator.PARAMTYPE); Parameter parameter = new QueryParameter(); parameter.setName("query"); - ParamValueProcessor processor = creator.create(parameter, String.class); Assert.assertEquals(QueryProcessor.class, processor.getClass()); + + ((QueryParameter) parameter).setCollectionFormat("xxx"); + ParamValueProcessor processorXXX = creator.create(parameter, String.class); + + Assert.assertEquals(QueryProcessor.class, processorXXX.getClass()); + + ((QueryParameter) parameter).setCollectionFormat(null); + ParamValueProcessor processorNull = creator.create(parameter, String.class); + + Assert.assertEquals(QueryProcessor.class, processorNull.getClass()); + + ((QueryParameter) parameter).setCollectionFormat("csv"); + ParamValueProcessor processorCsv = creator.create(parameter, String.class); + + Assert.assertEquals(CsvQueryProcessor.class, processorCsv.getClass()); } } diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java index 6dd2810e0..3698b932f 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java @@ -37,6 +37,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.servicecomb.common.rest.codec.RestObjectMapper; import io.servicecomb.core.Const; import io.servicecomb.demo.compute.Person; @@ -52,6 +55,7 @@ import io.servicecomb.swagger.invocation.response.Headers; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ResponseHeader; import io.vertx.core.json.JsonObject; @@ -60,6 +64,7 @@ @Path("/codeFirstJaxrs") @Produces(MediaType.APPLICATION_JSON) public class CodeFirstJaxrs { + private Logger LOGGER= LoggerFactory.getLogger(this.getClass()); // public Response getUserResponse() { // // } @@ -209,4 +214,13 @@ public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { public String getTraceId() { return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME); } + + @Path("/testGetStrArray") + @GET + public String[] testGetStrArray(@ApiParam(collectionFormat = "csv") @QueryParam(value = "str") String[] str) { + for (int i = 0; i < str.length; i++) { + LOGGER.info("*******" + str[i]); + } + return str; + } } diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java index 5c5a2745d..a6eb54984 100644 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java +++ b/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java @@ -17,6 +17,8 @@ package io.servicecomb.demo; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -75,9 +77,24 @@ public void testCodeFirst(RestTemplate template, String microserviceName, String testTraceIdOnContextContainsTraceId(template, cseUrlPrefix); testRawJson(template, cseUrlPrefix); + testStrArray(template, cseUrlPrefix); } } + private void testStrArray(RestTemplate template, String cseUrlPrefix) { + String[] instrArray = {"a1,b1,c1"}; + ArrayList result = template.getForObject(cseUrlPrefix + "testGetStrArray?str=a1,b1,c1", ArrayList.class); + ArrayList result1 = template + .getForObject(cseUrlPrefix + "testGetStrArray?str={instrArray}", ArrayList.class, "a1,b1,c1"); + ArrayList result2 = template + .getForObject(cseUrlPrefix + "testGetStrArray?str={instrArray}", ArrayList.class, instrArray); + + String[] output = {"a1", "b1", "c1"}; + TestMgr.check(true, Arrays.asList(output).equals(result)); + TestMgr.check(true, Arrays.asList(output).equals(result1)); + TestMgr.check(true, Arrays.asList(output).equals(result2)); + } + protected void testOnlyRest(RestTemplate template, String cseUrlPrefix) { testModelFieldIgnore(template, cseUrlPrefix); } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java index 8011dbf75..0f51df908 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java @@ -30,6 +30,8 @@ import javax.ws.rs.core.Response.Status; import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -75,6 +77,7 @@ import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.ResponseHeader; @@ -83,6 +86,7 @@ @RestSchema(schemaId = "codeFirst") @RequestMapping(path = "/codeFirstSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) public class CodeFirstSpringmvc { + private Logger LOGGER= LoggerFactory.getLogger(this.getClass()); private MetricsServoRegistry registry; @@ -364,4 +368,11 @@ public String prometheusForTest() { public String getTraceId() { return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME); } + @GetMapping(path = "/testGetStrArray") + public String[] testGetStrArray(@ApiParam(collectionFormat = "csv") String[] str) { + for (int i = 0; i < str.length; i++) { + LOGGER.info("*******" + str[i]); + } + return str; + } } diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/OperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/OperationGenerator.java index e961bb721..43cc807c3 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/OperationGenerator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/OperationGenerator.java @@ -31,7 +31,9 @@ import io.servicecomb.swagger.SwaggerUtils; import io.servicecomb.swagger.extend.parameter.ContextParameter; +import io.servicecomb.swagger.generator.core.processor.annotation.ApiParamAnnotationProcessor; import io.servicecomb.swagger.generator.core.utils.ParamUtils; +import io.swagger.annotations.ApiParam; import io.swagger.models.HttpMethod; import io.swagger.models.Operation; import io.swagger.models.Path; @@ -72,6 +74,8 @@ protected String httpMethod; + private Map<String, Annotation> apiParams = new HashMap(); + public OperationGenerator(SwaggerGenerator swaggerGenerator, Method providerMethod) { this.swaggerGenerator = swaggerGenerator; this.swagger = swaggerGenerator.swagger; @@ -266,6 +270,16 @@ protected void scanMethodParameters() { context.getDefaultParamProcessor().process(this, paramIdx); } } + + processByApiParamAnnotation(apiParams); + } + + private void processByApiParamAnnotation(Map<String, Annotation> apiParams) { + for (int paramIdx = 0; paramIdx < apiParams.size(); paramIdx++) { + Annotation annotation = apiParams.get(String.valueOf(paramIdx)); + ParameterAnnotationProcessor processor = context.findParameterAnnotationProcessor(annotation.annotationType()); + processor.process(annotation, this, paramIdx); + } } private boolean isArgumentNotProcessed(int swaggerParamCount) { @@ -274,9 +288,14 @@ private boolean isArgumentNotProcessed(int swaggerParamCount) { protected void processByParameterAnnotation(Annotation[] paramAnnotations, int paramIdx) { for (Annotation annotation : paramAnnotations) { + + if (annotation instanceof ApiParam) { + apiParams.put(String.valueOf(paramIdx), annotation); + } + ParameterAnnotationProcessor processor = context.findParameterAnnotationProcessor(annotation.annotationType()); - if (processor != null) { + if (processor != null && !(processor instanceof ApiParamAnnotationProcessor)) { processor.process(annotation, this, paramIdx); } } diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiParamAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiParamAnnotationProcessor.java new file mode 100644 index 000000000..d1f3b9223 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiParamAnnotationProcessor.java @@ -0,0 +1,37 @@ +/* + * 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 io.servicecomb.swagger.generator.core.processor.annotation; + +import java.util.List; + +import io.servicecomb.swagger.generator.core.OperationGenerator; +import io.servicecomb.swagger.generator.core.ParameterAnnotationProcessor; +import io.swagger.annotations.ApiParam; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.parameters.QueryParameter; + +public class ApiParamAnnotationProcessor implements ParameterAnnotationProcessor { + @Override + public void process(Object annotation, OperationGenerator operationGenerator, int paramIdx) { + List<Parameter> providerParameters = operationGenerator.getProviderParameters(); + String collectionFormat = ((ApiParam) annotation).collectionFormat(); + Parameter parameter = providerParameters.get(paramIdx); + if (parameter instanceof QueryParameter) { + ((QueryParameter) parameter).setCollectionFormat(collectionFormat); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java index 3ab4321c2..8947ea7e3 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java @@ -34,6 +34,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import io.servicecomb.swagger.generator.core.processor.annotation.ApiParamAnnotationProcessor; import io.servicecomb.swagger.generator.core.utils.ClassUtils; import io.servicecomb.swagger.generator.jaxrs.processor.annotation.ConsumesAnnotationProcessor; import io.servicecomb.swagger.generator.jaxrs.processor.annotation.CookieParamAnnotationProcessor; @@ -47,6 +48,7 @@ import io.servicecomb.swagger.generator.jaxrs.processor.annotation.QueryParamAnnotationProcessor; import io.servicecomb.swagger.generator.jaxrs.processor.parameter.JaxrsDefaultParameterProcessor; import io.servicecomb.swagger.generator.rest.RestSwaggerGeneratorContext; +import io.swagger.annotations.ApiParam; public class JaxrsSwaggerGeneratorContext extends RestSwaggerGeneratorContext { private static final int ORDER = 2000; @@ -110,5 +112,7 @@ protected void initParameterAnnotationMgr() { parameterAnnotationMgr.register(HeaderParam.class, new HeaderParamAnnotationProcessor()); parameterAnnotationMgr.register(QueryParam.class, new QueryParamAnnotationProcessor()); + parameterAnnotationMgr.register(ApiParam.class, new ApiParamAnnotationProcessor()); + } } diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorContext.java index a7a9fa113..08dab0ee7 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorContext.java +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorContext.java @@ -33,6 +33,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; +import io.servicecomb.swagger.generator.core.processor.annotation.ApiParamAnnotationProcessor; import io.servicecomb.swagger.generator.core.utils.ClassUtils; import io.servicecomb.swagger.generator.rest.RestSwaggerGeneratorContext; import io.servicecomb.swagger.generator.springmvc.processor.annotation.CookieValueAnnotationProcessor; @@ -50,6 +51,7 @@ import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestParamAnnotationProcessor; import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestPartAnnotationProcessor; import io.servicecomb.swagger.generator.springmvc.processor.parameter.SpringmvcDefaultParameterProcessor; +import io.swagger.annotations.ApiParam; public class SpringmvcSwaggerGeneratorContext extends RestSwaggerGeneratorContext { private static final int ORDER = 1000; @@ -104,6 +106,7 @@ protected void initParameterAnnotationMgr() { parameterAnnotationMgr.register(RequestParam.class, new RequestParamAnnotationProcessor()); parameterAnnotationMgr.register(RequestAttribute.class, new RequestAttributeAnnotationProcessor()); parameterAnnotationMgr.register(RequestPart.class, new RequestPartAnnotationProcessor()); + parameterAnnotationMgr.register(ApiParam.class, new ApiParamAnnotationProcessor()); } @Override ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services
