From 926c13739978bd707b19229cb524d3384060402a Mon Sep 17 00:00:00 2001 From: neilnaveen <42328488+neilnaveen@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:29:31 -0500 Subject: [PATCH 1/2] Added Glob searching for nodes - Added glob searching for nodes, allowing searchs for nodes like "*minefield*" and getting back two outputs "pkg:github/bitbomdev/minefield@v2" and "pkg:github/bitbomdev/minefield@v2" Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com> --- api/v1/service.go | 16 ++ api/v1/service.proto | 9 + api/v1/service_test.go | 14 + cmd/query/custom/custom.go | 139 ++++++++++ cmd/query/globsearch/globsearch.go | 82 ++++++ cmd/query/query.go | 130 +-------- cmd/start-service/start-service.go | 2 + gen/api/v1/apiv1connect/service.connect.go | 41 ++- gen/api/v1/service.pb.go | 291 +++++++++++++++------ pkg/graph/mockGraph.go | 18 ++ pkg/graph/storage.go | 1 + pkg/storages/redis_storage.go | 28 ++ 12 files changed, 561 insertions(+), 210 deletions(-) create mode 100644 cmd/query/custom/custom.go create mode 100644 cmd/query/globsearch/globsearch.go diff --git a/api/v1/service.go b/api/v1/service.go index ef46757..aaee1b6 100644 --- a/api/v1/service.go +++ b/api/v1/service.go @@ -81,6 +81,22 @@ func (s *Service) GetNodeByName(ctx context.Context, req *connect.Request[servic return connect.NewResponse(&service.GetNodeByNameResponse{Node: serviceNode}), nil } +func (s *Service) GetNodesByGlob(ctx context.Context, req *connect.Request[service.GetNodesByGlobRequest]) (*connect.Response[service.GetNodesByGlobResponse], error) { + nodes, err := s.storage.GetNodesByGlob(req.Msg.Pattern) + if err != nil { + return nil, err + } + serviceNodes := make([]*service.Node, 0, len(nodes)) + for _, node := range nodes { + serviceNode, err := NodeToServiceNode(node) + if err != nil { + return nil, err + } + serviceNodes = append(serviceNodes, serviceNode) + } + return connect.NewResponse(&service.GetNodesByGlobResponse{Nodes: serviceNodes}), nil +} + func (s *Service) Cache(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[emptypb.Empty], error) { err := graph.Cache(s.storage) if err != nil { diff --git a/api/v1/service.proto b/api/v1/service.proto index 258a6fa..90ac3b9 100644 --- a/api/v1/service.proto +++ b/api/v1/service.proto @@ -56,6 +56,14 @@ message GetNodeByNameResponse { Node node = 1; } +message GetNodesByGlobRequest { + string pattern = 1; +} + +message GetNodesByGlobResponse { + repeated Node nodes = 1; +} + service QueryService { rpc Query(QueryRequest) returns (QueryResponse) {} } @@ -72,5 +80,6 @@ service LeaderboardService { service GraphService { rpc GetNode(GetNodeRequest) returns (GetNodeResponse) {} + rpc GetNodesByGlob(GetNodesByGlobRequest) returns (GetNodesByGlobResponse) {} rpc GetNodeByName(GetNodeByNameRequest) returns (GetNodeByNameResponse) {} } \ No newline at end of file diff --git a/api/v1/service_test.go b/api/v1/service_test.go index beb1fb3..6e62abb 100644 --- a/api/v1/service_test.go +++ b/api/v1/service_test.go @@ -42,6 +42,20 @@ func TestGetNodeByName(t *testing.T) { assert.Equal(t, node.Name, resp.Msg.Node.Name) } +func TestGetNodesByGlob(t *testing.T) { + s := setupService() + node, err := graph.AddNode(s.storage, "type1", "metadata1", "name1") + require.NoError(t, err) + node2, err := graph.AddNode(s.storage, "type1", "metadata1", "name2") + require.NoError(t, err) + req := connect.NewRequest(&service.GetNodesByGlobRequest{Pattern: "name"}) + resp, err := s.GetNodesByGlob(context.Background(), req) + require.NoError(t, err) + for _, respNode := range resp.Msg.Nodes { + assert.Contains(t, []string{node.Name, node2.Name}, respNode.Name) + } +} + func TestQueriesAndCache(t *testing.T) { s := setupService() _, err := ingest.SBOM("../../testdata/osv-sboms/google_agi.sbom.json", s.storage, nil) diff --git a/cmd/query/custom/custom.go b/cmd/query/custom/custom.go new file mode 100644 index 0000000..08b17c2 --- /dev/null +++ b/cmd/query/custom/custom.go @@ -0,0 +1,139 @@ +package query + +import ( + "bufio" + "fmt" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + + "connectrpc.com/connect" + "github.com/RoaringBitmap/roaring" + apiv1 "github.com/bit-bom/minefield/gen/api/v1" + "github.com/bit-bom/minefield/gen/api/v1/apiv1connect" + "github.com/bit-bom/minefield/pkg/graph" + "github.com/bit-bom/minefield/pkg/tools" + "github.com/goccy/go-json" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" +) + +type options struct { + storage graph.Storage + outputdir string + visualizerAddr string + maxOutput int + visualize bool +} + +func (o *options) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.outputdir, "output-dir", "", "specify dir to write the output to") + cmd.Flags().IntVar(&o.maxOutput, "max-output", 10, "max output length") + cmd.Flags().BoolVar(&o.visualize, "visualize", false, "visualize the query") + cmd.Flags().StringVar(&o.visualizerAddr, "addr", "8081", "address to run the visualizer on") +} + +func (o *options) Run(cmd *cobra.Command, args []string) error { + script := strings.Join(args, " ") + httpClient := &http.Client{} + addr := os.Getenv("BITBOMDEV_ADDR") + if addr == "" { + addr = "http://localhost:8089" + } + client := apiv1connect.NewQueryServiceClient(httpClient, addr) + + // Create a new context + ctx := cmd.Context() + + // Create a new QueryRequest + req := connect.NewRequest(&apiv1.QueryRequest{ + Script: script, + }) + + // Make the Query request + res, err := client.Query(ctx, req) + if err != nil { + return fmt.Errorf("query failed: %v", err) + } + + // Print dependencies + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Name", "Type", "ID"}) + + count := 0 + for _, node := range res.Msg.Nodes { + if count > o.maxOutput { + break + } + + table.Append([]string{node.Name, node.Type, strconv.Itoa(int(node.Id))}) + + if o.outputdir != "" { + data, err := json.MarshalIndent(node.Metadata, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal node metadata: %w", err) + } + if _, err := os.Stat(o.outputdir); err != nil { + return fmt.Errorf("output directory does not exist: %w", err) + } + + filePath := filepath.Join(o.outputdir, tools.SanitizeFilename(node.Name)+".json") + file, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("failed to create file: %w", err) + } + defer file.Close() + + _, err = file.Write(data) + if err != nil { + return fmt.Errorf("failed to write data to file: %w", err) + } + } + count++ + } + + if o.visualize { + server := &http.Server{ + Addr: ":" + o.visualizerAddr, + } + + ids := roaring.New() + + for _, node := range res.Msg.Nodes { + ids.Add(node.Id) + } + + shutdown, err := graph.RunGraphVisualizer(o.storage, ids, script, server) + if err != nil { + return err + } + defer shutdown() + + fmt.Println("Press Enter to stop the server and continue...") + if _, err := bufio.NewReader(os.Stdin).ReadBytes('\n'); err != nil { + return err + } + } + + table.Render() + + return nil +} + +func New(storage graph.Storage) *cobra.Command { + o := &options{ + storage: storage, + } + cmd := &cobra.Command{ + Use: "custom [script]", + Short: "Quer dependencies and dependents of a project", + Args: cobra.ExactArgs(1), + RunE: o.Run, + DisableAutoGenTag: true, + } + o.AddFlags(cmd) + + return cmd +} diff --git a/cmd/query/globsearch/globsearch.go b/cmd/query/globsearch/globsearch.go new file mode 100644 index 0000000..07aa248 --- /dev/null +++ b/cmd/query/globsearch/globsearch.go @@ -0,0 +1,82 @@ +package query + +import ( + "fmt" + "net/http" + "os" + "strconv" + + "connectrpc.com/connect" + apiv1 "github.com/bit-bom/minefield/gen/api/v1" + "github.com/bit-bom/minefield/gen/api/v1/apiv1connect" + "github.com/bit-bom/minefield/pkg/graph" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" +) + +type options struct { + storage graph.Storage + maxOutput int +} + +func (o *options) AddFlags(cmd *cobra.Command) { + cmd.Flags().IntVar(&o.maxOutput, "max-output", 10, "max output length") +} + +func (o *options) Run(cmd *cobra.Command, args []string) error { + pattern := args[0] + httpClient := &http.Client{} + addr := os.Getenv("BITBOMDEV_ADDR") + if addr == "" { + addr = "http://localhost:8089" + } + client := apiv1connect.NewGraphServiceClient(httpClient, addr) + + // Create a new context + ctx := cmd.Context() + + // Create a new QueryRequest + req := connect.NewRequest(&apiv1.GetNodesByGlobRequest{ + Pattern: pattern, + }) + + // Make the Query request + res, err := client.GetNodesByGlob(ctx, req) + if err != nil { + return fmt.Errorf("query failed: %v", err) + } + + // Print dependencies + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Name", "Type", "ID"}) + + count := 0 + for _, node := range res.Msg.Nodes { + if count > o.maxOutput { + break + } + + table.Append([]string{node.Name, node.Type, strconv.Itoa(int(node.Id))}) + count++ + } + + table.Render() + + return nil +} + +func New(storage graph.Storage) *cobra.Command { + o := &options{ + storage: storage, + } + cmd := &cobra.Command{ + Use: "globsearch [pattern]", + Short: "Search for nodes by glob pattern", + Args: cobra.ExactArgs(1), + RunE: o.Run, + DisableAutoGenTag: true, + } + o.AddFlags(cmd) + + return cmd +} diff --git a/cmd/query/query.go b/cmd/query/query.go index fe607b8..86b981c 100644 --- a/cmd/query/query.go +++ b/cmd/query/query.go @@ -1,139 +1,21 @@ package query import ( - "bufio" - "fmt" - "net/http" - "os" - "path/filepath" - "strconv" - "strings" - - "connectrpc.com/connect" - "github.com/RoaringBitmap/roaring" - apiv1 "github.com/bit-bom/minefield/gen/api/v1" - "github.com/bit-bom/minefield/gen/api/v1/apiv1connect" + custom "github.com/bit-bom/minefield/cmd/query/custom" + globsearch "github.com/bit-bom/minefield/cmd/query/globsearch" "github.com/bit-bom/minefield/pkg/graph" - "github.com/bit-bom/minefield/pkg/tools" - "github.com/goccy/go-json" - "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" ) -type options struct { - storage graph.Storage - outputdir string - visualizerAddr string - maxOutput int - visualize bool -} - -func (o *options) AddFlags(cmd *cobra.Command) { - cmd.Flags().StringVar(&o.outputdir, "output-dir", "", "specify dir to write the output to") - cmd.Flags().IntVar(&o.maxOutput, "max-output", 10, "max output length") - cmd.Flags().BoolVar(&o.visualize, "visualize", false, "visualize the query") - cmd.Flags().StringVar(&o.visualizerAddr, "addr", "8081", "address to run the visualizer on") -} - -func (o *options) Run(cmd *cobra.Command, args []string) error { - script := strings.Join(args, " ") - httpClient := &http.Client{} - addr := os.Getenv("BITBOMDEV_ADDR") - if addr == "" { - addr = "http://localhost:8089" - } - client := apiv1connect.NewQueryServiceClient(httpClient, addr) - - // Create a new context - ctx := cmd.Context() - - // Create a new QueryRequest - req := connect.NewRequest(&apiv1.QueryRequest{ - Script: script, - }) - - // Make the Query request - res, err := client.Query(ctx, req) - if err != nil { - return fmt.Errorf("query failed: %v", err) - } - - // Print dependencies - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"Name", "Type", "ID"}) - - count := 0 - for _, node := range res.Msg.Nodes { - if count > o.maxOutput { - break - } - - table.Append([]string{node.Name, node.Type, strconv.Itoa(int(node.Id))}) - - if o.outputdir != "" { - data, err := json.MarshalIndent(node.Metadata, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal node metadata: %w", err) - } - if _, err := os.Stat(o.outputdir); err != nil { - return fmt.Errorf("output directory does not exist: %w", err) - } - - filePath := filepath.Join(o.outputdir, tools.SanitizeFilename(node.Name)+".json") - file, err := os.Create(filePath) - if err != nil { - return fmt.Errorf("failed to create file: %w", err) - } - defer file.Close() - - _, err = file.Write(data) - if err != nil { - return fmt.Errorf("failed to write data to file: %w", err) - } - } - count++ - } - - if o.visualize { - server := &http.Server{ - Addr: ":" + o.visualizerAddr, - } - - ids := roaring.New() - - for _, node := range res.Msg.Nodes { - ids.Add(node.Id) - } - - shutdown, err := graph.RunGraphVisualizer(o.storage, ids, script, server) - if err != nil { - return err - } - defer shutdown() - - fmt.Println("Press Enter to stop the server and continue...") - if _, err := bufio.NewReader(os.Stdin).ReadBytes('\n'); err != nil { - return err - } - } - - table.Render() - - return nil -} - func New(storage graph.Storage) *cobra.Command { - o := &options{ - storage: storage, - } cmd := &cobra.Command{ - Use: "query [script]", + Use: "query", Short: "Query dependencies and dependents of a project", - Args: cobra.ExactArgs(1), - RunE: o.Run, DisableAutoGenTag: true, } - o.AddFlags(cmd) + + cmd.AddCommand(custom.New(storage)) + cmd.AddCommand(globsearch.New(storage)) return cmd } diff --git a/cmd/start-service/start-service.go b/cmd/start-service/start-service.go index 70072ad..48f26da 100644 --- a/cmd/start-service/start-service.go +++ b/cmd/start-service/start-service.go @@ -44,6 +44,8 @@ func (o *options) Run(_ *cobra.Command, args []string) error { mux.Handle(path, handler) path, handler = apiv1connect.NewCacheServiceHandler(newService) mux.Handle(path, handler) + path, handler = apiv1connect.NewGraphServiceHandler(newService) + mux.Handle(path, handler) server := &http.Server{ Addr: serviceAddr, diff --git a/gen/api/v1/apiv1connect/service.connect.go b/gen/api/v1/apiv1connect/service.connect.go index 24630e9..eb345ad 100644 --- a/gen/api/v1/apiv1connect/service.connect.go +++ b/gen/api/v1/apiv1connect/service.connect.go @@ -5,14 +5,13 @@ package apiv1connect import ( + connect "connectrpc.com/connect" context "context" errors "errors" - http "net/http" - strings "strings" - - connect "connectrpc.com/connect" v1 "github.com/bit-bom/minefield/gen/api/v1" emptypb "google.golang.org/protobuf/types/known/emptypb" + http "net/http" + strings "strings" ) // This is a compile-time assertion to ensure that this generated file and the connect package are @@ -55,6 +54,9 @@ const ( LeaderboardServiceAllKeysProcedure = "/api.v1.LeaderboardService/AllKeys" // GraphServiceGetNodeProcedure is the fully-qualified name of the GraphService's GetNode RPC. GraphServiceGetNodeProcedure = "/api.v1.GraphService/GetNode" + // GraphServiceGetNodesByGlobProcedure is the fully-qualified name of the GraphService's + // GetNodesByGlob RPC. + GraphServiceGetNodesByGlobProcedure = "/api.v1.GraphService/GetNodesByGlob" // GraphServiceGetNodeByNameProcedure is the fully-qualified name of the GraphService's // GetNodeByName RPC. GraphServiceGetNodeByNameProcedure = "/api.v1.GraphService/GetNodeByName" @@ -72,6 +74,7 @@ var ( leaderboardServiceAllKeysMethodDescriptor = leaderboardServiceServiceDescriptor.Methods().ByName("AllKeys") graphServiceServiceDescriptor = v1.File_api_v1_service_proto.Services().ByName("GraphService") graphServiceGetNodeMethodDescriptor = graphServiceServiceDescriptor.Methods().ByName("GetNode") + graphServiceGetNodesByGlobMethodDescriptor = graphServiceServiceDescriptor.Methods().ByName("GetNodesByGlob") graphServiceGetNodeByNameMethodDescriptor = graphServiceServiceDescriptor.Methods().ByName("GetNodeByName") ) @@ -334,6 +337,7 @@ func (UnimplementedLeaderboardServiceHandler) AllKeys(context.Context, *connect. // GraphServiceClient is a client for the api.v1.GraphService service. type GraphServiceClient interface { GetNode(context.Context, *connect.Request[v1.GetNodeRequest]) (*connect.Response[v1.GetNodeResponse], error) + GetNodesByGlob(context.Context, *connect.Request[v1.GetNodesByGlobRequest]) (*connect.Response[v1.GetNodesByGlobResponse], error) GetNodeByName(context.Context, *connect.Request[v1.GetNodeByNameRequest]) (*connect.Response[v1.GetNodeByNameResponse], error) } @@ -353,6 +357,12 @@ func NewGraphServiceClient(httpClient connect.HTTPClient, baseURL string, opts . connect.WithSchema(graphServiceGetNodeMethodDescriptor), connect.WithClientOptions(opts...), ), + getNodesByGlob: connect.NewClient[v1.GetNodesByGlobRequest, v1.GetNodesByGlobResponse]( + httpClient, + baseURL+GraphServiceGetNodesByGlobProcedure, + connect.WithSchema(graphServiceGetNodesByGlobMethodDescriptor), + connect.WithClientOptions(opts...), + ), getNodeByName: connect.NewClient[v1.GetNodeByNameRequest, v1.GetNodeByNameResponse]( httpClient, baseURL+GraphServiceGetNodeByNameProcedure, @@ -364,8 +374,9 @@ func NewGraphServiceClient(httpClient connect.HTTPClient, baseURL string, opts . // graphServiceClient implements GraphServiceClient. type graphServiceClient struct { - getNode *connect.Client[v1.GetNodeRequest, v1.GetNodeResponse] - getNodeByName *connect.Client[v1.GetNodeByNameRequest, v1.GetNodeByNameResponse] + getNode *connect.Client[v1.GetNodeRequest, v1.GetNodeResponse] + getNodesByGlob *connect.Client[v1.GetNodesByGlobRequest, v1.GetNodesByGlobResponse] + getNodeByName *connect.Client[v1.GetNodeByNameRequest, v1.GetNodeByNameResponse] } // GetNode calls api.v1.GraphService.GetNode. @@ -373,6 +384,11 @@ func (c *graphServiceClient) GetNode(ctx context.Context, req *connect.Request[v return c.getNode.CallUnary(ctx, req) } +// GetNodesByGlob calls api.v1.GraphService.GetNodesByGlob. +func (c *graphServiceClient) GetNodesByGlob(ctx context.Context, req *connect.Request[v1.GetNodesByGlobRequest]) (*connect.Response[v1.GetNodesByGlobResponse], error) { + return c.getNodesByGlob.CallUnary(ctx, req) +} + // GetNodeByName calls api.v1.GraphService.GetNodeByName. func (c *graphServiceClient) GetNodeByName(ctx context.Context, req *connect.Request[v1.GetNodeByNameRequest]) (*connect.Response[v1.GetNodeByNameResponse], error) { return c.getNodeByName.CallUnary(ctx, req) @@ -381,6 +397,7 @@ func (c *graphServiceClient) GetNodeByName(ctx context.Context, req *connect.Req // GraphServiceHandler is an implementation of the api.v1.GraphService service. type GraphServiceHandler interface { GetNode(context.Context, *connect.Request[v1.GetNodeRequest]) (*connect.Response[v1.GetNodeResponse], error) + GetNodesByGlob(context.Context, *connect.Request[v1.GetNodesByGlobRequest]) (*connect.Response[v1.GetNodesByGlobResponse], error) GetNodeByName(context.Context, *connect.Request[v1.GetNodeByNameRequest]) (*connect.Response[v1.GetNodeByNameResponse], error) } @@ -396,6 +413,12 @@ func NewGraphServiceHandler(svc GraphServiceHandler, opts ...connect.HandlerOpti connect.WithSchema(graphServiceGetNodeMethodDescriptor), connect.WithHandlerOptions(opts...), ) + graphServiceGetNodesByGlobHandler := connect.NewUnaryHandler( + GraphServiceGetNodesByGlobProcedure, + svc.GetNodesByGlob, + connect.WithSchema(graphServiceGetNodesByGlobMethodDescriptor), + connect.WithHandlerOptions(opts...), + ) graphServiceGetNodeByNameHandler := connect.NewUnaryHandler( GraphServiceGetNodeByNameProcedure, svc.GetNodeByName, @@ -406,6 +429,8 @@ func NewGraphServiceHandler(svc GraphServiceHandler, opts ...connect.HandlerOpti switch r.URL.Path { case GraphServiceGetNodeProcedure: graphServiceGetNodeHandler.ServeHTTP(w, r) + case GraphServiceGetNodesByGlobProcedure: + graphServiceGetNodesByGlobHandler.ServeHTTP(w, r) case GraphServiceGetNodeByNameProcedure: graphServiceGetNodeByNameHandler.ServeHTTP(w, r) default: @@ -421,6 +446,10 @@ func (UnimplementedGraphServiceHandler) GetNode(context.Context, *connect.Reques return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.GraphService.GetNode is not implemented")) } +func (UnimplementedGraphServiceHandler) GetNodesByGlob(context.Context, *connect.Request[v1.GetNodesByGlobRequest]) (*connect.Response[v1.GetNodesByGlobResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.GraphService.GetNodesByGlob is not implemented")) +} + func (UnimplementedGraphServiceHandler) GetNodeByName(context.Context, *connect.Request[v1.GetNodeByNameRequest]) (*connect.Response[v1.GetNodeByNameResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("api.v1.GraphService.GetNodeByName is not implemented")) } diff --git a/gen/api/v1/service.pb.go b/gen/api/v1/service.pb.go index c4a2fbe..41698a9 100644 --- a/gen/api/v1/service.pb.go +++ b/gen/api/v1/service.pb.go @@ -7,12 +7,11 @@ package apiv1 import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" ) const ( @@ -587,6 +586,100 @@ func (x *GetNodeByNameResponse) GetNode() *Node { return nil } +type GetNodesByGlobRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` +} + +func (x *GetNodesByGlobRequest) Reset() { + *x = GetNodesByGlobRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_service_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNodesByGlobRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNodesByGlobRequest) ProtoMessage() {} + +func (x *GetNodesByGlobRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_service_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNodesByGlobRequest.ProtoReflect.Descriptor instead. +func (*GetNodesByGlobRequest) Descriptor() ([]byte, []int) { + return file_api_v1_service_proto_rawDescGZIP(), []int{11} +} + +func (x *GetNodesByGlobRequest) GetPattern() string { + if x != nil { + return x.Pattern + } + return "" +} + +type GetNodesByGlobResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` +} + +func (x *GetNodesByGlobResponse) Reset() { + *x = GetNodesByGlobResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v1_service_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNodesByGlobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNodesByGlobResponse) ProtoMessage() {} + +func (x *GetNodesByGlobResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v1_service_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNodesByGlobResponse.ProtoReflect.Descriptor instead. +func (*GetNodesByGlobResponse) Descriptor() ([]byte, []int) { + return file_api_v1_service_proto_rawDescGZIP(), []int{12} +} + +func (x *GetNodesByGlobResponse) GetNodes() []*Node { + if x != nil { + return x.Nodes + } + return nil +} + var File_api_v1_service_proto protoreflect.FileDescriptor var file_api_v1_service_proto_rawDesc = []byte{ @@ -636,45 +729,57 @@ var file_api_v1_service_proto_rawDesc = []byte{ 0x22, 0x39, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x32, 0x46, 0x0a, 0x0c, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x32, 0x84, 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x05, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, - 0x39, 0x0a, 0x05, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0xae, 0x01, 0x0a, 0x12, 0x4c, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x5a, 0x0a, 0x11, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, 0x0a, - 0x07, 0x41, 0x6c, 0x6c, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x4b, 0x65, 0x79, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x9c, 0x01, 0x0a, 0x0c, - 0x47, 0x72, 0x61, 0x70, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x07, - 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0d, 0x47, 0x65, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x4e, 0x61, - 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x74, 0x2d, 0x62, 0x6f, 0x6d, - 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x31, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x42, 0x79, 0x47, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0x3c, + 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x42, 0x79, 0x47, 0x6c, 0x6f, 0x62, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x32, 0x46, 0x0a, 0x0c, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x32, 0x84, 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x05, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, + 0x12, 0x39, 0x0a, 0x05, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0xae, 0x01, 0x0a, 0x12, + 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x5a, 0x0a, 0x11, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x12, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x61, + 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x62, + 0x6f, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, + 0x0a, 0x07, 0x41, 0x6c, 0x6c, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6c, 0x6c, 0x4b, 0x65, + 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xef, 0x01, 0x0a, + 0x0c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3c, 0x0a, + 0x07, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x42, 0x79, 0x47, 0x6c, 0x6f, 0x62, 0x12, 0x1d, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x42, + 0x79, 0x47, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x42, 0x79, + 0x47, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, + 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2f, + 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x74, + 0x2d, 0x62, 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2f, 0x67, + 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -689,24 +794,23 @@ func file_api_v1_service_proto_rawDescGZIP() []byte { return file_api_v1_service_proto_rawDescData } -var ( - file_api_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 11) - file_api_v1_service_proto_goTypes = []any{ - (*QueryRequest)(nil), // 0: api.v1.QueryRequest - (*QueryResponse)(nil), // 1: api.v1.QueryResponse - (*AllKeysResponse)(nil), // 2: api.v1.AllKeysResponse - (*Node)(nil), // 3: api.v1.Node - (*Query)(nil), // 4: api.v1.Query - (*CustomLeaderboardRequest)(nil), // 5: api.v1.CustomLeaderboardRequest - (*CustomLeaderboardResponse)(nil), // 6: api.v1.CustomLeaderboardResponse - (*GetNodeRequest)(nil), // 7: api.v1.GetNodeRequest - (*GetNodeResponse)(nil), // 8: api.v1.GetNodeResponse - (*GetNodeByNameRequest)(nil), // 9: api.v1.GetNodeByNameRequest - (*GetNodeByNameResponse)(nil), // 10: api.v1.GetNodeByNameResponse - (*emptypb.Empty)(nil), // 11: google.protobuf.Empty - } -) - +var file_api_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_api_v1_service_proto_goTypes = []any{ + (*QueryRequest)(nil), // 0: api.v1.QueryRequest + (*QueryResponse)(nil), // 1: api.v1.QueryResponse + (*AllKeysResponse)(nil), // 2: api.v1.AllKeysResponse + (*Node)(nil), // 3: api.v1.Node + (*Query)(nil), // 4: api.v1.Query + (*CustomLeaderboardRequest)(nil), // 5: api.v1.CustomLeaderboardRequest + (*CustomLeaderboardResponse)(nil), // 6: api.v1.CustomLeaderboardResponse + (*GetNodeRequest)(nil), // 7: api.v1.GetNodeRequest + (*GetNodeResponse)(nil), // 8: api.v1.GetNodeResponse + (*GetNodeByNameRequest)(nil), // 9: api.v1.GetNodeByNameRequest + (*GetNodeByNameResponse)(nil), // 10: api.v1.GetNodeByNameResponse + (*GetNodesByGlobRequest)(nil), // 11: api.v1.GetNodesByGlobRequest + (*GetNodesByGlobResponse)(nil), // 12: api.v1.GetNodesByGlobResponse + (*emptypb.Empty)(nil), // 13: google.protobuf.Empty +} var file_api_v1_service_proto_depIdxs = []int32{ 3, // 0: api.v1.QueryResponse.nodes:type_name -> api.v1.Node 3, // 1: api.v1.AllKeysResponse.nodes:type_name -> api.v1.Node @@ -714,25 +818,28 @@ var file_api_v1_service_proto_depIdxs = []int32{ 4, // 3: api.v1.CustomLeaderboardResponse.queries:type_name -> api.v1.Query 3, // 4: api.v1.GetNodeResponse.node:type_name -> api.v1.Node 3, // 5: api.v1.GetNodeByNameResponse.node:type_name -> api.v1.Node - 0, // 6: api.v1.QueryService.Query:input_type -> api.v1.QueryRequest - 11, // 7: api.v1.CacheService.Cache:input_type -> google.protobuf.Empty - 11, // 8: api.v1.CacheService.Clear:input_type -> google.protobuf.Empty - 5, // 9: api.v1.LeaderboardService.CustomLeaderboard:input_type -> api.v1.CustomLeaderboardRequest - 11, // 10: api.v1.LeaderboardService.AllKeys:input_type -> google.protobuf.Empty - 7, // 11: api.v1.GraphService.GetNode:input_type -> api.v1.GetNodeRequest - 9, // 12: api.v1.GraphService.GetNodeByName:input_type -> api.v1.GetNodeByNameRequest - 1, // 13: api.v1.QueryService.Query:output_type -> api.v1.QueryResponse - 11, // 14: api.v1.CacheService.Cache:output_type -> google.protobuf.Empty - 11, // 15: api.v1.CacheService.Clear:output_type -> google.protobuf.Empty - 6, // 16: api.v1.LeaderboardService.CustomLeaderboard:output_type -> api.v1.CustomLeaderboardResponse - 2, // 17: api.v1.LeaderboardService.AllKeys:output_type -> api.v1.AllKeysResponse - 8, // 18: api.v1.GraphService.GetNode:output_type -> api.v1.GetNodeResponse - 10, // 19: api.v1.GraphService.GetNodeByName:output_type -> api.v1.GetNodeByNameResponse - 13, // [13:20] is the sub-list for method output_type - 6, // [6:13] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 3, // 6: api.v1.GetNodesByGlobResponse.nodes:type_name -> api.v1.Node + 0, // 7: api.v1.QueryService.Query:input_type -> api.v1.QueryRequest + 13, // 8: api.v1.CacheService.Cache:input_type -> google.protobuf.Empty + 13, // 9: api.v1.CacheService.Clear:input_type -> google.protobuf.Empty + 5, // 10: api.v1.LeaderboardService.CustomLeaderboard:input_type -> api.v1.CustomLeaderboardRequest + 13, // 11: api.v1.LeaderboardService.AllKeys:input_type -> google.protobuf.Empty + 7, // 12: api.v1.GraphService.GetNode:input_type -> api.v1.GetNodeRequest + 11, // 13: api.v1.GraphService.GetNodesByGlob:input_type -> api.v1.GetNodesByGlobRequest + 9, // 14: api.v1.GraphService.GetNodeByName:input_type -> api.v1.GetNodeByNameRequest + 1, // 15: api.v1.QueryService.Query:output_type -> api.v1.QueryResponse + 13, // 16: api.v1.CacheService.Cache:output_type -> google.protobuf.Empty + 13, // 17: api.v1.CacheService.Clear:output_type -> google.protobuf.Empty + 6, // 18: api.v1.LeaderboardService.CustomLeaderboard:output_type -> api.v1.CustomLeaderboardResponse + 2, // 19: api.v1.LeaderboardService.AllKeys:output_type -> api.v1.AllKeysResponse + 8, // 20: api.v1.GraphService.GetNode:output_type -> api.v1.GetNodeResponse + 12, // 21: api.v1.GraphService.GetNodesByGlob:output_type -> api.v1.GetNodesByGlobResponse + 10, // 22: api.v1.GraphService.GetNodeByName:output_type -> api.v1.GetNodeByNameResponse + 15, // [15:23] is the sub-list for method output_type + 7, // [7:15] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_api_v1_service_proto_init() } @@ -873,6 +980,30 @@ func file_api_v1_service_proto_init() { return nil } } + file_api_v1_service_proto_msgTypes[11].Exporter = func(v any, i int) any { + switch v := v.(*GetNodesByGlobRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v1_service_proto_msgTypes[12].Exporter = func(v any, i int) any { + switch v := v.(*GetNodesByGlobResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -880,7 +1011,7 @@ func file_api_v1_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v1_service_proto_rawDesc, NumEnums: 0, - NumMessages: 11, + NumMessages: 13, NumExtensions: 0, NumServices: 4, }, diff --git a/pkg/graph/mockGraph.go b/pkg/graph/mockGraph.go index 148b6d9..3ab2b68 100644 --- a/pkg/graph/mockGraph.go +++ b/pkg/graph/mockGraph.go @@ -3,6 +3,7 @@ package graph import ( "errors" "fmt" + "path/filepath" "sync" "github.com/RoaringBitmap/roaring" @@ -50,6 +51,23 @@ func (m *MockStorage) GetNode(id uint32) (*Node, error) { return node, nil } +func (m *MockStorage) GetNodesByGlob(pattern string) ([]*Node, error) { + m.mu.Lock() + defer m.mu.Unlock() + + nodes := make([]*Node, 0) + for _, node := range m.nodes { + matched, err := filepath.Match(pattern, node.Name) + if err != nil { + return nil, fmt.Errorf("invalid glob pattern: %v", err) + } + if matched { + nodes = append(nodes, node) + } + } + return nodes, nil +} + func (m *MockStorage) GetAllKeys() ([]uint32, error) { m.mu.Lock() defer m.mu.Unlock() diff --git a/pkg/graph/storage.go b/pkg/graph/storage.go index a20727c..e9ee011 100644 --- a/pkg/graph/storage.go +++ b/pkg/graph/storage.go @@ -6,6 +6,7 @@ type Storage interface { SaveNode(node *Node) error GetNode(id uint32) (*Node, error) GetNodes(ids []uint32) (map[uint32]*Node, error) + GetNodesByGlob(pattern string) ([]*Node, error) GetAllKeys() ([]uint32, error) SaveCache(cache *NodeCache) error SaveCaches(cache []*NodeCache) error diff --git a/pkg/storages/redis_storage.go b/pkg/storages/redis_storage.go index aca394f..4f87b30 100644 --- a/pkg/storages/redis_storage.go +++ b/pkg/storages/redis_storage.go @@ -72,6 +72,34 @@ func (r *RedisStorage) GetNode(id uint32) (*graph.Node, error) { return &node, nil } +func (r *RedisStorage) GetNodesByGlob(pattern string) ([]*graph.Node, error) { + // Use pattern matching for Redis keys + keys, err := r.Client.Keys(context.Background(), fmt.Sprintf("name_to_id:%s", pattern)).Result() + if err != nil { + return nil, fmt.Errorf("failed to get nodes by pattern %s: %w", pattern, err) + } + + nodes := make([]*graph.Node, 0, len(keys)) + for _, key := range keys { + // Extract the name from the key + name := strings.TrimPrefix(key, "name_to_id:") + + // Get the ID using the name + id, err := r.NameToID(name) + if err != nil { + return nil, fmt.Errorf("failed to get ID for name %s: %w", name, err) + } + + node, err := r.GetNode(id) + if err != nil { + return nil, fmt.Errorf("failed to get node for ID %d: %w", id, err) + } + nodes = append(nodes, node) + } + + return nodes, nil +} + func (r *RedisStorage) GetAllKeys() ([]uint32, error) { keys, err := r.Client.Keys(context.Background(), "node:*").Result() if err != nil { From 640953d23cf072f3f94ac6b2e823ec6e72081b14 Mon Sep 17 00:00:00 2001 From: Neil Naveen <42328488+neilnaveen@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:13:09 -0500 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- cmd/query/custom/custom.go | 16 ++++++++++++---- cmd/query/globsearch/globsearch.go | 5 +++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd/query/custom/custom.go b/cmd/query/custom/custom.go index 08b17c2..e928eef 100644 --- a/cmd/query/custom/custom.go +++ b/cmd/query/custom/custom.go @@ -37,6 +37,10 @@ func (o *options) AddFlags(cmd *cobra.Command) { func (o *options) Run(cmd *cobra.Command, args []string) error { script := strings.Join(args, " ") + + if strings.TrimSpace(script) == "" { + return fmt.Errorf("script cannot be empty") + } httpClient := &http.Client{} addr := os.Getenv("BITBOMDEV_ADDR") if addr == "" { @@ -64,7 +68,7 @@ func (o *options) Run(cmd *cobra.Command, args []string) error { count := 0 for _, node := range res.Msg.Nodes { - if count > o.maxOutput { + if count >= o.maxOutput { break } @@ -75,8 +79,12 @@ func (o *options) Run(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("failed to marshal node metadata: %w", err) } - if _, err := os.Stat(o.outputdir); err != nil { - return fmt.Errorf("output directory does not exist: %w", err) + if _, err := os.Stat(o.outputdir); os.IsNotExist(err) { + if err := os.MkdirAll(o.outputdir, os.ModePerm); err != nil { + return fmt.Errorf("failed to create output directory: %w", err) + } + } else if err != nil { + return fmt.Errorf("failed to check output directory: %w", err) } filePath := filepath.Join(o.outputdir, tools.SanitizeFilename(node.Name)+".json") @@ -129,7 +137,7 @@ func New(storage graph.Storage) *cobra.Command { cmd := &cobra.Command{ Use: "custom [script]", Short: "Quer dependencies and dependents of a project", - Args: cobra.ExactArgs(1), + Args: cobra.MinimumNArgs(1), RunE: o.Run, DisableAutoGenTag: true, } diff --git a/cmd/query/globsearch/globsearch.go b/cmd/query/globsearch/globsearch.go index 07aa248..be25891 100644 --- a/cmd/query/globsearch/globsearch.go +++ b/cmd/query/globsearch/globsearch.go @@ -46,6 +46,11 @@ func (o *options) Run(cmd *cobra.Command, args []string) error { return fmt.Errorf("query failed: %v", err) } + if len(res.Msg.Nodes) == 0 { + fmt.Println("No nodes found matching pattern:", pattern) + return nil + } + // Print dependencies table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"Name", "Type", "ID"})