You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by "eye-gu (via GitHub)" <gi...@apache.org> on 2026/05/12 03:21:27 UTC
[PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
eye-gu opened a new pull request, #6339:
URL: https://github.com/apache/shenyu/pull/6339
<!-- Describe your PR here; e.g. Fixes #issueNo -->
close #6338
Previously, API document generation only supported Spring Web annotation-based parameter parsing (`@RequestParam`, `@RequestPart`, path variables), which only works for HTTP/WebSocket/Spring Cloud. RPC types (Dubbo, SOFA, TARS, gRPC) had no request parameter information in generated documents.
### Changes
**OpenApiUtils - RPC-aware document generation**
- Add `buildDocumentJson()` that dispatches by RPC type:
- **Dubbo/TARS**: parse request parameters from Java method signatures
- **gRPC**: parse request/response from `StreamObserver` method pattern
- **Dubbo + Protobuf**: parse protobuf message fields via reflection for both request and response
- HTTP/WebSocket/Spring Cloud: unchanged Spring MVC annotation parsing
- Unify document fields to `requestParameters` / `responseParameters`
**SOFA - enable API document support**
- Refactor `SofaServiceEventListener` to extend `AbstractContextRefreshedEventListener` common flow
- Replace custom `handler()` / `onApplicationEvent()` with standard `getCorrectedClass()` + `buildApiPath()` hooks
- SOFA services now go through the unified API doc registration pipeline
**Examples**
- Add `@ApiModule` / `@ApiDoc` annotations to Dubbo protobuf example services
### Impact
- Dubbo/SOFA/TARS/gRPC API documents now include detailed request parameter schemas
- SOFA module gains API document generation capability
- No breaking change for HTTP/WebSocket/Spring Cloud document generation
<!--
Thank you for proposing a pull request. This template will guide you through the essential steps necessary for a pull request.
-->
Make sure that:
- [x] You have read the [contribution guidelines](https://shenyu.apache.org/community/contributor-guide).
- [x] You submit test cases (unit or integration tests) that back your changes.
- [x] Your local test passed `./mvnw clean install -Dmaven.javadoc.skip=true`.
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "moremind (via GitHub)" <gi...@apache.org>.
moremind commented on code in PR #6339:
URL: https://github.com/apache/shenyu/pull/6339#discussion_r3252993599
##########
shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ApiServiceImpl.java:
##########
@@ -243,12 +243,14 @@ public ApiVO findById(final String id) {
tagVOs = tagDOS.stream().map(TagVO::buildTagVO).collect(Collectors.toList());
}
ApiVO apiVO = ApiVO.buildApiVO(item, tagVOs);
- if (apiVO.getApiSource().equals(ApiSourceEnum.SWAGGER.getValue())) {
+ if (StringUtils.isNotBlank(apiVO.getDocument())) {
DocItem docItem = JsonUtils.jsonToObject(apiVO.getDocument(), DocItem.class);
- apiVO.setRequestHeaders(docItem.getRequestHeaders());
- apiVO.setRequestParameters(docItem.getRequestParameters());
- apiVO.setResponseParameters(docItem.getResponseParameters());
- apiVO.setBizCustomCodeList(docItem.getBizCodeList());
+ if (Objects.nonNull(docItem)) {
+ apiVO.setRequestHeaders(docItem.getRequestHeaders());
+ apiVO.setRequestParameters(docItem.getRequestParameters());
+ apiVO.setResponseParameters(docItem.getResponseParameters());
+ apiVO.setBizCustomCodeList(docItem.getBizCodeList());
+ }
}
Review Comment:
else {? how to resolve?}
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "moremind (via GitHub)" <gi...@apache.org>.
moremind merged PR #6339:
URL: https://github.com/apache/shenyu/pull/6339
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #6339:
URL: https://github.com/apache/shenyu/pull/6339#discussion_r3252999456
##########
shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/utils/OpenApiUtils.java:
##########
@@ -296,6 +747,226 @@ 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 (Collection.class.isAssignableFrom(clazz)) {
+ return new Schema("array", null);
+ } else if (Map.class.isAssignableFrom(clazz)) {
+ return new Schema("object", null);
+ } 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);
+ elementSchema.setName("items");
+ 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);
+ keySchema.setName("key");
+ Schema valueSchema = parseSchema(actualTypeArguments[1], depth + 1, newTypeVariableMap);
+ valueSchema.setName("value");
+ Schema schema = new Schema("object", null);
+ schema.setRefs(Arrays.asList(keySchema, valueSchema));
+ return schema;
+ } else {
+ List<Schema> refs = new ArrayList<>();
+ for (Field field : rawType.getDeclaredFields()) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ continue;
+ }
+ Schema fieldSchema = parseSchema(field.getGenericType(), depth + 1, newTypeVariableMap);
+ fieldSchema.setName(field.getName());
+ refs.add(fieldSchema);
+ }
+ Schema schema = new Schema("object", null);
+ schema.setRefs(refs);
+ return schema;
+ }
+ }
+
+ private static Schema parseProtobufClassSchema(final Class<?> clazz, final int depth, final Map<TypeVariable<?>, Type> typeVariableMap) {
+ if (isProtobufEmpty(clazz)) {
+ return new Schema("object", null);
+ }
+ try {
+ java.lang.reflect.Method getDescriptorMethod = clazz.getMethod("getDescriptor");
+ Object descriptor = getDescriptorMethod.invoke(null);
+ java.lang.reflect.Method getFieldsMethod = descriptor.getClass().getMethod("getFields");
+ @SuppressWarnings("unchecked")
+ List<?> fields = (List<?>) getFieldsMethod.invoke(descriptor);
+ List<Schema> refs = parseProtobufFieldsSchema(fields, clazz, depth, typeVariableMap);
+ Schema schema = new Schema("object", null);
+ schema.setRefs(refs);
+ return schema;
+ } catch (Exception e) {
+ return new Schema("object", null);
+ }
+ }
+
+ private static List<Schema> parseProtobufFieldsSchema(final List<?> fields, final Class<?> clazz,
+ final int depth, final Map<TypeVariable<?>, Type> typeVariableMap) throws Exception {
+ List<Schema> refs = new ArrayList<>();
+ java.lang.reflect.Method getNameMethod = null;
+ java.lang.reflect.Method getTypeMethod = null;
+ java.lang.reflect.Method isRepeatedMethod = null;
+ java.lang.reflect.Method getMessageTypeMethod = null;
+ for (Object field : fields) {
+ if (Objects.isNull(getNameMethod)) {
+ getNameMethod = field.getClass().getMethod("getName");
+ getTypeMethod = field.getClass().getMethod("getType");
+ isRepeatedMethod = field.getClass().getMethod("isRepeated");
+ getMessageTypeMethod = field.getClass().getMethod("getMessageType");
+ }
+ String fieldName = (String) getNameMethod.invoke(field);
+ Object fieldType = getTypeMethod.invoke(field);
+ boolean isRepeated = (boolean) isRepeatedMethod.invoke(field);
+ refs.add(parseProtobufFieldSchema(fieldName, fieldType, isRepeated,
+ getMessageTypeMethod, field, clazz, depth, typeVariableMap));
+ }
+ return refs;
+ }
+
+ private static Schema parseProtobufFieldSchema(final String fieldName, final Object fieldType, final boolean isRepeated,
+ final java.lang.reflect.Method getMessageTypeMethod, final Object field,
+ final Class<?> clazz, final int depth,
+ final Map<TypeVariable<?>, Type> typeVariableMap) throws Exception {
+ if (isRepeated) {
+ return parseRepeatedProtobufFieldSchema(fieldName, fieldType, getMessageTypeMethod, field);
+ }
+ String fieldTypeName = fieldType.toString();
+ Schema schema;
+ if ("MESSAGE".equals(fieldTypeName)) {
+ schema = resolveProtobufMessageFieldSchema(getMessageTypeMethod, field, clazz, depth, typeVariableMap);
+ } else if ("ENUM".equals(fieldTypeName)) {
+ schema = new Schema("string", null);
+ } else {
+ schema = new Schema(mapProtobufTypeToOpenApi(fieldTypeName), null);
+ }
+ schema.setName(fieldName);
+ return schema;
+ }
+
+ private static Schema parseRepeatedProtobufFieldSchema(final String fieldName, final Object fieldType,
+ final java.lang.reflect.Method getMessageTypeMethod,
+ final Object field) throws Exception {
+ String fieldTypeName = fieldType.toString();
+ Schema elementSchema;
+ if ("MESSAGE".equals(fieldTypeName)) {
+ Object msgDescriptor = getMessageTypeMethod.invoke(field);
+ java.lang.reflect.Method getFullNameMethod = msgDescriptor.getClass().getMethod("getFullName");
+ String fullMsgName = (String) getFullNameMethod.invoke(msgDescriptor);
+ elementSchema = new Schema("object", null);
+ } else {
+ elementSchema = new Schema(mapProtobufTypeToOpenApi(fieldTypeName), null);
+ }
Review Comment:
In parseRepeatedProtobufFieldSchema(), the computed fullMsgName is unused and the elementSchema never gets a name. Because convertSchemaToParameter() uses ref.getName() when converting schema refs, repeated protobuf fields currently produce an item ref with a null name in the generated requestParameters. Consider setting a stable item name (e.g., "items") and either remove fullMsgName or use it (e.g., as a description / to drive nested schema parsing) to avoid losing message context.
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "eye-gu (via GitHub)" <gi...@apache.org>.
eye-gu commented on code in PR #6339:
URL: https://github.com/apache/shenyu/pull/6339#discussion_r3253900504
##########
shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ApiServiceImpl.java:
##########
@@ -243,12 +243,14 @@ public ApiVO findById(final String id) {
tagVOs = tagDOS.stream().map(TagVO::buildTagVO).collect(Collectors.toList());
}
ApiVO apiVO = ApiVO.buildApiVO(item, tagVOs);
- if (apiVO.getApiSource().equals(ApiSourceEnum.SWAGGER.getValue())) {
+ if (StringUtils.isNotBlank(apiVO.getDocument())) {
DocItem docItem = JsonUtils.jsonToObject(apiVO.getDocument(), DocItem.class);
- apiVO.setRequestHeaders(docItem.getRequestHeaders());
- apiVO.setRequestParameters(docItem.getRequestParameters());
- apiVO.setResponseParameters(docItem.getResponseParameters());
- apiVO.setBizCustomCodeList(docItem.getBizCodeList());
+ if (Objects.nonNull(docItem)) {
+ apiVO.setRequestHeaders(docItem.getRequestHeaders());
+ apiVO.setRequestParameters(docItem.getRequestParameters());
+ apiVO.setResponseParameters(docItem.getResponseParameters());
+ apiVO.setBizCustomCodeList(docItem.getBizCodeList());
+ }
}
Review Comment:
@moremind I think when document is blank or docItem is null, it means the API has no document data, so the page simply won't display any API Doc content — which is the expected behavior. The null checks (StringUtils.isNotBlank + Objects.nonNull) added in this PR already make the code safer than before. No additional else branch is needed here.
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "eye-gu (via GitHub)" <gi...@apache.org>.
eye-gu commented on PR #6339:
URL: https://github.com/apache/shenyu/pull/6339#issuecomment-5191982270
@Aias00 PTAL.
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "moremind (via GitHub)" <gi...@apache.org>.
moremind commented on code in PR #6339:
URL: https://github.com/apache/shenyu/pull/6339#discussion_r3363012980
##########
shenyu-client/shenyu-client-core/pom.xml:
##########
@@ -81,5 +81,55 @@
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-stub</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>kr.motd.maven</groupId>
+ <artifactId>os-maven-plugin</artifactId>
+ <version>1.6.2</version>
+ </extension>
+ </extensions>
+ <plugins>
+ <plugin>
+ <groupId>org.xolstice.maven.plugins</groupId>
+ <artifactId>protobuf-maven-plugin</artifactId>
+ <version>0.6.1</version>
Review Comment:
use common version
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] feat(api-doc): support request parameter parsing for RPC types (Dubbo/SOFA/TARS/gRPC) [shenyu]
Posted by "eye-gu (via GitHub)" <gi...@apache.org>.
eye-gu commented on code in PR #6339:
URL: https://github.com/apache/shenyu/pull/6339#discussion_r3367553247
##########
shenyu-client/shenyu-client-core/pom.xml:
##########
@@ -81,5 +81,55 @@
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.grpc</groupId>
+ <artifactId>grpc-stub</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>kr.motd.maven</groupId>
+ <artifactId>os-maven-plugin</artifactId>
+ <version>1.6.2</version>
+ </extension>
+ </extensions>
+ <plugins>
+ <plugin>
+ <groupId>org.xolstice.maven.plugins</groupId>
+ <artifactId>protobuf-maven-plugin</artifactId>
+ <version>0.6.1</version>
Review Comment:
Done.
--
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: notifications-unsubscribe@shenyu.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org