robocanic commented on code in PR #1429:
URL: https://github.com/apache/dubbo-admin/pull/1429#discussion_r2901847914


##########
pkg/console/service/service_generic_invoke.go:
##########
@@ -0,0 +1,292 @@
+/*
+ * 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 service
+
+import (
+       "context"
+       "fmt"
+       "sort"
+       "time"
+
+       hessian "github.com/apache/dubbo-go-hessian2"
+
+       dubbo "dubbo.apache.org/dubbo-go/v3"
+       "dubbo.apache.org/dubbo-go/v3/client"
+       dubboconstant "dubbo.apache.org/dubbo-go/v3/common/constant"
+       _ "dubbo.apache.org/dubbo-go/v3/imports"
+
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
+       consolectx "github.com/apache/dubbo-admin/pkg/console/context"
+       "github.com/apache/dubbo-admin/pkg/console/model"
+       "github.com/apache/dubbo-admin/pkg/core/logger"
+       "github.com/apache/dubbo-admin/pkg/core/manager"
+       meshresource 
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
+       "github.com/apache/dubbo-admin/pkg/core/store/index"
+)
+
+const genericInvokeInstanceName = "dubbo-admin-generic-invoke"
+
+type genericInvocation struct {
+       URL            string
+       ServiceName    string
+       Group          string
+       Version        string
+       MethodName     string
+       ParameterTypes []string
+       Args           []hessian.Object
+}
+
+type tripleInvokeTarget struct {
+       instance *meshresource.RPCInstanceResource
+       port     int64
+}
+
+var invokeGenericServiceRPC = func(callCtx context.Context, invocation 
genericInvocation) (any, error) {
+       ins, err := dubbo.NewInstance(dubbo.WithName(genericInvokeInstanceName))
+       if err != nil {
+               return nil, err
+       }
+
+       cli, err := ins.NewClient(

Review Comment:
   Question:这里是不是应该根据目标service的协议和序列化来new client?



##########
pkg/console/model/service.go:
##########
@@ -120,3 +122,121 @@ func (s *BaseServiceReq) Query(c *gin.Context) error {
 func (s *BaseServiceReq) ServiceKey() string {
        return s.ServiceName + constants.ColonSeparator + s.Version + 
constants.ColonSeparator + s.Group
 }
+
+type ServiceMethodsReq struct {
+       ServiceName     string `form:"serviceName" json:"serviceName"`
+       Group           string `form:"group" json:"group"`
+       Version         string `form:"version" json:"version"`
+       Mesh            string `form:"mesh" json:"mesh"`
+       ProviderAppName string `form:"providerAppName" json:"providerAppName"`
+}
+
+func (s *ServiceMethodsReq) Query(c *gin.Context) error {
+       s.ServiceName = strings.TrimSpace(c.Query("serviceName"))
+       if s.ServiceName == "" {
+               return fmt.Errorf("service name is empty")
+       }
+       s.Mesh = strings.TrimSpace(c.Query("mesh"))
+       if s.Mesh == "" {
+               return fmt.Errorf("mesh is empty")
+       }
+       s.Group = strings.TrimSpace(c.Query("group"))
+       s.Version = strings.TrimSpace(c.Query("version"))
+       s.ProviderAppName = strings.TrimSpace(c.Query("providerAppName"))
+       return nil
+}
+
+type ServiceMethodDetailReq struct {
+       ServiceMethodsReq
+
+       MethodName string `form:"methodName" json:"methodName"`
+       Signature  string `form:"signature" json:"signature"`
+}
+
+func (s *ServiceMethodDetailReq) Query(c *gin.Context) error {
+       if err := s.ServiceMethodsReq.Query(c); err != nil {
+               return err
+       }
+       s.MethodName = strings.TrimSpace(c.Query("methodName"))
+       if s.MethodName == "" {
+               return fmt.Errorf("method name is empty")
+       }
+       s.Signature = strings.TrimSpace(c.Query("signature"))
+       return nil
+}
+
+type ServiceMethodSummaryResp struct {
+       MethodName     string   `json:"methodName"`
+       ParameterTypes []string `json:"parameterTypes"`
+       Signature      string   `json:"signature,omitempty"`
+}
+
+type ServiceMethodParameter struct {
+       Name string `json:"name"`
+       Type string `json:"type"`
+}
+
+type ServiceMethodDetailResp struct {
+       MethodName     string                   `json:"methodName"`
+       Signature      string                   `json:"signature,omitempty"`
+       ParameterTypes []string                 `json:"parameterTypes"`
+       Parameters     []ServiceMethodParameter `json:"parameters"`
+       ReturnType     string                   `json:"returnType"`
+       Types          []ServiceMethodTypeResp  `json:"types"`
+}
+
+type ServiceMethodTypeResp struct {
+       Type       string            `json:"type"`
+       Properties map[string]string `json:"properties"`
+       Items      []string          `json:"items"`
+       Enums      []string          `json:"enums"`
+}
+
+const DefaultServiceGenericInvokeTimeoutMs int64 = 3000
+
+type ServiceGenericInvokeReq struct {
+       Mesh            string            `json:"mesh"`
+       ServiceName     string            `json:"serviceName"`
+       MethodName      string            `json:"methodName"`
+       Signature       string            `json:"signature"`
+       Args            []json.RawMessage `json:"args"`
+       Group           string            `json:"group"`
+       Version         string            `json:"version"`
+       ProviderAppName string            `json:"providerAppName"`
+       TimeoutMs       int64             `json:"timeoutMs"`
+       Attachments     map[string]string `json:"attachments"`
+}
+
+func (s *ServiceGenericInvokeReq) Validate() error {
+       s.Mesh = strings.TrimSpace(s.Mesh)

Review Comment:
   suggestrion: 这里都可以简化用lancet的工具类strutil.isBlank来判断



##########
pkg/console/service/service_generic_invoke.go:
##########
@@ -0,0 +1,292 @@
+/*
+ * 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 service
+
+import (
+       "context"
+       "fmt"
+       "sort"
+       "time"
+
+       hessian "github.com/apache/dubbo-go-hessian2"
+
+       dubbo "dubbo.apache.org/dubbo-go/v3"
+       "dubbo.apache.org/dubbo-go/v3/client"
+       dubboconstant "dubbo.apache.org/dubbo-go/v3/common/constant"
+       _ "dubbo.apache.org/dubbo-go/v3/imports"
+
+       "github.com/apache/dubbo-admin/pkg/common/bizerror"
+       consolectx "github.com/apache/dubbo-admin/pkg/console/context"
+       "github.com/apache/dubbo-admin/pkg/console/model"
+       "github.com/apache/dubbo-admin/pkg/core/logger"
+       "github.com/apache/dubbo-admin/pkg/core/manager"
+       meshresource 
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
+       "github.com/apache/dubbo-admin/pkg/core/store/index"
+)
+
+const genericInvokeInstanceName = "dubbo-admin-generic-invoke"
+
+type genericInvocation struct {
+       URL            string
+       ServiceName    string
+       Group          string
+       Version        string
+       MethodName     string
+       ParameterTypes []string
+       Args           []hessian.Object
+}
+
+type tripleInvokeTarget struct {
+       instance *meshresource.RPCInstanceResource
+       port     int64
+}
+
+var invokeGenericServiceRPC = func(callCtx context.Context, invocation 
genericInvocation) (any, error) {
+       ins, err := dubbo.NewInstance(dubbo.WithName(genericInvokeInstanceName))
+       if err != nil {
+               return nil, err
+       }
+
+       cli, err := ins.NewClient(
+               client.WithClientProtocolTriple(),
+               
client.WithClientSerialization(dubboconstant.Hessian2Serialization),
+       )
+       if err != nil {
+               return nil, err
+       }
+
+       svc, err := cli.NewGenericService(
+               invocation.ServiceName,
+               client.WithURL(invocation.URL),
+               client.WithVersion(invocation.Version),
+               client.WithGroup(invocation.Group),
+       )
+       if err != nil {
+               return nil, err
+       }
+
+       return svc.Invoke(callCtx, invocation.MethodName, 
invocation.ParameterTypes, invocation.Args)
+}

Review Comment:
   copilot这里说的有道理,这里可以在代码里记一个todo,加上client的缓存



##########
pkg/console/service/service.go:
##########
@@ -160,6 +162,421 @@ func ToServiceSearchRespByConsumer(res 
*meshresource.ServiceConsumerMetadataReso
        }
 }
 
+type serviceMethodCandidate struct {
+       detail    *model.ServiceMethodDetailResp
+       signature string
+       method    *meshproto.Method
+}
+
+func GetServiceMethodNames(ctx consolectx.Context, req 
model.ServiceMethodsReq) ([]model.ServiceMethodSummaryResp, error) {
+       metadataList, err := listServiceProviderMetadata(ctx, req)
+       if err != nil {
+               return nil, err
+       }
+
+       return buildServiceMethodSummaries(metadataList), nil
+}
+
+func GetServiceMethodDetail(ctx consolectx.Context, req 
model.ServiceMethodDetailReq) (*model.ServiceMethodDetailResp, error) {
+       metadataList, err := listServiceProviderMetadata(ctx, 
req.ServiceMethodsReq)
+       if err != nil {
+               return nil, err
+       }
+
+       candidate, err := 
resolveStructuredServiceMethodCandidate(buildServiceMethodCandidates(metadataList),
 req)
+       if err != nil {
+               return nil, err
+       }
+
+       detail := cloneServiceMethodDetailResp(candidate.detail)
+       detail.Types = buildServiceMethodRelatedTypes(metadataList, 
candidate.method)
+       return detail, nil
+}
+
+func listServiceProviderMetadata(ctx consolectx.Context, req 
model.ServiceMethodsReq) ([]*meshresource.ServiceProviderMetadataResource, 
error) {
+       indexes := map[string]string{
+               index.ByMeshIndex:                  req.Mesh,
+               index.ByServiceProviderServiceName: req.ServiceName,
+       }
+       if req.ProviderAppName != "" {
+               indexes[index.ByServiceProviderAppName] = req.ProviderAppName
+       }
+
+       metadataList, err := 
manager.ListByIndexes[*meshresource.ServiceProviderMetadataResource](
+               ctx.ResourceManager(),
+               meshresource.ServiceProviderMetadataKind,
+               indexes,
+       )
+       if err != nil {
+               return nil, err
+       }
+
+       filtered := make([]*meshresource.ServiceProviderMetadataResource, 0, 
len(metadataList))
+       for _, metadata := range metadataList {
+               if !matchesServiceMethodsReq(metadata, req) {
+                       continue
+               }
+               filtered = append(filtered, metadata)
+       }
+
+       return filtered, nil
+}
+
+func matchesServiceMethodsReq(metadata 
*meshresource.ServiceProviderMetadataResource, req model.ServiceMethodsReq) 
bool {

Review Comment:
   
Suggestion:这里的过滤其实可以在查询DB的时候做掉。可以在ServiceProviderMetadata的index(pkg/core/store/index/service_provider_metadata.go)里面新增对于serviceKey(serviceName:version:group)的一个索引,这样就不用在上层过滤了。



##########
pkg/console/service/service.go:
##########
@@ -160,6 +162,421 @@ func ToServiceSearchRespByConsumer(res 
*meshresource.ServiceConsumerMetadataReso
        }
 }
 
+type serviceMethodCandidate struct {
+       detail    *model.ServiceMethodDetailResp
+       signature string
+       method    *meshproto.Method
+}
+
+func GetServiceMethodNames(ctx consolectx.Context, req 
model.ServiceMethodsReq) ([]model.ServiceMethodSummaryResp, error) {
+       metadataList, err := listServiceProviderMetadata(ctx, req)
+       if err != nil {
+               return nil, err
+       }
+
+       return buildServiceMethodSummaries(metadataList), nil
+}
+
+func GetServiceMethodDetail(ctx consolectx.Context, req 
model.ServiceMethodDetailReq) (*model.ServiceMethodDetailResp, error) {
+       metadataList, err := listServiceProviderMetadata(ctx, 
req.ServiceMethodsReq)
+       if err != nil {
+               return nil, err
+       }
+
+       candidate, err := 
resolveStructuredServiceMethodCandidate(buildServiceMethodCandidates(metadataList),
 req)
+       if err != nil {
+               return nil, err
+       }
+
+       detail := cloneServiceMethodDetailResp(candidate.detail)
+       detail.Types = buildServiceMethodRelatedTypes(metadataList, 
candidate.method)
+       return detail, nil
+}
+
+func listServiceProviderMetadata(ctx consolectx.Context, req 
model.ServiceMethodsReq) ([]*meshresource.ServiceProviderMetadataResource, 
error) {
+       indexes := map[string]string{
+               index.ByMeshIndex:                  req.Mesh,
+               index.ByServiceProviderServiceName: req.ServiceName,
+       }
+       if req.ProviderAppName != "" {
+               indexes[index.ByServiceProviderAppName] = req.ProviderAppName
+       }
+
+       metadataList, err := 
manager.ListByIndexes[*meshresource.ServiceProviderMetadataResource](
+               ctx.ResourceManager(),
+               meshresource.ServiceProviderMetadataKind,
+               indexes,
+       )
+       if err != nil {
+               return nil, err
+       }
+
+       filtered := make([]*meshresource.ServiceProviderMetadataResource, 0, 
len(metadataList))
+       for _, metadata := range metadataList {
+               if !matchesServiceMethodsReq(metadata, req) {
+                       continue
+               }
+               filtered = append(filtered, metadata)
+       }
+
+       return filtered, nil
+}
+
+func matchesServiceMethodsReq(metadata 
*meshresource.ServiceProviderMetadataResource, req model.ServiceMethodsReq) 
bool {
+       if metadata == nil || metadata.Spec == nil {
+               return false
+       }
+       if req.Group != "" && metadata.Spec.Group != req.Group {
+               return false
+       }
+       if req.Version != "" && metadata.Spec.Version != req.Version {
+               return false
+       }
+       if req.ProviderAppName != "" && metadata.Spec.ProviderAppName != 
req.ProviderAppName {
+               return false
+       }
+       return true
+}
+
+func buildServiceMethodSummaries(metadataList 
[]*meshresource.ServiceProviderMetadataResource) 
[]model.ServiceMethodSummaryResp {
+       candidates := buildServiceMethodCandidates(metadataList)
+       summaries := make([]model.ServiceMethodSummaryResp, 0, len(candidates))
+       for _, candidate := range candidates {
+               summaries = append(summaries, model.ServiceMethodSummaryResp{
+                       MethodName:     candidate.detail.MethodName,
+                       ParameterTypes: append([]string{}, 
candidate.detail.ParameterTypes...),
+                       Signature:      candidate.signature,
+               })
+       }
+       return summaries
+}
+
+func buildServiceMethodCandidates(metadataList 
[]*meshresource.ServiceProviderMetadataResource) []*serviceMethodCandidate {
+       candidateByKey := make(map[string]*serviceMethodCandidate)
+       structuredMethodNames := make(map[string]struct{})
+       fallbackMethodNames := make(map[string]struct{})
+
+       for _, metadata := range metadataList {
+               for _, method := range metadata.Spec.Methods {
+                       candidate, ok := 
newStructuredServiceMethodCandidate(method)
+                       if !ok {
+                               continue
+                       }
+                       
candidateByKey[serviceMethodKey(candidate.detail.MethodName, 
candidate.signature)] = candidate
+                       structuredMethodNames[candidate.detail.MethodName] = 
struct{}{}
+               }
+
+               if len(metadata.Spec.Methods) > 0 {
+                       continue
+               }
+               for _, methodName := range 
methodNamesFromMetadataParameters(metadata.Spec.Parameters) {
+                       fallbackMethodNames[methodName] = struct{}{}
+               }
+       }
+
+       for methodName := range fallbackMethodNames {

Review Comment:
   Question:这段逻辑有点没看懂,可以写写注释



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to