Skip to content

Commit 27a5231

Browse files
committed
Feat: Enhance service metadata derivation and detail retrieval ([#1430](#1430))
* feat(api): replace Service.{providers,consumers,features} with {methods} field Simplify Service proto by removing providers, consumers, and features map, keeping only the aggregated methods list derived from provider metadata. * feat(api): derive Service resource from ServiceProviderMetadata on add/update/delete ServiceProviderMetadataEventSubscriber now maintains Service resources by aggregating methods from all provider instances sharing the same serviceKey. Handles add, update, and delete events to keep Service spec in sync. * feat(api): add language detection from provider metadata parameters Detect provider language (golang/java) from metadata parameters and method type signatures when explicit language field is absent. * feat(api): add GetServiceDetail endpoint returning language and methods Add GET /service/detail returning ServiceDetailResp with language and aggregated method names from the derived Service resource. * feat(api): add BuildServiceIdentityKey helper for {service}:{version}:{group} * feat(api): add ByServiceName index for ServiceKind * feat(api): refactor SearchServices to query ServiceResource directly SearchServices and SearchServicesByKeywords now use ServiceResource instead of ServiceProviderMetadataResource for service listing. * feat(api): remove providerAppName from ServiceSearchResp and ServiceTabDistributionReq * feat(api): add ServiceDetailReq and ServiceDetailResp models * feat(router): register /service/detail and /service/interfaces routes * feat(ui-vue3): remove providerAppName from grafana types and tab components
1 parent 39b4666 commit 27a5231

15 files changed

Lines changed: 482 additions & 113 deletions

File tree

api/mesh/v1alpha1/service.pb.go

Lines changed: 26 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/mesh/v1alpha1/service.proto

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,19 @@ option go_package = "github.com/apache/dubbo-admin/api/mesh/v1alpha1";
77
import "api/mesh/options.proto";
88

99

10-
message Service{
11-
option (dubbo.mesh.resource).name = "Service";
12-
option (dubbo.mesh.resource).plural_name = "Services";
13-
option (dubbo.mesh.resource).package = "mesh";
14-
option (dubbo.mesh.resource).is_experimental = true;
10+
message Service{
11+
option (dubbo.mesh.resource).name = "Service";
12+
option (dubbo.mesh.resource).plural_name = "Services";
13+
option (dubbo.mesh.resource).package = "mesh";
14+
option (dubbo.mesh.resource).is_experimental = true;
1515

1616
string name = 1;
1717

1818
string group = 2;
1919

20-
string version = 3;
21-
22-
string language = 4;
23-
24-
repeated string providers = 5;
25-
26-
repeated string consumers = 6;
27-
28-
map<string, string> features = 99;
29-
}
20+
string version = 3;
21+
22+
string language = 4;
23+
24+
repeated string methods = 5;
25+
}

pkg/console/handler/service.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,42 @@ func GetServiceGraph(ctx consolectx.Context) gin.HandlerFunc {
325325
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
326326
}
327327
}
328+
329+
// GetServiceDetail returns service detail information
330+
func GetServiceDetail(ctx consolectx.Context) gin.HandlerFunc {
331+
return func(c *gin.Context) {
332+
req := &model.ServiceDetailReq{}
333+
if err := c.ShouldBindQuery(req); err != nil {
334+
c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error()))
335+
return
336+
}
337+
338+
resp, err := service.GetServiceDetail(ctx, req)
339+
if err != nil {
340+
util.HandleServiceError(c, err)
341+
return
342+
}
343+
344+
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
345+
}
346+
}
347+
348+
// GetServiceInterfaces returns service interfaces information
349+
func GetServiceInterfaces(ctx consolectx.Context) gin.HandlerFunc {
350+
// return func(c *gin.Context) {
351+
// req := &model.ServiceInterfacesReq{}
352+
// if err := c.ShouldBindQuery(req); err != nil {
353+
// c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error()))
354+
// return
355+
// }
356+
357+
// resp, err := service.GetServiceInterfaces(ctx, req)
358+
// if err != nil {
359+
// util.HandleServiceError(c, err)
360+
// return
361+
// }
362+
363+
// c.JSON(http.StatusOK, model.NewSuccessResp(resp))
364+
// }
365+
return nil
366+
}

pkg/console/model/observability.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@ type InstanceDashboardReq struct {
2828
}
2929

3030
type ServiceDashboardReq struct {
31-
ServiceName string `form:"serviceName"`
32-
Version string `form:"version"`
33-
Group string `form:"group"`
34-
ProviderAppName string `form:"providerAppName"`
35-
Mesh string `form:"mesh"`
31+
ServiceName string `form:"serviceName"`
32+
Version string `form:"version"`
33+
Group string `form:"group"`
34+
Mesh string `form:"mesh"`
3635
}
3736

3837
type DashboardResp struct {

pkg/console/model/service.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ type ServiceSearchResp struct {
5050
ServiceName string `json:"serviceName"`
5151
Version string `json:"version"`
5252
Group string `json:"group"`
53-
ProviderAppName string `json:"providerAppName,omitempty"`
5453
ConsumerAppName string `json:"consumerAppName,omitempty"`
5554
}
5655

@@ -65,13 +64,12 @@ func (a ByServiceName) Less(i, j int) bool {
6564
func (a ByServiceName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
6665

6766
type ServiceTabDistributionReq struct {
68-
ServiceName string `json:"serviceName" form:"serviceName" binding:"required"`
69-
Version string `json:"version" form:"version"`
70-
Group string `json:"group" form:"group"`
71-
Side string `json:"side" form:"side" binding:"required"`
72-
Mesh string `json:"mesh" form:"mesh" binding:"required"`
73-
ProviderAppName string `json:"providerAppName" form:"providerAppName"`
74-
Keywords string `json:"keywords" form:"keywords"`
67+
ServiceName string `json:"serviceName" form:"serviceName" binding:"required"`
68+
Version string `json:"version" form:"version"`
69+
Group string `json:"group" form:"group"`
70+
Side string `json:"side" form:"side" binding:"required"`
71+
Mesh string `json:"mesh" form:"mesh" binding:"required"`
72+
Keywords string `json:"keywords" form:"keywords"`
7573
coremodel.PageReq
7674
}
7775

@@ -226,3 +224,15 @@ type ServiceGenericInvokeResp struct {
226224
ElapsedMs int64 `json:"elapsedMs"`
227225
RawResult any `json:"rawResult"`
228226
}
227+
228+
type ServiceDetailReq struct {
229+
ServiceName string `form:"serviceName" json:"serviceName" binding:"required"`
230+
Version string `form:"version" json:"version"`
231+
Group string `form:"group" json:"group"`
232+
Mesh string `form:"mesh" json:"mesh" binding:"required"`
233+
}
234+
235+
type ServiceDetailResp struct {
236+
Language string `json:"language"`
237+
Methods []string `json:"methods"`
238+
}

pkg/console/router/router.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ func InitRouter(r *gin.Engine, ctx consolectx.Context) {
105105
service.GET("/methods", handler.GetServiceMethodNames(ctx))
106106
service.GET("/search", handler.SearchServices(ctx))
107107
service.GET("/graph", handler.GetServiceGraph(ctx))
108+
service.GET("/detail", handler.GetServiceDetail(ctx))
109+
service.GET("/interfaces", handler.GetServiceInterfaces(ctx))
108110
}
109111

110112
{

pkg/console/service/service.go

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func SearchServices(ctx consolectx.Context, req *model.ServiceSearchReq) (*model
9999
if strutil.IsNotBlank(req.Keywords) {
100100
return SearchServicesByKeywords(ctx, req)
101101
}
102-
pageData, err := manager.PageListByIndexes[*meshresource.ServiceProviderMetadataResource](
102+
pageData, err := manager.PageListByIndexes[*meshresource.ServiceResource](
103103
ctx.ResourceManager(),
104104
meshresource.ServiceProviderMetadataKind,
105105
[]index.IndexCondition{
@@ -112,11 +112,14 @@ func SearchServices(ctx consolectx.Context, req *model.ServiceSearchReq) (*model
112112
return nil, err
113113
}
114114
if pageData.Data == nil || len(pageData.Data) == 0 {
115-
return nil, nil
115+
return &model.SearchPaginationResult{
116+
List: []*model.ServiceSearchResp{},
117+
PageInfo: pageData.Pagination,
118+
}, nil
116119
}
117120
serviceSearchResps := slice.Map(pageData.Data,
118-
func(_ int, item *meshresource.ServiceProviderMetadataResource) *model.ServiceSearchResp {
119-
return ToServiceSearchRespByProvider(item)
121+
func(_ int, item *meshresource.ServiceResource) *model.ServiceSearchResp {
122+
return ToServiceSearchRespByService(item)
120123
})
121124
return &model.SearchPaginationResult{
122125
List: serviceSearchResps,
@@ -126,7 +129,7 @@ func SearchServices(ctx consolectx.Context, req *model.ServiceSearchReq) (*model
126129

127130
// SearchServicesByKeywords search services by keywords with prefix matching
128131
func SearchServicesByKeywords(ctx consolectx.Context, req *model.ServiceSearchReq) (*model.SearchPaginationResult, error) {
129-
pageData, err := manager.PageListByIndexes[*meshresource.ServiceProviderMetadataResource](
132+
pageData, err := manager.PageListByIndexes[*meshresource.ServiceResource](
130133
ctx.ResourceManager(),
131134
meshresource.ServiceProviderMetadataKind,
132135
[]index.IndexCondition{
@@ -139,21 +142,28 @@ func SearchServicesByKeywords(ctx consolectx.Context, req *model.ServiceSearchRe
139142
return nil, err
140143
}
141144
searchRespList := slice.Map(pageData.Data,
142-
func(_ int, item *meshresource.ServiceProviderMetadataResource) *model.ServiceSearchResp {
143-
return ToServiceSearchRespByProvider(item)
145+
func(_ int, item *meshresource.ServiceResource) *model.ServiceSearchResp {
146+
return ToServiceSearchRespByService(item)
144147
})
145148
return &model.SearchPaginationResult{
146149
List: searchRespList,
147150
PageInfo: pageData.Pagination,
148151
}, nil
149152
}
150153

154+
func ToServiceSearchRespByService(res *meshresource.ServiceResource) *model.ServiceSearchResp {
155+
return &model.ServiceSearchResp{
156+
ServiceName: res.Spec.Name,
157+
Group: res.Spec.Group,
158+
Version: res.Spec.Version,
159+
}
160+
}
161+
151162
func ToServiceSearchRespByProvider(res *meshresource.ServiceProviderMetadataResource) *model.ServiceSearchResp {
152163
return &model.ServiceSearchResp{
153-
ServiceName: res.Spec.ServiceName,
154-
Group: res.Spec.Group,
155-
Version: res.Spec.Version,
156-
ProviderAppName: res.Spec.ProviderAppName,
164+
ServiceName: res.Spec.ServiceName,
165+
Group: res.Spec.Group,
166+
Version: res.Spec.Version,
157167
}
158168
}
159169

@@ -754,6 +764,27 @@ func isArgumentRoute(condition string) bool {
754764
return false
755765
}
756766

767+
func GetServiceDetail(ctx consolectx.Context, req *model.ServiceDetailReq) (*model.ServiceDetailResp, error) {
768+
serviceKey := coremodel.BuildResourceKey(req.Mesh, meshresource.BuildServiceIdentityKey(req.ServiceName, req.Version, req.Group))
769+
serviceRes, exists, err := manager.GetByKey[*meshresource.ServiceResource](
770+
ctx.ResourceManager(),
771+
meshresource.ServiceKind,
772+
serviceKey,
773+
)
774+
if err != nil {
775+
logger.Errorf("get service detail failed, serviceKey: %s, cause: %v", serviceKey, err)
776+
return nil, err
777+
}
778+
if !exists || serviceRes.Spec == nil {
779+
return nil, bizerror.New(bizerror.NotFoundError, "service not found")
780+
}
781+
782+
return &model.ServiceDetailResp{
783+
Language: serviceRes.Spec.Language,
784+
Methods: serviceRes.Spec.Methods,
785+
}, nil
786+
}
787+
757788
// GraphServices builds a service dependency graph for the given service key.
758789
//
759790
// It gathers both provider and consumer metadata for serviceKey and creates
@@ -871,3 +902,12 @@ func GraphServices(ctx consolectx.Context, req *model.ServiceGraphReq) (*model.G
871902
Edges: edges,
872903
}, nil
873904
}
905+
906+
func sortedKeys(items map[string]struct{}) []string {
907+
keys := make([]string, 0, len(items))
908+
for key := range items {
909+
keys = append(keys, key)
910+
}
911+
sort.Strings(keys)
912+
return keys
913+
}

0 commit comments

Comments
 (0)