Copilot commented on code in PR #6339:
URL: https://github.com/apache/shenyu/pull/6339#discussion_r3245194469
##########
shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/OpenApiUtils.java:
##########
@@ -111,14 +171,163 @@ public static List<Parameter>
generateDocumentParameters(final String path, fina
parameter.setIn("path");
parameter.setName(name);
parameter.setRequired(true);
- parameter.setSchema(new Schema("string", null));
+ parameter.setType("string");
list.add(parameter);
}
}
}
return list;
}
+ /**
+ * Generate request parameters for RPC methods.
+ * Unlike HTTP methods that use Spring annotations, RPC method parameters
+ * are parsed from Java method parameter types directly.
+
+ * @param method the method
+ * @return request parameters
+ */
+ public static List<Parameter> generateRpcRequestDocParameters(final Method
method) {
+ List<Parameter> list = new ArrayList<>();
+ java.lang.reflect.Parameter[] methodParams = method.getParameters();
+ for (java.lang.reflect.Parameter methodParam : methodParams) {
+ Class<?> paramType = methodParam.getType();
+ Schema schema = parseSchema(paramType, 0, new HashMap<>(16));
+ Parameter parameter =
convertSchemaToParameter(methodParam.getName(), schema);
Review Comment:
RPC request parameter parsing uses `methodParam.getType()` (raw `Class<?>`),
which discards generic type information. This causes `List<String>` /
`Map<K,V>` parameters to be treated as plain `object` and prevents accurate
array/map schema generation. Use `methodParam.getParameterizedType()` (a
`Type`) and pass that into `parseSchema`, and add handling for raw
`Collection`/`Map` classes in `parseClassSchema` as a fallback.
##########
shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/OpenApiUtils.java:
##########
@@ -58,16 +61,73 @@ public class OpenApiUtils {
private static final String[] QUERY_CLASSES = new
String[]{"org.springframework.web.bind.annotation.RequestParam",
"org.springframework.web.bind.annotation.RequestPart"};
+ /**
+ * Check if the given RPC type uses Spring MVC parameter parsing.
+ * HTTP, WebSocket and Spring Cloud types use Spring MVC annotations for
parameter resolution,
+ * while other RPC types (Dubbo, gRPC, etc.) parse parameters from Java
method signatures directly.
+ *
+ * @param rpcTypeEnum the RPC type enum
+ * @return true if Spring MVC parameter parsing should be used
+ */
+ public static boolean useSpringMvcParamParsing(final RpcTypeEnum
rpcTypeEnum) {
+ return rpcTypeEnum == RpcTypeEnum.HTTP
+ || rpcTypeEnum == RpcTypeEnum.WEB_SOCKET
+ || rpcTypeEnum == RpcTypeEnum.SPRING_CLOUD;
+ }
/**
- * generateDocumentParameters.
+ * Build document JSON string for the given API method.
+ * Dispatches to the appropriate parameter/response generation based on
RPC type.
+ *
+ * @param tags the API tags
+ * @param path the API path
+ * @param method the Java method
+ * @param rpcTypeEnum the RPC type
+ * @return document JSON string
+ */
+ public static String buildDocumentJson(final List<String> tags, final
String path,
+ final Method method, final
RpcTypeEnum rpcTypeEnum) {
+ boolean useSpringMvcParamParsing =
useSpringMvcParamParsing(rpcTypeEnum);
+ Map<String, Object> documentMap;
+ if (useSpringMvcParamParsing) {
+ documentMap = ImmutableMap.<String, Object>builder()
+ .put("tags", tags)
+ .put("operationId", path)
+ .put("requestParameters",
generateRequestDocParameters(path, method))
+ .put("responseParameters",
Collections.singletonList(parseReturnType(method)))
+ .put("responses", generateDocumentResponse(path))
+ .build();
+ } else if (rpcTypeEnum == RpcTypeEnum.GRPC) {
+ documentMap = ImmutableMap.<String, Object>builder()
+ .put("tags", tags)
+ .put("operationId", path)
+ .put("requestParameters",
generateGrpcRequestDocParameters(method))
+ .put("responseParameters",
Collections.singletonList(parseGrpcReturnType(method)))
+ .put("responses", generateGrpcDocumentResponse(path,
method))
+ .build();
+ } else {
+ documentMap = ImmutableMap.<String, Object>builder()
+ .put("tags", tags)
+ .put("operationId", path)
+ .put("requestParameters",
generateRpcRequestDocParameters(method))
+ .put("responseParameters",
Collections.singletonList(parseReturnType(method)))
+ .put("responses", generateRpcDocumentResponse(path,
method))
+ .build();
+ }
+ return GsonUtils.getInstance().toJson(documentMap);
+ }
+
+
+ /**
+ * Generate request parameters for HTTP methods.
+ * This produces OpenAPI-style Parameter objects with in and schema fields.
Review Comment:
The Javadoc says request parameters include "schema" fields, but `Parameter`
no longer has a `schema` property (it now uses `type`/`refs`). Please update
the comment to reflect the current JSON shape to avoid misleading future
changes.
##########
shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/OpenApiUtils.java:
##########
@@ -296,6 +749,219 @@ private static ResponseType parseGenericArrayType(final
ResponseType responseTyp
return responseType;
}
+ private static Schema parseSchema(final Type type, final int depth, final
Map<TypeVariable<?>, Type> typeVariableMap) {
+ if (depth > 5) {
+ return new Schema("object", null);
+ }
+ if (type instanceof Class) {
+ return parseClassSchema((Class<?>) type, depth, typeVariableMap);
+ } else if (type instanceof ParameterizedType) {
+ return parseParameterizedTypeSchema((ParameterizedType) type,
depth, typeVariableMap);
+ } else if (type instanceof GenericArrayType) {
+ Schema elementSchema = parseSchema(((GenericArrayType)
type).getGenericComponentType(), depth + 1, typeVariableMap);
+ Schema schema = new Schema("array", null);
+ schema.setRefs(Collections.singletonList(elementSchema));
+ return schema;
+ } else if (type instanceof TypeVariable) {
+ Type actualType = typeVariableMap.get(type);
+ if (Objects.nonNull(actualType)) {
+ return parseSchema(actualType, depth, typeVariableMap);
+ } else if (((TypeVariable<?>) type).getBounds().length > 0) {
+ return parseSchema(((TypeVariable<?>) type).getBounds()[0],
depth, typeVariableMap);
+ } else {
+ return new Schema("object", null);
+ }
+ } else {
+ return new Schema("object", null);
+ }
+ }
+
+ private static Schema parseClassSchema(final Class<?> clazz, final int
depth, final Map<TypeVariable<?>, Type> typeVariableMap) {
+ if (clazz.isArray()) {
+ Schema elementSchema = parseSchema(clazz.getComponentType(), depth
+ 1, typeVariableMap);
+ Schema schema = new Schema("array", null);
+ schema.setRefs(Collections.singletonList(elementSchema));
+ return schema;
+ } else if (clazz.isEnum()) {
+ return new Schema("string", null);
+ } else if (isBooleanType(clazz)) {
+ return new Schema("boolean", null);
+ } else if (isIntegerType(clazz)) {
+ return new Schema("integer", null);
+ } else if (isNumberType(clazz)) {
+ return new Schema("number", null);
+ } else if (isStringType(clazz)) {
+ return new Schema("string", null);
+ } else if (isDateType(clazz)) {
+ return new Schema("string", "date");
+ } else if (isProtobufMessage(clazz)) {
+ return parseProtobufClassSchema(clazz, depth, typeVariableMap);
+ } else {
+ List<Schema> refs = new ArrayList<>();
+ for (Field field : clazz.getDeclaredFields()) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ continue;
+ }
+ Schema fieldSchema = parseSchema(field.getGenericType(), depth
+ 1, typeVariableMap);
+ fieldSchema.setName(field.getName());
+ refs.add(fieldSchema);
+ }
+ Schema schema = new Schema("object", null);
+ schema.setRefs(refs);
+ return schema;
+ }
+ }
+
+ private static Schema parseParameterizedTypeSchema(final ParameterizedType
type, final int depth, final Map<TypeVariable<?>, Type> typeVariableMap) {
+ Class<?> rawType = (Class<?>) type.getRawType();
+ Type[] actualTypeArguments = type.getActualTypeArguments();
+ TypeVariable<?>[] typeVariables = rawType.getTypeParameters();
+ Map<TypeVariable<?>, Type> newTypeVariableMap = new
HashMap<>(typeVariableMap);
+ for (int i = 0; i < typeVariables.length; i++) {
+ newTypeVariableMap.put(typeVariables[i], actualTypeArguments[i]);
+ }
+ if (Collection.class.isAssignableFrom(rawType)) {
+ Schema elementSchema = parseSchema(actualTypeArguments[0], depth +
1, newTypeVariableMap);
+ Schema schema = new Schema("array", null);
+ schema.setRefs(Collections.singletonList(elementSchema));
+ return schema;
+ } else if (Map.class.isAssignableFrom(rawType)) {
+ Schema keySchema = parseSchema(actualTypeArguments[0], depth + 1,
newTypeVariableMap);
+ Schema valueSchema = parseSchema(actualTypeArguments[1], depth +
1, newTypeVariableMap);
Review Comment:
For `Map` schemas, `parseParameterizedTypeSchema` adds key/value schemas to
`refs` without setting their names. `convertSchemaToParameter(ref.getName(),
ref)` then produces nested parameters with `name=null`, which is awkward for
consumers rendering the schema. Consider setting names like "key"/"value" (or
representing maps via an `additionalProperties`-style structure) and similarly
naming array element schemas (e.g., "items").
##########
shenyu-client/shenyu-client-core/src/test/java/org/apache/shenyu/client/core/utils/OpenApiUtilsTest.java:
##########
@@ -0,0 +1,502 @@
+/*
+ * 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.shenyu.client.core.utils;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.protobuf.Empty;
+import org.apache.shenyu.client.core.test.Test.Address;
+import org.apache.shenyu.client.core.test.Test.TestRequest;
+import org.apache.shenyu.client.core.utils.OpenApiUtils.Parameter;
+import org.apache.shenyu.client.core.utils.OpenApiUtils.ResponseType;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+
+public class OpenApiUtilsTest {
+
+ @Test
+ void testGenerateRpcDocumentResponse() throws Exception {
+ Method method = DubboTestService.class.getMethod("findById",
String.class);
+ Map<String, Object> response =
OpenApiUtils.generateRpcDocumentResponse("/dubbo/findById", method);
+
+ assertThat(response.containsKey("200"), is(true));
+ assertThat(response.containsKey("404"), is(true));
+ assertThat(response.containsKey("409"), is(false));
+ }
+
+ @Test
+ void testGenerateRpcDocumentResponseVoidReturn() throws Exception {
+ Method method = RpcComplexParamService.class.getMethod("deleteById",
String.class);
+ Map<String, Object> response =
OpenApiUtils.generateRpcDocumentResponse("/rpc/deleteById", method);
+ assertThat(response.containsKey("200"), is(true));
+ assertThat(response.containsKey("404"), is(true));
+ assertThat(response.containsKey("409"), is(false));
+ Map<String, Object> successResponse = (Map<String, Object>)
response.get("200");
+ Map<String, Object> content = (Map<String, Object>)
successResponse.get("content");
+ Map<String, Object> schema = (Map<String, Object>) ((Map<String,
Object>) content.get("*/*")).get("schema");
+ assertThat(schema.get("type"), is("void"));
+ }
+
+ @Test
+ void testParseReturnTypeObject() throws Exception {
+ Method method = DubboTestService.class.getMethod("findById",
String.class);
+ ResponseType returnType = OpenApiUtils.parseReturnType(method);
+
+ assertThat(returnType.getType(), is("object"));
+ assertThat(returnType.getRefs(), notNullValue());
+ assertThat(returnType.getRefs(), hasSize(2));
+ assertThat(returnType.getRefs().get(0).getName(), is("id"));
+ assertThat(returnType.getRefs().get(0).getType(), is("string"));
+ assertThat(returnType.getRefs().get(1).getName(), is("name"));
+ assertThat(returnType.getRefs().get(1).getType(), is("string"));
+ }
+
+ @Test
+ void testParseReturnTypeList() throws Exception {
+ Method method = DubboTestService.class.getMethod("findAll");
+ ResponseType returnType = OpenApiUtils.parseReturnType(method);
+
+ assertThat(returnType.getType(), is("array"));
+ assertThat(returnType.getRefs(), notNullValue());
+ assertThat(returnType.getRefs(), hasSize(1));
+ assertThat(returnType.getRefs().get(0).getType(), is("object"));
+ }
+
+ @Test
+ void testIsProtobufMessageNegative() {
+ assertThat(OpenApiUtils.isProtobufMessage(DubboTest.class), is(false));
+ assertThat(OpenApiUtils.isProtobufMessage(String.class), is(false));
+ assertThat(OpenApiUtils.isProtobufMessage(null), is(false));
+ assertThat(OpenApiUtils.isProtobufMessage(int.class), is(false));
+ }
+
+ @Test
+ void testIsProtobufMessagePositive() {
+ assertThat(OpenApiUtils.isProtobufMessage(TestRequest.class),
is(true));
+ assertThat(OpenApiUtils.isProtobufMessage(Address.class), is(true));
+ assertThat(OpenApiUtils.isProtobufMessage(Empty.class), is(true));
+ }
+
+ @Test
+ void testParseReturnTypeProtobufWithAllFieldTypes() throws Exception {
+ Method method = ProtobufTestService.class.getMethod("getTestRequest");
+ ResponseType returnType = OpenApiUtils.parseReturnType(method);
+ assertThat(returnType.getType(), is("object"));
+ assertThat(returnType.getRefs(), notNullValue());
+ assertThat(returnType.getRefs(), hasSize(7));
+
+ assertThat(returnType.getRefs().get(0).getName(), is("id"));
+ assertThat(returnType.getRefs().get(0).getType(), is("string"));
+
+ assertThat(returnType.getRefs().get(1).getName(), is("count"));
+ assertThat(returnType.getRefs().get(1).getType(), is("integer"));
+
+ assertThat(returnType.getRefs().get(2).getName(), is("enabled"));
+ assertThat(returnType.getRefs().get(2).getType(), is("boolean"));
+
+ assertThat(returnType.getRefs().get(3).getName(), is("status"));
+ assertThat(returnType.getRefs().get(3).getType(), is("string"));
+
+ assertThat(returnType.getRefs().get(4).getName(), is("address"));
+ assertThat(returnType.getRefs().get(4).getType(), is("object"));
+ assertThat(returnType.getRefs().get(4).getRefs(), notNullValue());
+ assertThat(returnType.getRefs().get(4).getRefs(), hasSize(2));
+ assertThat(returnType.getRefs().get(4).getRefs().get(0).getName(),
is("street"));
+ assertThat(returnType.getRefs().get(4).getRefs().get(0).getType(),
is("string"));
+ assertThat(returnType.getRefs().get(4).getRefs().get(1).getName(),
is("city"));
+ assertThat(returnType.getRefs().get(4).getRefs().get(1).getType(),
is("string"));
+
+ assertThat(returnType.getRefs().get(5).getName(), is("tags"));
+ assertThat(returnType.getRefs().get(5).getType(), is("array"));
+ assertThat(returnType.getRefs().get(5).getRefs(), notNullValue());
+ assertThat(returnType.getRefs().get(5).getRefs(), hasSize(1));
+ assertThat(returnType.getRefs().get(5).getRefs().get(0).getType(),
is("string"));
+
+ assertThat(returnType.getRefs().get(6).getName(), is("addresses"));
+ assertThat(returnType.getRefs().get(6).getType(), is("array"));
+ assertThat(returnType.getRefs().get(6).getRefs(), notNullValue());
+ assertThat(returnType.getRefs().get(6).getRefs(), hasSize(1));
+ assertThat(returnType.getRefs().get(6).getRefs().get(0).getType(),
is("object"));
+ }
+
+ @Test
+ void testParseReturnTypeProtobufEmpty() throws Exception {
+ Method method = ProtobufTestService.class.getMethod("getEmpty");
+ ResponseType returnType = OpenApiUtils.parseReturnType(method);
+ assertThat(returnType.getType(), is("object"));
+ assertThat(returnType.getRefs(), nullValue());
+ }
+
+ @Test
+ void testGenerateRpcRequestDocParametersProtobuf() throws Exception {
+ Method method = ProtobufTestService.class.getMethod("sendTestRequest",
TestRequest.class);
+ List<Parameter> params =
OpenApiUtils.generateRpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("request"));
+ assertThat(params.get(0).getType(), is("object"));
+ assertThat(params.get(0).getRefs(), notNullValue());
+ assertThat(params.get(0).getRefs(), hasSize(7));
+ assertThat(params.get(0).getRefs().get(0).getName(), is("id"));
+ assertThat(params.get(0).getRefs().get(0).getType(), is("string"));
+ assertThat(params.get(0).getRefs().get(3).getName(), is("status"));
+ assertThat(params.get(0).getRefs().get(3).getType(), is("string"));
+ assertThat(params.get(0).getRefs().get(5).getName(), is("tags"));
+ assertThat(params.get(0).getRefs().get(5).getType(), is("array"));
+ }
+
+ @Test
+ void testGenerateDocumentResponseExistingBehavior() {
+ Map<String, Object> response =
OpenApiUtils.generateDocumentResponse("/test/path");
+ assertThat(response.containsKey("200"), is(true));
+ assertThat(response.containsKey("404"), is(true));
+ assertThat(response.containsKey("409"), is(true));
+ }
+
+ @Test
+ void testGenerateRequestDocParametersWithRequestParam() throws Exception {
+ Method method = SpringMvcController.class.getMethod("query",
String.class);
+ List<Parameter> params =
OpenApiUtils.generateRequestDocParameters("/test/query", method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("name"));
+ assertThat(params.get(0).getType(), is("string"));
+ assertThat(params.get(0).isRequired(), is(true));
+ }
+
+ @Test
+ void testGenerateRequestDocParametersWithPathVariable() throws Exception {
+ Method method = SpringMvcController.class.getMethod("getByPath",
String.class);
+ List<Parameter> params =
OpenApiUtils.generateRequestDocParameters("/test/{id}", method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("id"));
+ assertThat(params.get(0).getType(), is("string"));
+ assertThat(params.get(0).isRequired(), is(true));
+ }
+
+ @Test
+ void testGenerateRequestDocParametersNoAnnotations() throws Exception {
+ Method method = DubboTestService.class.getMethod("findById",
String.class);
+ List<Parameter> params =
OpenApiUtils.generateRequestDocParameters("/dubbo/findById", method);
+ assertThat(params, hasSize(0));
+ }
+
+ @Test
+ void testGenerateRpcRequestDocParametersSimpleType() throws Exception {
+ Method method = DubboTestService.class.getMethod("findById",
String.class);
+ List<Parameter> params =
OpenApiUtils.generateRpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("id"));
+ assertThat(params.get(0).getType(), is("string"));
+ assertThat(params.get(0).isRequired(), is(true));
+ }
+
+ @Test
+ void testGenerateRpcRequestDocParametersComplexType() throws Exception {
+ Method method = DubboTestService.class.getMethod("insert",
DubboTest.class);
+ List<Parameter> params =
OpenApiUtils.generateRpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getType(), is("object"));
+ assertThat(params.get(0).getRefs(), notNullValue());
+ assertThat(params.get(0).getRefs(), hasSize(2));
+ assertThat(params.get(0).isRequired(), is(true));
+ }
+
+ @Test
+ void testGenerateRpcRequestDocParametersWithListParameter() throws
Exception {
+ Method method = RpcComplexParamService.class.getMethod("batchInsert",
List.class);
+ List<Parameter> params =
OpenApiUtils.generateRpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("ids"));
+ assertThat(params.get(0).getType(), is("object"));
+ }
+
+ @Test
+ void testGenerateRpcRequestDocParametersWithMapParameter() throws
Exception {
+ Method method = RpcComplexParamService.class.getMethod("searchByMap",
Map.class);
+ List<Parameter> params =
OpenApiUtils.generateRpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("params"));
+ assertThat(params.get(0).getType(), is("object"));
+ }
+
+ @Test
+ void testUseSpringMvcParamParsing() {
+ assertThat(OpenApiUtils.useSpringMvcParamParsing(RpcTypeEnum.HTTP),
is(true));
+
assertThat(OpenApiUtils.useSpringMvcParamParsing(RpcTypeEnum.WEB_SOCKET),
is(true));
+
assertThat(OpenApiUtils.useSpringMvcParamParsing(RpcTypeEnum.SPRING_CLOUD),
is(true));
+ assertThat(OpenApiUtils.useSpringMvcParamParsing(RpcTypeEnum.DUBBO),
is(false));
+ assertThat(OpenApiUtils.useSpringMvcParamParsing(RpcTypeEnum.GRPC),
is(false));
+ assertThat(OpenApiUtils.useSpringMvcParamParsing(RpcTypeEnum.SOFA),
is(false));
+ }
+
+ // --- gRPC tests ---
+
+ @Test
+ void testGenerateGrpcRequestDocParametersSimpleType() throws Exception {
+ Method method = GrpcTestService.class.getMethod("unaryCall",
String.class, io.grpc.stub.StreamObserver.class);
+ List<Parameter> params =
OpenApiUtils.generateGrpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("request"));
+ assertThat(params.get(0).getType(), is("string"));
+ assertThat(params.get(0).isRequired(), is(true));
+ }
+
+ @Test
+ void testGenerateGrpcRequestDocParametersComplexType() throws Exception {
+ Method method = GrpcTestService.class.getMethod("unaryCallComplex",
GrpcTestClass.class, io.grpc.stub.StreamObserver.class);
+ List<Parameter> params =
OpenApiUtils.generateGrpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("request"));
+ assertThat(params.get(0).getType(), is("object"));
+ assertThat(params.get(0).getRefs(), notNullValue());
+ assertThat(params.get(0).isRequired(), is(true));
+ }
+
+ @Test
+ void testGenerateGrpcRequestDocParametersClientStreaming() throws
Exception {
+ Method method =
GrpcClientStreamingService.class.getMethod("clientStreaming",
io.grpc.stub.StreamObserver.class);
+ List<Parameter> params =
OpenApiUtils.generateGrpcRequestDocParameters(method);
+ assertThat(params, hasSize(1));
+ assertThat(params.get(0).getName(), is("request"));
+ assertThat(params.get(0).getType(), is("string"));
+ assertThat(params.get(0).isRequired(), is(true));
+ }
+
+ @Test
+ void testParseGrpcReturnTypeWithStreamObserver() throws Exception {
+ Method method = GrpcTestService.class.getMethod("unaryCall",
String.class, io.grpc.stub.StreamObserver.class);
+ ResponseType returnType = OpenApiUtils.parseGrpcReturnType(method);
+ assertThat(returnType.getName(), is("ROOT"));
+ assertThat(returnType.getType(), is("object"));
+ assertThat(returnType.getRefs(), notNullValue());
+ assertThat(returnType.getRefs(), hasSize(2));
+ }
+
+ @Test
+ void testParseGrpcReturnTypeVoid() throws Exception {
+ Method method = GrpcTestService.class.getMethod("noStreamObserver",
String.class);
+ ResponseType returnType = OpenApiUtils.parseGrpcReturnType(method);
+ assertThat(returnType.getName(), is("ROOT"));
+ assertThat(returnType.getType(), is("void"));
+ }
+
+ @Test
+ void testParseGrpcReturnTypeClientStreaming() throws Exception {
+ Method method =
GrpcClientStreamingService.class.getMethod("clientStreaming",
io.grpc.stub.StreamObserver.class);
+ ResponseType returnType = OpenApiUtils.parseGrpcReturnType(method);
+ assertThat(returnType.getName(), is("ROOT"));
+ assertThat(returnType.getType(), is("object"));
+ assertThat(returnType.getRefs(), notNullValue());
+ assertThat(returnType.getRefs(), hasSize(2));
+ }
+
+ @Test
+ void testGenerateGrpcDocumentResponseWithStreamObserver() throws Exception
{
+ Method method = GrpcTestService.class.getMethod("unaryCall",
String.class, io.grpc.stub.StreamObserver.class);
+ Map<String, Object> response =
OpenApiUtils.generateGrpcDocumentResponse("/grpc/unaryCall", method);
+ assertThat(response.containsKey("200"), is(true));
+ assertThat(response.containsKey("404"), is(true));
+ assertThat(response.containsKey("409"), is(false));
+ }
+
+ @Test
+ void testGenerateGrpcDocumentResponseVoid() throws Exception {
+ Method method = GrpcTestService.class.getMethod("noStreamObserver",
String.class);
+ Map<String, Object> response =
OpenApiUtils.generateGrpcDocumentResponse("/grpc/noStream", method);
+ assertThat(response.containsKey("200"), is(true));
+ assertThat(response.containsKey("404"), is(true));
+ }
+
+ // --- buildDocumentJson dispatch tests ---
+
+ @Test
+ void testBuildDocumentJsonHttp() throws Exception {
+ Method method = SpringMvcController.class.getMethod("query",
String.class);
+ String json = OpenApiUtils.buildDocumentJson(Arrays.asList("tag1"),
"/test/query", method, RpcTypeEnum.HTTP);
+ JsonObject doc = JsonParser.parseString(json).getAsJsonObject();
+ assertThat(doc.has("requestParameters"), is(true));
+ assertThat(doc.has("responseParameters"), is(true));
+ assertThat(doc.has("parameters"), is(false));
+ assertThat(doc.has("responseType"), is(false));
+ JsonObject responses = doc.getAsJsonObject("responses");
+ assertThat(responses.has("200"), is(true));
+ assertThat(responses.has("404"), is(true));
+ assertThat(responses.has("409"), is(true));
+ }
+
+ @Test
+ void testBuildDocumentJsonGrpc() throws Exception {
+ Method method = GrpcTestService.class.getMethod("unaryCall",
String.class, io.grpc.stub.StreamObserver.class);
+ String json = OpenApiUtils.buildDocumentJson(Arrays.asList("tag1"),
"/grpc/unaryCall", method, RpcTypeEnum.GRPC);
+ JsonObject doc = JsonParser.parseString(json).getAsJsonObject();
+ assertThat(doc.has("requestParameters"), is(true));
+ assertThat(doc.has("responseParameters"), is(true));
+ assertThat(doc.has("parameters"), is(false));
+ assertThat(doc.has("responseType"), is(false));
+ JsonObject responses = doc.getAsJsonObject("responses");
+ assertThat(responses.has("200"), is(true));
+ assertThat(responses.has("404"), is(true));
+ assertThat(responses.has("409"), is(false));
+ }
+
+ @Test
+ void testBuildDocumentJsonRpc() throws Exception {
+ Method method = DubboTestService.class.getMethod("findById",
String.class);
+ String json = OpenApiUtils.buildDocumentJson(Arrays.asList("tag1"),
"/dubbo/findById", method, RpcTypeEnum.DUBBO);
+ JsonObject doc = JsonParser.parseString(json).getAsJsonObject();
+ assertThat(doc.has("requestParameters"), is(true));
+ assertThat(doc.has("responseParameters"), is(true));
+ assertThat(doc.has("parameters"), is(false));
+ assertThat(doc.has("responseType"), is(false));
+ JsonObject responses = doc.getAsJsonObject("responses");
+ assertThat(responses.has("200"), is(true));
+ assertThat(responses.has("404"), is(true));
+ assertThat(responses.has("409"), is(false));
+ }
+
+ // --- Inner types (must be after all methods per checkstyle
InnerTypeLast) ---
+
+ public static class DubboTest {
+
+ private String id;
+
+ private String name;
+
+ public DubboTest() {
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public static class DubboTestService {
+
+ public DubboTest findById(final String id) {
+ return null;
+ }
+
+ public DubboTest insert(final DubboTest dubboTest) {
+ return null;
+ }
+
+ public List<DubboTest> findAll() {
+ return null;
+ }
+ }
+
+ @RestController
+ @RequestMapping("/test")
+ public static class SpringMvcController {
+
+ @RequestMapping("/query")
+ public String query(@RequestParam("name") final String name) {
+ return "";
+ }
+
+ @RequestMapping("/{id}")
+ public String getByPath(@RequestParam("id") final String id) {
+ return "";
Review Comment:
This method is intended to validate path-variable parsing, but the parameter
is annotated with `@RequestParam`. Using `@PathVariable` here would better
reflect the test name/intent and avoid conflating query parsing with
path-template parsing.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]