This is an automated email from the ASF dual-hosted git repository.

tianxiaoliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new 4fb55ad9 Feature: add properties filter in ListServiceDetail API 
(#1324)
4fb55ad9 is described below

commit 4fb55ad9b788530e93812f71eaec969228b481c4
Author: little-cui <[email protected]>
AuthorDate: Sat Aug 20 09:31:19 2022 +0800

    Feature: add properties filter in ListServiceDetail API (#1324)
    
    * [fix]govern properties is nil
    
    * Feature: add properties filter in ListServiceDetail API
    
    * Feature: add properties filter in ListServiceDetail API
---
 datasource/etcd/etcd.go                        |   3 +-
 datasource/etcd/ms.go                          |  93 +----------
 datasource/etcd/util.go                        | 100 ------------
 datasource/mongo/mongo.go                      |   3 +-
 datasource/mongo/ms.go                         | 148 +----------------
 datasource/ms.go                               |   3 +-
 datasource/ms_util.go                          |  29 ----
 datasource/ms_util_test.go                     |  62 -------
 datasource/options.go                          |   3 +-
 datasource/service_test.go                     | 146 -----------------
 server/resource/govern/govern_resource.go      |  18 +++
 server/resource/govern/govern_resource_test.go |  90 ++++++++++-
 server/server.go                               |   5 +-
 server/service/disco/retire_test.go            |   3 +-
 server/service/govern/view.go                  | 216 ++++++++++++++++++++++++-
 server/service/govern/view_test.go             | 105 ++++++++++--
 16 files changed, 428 insertions(+), 599 deletions(-)

diff --git a/datasource/etcd/etcd.go b/datasource/etcd/etcd.go
index 4d66c5d1..684beaed 100644
--- a/datasource/etcd/etcd.go
+++ b/datasource/etcd/etcd.go
@@ -94,8 +94,7 @@ func NewDataSource(opts datasource.Options) 
(datasource.DataSource, error) {
                return nil, err
        }
        inst.metadataManager = &MetadataManager{
-               InstanceTTL:        opts.InstanceTTL,
-               InstanceProperties: opts.InstanceProperties,
+               InstanceTTL: opts.InstanceTTL,
        }
        inst.sysManager = &SysManager{}
        inst.depManager = &DepManager{}
diff --git a/datasource/etcd/ms.go b/datasource/etcd/ms.go
index 4509a85e..66cc4908 100644
--- a/datasource/etcd/ms.go
+++ b/datasource/etcd/ms.go
@@ -49,8 +49,7 @@ import (
 
 type MetadataManager struct {
        // InstanceTTL options
-       InstanceTTL        int64
-       InstanceProperties map[string]string
+       InstanceTTL int64
 }
 
 // RegisterService implement:
@@ -186,92 +185,6 @@ func (ds *MetadataManager) GetService(ctx context.Context, 
request *pb.GetServic
        return singleService, nil
 }
 
-func (ds *MetadataManager) ListServiceDetail(ctx context.Context, request 
*pb.GetServicesInfoRequest) (
-       *pb.GetServicesInfoResponse, error) {
-       ctx = util.WithCacheOnly(ctx)
-
-       optionMap := make(map[string]struct{}, len(request.Options))
-       for _, opt := range request.Options {
-               optionMap[opt] = struct{}{}
-       }
-
-       options := make([]string, 0, len(optionMap))
-       if _, ok := optionMap["all"]; ok {
-               optionMap["statistics"] = struct{}{}
-               options = []string{"tags", "instances", "schemas", 
"dependencies"}
-       } else {
-               for opt := range optionMap {
-                       options = append(options, opt)
-               }
-       }
-
-       var st *pb.Statistics
-       if _, ok := optionMap["statistics"]; ok {
-               var err error
-               st, err = statistics(ctx, request.WithShared)
-               if err != nil {
-                       return nil, pb.NewError(pb.ErrInternal, err.Error())
-               }
-               if len(optionMap) == 1 {
-                       return &pb.GetServicesInfoResponse{
-                               Statistics: st,
-                       }, nil
-               }
-       }
-
-       //获取所有服务
-       services, err := eutil.GetAllServiceUtil(ctx)
-       if err != nil {
-               log.Error("get all services by domain failed", err)
-               return nil, pb.NewError(pb.ErrInternal, err.Error())
-       }
-
-       allServiceDetails := make([]*pb.ServiceDetail, 0, len(services))
-       domainProject := util.ParseDomainProject(ctx)
-       for _, service := range services {
-               if !ds.filterServices(domainProject, request, service) {
-                       continue
-               }
-
-               serviceDetail, err := getServiceDetailUtil(ctx, 
ServiceDetailOpt{
-                       domainProject: domainProject,
-                       service:       service,
-                       countOnly:     request.CountOnly,
-                       options:       options,
-               })
-               if err != nil {
-                       return nil, pb.NewError(pb.ErrInternal, err.Error())
-               }
-               serviceDetail.MicroService = service
-               tmpServiceDetail, err := 
datasource.NewServiceOverview(serviceDetail, ds.InstanceProperties)
-               if err != nil {
-                       return nil, err
-               }
-               allServiceDetails = append(allServiceDetails, tmpServiceDetail)
-       }
-
-       return &pb.GetServicesInfoResponse{
-               AllServicesDetail: allServiceDetails,
-               Statistics:        st,
-       }, nil
-}
-
-func (ds *MetadataManager) filterServices(domainProject string, request 
*pb.GetServicesInfoRequest, service *pb.MicroService) bool {
-       if !request.WithShared && 
datasource.IsGlobal(pb.MicroServiceToKey(domainProject, service)) {
-               return false
-       }
-       if len(request.Environment) > 0 && request.Environment != 
service.Environment {
-               return false
-       }
-       if len(request.AppId) > 0 && request.AppId != service.AppId {
-               return false
-       }
-       if len(request.ServiceName) > 0 && request.ServiceName != 
service.ServiceName {
-               return false
-       }
-       return true
-}
-
 func (ds *MetadataManager) GetOverview(ctx context.Context, request 
*pb.GetServicesRequest) (
        *pb.Statistics, error) {
        ctx = util.WithCacheOnly(ctx)
@@ -1652,3 +1565,7 @@ func (ds *MetadataManager) UnregisterService(ctx 
context.Context, request *pb.De
        log.Info(fmt.Sprintf("%s micro-service[%s] successfully, operator: %s", 
title, serviceID, remoteIP))
        return nil
 }
+
+func (ds *MetadataManager) Statistics(ctx context.Context, withShared bool) 
(*pb.Statistics, error) {
+       return statistics(ctx, withShared)
+}
diff --git a/datasource/etcd/util.go b/datasource/etcd/util.go
index 6aca94e1..d41215f4 100644
--- a/datasource/etcd/util.go
+++ b/datasource/etcd/util.go
@@ -36,13 +36,6 @@ import (
        "github.com/apache/servicecomb-service-center/pkg/util"
 )
 
-type ServiceDetailOpt struct {
-       domainProject string
-       service       *pb.MicroService
-       countOnly     bool
-       options       []string
-}
-
 // schema
 func getSchemaSummary(ctx context.Context, domainProject string, serviceID 
string, schemaID string) (string, error) {
        key := path.GenerateServiceSchemaSummaryKey(domainProject, serviceID, 
schemaID)
@@ -205,99 +198,6 @@ func revokeInstance(ctx context.Context, domainProject 
string, serviceID string,
        return nil
 }
 
-func getServiceDetailUtil(ctx context.Context, serviceDetailOpt 
ServiceDetailOpt) (*pb.ServiceDetail, error) {
-       serviceID := serviceDetailOpt.service.ServiceId
-       options := serviceDetailOpt.options
-       domainProject := serviceDetailOpt.domainProject
-       serviceDetail := new(pb.ServiceDetail)
-       if serviceDetailOpt.countOnly {
-               serviceDetail.Statics = new(pb.Statistics)
-       }
-
-       for _, opt := range options {
-               expr := opt
-               switch expr {
-               case "tags":
-                       tags, err := serviceUtil.GetTagsUtils(ctx, 
domainProject, serviceID)
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get service[%s]'s all 
tags failed", serviceID), err)
-                               return nil, err
-                       }
-                       serviceDetail.Tags = tags
-               case "instances":
-                       if serviceDetailOpt.countOnly {
-                               instanceCount, err := 
serviceUtil.GetInstanceCountOfOneService(ctx, domainProject, serviceID)
-                               if err != nil {
-                                       log.Error(fmt.Sprintf("get number of 
service[%s]'s instances failed", serviceID), err)
-                                       return nil, err
-                               }
-                               serviceDetail.Statics.Instances = 
&pb.StInstance{
-                                       Count: instanceCount}
-                               continue
-                       }
-                       instances, err := 
serviceUtil.GetAllInstancesOfOneService(ctx, domainProject, serviceID)
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get service[%s]'s all 
instances failed", serviceID), err)
-                               return nil, err
-                       }
-                       serviceDetail.Instances = instances
-               case "schemas":
-                       schemas, err := getSchemaInfoUtil(ctx, domainProject, 
serviceID)
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get service[%s]'s all 
schemas failed", serviceID), err)
-                               return nil, err
-                       }
-                       serviceDetail.SchemaInfos = schemas
-               case "dependencies":
-                       service := serviceDetailOpt.service
-                       consumers, err := serviceUtil.GetConsumers(ctx, 
domainProject, service,
-                               serviceUtil.WithoutSelfDependency(),
-                               serviceUtil.WithSameDomainProject())
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get 
service[%s][%s/%s/%s/%s]'s all consumers failed",
-                                       service.ServiceId, service.Environment, 
service.AppId, service.ServiceName, service.Version), err)
-                               return nil, err
-                       }
-                       providers, err := serviceUtil.GetProviders(ctx, 
domainProject, service,
-                               serviceUtil.WithoutSelfDependency(),
-                               serviceUtil.WithSameDomainProject())
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get 
service[%s][%s/%s/%s/%s]'s all providers failed",
-                                       service.ServiceId, service.Environment, 
service.AppId, service.ServiceName, service.Version), err)
-                               return nil, err
-                       }
-
-                       serviceDetail.Consumers = consumers
-                       serviceDetail.Providers = providers
-               case "":
-                       continue
-               default:
-                       log.Error(fmt.Sprintf("request option[%s] is invalid", 
opt), nil)
-               }
-       }
-       return serviceDetail, nil
-}
-
-func getSchemaInfoUtil(ctx context.Context, domainProject string, serviceID 
string) ([]*pb.Schema, error) {
-       key := path.GenerateServiceSchemaKey(domainProject, serviceID, "")
-
-       resp, err := sd.Schema().Search(ctx,
-               etcdadpt.WithStrKey(key),
-               etcdadpt.WithPrefix())
-       if err != nil {
-               log.Error(fmt.Sprintf("get service[%s]'s schemas failed", 
serviceID), err)
-               return make([]*pb.Schema, 0), err
-       }
-       schemas := make([]*pb.Schema, 0, len(resp.Kvs))
-       for _, kv := range resp.Kvs {
-               schemaInfo := &pb.Schema{}
-               schemaInfo.Schema = 
util.BytesToStringWithNoCopy(kv.Value.([]byte))
-               schemaInfo.SchemaId = 
util.BytesToStringWithNoCopy(kv.Key[len(key):])
-               schemas = append(schemas, schemaInfo)
-       }
-       return schemas, nil
-}
-
 func statistics(ctx context.Context, withShared bool) (*pb.Statistics, error) {
        result := &pb.Statistics{
                Services:  &pb.StService{},
diff --git a/datasource/mongo/mongo.go b/datasource/mongo/mongo.go
index d6c2a2c2..08d850d4 100644
--- a/datasource/mongo/mongo.go
+++ b/datasource/mongo/mongo.go
@@ -82,8 +82,7 @@ func NewDataSource(opts datasource.Options) 
(datasource.DataSource, error) {
        inst.depManager = &DepManager{}
        inst.sysManager = &SysManager{}
        inst.metadataManager = &MetadataManager{
-               InstanceTTL:        opts.InstanceTTL,
-               InstanceProperties: opts.InstanceProperties,
+               InstanceTTL: opts.InstanceTTL,
        }
        inst.metricsManager = &MetricsManager{}
        inst.syncManager = &SyncManager{}
diff --git a/datasource/mongo/ms.go b/datasource/mongo/ms.go
index 58f0cd3c..3fed2580 100644
--- a/datasource/mongo/ms.go
+++ b/datasource/mongo/ms.go
@@ -57,8 +57,7 @@ const baseTen = 10
 
 type MetadataManager struct {
        // InstanceTTL options
-       InstanceTTL        int64
-       InstanceProperties map[string]string
+       InstanceTTL int64
 }
 
 func (ds *MetadataManager) RegisterService(ctx context.Context, request 
*discovery.CreateServiceRequest) (*discovery.CreateServiceResponse, error) {
@@ -392,80 +391,6 @@ func updateServiceTxn(ctx context.Context, request 
*discovery.UpdateServiceProps
        })
 }
 
-func (ds *MetadataManager) ListServiceDetail(ctx context.Context, request 
*discovery.GetServicesInfoRequest) (*discovery.GetServicesInfoResponse, error) {
-       optionMap := make(map[string]struct{}, len(request.Options))
-       for _, opt := range request.Options {
-               optionMap[opt] = struct{}{}
-       }
-
-       options := make([]string, 0, len(optionMap))
-       if _, ok := optionMap["all"]; ok {
-               optionMap["statistics"] = struct{}{}
-               options = []string{"tags", "instances", "schemas", 
"dependencies"}
-       } else {
-               for opt := range optionMap {
-                       options = append(options, opt)
-               }
-       }
-       var st *discovery.Statistics
-       if _, ok := optionMap["statistics"]; ok {
-               var err error
-               st, err = statistics(ctx, request.WithShared)
-               if err != nil {
-                       return nil, discovery.NewError(discovery.ErrInternal, 
err.Error())
-               }
-               if len(optionMap) == 1 {
-                       return &discovery.GetServicesInfoResponse{
-                               Statistics: st,
-                       }, nil
-               }
-       }
-       filters := ds.filterServices(ctx, request)
-       services, err := dao.GetServices(ctx, filters)
-       if err != nil {
-               log.Error("get all services by domain failed", err)
-               return nil, discovery.NewError(discovery.ErrInternal, 
err.Error())
-       }
-       allServiceDetails := make([]*discovery.ServiceDetail, 0, len(services))
-       domainProject := util.ParseDomainProject(ctx)
-       for _, mgSvc := range services {
-               if !request.WithShared && 
datasource.IsGlobal(discovery.MicroServiceToKey(domainProject, mgSvc.Service)) {
-                       continue
-               }
-
-               serviceDetail, err := getServiceDetailUtil(ctx, mgSvc, 
request.CountOnly, options)
-               if err != nil {
-                       return nil, discovery.NewError(discovery.ErrInternal, 
err.Error())
-               }
-               serviceDetail.MicroService = mgSvc.Service
-               tmpServiceDetail, err := 
datasource.NewServiceOverview(serviceDetail, ds.InstanceProperties)
-               if err != nil {
-                       return nil, err
-               }
-               allServiceDetails = append(allServiceDetails, tmpServiceDetail)
-       }
-
-       return &discovery.GetServicesInfoResponse{
-               AllServicesDetail: allServiceDetails,
-               Statistics:        st,
-       }, nil
-}
-
-func (ds *MetadataManager) filterServices(ctx context.Context, request 
*discovery.GetServicesInfoRequest) bson.M {
-       var opts []func(filter bson.M)
-
-       if len(request.Environment) > 0 {
-               opts = append(opts, mutil.ServiceEnv(request.Environment))
-       }
-       if len(request.AppId) > 0 {
-               opts = append(opts, mutil.ServiceAppID(request.AppId))
-       }
-       if len(request.ServiceName) > 0 {
-               opts = append(opts, 
mutil.ServiceServiceName(request.ServiceName))
-       }
-       return mutil.NewBasicFilter(ctx, opts...)
-}
-
 func (ds *MetadataManager) GetOverview(ctx context.Context, request 
*discovery.GetServicesRequest) (
        *discovery.Statistics, error) {
        ctx = util.WithCacheOnly(ctx)
@@ -855,73 +780,6 @@ func (ds *MetadataManager) modifySchema(ctx 
context.Context, serviceID string, s
        return nil
 }
 
-func getServiceDetailUtil(ctx context.Context, mgs *model.Service, countOnly 
bool, options []string) (*discovery.ServiceDetail, error) {
-       serviceDetail := new(discovery.ServiceDetail)
-       serviceID := mgs.Service.ServiceId
-       domainProject := util.ParseDomainProject(ctx)
-       domain := util.ParseDomain(ctx)
-       project := util.ParseProject(ctx)
-       if countOnly {
-               serviceDetail.Statics = new(discovery.Statistics)
-       }
-       for _, opt := range options {
-               expr := opt
-               switch expr {
-               case "tags":
-                       serviceDetail.Tags = mgs.Tags
-               case "instances":
-                       if countOnly {
-                               instanceCount, err := CountInstance(ctx, 
serviceID)
-                               if err != nil {
-                                       log.Error(fmt.Sprintf("get number of 
service [%s]'s instances failed", serviceID), err)
-                                       return nil, err
-                               }
-                               serviceDetail.Statics.Instances = 
&discovery.StInstance{
-                                       Count: instanceCount,
-                               }
-                               continue
-                       }
-                       filter := mutil.NewDomainProjectFilter(domain, project, 
mutil.InstanceServiceID(serviceID))
-                       instances, err := dao.GetMicroServiceInstances(ctx, 
filter)
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get service[%s]'s all 
instances failed", serviceID), err)
-                               return nil, err
-                       }
-                       serviceDetail.Instances = instances
-               case "schemas":
-                       filter := mutil.NewDomainProjectFilter(domain, project, 
mutil.ServiceID(serviceID))
-                       schemas, err := dao.GetSchemas(ctx, filter)
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get service %s's all 
schemas failed", mgs.Service.ServiceId), err)
-                               return nil, err
-                       }
-                       serviceDetail.SchemaInfos = schemas
-               case "dependencies":
-                       service := mgs.Service
-                       consumers, err := GetConsumers(ctx, domainProject, 
service,
-                               WithoutSelfDependency(), 
WithSameDomainProject())
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get 
service[%s][%s/%s/%s/%s]'s all consumers failed",
-                                       service.ServiceId, service.Environment, 
service.AppId, service.ServiceName, service.Version), err)
-                       }
-                       providers, err := GetProviders(ctx, domainProject, 
service,
-                               WithoutSelfDependency(), 
WithSameDomainProject())
-                       if err != nil {
-                               log.Error(fmt.Sprintf("get 
service[%s][%s/%s/%s/%s]'s all providers failed",
-                                       service.ServiceId, service.Environment, 
service.AppId, service.ServiceName, service.Version), err)
-                               return nil, err
-                       }
-                       serviceDetail.Consumers = consumers
-                       serviceDetail.Providers = providers
-               case "":
-                       continue
-               default:
-                       log.Info(fmt.Sprintf("request option %s is invalid", 
opt))
-               }
-       }
-       return serviceDetail, nil
-}
-
 // Instance management
 func (ds *MetadataManager) RegisterInstance(ctx context.Context,
        request *discovery.RegisterInstanceRequest) 
(*discovery.RegisterInstanceResponse, error) {
@@ -1815,3 +1673,7 @@ func formatRevision(consumerServiceID string, instances 
[]*discovery.MicroServic
        s := fmt.Sprintf("%s.%x", consumerServiceID, sha1.Sum(data))
        return fmt.Sprintf("%x", sha1.Sum(util.StringToBytesWithNoCopy(s))), nil
 }
+
+func (ds *MetadataManager) Statistics(ctx context.Context, withShared bool) 
(*discovery.Statistics, error) {
+       return statistics(ctx, withShared)
+}
diff --git a/datasource/ms.go b/datasource/ms.go
index 073a3dd5..52a6e29e 100644
--- a/datasource/ms.go
+++ b/datasource/ms.go
@@ -46,7 +46,6 @@ type MetadataManager interface {
        ListService(ctx context.Context, request *pb.GetServicesRequest) 
(*pb.GetServicesResponse, error)
        GetService(ctx context.Context, request *pb.GetServiceRequest) 
(*pb.MicroService, error)
 
-       ListServiceDetail(ctx context.Context, request 
*pb.GetServicesInfoRequest) (*pb.GetServicesInfoResponse, error)
        GetOverview(ctx context.Context, request *pb.GetServicesRequest) 
(*pb.Statistics, error)
        ListApp(ctx context.Context, request *pb.GetAppsRequest) 
(*pb.GetAppsResponse, error)
 
@@ -92,4 +91,6 @@ type MetadataManager interface {
        // RetireService retire the 'RetirePlan.Reserve' latest versions for 
each of service,
        // delete other versions which doesn't register any instances.
        RetireService(ctx context.Context, plan *RetirePlan) error
+
+       Statistics(ctx context.Context, withShared bool) (*pb.Statistics, error)
 }
diff --git a/datasource/ms_util.go b/datasource/ms_util.go
index c948dc2a..54df3181 100644
--- a/datasource/ms_util.go
+++ b/datasource/ms_util.go
@@ -20,7 +20,6 @@ package datasource
 import (
        "github.com/apache/servicecomb-service-center/pkg/util"
        "github.com/go-chassis/cari/discovery"
-       "github.com/jinzhu/copier"
 )
 
 var GlobalServiceNames = make(map[string]struct{})
@@ -145,31 +144,3 @@ func RemoveGlobalServices(withShared bool, domainProject 
string,
 func IsDefaultDomainProject(domainProject string) bool {
        return domainProject == RegistryDomainProject
 }
-
-func NewServiceOverview(serviceDetail *discovery.ServiceDetail, 
innerProperties map[string]string) (*discovery.ServiceDetail, error) {
-       tmpServiceDetail := &discovery.ServiceDetail{}
-       err := copier.CopyWithOption(tmpServiceDetail, serviceDetail, 
copier.Option{DeepCopy: true})
-       if err != nil {
-               return nil, discovery.NewError(discovery.ErrInternal, 
err.Error())
-       }
-       tmpServiceDetail.MicroService.Properties = nil
-       tmpServiceDetail.MicroService.Schemas = nil
-       instances := tmpServiceDetail.Instances
-       for _, instance := range instances {
-               instance.Properties = 
removeCustomProperties(instance.Properties, innerProperties)
-       }
-       return tmpServiceDetail, nil
-}
-
-func removeCustomProperties(properties, innerProperties map[string]string) 
map[string]string {
-       if len(innerProperties) == 0 {
-               return nil
-       }
-       props := make(map[string]string)
-       for k, v := range properties {
-               if _, ok := innerProperties[k]; ok {
-                       props[k] = v
-               }
-       }
-       return props
-}
diff --git a/datasource/ms_util_test.go b/datasource/ms_util_test.go
index b4fd2a29..f89f3fe8 100644
--- a/datasource/ms_util_test.go
+++ b/datasource/ms_util_test.go
@@ -116,65 +116,3 @@ func hasGlobalService(services []*discovery.MicroService) 
bool {
        }
        return false
 }
-
-func TestNewServiceOverview(t *testing.T) {
-       t.Run("no instances, should be ok", func(t *testing.T) {
-               _, err := 
datasource.NewServiceOverview(&discovery.ServiceDetail{
-                       MicroService: &discovery.MicroService{},
-               }, nil)
-               assert.NoError(t, err)
-
-               _, err = datasource.NewServiceOverview(&discovery.ServiceDetail{
-                       MicroService: &discovery.MicroService{},
-                       Instances:    []*discovery.MicroServiceInstance{},
-               }, nil)
-               assert.NoError(t, err)
-       })
-
-       t.Run("has schema or service properties, should be ok", func(t 
*testing.T) {
-               overview, err := 
datasource.NewServiceOverview(&discovery.ServiceDetail{
-                       MicroService: &discovery.MicroService{
-                               Schemas:    []string{"test"},
-                               Properties: map[string]string{"test": "A"},
-                       },
-               }, nil)
-               assert.NoError(t, err)
-               assert.Empty(t, overview.MicroService.Schemas)
-               assert.Empty(t, overview.MicroService.Properties)
-       })
-
-       t.Run("has instance properties, should be ok", func(t *testing.T) {
-               overview, err := 
datasource.NewServiceOverview(&discovery.ServiceDetail{
-                       MicroService: &discovery.MicroService{},
-                       Instances: []*discovery.MicroServiceInstance{
-                               {
-                                       Properties: map[string]string{"test": 
"A"},
-                               },
-                       },
-               }, nil)
-               assert.NoError(t, err)
-               assert.Empty(t, overview.Instances[0].Properties)
-
-               overview, err = 
datasource.NewServiceOverview(&discovery.ServiceDetail{
-                       MicroService: &discovery.MicroService{},
-                       Instances: []*discovery.MicroServiceInstance{
-                               {
-                                       Properties: map[string]string{"test": 
"A"},
-                               },
-                       },
-               }, map[string]string{"inner": "B"})
-               assert.NoError(t, err)
-               assert.Empty(t, overview.Instances[0].Properties)
-
-               overview, err = 
datasource.NewServiceOverview(&discovery.ServiceDetail{
-                       MicroService: &discovery.MicroService{},
-                       Instances: []*discovery.MicroServiceInstance{
-                               {
-                                       Properties: map[string]string{"test": 
"A", "inner": "C"},
-                               },
-                       },
-               }, map[string]string{"inner": "B"})
-               assert.NoError(t, err)
-               assert.Equal(t, "C", overview.Instances[0].Properties["inner"])
-       })
-}
diff --git a/datasource/options.go b/datasource/options.go
index 9bce1a4a..f5295143 100644
--- a/datasource/options.go
+++ b/datasource/options.go
@@ -43,6 +43,5 @@ type Options struct {
 
        EnableCache bool
        // InstanceTTL: the default ttl of instance lease
-       InstanceTTL        int64
-       InstanceProperties map[string]string
+       InstanceTTL int64
 }
diff --git a/datasource/service_test.go b/datasource/service_test.go
index b736bfb2..5e373e21 100644
--- a/datasource/service_test.go
+++ b/datasource/service_test.go
@@ -571,152 +571,6 @@ func TestService_Delete(t *testing.T) {
        })
 }
 
-func TestService_Info(t *testing.T) {
-       var (
-               serviceID  string
-               instanceID string
-       )
-       ctx := getContext()
-       defer datasource.GetMetadataManager().UnregisterService(ctx, 
&pb.DeleteServiceRequest{ServiceId: serviceID, Force: true})
-
-       t.Run("register a tested service & instance, should be passed", func(t 
*testing.T) {
-               resp, err := 
datasource.GetMetadataManager().RegisterService(ctx, &pb.CreateServiceRequest{
-                       Service: &pb.MicroService{
-                               ServiceName: "TestServic1",
-                               AppId:       "default",
-                               Version:     "1.0.0",
-                               Level:       "FRONT",
-                               Status:      "UP",
-                       },
-               })
-               assert.NoError(t, err)
-               serviceID = resp.ServiceId
-
-               instance := &pb.MicroServiceInstance{
-                       ServiceId: serviceID,
-                       Endpoints: []string{
-                               "rest://[::]:301000/",
-                       },
-                       HostName: "delete-host-ms",
-                       Status:   pb.MSI_UP,
-               }
-               respInstance, err := 
datasource.GetMetadataManager().RegisterInstance(ctx, 
&pb.RegisterInstanceRequest{
-                       Instance: instance,
-               })
-               assert.NoError(t, err)
-               instanceID = respInstance.InstanceId
-       })
-
-       t.Run("get tested service info, should be passed", func(t *testing.T) {
-               resp, err := 
datasource.GetMetadataManager().ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
-                       Options:     []string{"all"},
-                       AppId:       "default",
-                       ServiceName: "TestServic1",
-               })
-               assert.NoError(t, err)
-               assert.NotEqual(t, 0, len(resp.AllServicesDetail))
-               assert.NotEqual(t, 0, resp.Statistics.Services.Count)
-               assert.NotEqual(t, 0, resp.Statistics.Instances.Count)
-               assert.NotEqual(t, 0, resp.Statistics.Apps.Count)
-       })
-
-       t.Run("unregister tested service & instance, should be passed", func(t 
*testing.T) {
-               err := datasource.GetMetadataManager().UnregisterInstance(ctx, 
&pb.UnregisterInstanceRequest{
-                       ServiceId:  serviceID,
-                       InstanceId: instanceID,
-               })
-               assert.NoError(t, err)
-
-               err = datasource.GetMetadataManager().UnregisterService(ctx, 
&pb.DeleteServiceRequest{
-                       ServiceId: serviceID,
-                       Force:     true,
-               })
-               assert.NoError(t, err)
-       })
-
-       t.Run("get all services", func(t *testing.T) {
-               log.Info("should be passed")
-               resp, err := 
datasource.GetMetadataManager().ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
-                       Options: []string{"all"},
-               })
-               assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-
-               resp, err = 
datasource.GetMetadataManager().ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
-                       Options: []string{""},
-               })
-               assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-
-               resp, err = 
datasource.GetMetadataManager().ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
-                       Options: []string{"tags", "rules", "instances", 
"schemas", "statistics"},
-               })
-               assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-
-               resp, err = 
datasource.GetMetadataManager().ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
-                       Options: []string{"statistics"},
-               })
-               assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-
-               resp, err = 
datasource.GetMetadataManager().ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
-                       Options:   []string{"instances"},
-                       CountOnly: true,
-               })
-               assert.NoError(t, err)
-               assert.Equal(t, pb.ResponseSuccess, resp.Response.GetCode())
-       })
-}
-
-func TestService_Detail(t *testing.T) {
-       var (
-               serviceId string
-       )
-       ctx := getContext()
-       defer datasource.GetMetadataManager().UnregisterService(ctx, 
&pb.DeleteServiceRequest{ServiceId: serviceId, Force: true})
-
-       t.Run("execute 'get detail' operation", func(t *testing.T) {
-               log.Info("should be passed")
-               resp, err := 
datasource.GetMetadataManager().RegisterService(ctx, &pb.CreateServiceRequest{
-                       Service: &pb.MicroService{
-                               AppId:       "govern_service_group",
-                               ServiceName: "govern_service_name",
-                               Version:     "3.0.0",
-                               Level:       "FRONT",
-                               Status:      pb.MS_UP,
-                       },
-               })
-               assert.NoError(t, err)
-               serviceId = resp.ServiceId
-
-               _, err = datasource.GetMetadataManager().ModifySchema(ctx, 
&pb.ModifySchemaRequest{
-                       ServiceId: serviceId,
-                       SchemaId:  "schemaId",
-                       Schema:    "detail",
-               })
-               assert.NoError(t, err)
-
-               _, err = datasource.GetMetadataManager().RegisterInstance(ctx, 
&pb.RegisterInstanceRequest{
-                       Instance: &pb.MicroServiceInstance{
-                               ServiceId: serviceId,
-                               Endpoints: []string{
-                                       "govern:127.0.0.1:8080",
-                               },
-                               HostName: "UT-HOST",
-                               Status:   pb.MSI_UP,
-                       },
-               })
-               assert.NoError(t, err)
-
-               err = datasource.GetMetadataManager().UnregisterService(ctx, 
&pb.DeleteServiceRequest{
-                       ServiceId: serviceId,
-                       Force:     true,
-               })
-               assert.NoError(t, err)
-       })
-}
-
 func TestApplication_Get(t *testing.T) {
        t.Run("execute 'get apps' operation", func(t *testing.T) {
                log.Info("when request is valid, should be passed")
diff --git a/server/resource/govern/govern_resource.go 
b/server/resource/govern/govern_resource.go
index bc3189d5..eb5cc6d3 100644
--- a/server/resource/govern/govern_resource.go
+++ b/server/resource/govern/govern_resource.go
@@ -19,12 +19,14 @@ package govern
 
 import (
        "net/http"
+       "net/url"
        "strings"
 
        "github.com/apache/servicecomb-service-center/pkg/rest"
        "github.com/apache/servicecomb-service-center/pkg/util"
        governsvc 
"github.com/apache/servicecomb-service-center/server/service/govern"
        pb "github.com/go-chassis/cari/discovery"
+       "github.com/go-chassis/foundation/stringutil"
 )
 
 // Service 治理相关接口服务
@@ -78,6 +80,7 @@ func (res *Resource) ListService(w http.ResponseWriter, r 
*http.Request) {
        request.ServiceName = query.Get("serviceName")
        request.Environment = query.Get("env")
        request.WithShared = util.StringTRUE(query.Get("withShared"))
+       request.Properties = ParseProperties(query, "property")
        countOnly := query.Get("countOnly")
        if countOnly != "0" && countOnly != "1" && strings.TrimSpace(countOnly) 
!= "" {
                rest.WriteError(w, pb.ErrInvalidParams, "parameter countOnly 
must be 1 or 0")
@@ -94,6 +97,21 @@ func (res *Resource) ListService(w http.ResponseWriter, r 
*http.Request) {
        rest.WriteResponse(w, r, nil, resp)
 }
 
+func ParseProperties(query url.Values, key string) map[string]string {
+       propertyList := query[key]
+       properties := make(map[string]string, len(propertyList))
+       for _, kv := range propertyList {
+               if !strings.Contains(kv, ":") {
+                       properties[kv] = ""
+                       continue
+               }
+
+               k, v := stringutil.SplitToTwo(kv, ":")
+               properties[k] = v
+       }
+       return properties
+}
+
 func (res *Resource) GetOverview(w http.ResponseWriter, r *http.Request) {
        st, err := governsvc.GetOverview(r.Context(), &pb.GetServicesRequest{})
        if err != nil {
diff --git a/server/resource/govern/govern_resource_test.go 
b/server/resource/govern/govern_resource_test.go
index 1ce38542..c60421c7 100644
--- a/server/resource/govern/govern_resource_test.go
+++ b/server/resource/govern/govern_resource_test.go
@@ -21,8 +21,11 @@ import (
        "context"
        "encoding/json"
        "io"
+       "io/ioutil"
        "net/http"
        "net/http/httptest"
+       "net/url"
+       "reflect"
        "testing"
 
        _ "github.com/apache/servicecomb-service-center/test"
@@ -43,6 +46,8 @@ func TestResource_GetServiceDetail(t *testing.T) {
 
        service, err := discosvc.RegisterService(ctx, 
&pb.CreateServiceRequest{Service: &pb.MicroService{
                ServiceName: "get_service_detail",
+               Schemas:     []string{"test"},
+               Properties:  map[string]string{"test": "list"},
        }})
        assert.NoError(t, err)
        serviceID := service.ServiceId
@@ -58,7 +63,10 @@ func TestResource_GetServiceDetail(t *testing.T) {
                body, _ := io.ReadAll(w.Body)
                err := json.Unmarshal(body, &resp)
                assert.NoError(t, err)
-               assert.Equal(t, serviceID, resp.Service.MicroService.ServiceId)
+               detail := resp.Service
+               assert.Equal(t, serviceID, detail.MicroService.ServiceId)
+               assert.NotEmpty(t, detail.MicroService.Schemas)
+               assert.NotEmpty(t, detail.MicroService.Properties)
        })
 
        t.Run("query not exist service detail, should fail", func(t *testing.T) 
{
@@ -75,6 +83,8 @@ func TestResource_ListServiceDetail(t *testing.T) {
        const serviceName = "list_service_detail"
        service, err := discosvc.RegisterService(ctx, 
&pb.CreateServiceRequest{Service: &pb.MicroService{
                ServiceName: serviceName,
+               Schemas:     []string{"test"},
+               Properties:  map[string]string{"test": "list"},
        }})
        assert.NoError(t, err)
        serviceID := service.ServiceId
@@ -90,7 +100,10 @@ func TestResource_ListServiceDetail(t *testing.T) {
                body, _ := io.ReadAll(w.Body)
                err := json.Unmarshal(body, &resp)
                assert.NoError(t, err)
-               assert.Equal(t, serviceID, 
resp.AllServicesDetail[0].MicroService.ServiceId)
+               detail := resp.AllServicesDetail[0]
+               assert.Equal(t, serviceID, detail.MicroService.ServiceId)
+               assert.NotEmpty(t, detail.MicroService.Properties)
+               assert.Empty(t, detail.MicroService.Schemas)
                assert.NotEqual(t, int64(0), resp.Statistics.Services.Count)
                assert.NotEqual(t, int64(0), resp.Statistics.Apps.Count)
        })
@@ -109,6 +122,49 @@ func TestResource_ListServiceDetail(t *testing.T) {
                assert.NotEqual(t, int64(0), resp.Statistics.Services.Count)
                assert.NotEqual(t, int64(0), resp.Statistics.Apps.Count)
        })
+
+       t.Run("list service detail with properties filter, should ok", func(t 
*testing.T) {
+               r, _ := http.NewRequest(http.MethodGet, 
"/v4/default/govern/microservices?noCache=true&options=all&serviceName="+serviceName+"&property=test:list",
 nil)
+               w := httptest.NewRecorder()
+               rest.GetRouter().ServeHTTP(w, r)
+               assert.Equal(t, http.StatusOK, w.Code)
+
+               var resp pb.GetServicesInfoResponse
+               body, _ := ioutil.ReadAll(w.Body)
+               err := json.Unmarshal(body, &resp)
+               assert.NoError(t, err)
+               assert.Equal(t, serviceID, 
resp.AllServicesDetail[0].MicroService.ServiceId)
+               assert.NotEqual(t, int64(0), resp.Statistics.Services.Count)
+               assert.NotEqual(t, int64(0), resp.Statistics.Apps.Count)
+       })
+
+       t.Run("list service detail with only properties filter, should ok", 
func(t *testing.T) {
+               r, _ := http.NewRequest(http.MethodGet, 
"/v4/default/govern/microservices?noCache=true&options=all&property=test:list", 
nil)
+               w := httptest.NewRecorder()
+               rest.GetRouter().ServeHTTP(w, r)
+               assert.Equal(t, http.StatusOK, w.Code)
+
+               var resp pb.GetServicesInfoResponse
+               body, _ := ioutil.ReadAll(w.Body)
+               err := json.Unmarshal(body, &resp)
+               assert.NoError(t, err)
+               assert.Equal(t, serviceID, 
resp.AllServicesDetail[0].MicroService.ServiceId)
+               assert.NotEqual(t, int64(0), resp.Statistics.Services.Count)
+               assert.NotEqual(t, int64(0), resp.Statistics.Apps.Count)
+       })
+
+       t.Run("list service detail with not exist properties, should return 
empty", func(t *testing.T) {
+               r, _ := http.NewRequest(http.MethodGet, 
"/v4/default/govern/microservices?noCache=true&options=all&serviceName="+serviceName+"&property=none:none",
 nil)
+               w := httptest.NewRecorder()
+               rest.GetRouter().ServeHTTP(w, r)
+               assert.Equal(t, http.StatusOK, w.Code)
+
+               var resp pb.GetServicesInfoResponse
+               body, _ := ioutil.ReadAll(w.Body)
+               err := json.Unmarshal(body, &resp)
+               assert.NoError(t, err)
+               assert.Equal(t, 0, len(resp.AllServicesDetail))
+       })
 }
 
 func TestResource_ListApp(t *testing.T) {
@@ -136,3 +192,33 @@ func TestResource_ListApp(t *testing.T) {
                assert.Contains(t, resp.AppIds, "list_app_app")
        })
 }
+
+func TestParseProperties(t *testing.T) {
+       type args struct {
+               query url.Values
+               key   string
+       }
+       tests := []struct {
+               name string
+               args args
+               want map[string]string
+       }{
+               {"invalid queries", args{nil, "any"}, map[string]string{}},
+               {"invalid queries", args{url.Values{}, "any"}, 
map[string]string{}},
+               {"invalid queries", args{url.Values{"a": {"b:a"}}, "other"}, 
map[string]string{}},
+               {"valid queries", args{url.Values{"a": {""}}, "a"}, 
map[string]string{"": ""}},
+               {"valid queries", args{url.Values{"a": {"b"}}, "a"}, 
map[string]string{"b": ""}},
+               {"valid queries", args{url.Values{"a": {"b:"}}, "a"}, 
map[string]string{"b": ""}},
+               {"valid queries", args{url.Values{"a": {":"}}, "a"}, 
map[string]string{"": ""}},
+               {"valid queries", args{url.Values{"a": {":a"}}, "a"}, 
map[string]string{"": "a"}},
+               {"valid queries", args{url.Values{"a": {"b:a"}}, "a"}, 
map[string]string{"b": "a"}},
+               {"valid queries", args{url.Values{"a": {"b:a", "c:d"}}, "a"}, 
map[string]string{"b": "a", "c": "d"}},
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := govern.ParseProperties(tt.args.query, 
tt.args.key); !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("ParseProperties() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
diff --git a/server/server.go b/server/server.go
index fbef2fe2..ce06edd8 100644
--- a/server/server.go
+++ b/server/server.go
@@ -118,9 +118,8 @@ func (s *ServiceCenterServer) initDatasource() {
                                log.Error("", err)
                        }
                },
-               EnableCache:        config.GetRegistry().EnableCache,
-               InstanceTTL:        config.GetRegistry().InstanceTTL,
-               InstanceProperties: 
config.GetStringMap("registry.instance.properties"),
+               EnableCache: config.GetRegistry().EnableCache,
+               InstanceTTL: config.GetRegistry().InstanceTTL,
        }); err != nil {
                log.Fatal("init datasource failed", err)
        }
diff --git a/server/service/disco/retire_test.go 
b/server/service/disco/retire_test.go
index c41fbc63..a66380bd 100644
--- a/server/service/disco/retire_test.go
+++ b/server/service/disco/retire_test.go
@@ -24,6 +24,7 @@ import (
        "github.com/apache/servicecomb-service-center/datasource"
        "github.com/apache/servicecomb-service-center/datasource/schema"
        discosvc 
"github.com/apache/servicecomb-service-center/server/service/disco"
+       "github.com/apache/servicecomb-service-center/server/service/govern"
        "github.com/apache/servicecomb-service-center/test"
        pb "github.com/go-chassis/cari/discovery"
        "github.com/stretchr/testify/assert"
@@ -63,7 +64,7 @@ func TestRetireService(t *testing.T) {
                err := discosvc.RetireService(ctx, 
&datasource.RetirePlan{Reserve: 1})
                assert.NoError(t, err)
 
-               resp, err := 
datasource.GetMetadataManager().ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
+               resp, err := govern.ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
                        ServiceName: serviceIDPrefix,
                })
                assert.NoError(t, err)
diff --git a/server/service/govern/view.go b/server/service/govern/view.go
index d519cdde..2664bbec 100644
--- a/server/service/govern/view.go
+++ b/server/service/govern/view.go
@@ -24,14 +24,228 @@ import (
        "github.com/apache/servicecomb-service-center/datasource"
        "github.com/apache/servicecomb-service-center/pkg/log"
        "github.com/apache/servicecomb-service-center/pkg/util"
+       "github.com/apache/servicecomb-service-center/server/config"
        discosvc 
"github.com/apache/servicecomb-service-center/server/service/disco"
        "github.com/apache/servicecomb-service-center/server/service/validator"
        pb "github.com/go-chassis/cari/discovery"
+       "github.com/jinzhu/copier"
 )
 
+var defaultOptions = []string{"tags", "instances", "schemas", "dependencies"}
+
+type ServiceDetailOpt struct {
+       domainProject string
+       service       *pb.MicroService
+       countOnly     bool
+       options       []string
+}
+
 func ListServiceDetail(ctx context.Context, in *pb.GetServicesInfoRequest) 
(*pb.GetServicesInfoResponse, error) {
        ctx = util.WithCacheOnly(ctx)
-       return datasource.GetMetadataManager().ListServiceDetail(ctx, in)
+
+       optionMap := make(map[string]struct{}, len(in.Options))
+       for _, opt := range in.Options {
+               optionMap[opt] = struct{}{}
+       }
+
+       options := make([]string, 0, len(optionMap))
+       if _, ok := optionMap["all"]; ok {
+               optionMap["statistics"] = struct{}{}
+               options = defaultOptions
+       } else {
+               for opt := range optionMap {
+                       options = append(options, opt)
+               }
+       }
+
+       var st *pb.Statistics
+       if _, ok := optionMap["statistics"]; ok {
+               var err error
+               st, err = datasource.GetMetadataManager().Statistics(ctx, 
in.WithShared)
+               if err != nil {
+                       return nil, pb.NewError(pb.ErrInternal, err.Error())
+               }
+               if len(optionMap) == 1 {
+                       return &pb.GetServicesInfoResponse{
+                               Statistics: st,
+                       }, nil
+               }
+       }
+
+       //获取所有服务
+       resp, err := discosvc.ListService(ctx, &pb.GetServicesRequest{})
+       if err != nil {
+               log.Error("get all services by domain failed", err)
+               return nil, pb.NewError(pb.ErrInternal, err.Error())
+       }
+       services := resp.Services
+
+       allServiceDetails := make([]*pb.ServiceDetail, 0, len(services))
+       domainProject := util.ParseDomainProject(ctx)
+       instanceProperties := 
config.GetStringMap("registry.instance.properties")
+       for _, service := range services {
+               if !filterServices(domainProject, in, service) {
+                       continue
+               }
+
+               serviceDetail, err := getServiceDetailUtil(ctx, 
ServiceDetailOpt{
+                       domainProject: domainProject,
+                       service:       service,
+                       countOnly:     in.CountOnly,
+                       options:       options,
+               })
+               if err != nil {
+                       return nil, pb.NewError(pb.ErrInternal, err.Error())
+               }
+               serviceDetail.MicroService = service
+               tmpServiceDetail, err := NewServiceOverview(serviceDetail, 
instanceProperties)
+               if err != nil {
+                       return nil, err
+               }
+               allServiceDetails = append(allServiceDetails, tmpServiceDetail)
+       }
+
+       return &pb.GetServicesInfoResponse{
+               AllServicesDetail: allServiceDetails,
+               Statistics:        st,
+       }, nil
+}
+
+func getServiceDetailUtil(ctx context.Context, opts ServiceDetailOpt) 
(*pb.ServiceDetail, error) {
+       service := opts.service
+       serviceID := service.ServiceId
+       serviceLogName := fmt.Sprintf("%s][%s/%s/%s/%s", service.ServiceId, 
service.Environment, service.AppId, service.ServiceName, service.Version)
+       options := opts.options
+       serviceDetail := new(pb.ServiceDetail)
+       if opts.countOnly {
+               serviceDetail.Statics = new(pb.Statistics)
+       }
+
+       for _, opt := range options {
+               expr := opt
+               switch expr {
+               case "tags":
+                       resp, err := discosvc.ListTag(ctx, 
&pb.GetServiceTagsRequest{
+                               ServiceId: serviceID,
+                       })
+                       if err != nil {
+                               log.Error(fmt.Sprintf("get service[%s]'s all 
tags failed", serviceLogName), err)
+                               return nil, err
+                       }
+                       serviceDetail.Tags = resp.Tags
+               case "instances":
+                       resp, err := discosvc.ListInstance(ctx, 
&pb.GetInstancesRequest{
+                               ProviderServiceId: serviceID,
+                       })
+                       if err != nil {
+                               log.Error(fmt.Sprintf("get service[%s]'s all 
instances failed", serviceLogName), err)
+                               return nil, err
+                       }
+                       if opts.countOnly {
+                               if err != nil {
+                                       log.Error(fmt.Sprintf("get number of 
service[%s]'s instances failed", serviceLogName), err)
+                                       return nil, err
+                               }
+                               serviceDetail.Statics.Instances = 
&pb.StInstance{
+                                       Count: int64(len(resp.Instances)),
+                               }
+                               continue
+                       }
+                       serviceDetail.Instances = resp.Instances
+               case "schemas":
+                       schemas, err := discosvc.ListSchema(ctx, 
&pb.GetAllSchemaRequest{
+                               ServiceId:  serviceID,
+                               WithSchema: true,
+                       })
+                       if err != nil {
+                               log.Error(fmt.Sprintf("get service[%s]'s all 
schemas failed", serviceLogName), err)
+                               return nil, err
+                       }
+                       serviceDetail.SchemaInfos = schemas
+               case "dependencies":
+                       consumerResp, err := discosvc.ListConsumers(ctx, 
&pb.GetDependenciesRequest{
+                               ServiceId:  serviceID,
+                               NoSelf:     true,
+                               SameDomain: true,
+                       })
+                       if err != nil {
+                               log.Error(fmt.Sprintf("get service[%s]'s all 
consumers failed", serviceLogName), err)
+                               return nil, err
+                       }
+                       providerResp, err := discosvc.ListProviders(ctx, 
&pb.GetDependenciesRequest{
+                               ServiceId:  serviceID,
+                               NoSelf:     true,
+                               SameDomain: true,
+                       })
+                       if err != nil {
+                               log.Error(fmt.Sprintf("get service[%s]'s all 
providers failed", serviceLogName), err)
+                               return nil, err
+                       }
+                       serviceDetail.Consumers = consumerResp.Consumers
+                       serviceDetail.Providers = providerResp.Providers
+               case "":
+                       continue
+               default:
+                       log.Error(fmt.Sprintf("request option[%s] is invalid", 
opt), nil)
+               }
+       }
+       return serviceDetail, nil
+}
+
+func filterServices(domainProject string, request *pb.GetServicesInfoRequest, 
service *pb.MicroService) bool {
+       if !request.WithShared && 
datasource.IsGlobal(pb.MicroServiceToKey(domainProject, service)) {
+               return false
+       }
+       if len(request.Environment) > 0 && request.Environment != 
service.Environment {
+               return false
+       }
+       if len(request.AppId) > 0 && request.AppId != service.AppId {
+               return false
+       }
+       if len(request.ServiceName) > 0 && request.ServiceName != 
service.ServiceName {
+               return false
+       }
+       if len(request.Properties) > 0 && 
!matchAllProperties(request.Properties, service) {
+               return false
+       }
+       return true
+}
+
+func matchAllProperties(properties map[string]string, service 
*pb.MicroService) bool {
+       for k, v := range properties {
+               val, ok := service.Properties[k]
+               if !ok || v != val {
+                       return false
+               }
+       }
+       return true
+}
+
+func NewServiceOverview(serviceDetail *pb.ServiceDetail, innerProperties 
map[string]string) (*pb.ServiceDetail, error) {
+       tmpServiceDetail := &pb.ServiceDetail{}
+       err := copier.CopyWithOption(tmpServiceDetail, serviceDetail, 
copier.Option{DeepCopy: true})
+       if err != nil {
+               return nil, pb.NewError(pb.ErrInternal, err.Error())
+       }
+       tmpServiceDetail.MicroService.Schemas = nil
+       instances := tmpServiceDetail.Instances
+       for _, instance := range instances {
+               instance.Properties = 
removeCustomProperties(instance.Properties, innerProperties)
+       }
+       return tmpServiceDetail, nil
+}
+
+func removeCustomProperties(properties, innerProperties map[string]string) 
map[string]string {
+       if len(innerProperties) == 0 {
+               return nil
+       }
+       props := make(map[string]string)
+       for k, v := range properties {
+               if _, ok := innerProperties[k]; ok {
+                       props[k] = v
+               }
+       }
+       return props
 }
 
 func GetServiceDetail(ctx context.Context, in *pb.GetServiceRequest) 
(*pb.ServiceDetail, error) {
diff --git a/server/service/govern/view_test.go 
b/server/service/govern/view_test.go
index 70684fe1..608e4888 100644
--- a/server/service/govern/view_test.go
+++ b/server/service/govern/view_test.go
@@ -35,32 +35,52 @@ func getContext() context.Context {
 }
 
 func TestListServiceDetail(t *testing.T) {
+       ctx := getContext()
+       respC, err := disco.RegisterService(ctx, &pb.CreateServiceRequest{
+               Service: &pb.MicroService{
+                       AppId:       "govern_service_group",
+                       ServiceName: "govern_service_graph",
+                       Version:     "1.0.0",
+                       Level:       "FRONT",
+                       Status:      pb.MS_UP,
+               },
+       })
+       assert.NoError(t, err)
+       assert.NotNil(t, respC)
+       defer disco.UnregisterService(ctx, &pb.DeleteServiceRequest{ServiceId: 
respC.ServiceId, Force: true})
+
        t.Run("when get all services, should be passed", func(t *testing.T) {
-               resp, err := govern.ListServiceDetail(getContext(), 
&pb.GetServicesInfoRequest{
+               resp, err := govern.ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
                        Options: []string{"all"},
                })
                assert.NoError(t, err)
                assert.NotNil(t, resp)
+               assert.NotNil(t, resp.Statistics)
+               assert.NotNil(t, resp.AllServicesDetail)
 
-               resp, err = govern.ListServiceDetail(getContext(), 
&pb.GetServicesInfoRequest{
+               resp, err = govern.ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
                        Options: []string{""},
                })
                assert.NoError(t, err)
                assert.NotNil(t, resp)
+               assert.Nil(t, resp.Statistics)
+               assert.NotNil(t, resp.AllServicesDetail)
 
-               resp, err = govern.ListServiceDetail(getContext(), 
&pb.GetServicesInfoRequest{
+               resp, err = govern.ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
                        Options: []string{"tags", "rules", "instances", 
"schemas", "statistics"},
                })
                assert.NoError(t, err)
                assert.NotNil(t, resp)
 
-               resp, err = govern.ListServiceDetail(getContext(), 
&pb.GetServicesInfoRequest{
+               resp, err = govern.ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
                        Options: []string{"statistics"},
                })
                assert.NoError(t, err)
                assert.NotNil(t, resp)
+               assert.NotNil(t, resp.Statistics)
+               assert.Nil(t, resp.AllServicesDetail)
 
-               resp, err = govern.ListServiceDetail(getContext(), 
&pb.GetServicesInfoRequest{
+               resp, err = govern.ListServiceDetail(ctx, 
&pb.GetServicesInfoRequest{
                        Options:   []string{"instances"},
                        CountOnly: true,
                })
@@ -69,19 +89,8 @@ func TestListServiceDetail(t *testing.T) {
        })
 
        t.Run("when get top graph, should be passed", func(t *testing.T) {
-               respC, err := disco.RegisterService(getContext(), 
&pb.CreateServiceRequest{
-                       Service: &pb.MicroService{
-                               AppId:       "govern_service_group",
-                               ServiceName: "govern_service_graph",
-                               Version:     "1.0.0",
-                               Level:       "FRONT",
-                               Status:      pb.MS_UP,
-                       },
-               })
-               assert.NoError(t, err)
-               assert.NotNil(t, respC)
 
-               graph, err := govern.Draw(getContext(), false)
+               graph, err := govern.Draw(ctx, false)
                assert.NoError(t, err)
                assert.NotEqual(t, 0, len(graph.Nodes))
        })
@@ -176,3 +185,65 @@ func TestGetServiceDetail(t *testing.T) {
                assert.Nil(t, respGetServiceDetail)
        })
 }
+
+func TestNewServiceOverview(t *testing.T) {
+       t.Run("no instances, should be ok", func(t *testing.T) {
+               _, err := govern.NewServiceOverview(&pb.ServiceDetail{
+                       MicroService: &pb.MicroService{},
+               }, nil)
+               assert.NoError(t, err)
+
+               _, err = govern.NewServiceOverview(&pb.ServiceDetail{
+                       MicroService: &pb.MicroService{},
+                       Instances:    []*pb.MicroServiceInstance{},
+               }, nil)
+               assert.NoError(t, err)
+       })
+
+       t.Run("has schema or service properties, should be ok", func(t 
*testing.T) {
+               overview, err := govern.NewServiceOverview(&pb.ServiceDetail{
+                       MicroService: &pb.MicroService{
+                               Schemas:    []string{"test"},
+                               Properties: map[string]string{"test": "A"},
+                       },
+               }, nil)
+               assert.NoError(t, err)
+               assert.Empty(t, overview.MicroService.Schemas)
+               assert.NotEmpty(t, overview.MicroService.Properties)
+       })
+
+       t.Run("has instance properties, should be ok", func(t *testing.T) {
+               overview, err := govern.NewServiceOverview(&pb.ServiceDetail{
+                       MicroService: &pb.MicroService{},
+                       Instances: []*pb.MicroServiceInstance{
+                               {
+                                       Properties: map[string]string{"test": 
"A"},
+                               },
+                       },
+               }, nil)
+               assert.NoError(t, err)
+               assert.Empty(t, overview.Instances[0].Properties)
+
+               overview, err = govern.NewServiceOverview(&pb.ServiceDetail{
+                       MicroService: &pb.MicroService{},
+                       Instances: []*pb.MicroServiceInstance{
+                               {
+                                       Properties: map[string]string{"test": 
"A"},
+                               },
+                       },
+               }, map[string]string{"inner": "B"})
+               assert.NoError(t, err)
+               assert.Empty(t, overview.Instances[0].Properties)
+
+               overview, err = govern.NewServiceOverview(&pb.ServiceDetail{
+                       MicroService: &pb.MicroService{},
+                       Instances: []*pb.MicroServiceInstance{
+                               {
+                                       Properties: map[string]string{"test": 
"A", "inner": "C"},
+                               },
+                       },
+               }, map[string]string{"inner": "B"})
+               assert.NoError(t, err)
+               assert.Equal(t, "C", overview.Instances[0].Properties["inner"])
+       })
+}

Reply via email to