@@ -522,3 +522,161 @@ func isArgumentRoute(condition string) bool {
522522 }
523523 return false
524524}
525+
526+ // SearchServiceAsCrossLinkedList builds a service dependency graph.
527+ // If serviceName is provided, it finds applications that consume or provide that specific service.
528+ // If serviceName is empty, it returns all service dependencies in the mesh.
529+ // It constructs nodes (applications) and edges (dependencies) for graph visualization.
530+ func SearchServiceAsCrossLinkedList (ctx consolectx.Context , req * model.ServiceGraphReq ) (* model.GraphData , error ) {
531+ // Build indexes conditionally based on whether serviceName is provided
532+ consumerIndexes := map [string ]string {
533+ index .ByMeshIndex : req .Mesh ,
534+ }
535+ providerIndexes := map [string ]string {
536+ index .ByMeshIndex : req .Mesh ,
537+ }
538+
539+ // Only filter by serviceName if it's provided
540+ if req .ServiceName != "" {
541+ consumerIndexes [index .ByServiceConsumerServiceName ] = req .ServiceName
542+ providerIndexes [index .ByServiceProviderServiceName ] = req .ServiceName
543+ }
544+
545+ // Use ListByIndexes instead of PageListByIndexes to get all related resources
546+ // since we need complete dependency graph, not paginated results
547+ consumers , err := manager .ListByIndexes [* meshresource.ServiceConsumerMetadataResource ](
548+ ctx .ResourceManager (),
549+ meshresource .ServiceConsumerMetadataKind ,
550+ consumerIndexes )
551+ if err != nil {
552+ logger .Errorf ("get service consumer for mesh %s failed, cause: %v" , req .Mesh , err )
553+ return nil , bizerror .New (bizerror .InternalError , "get service consumer failed, please try again" )
554+ }
555+
556+ providers , err := manager .ListByIndexes [* meshresource.ServiceProviderMetadataResource ](
557+ ctx .ResourceManager (),
558+ meshresource .ServiceProviderMetadataKind ,
559+ providerIndexes )
560+ if err != nil {
561+ logger .Errorf ("get service provider for mesh %s failed, cause: %v" , req .Mesh , err )
562+ return nil , bizerror .New (bizerror .InternalError , "get service provider failed, please try again" )
563+ }
564+
565+ // Collect all unique applications (both consumers and providers)
566+ consumerApps := make (map [string ]bool )
567+ for _ , consumer := range consumers {
568+ if consumer .Spec != nil {
569+ consumerApps [consumer .Spec .ConsumerAppName ] = true
570+ }
571+ }
572+
573+ providerApps := make (map [string ]bool )
574+ for _ , provider := range providers {
575+ if provider .Spec != nil {
576+ providerApps [provider .Spec .ProviderAppName ] = true
577+ }
578+ }
579+
580+ allApps := make (map [string ]bool )
581+ for app := range consumerApps {
582+ allApps [app ] = true
583+ }
584+ for app := range providerApps {
585+ allApps [app ] = true
586+ }
587+
588+ nodes := make ([]model.GraphNode , 0 , len (allApps ))
589+ edges := make ([]model.GraphEdge , 0 )
590+
591+ // Build app to service instances mapping
592+ // For providers: map providerAppName -> list of instances providing this service
593+ // For consumers: map consumerAppName -> empty list (consumers don't provide instances)
594+ appInstances := make (map [string ][]* meshresource.InstanceResource )
595+
596+ // Get all instances for the mesh first
597+ allInstances , err := manager .ListByIndexes [* meshresource.InstanceResource ](
598+ ctx .ResourceManager (),
599+ meshresource .InstanceKind ,
600+ map [string ]string {
601+ index .ByMeshIndex : req .Mesh ,
602+ })
603+ if err != nil {
604+ logger .Errorf ("get instances for mesh %s failed, cause: %v" , req .Mesh , err )
605+ }
606+
607+ // Build app -> instances mapping
608+ for _ , instance := range allInstances {
609+ if instance .Spec != nil && instance .Spec .AppName != "" {
610+ appInstances [instance .Spec .AppName ] = append (appInstances [instance .Spec .AppName ], instance )
611+ }
612+ }
613+
614+ // Build nodes for each app
615+ // Provider nodes: data contains instances providing this service
616+ // Consumer nodes: data is nil (consumers don't provide instances)
617+ for appName := range allApps {
618+ var instanceData interface {}
619+
620+ instances := make ([]interface {}, 0 )
621+ if appInsts , ok := appInstances [appName ]; ok {
622+ for _ , instance := range appInsts {
623+ instances = append (instances , toInstanceData (instance ))
624+ }
625+ }
626+ instanceData = instances
627+
628+ nodes = append (nodes , model.GraphNode {
629+ ID : appName ,
630+ Label : appName ,
631+ Data : instanceData ,
632+ })
633+ }
634+
635+ // Build edges between consumers and providers
636+ // Only create edges between apps that actually have the service relationship
637+ for _ , consumer := range consumers {
638+ if consumer .Spec != nil {
639+ for _ , provider := range providers {
640+ if provider .Spec != nil {
641+ // This is where you should check if the provider's service name and the consumer's service name match.
642+ if consumer .Spec .ServiceName != provider .Spec .ServiceName {
643+ continue
644+ }
645+ edges = append (edges , model.GraphEdge {
646+ Source : consumer .Spec .ConsumerAppName ,
647+ Target : provider .Spec .ProviderAppName ,
648+ Data : map [string ]interface {}{
649+ "type" : "dependency" ,
650+ "serviceName" : req .ServiceName ,
651+ "consumerApp" : consumer .Spec .ConsumerAppName ,
652+ "providerApp" : provider .Spec .ProviderAppName ,
653+ },
654+ })
655+ }
656+ }
657+ }
658+ }
659+
660+ return & model.GraphData {
661+ Nodes : nodes ,
662+ Edges : edges ,
663+ }, nil
664+ }
665+
666+ func toInstanceData (instance * meshresource.InstanceResource ) map [string ]interface {} {
667+ if instance == nil || instance .Spec == nil {
668+ return nil
669+ }
670+
671+ data := map [string ]interface {}{
672+ "appName" : instance .Spec .AppName ,
673+ "ip" : instance .Spec .Ip ,
674+ "name" : instance .Spec .Name ,
675+ "protocol" : instance .Spec .Protocol ,
676+ "qosPort" : instance .Spec .QosPort ,
677+ "rpcPort" : instance .Spec .RpcPort ,
678+ "tags" : instance .Spec .Tags ,
679+ }
680+
681+ return data
682+ }
0 commit comments