Skip to content

Add more endpoints Add AddNode and SetDependency methods to Service struct in API #158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 51 additions & 23 deletions api/v1/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,67 +48,95 @@ type Query struct {
func (s *Service) GetNode(ctx context.Context, req *connect.Request[service.GetNodeRequest]) (*connect.Response[service.GetNodeResponse], error) {
node, err := s.storage.GetNode(req.Msg.Id)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get node by id: %w", err)
}
serviceNode, err := NodeToServiceNode(node)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert node to service node: %w", err)
}
return connect.NewResponse(&service.GetNodeResponse{Node: serviceNode}), nil
}

func (s *Service) GetNodeByName(ctx context.Context, req *connect.Request[service.GetNodeByNameRequest]) (*connect.Response[service.GetNodeByNameResponse], error) {
id, err := s.storage.NameToID(req.Msg.Name)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get node by name: %w", err)
}
node, err := s.storage.GetNode(id)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get node by id: %w", err)
}
serviceNode, err := NodeToServiceNode(node)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert node to service node: %w", err)
}
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
return nil, fmt.Errorf("failed to get nodes by glob: %w", err)
}
serviceNodes := make([]*service.Node, 0, len(nodes))
for _, node := range nodes {
serviceNode, err := NodeToServiceNode(node)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert node to service node: %w", err)
}
serviceNodes = append(serviceNodes, serviceNode)
}
return connect.NewResponse(&service.GetNodesByGlobResponse{Nodes: serviceNodes}), nil
}

func (s *Service) AddNode(ctx context.Context, req *connect.Request[service.AddNodeRequest]) (*connect.Response[service.AddNodeResponse], error) {
resultNode, err := graph.AddNode(s.storage, req.Msg.Node.Type, req.Msg.Node.Metadata, req.Msg.Node.Name)
if err != nil {
return nil, fmt.Errorf("failed to add node: %w", err)
}
serviceNode, err := NodeToServiceNode(resultNode)
if err != nil {
return nil, fmt.Errorf("failed to convert node to service node: %w", err)
}
return connect.NewResponse(&service.AddNodeResponse{Node: serviceNode}), nil
}

func (s *Service) SetDependency(ctx context.Context, req *connect.Request[service.SetDependencyRequest]) (*connect.Response[emptypb.Empty], error) {
fromNode, err := s.storage.GetNode(req.Msg.NodeId)
if err != nil {
return nil, err
}
toNode, err := s.storage.GetNode(req.Msg.DependencyID)
if err != nil {
return nil, err
}
err = fromNode.SetDependency(s.storage, toNode)
if err != nil {
return nil, err
}
return connect.NewResponse(&emptypb.Empty{}), 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 {
return nil, err
return nil, fmt.Errorf("failed to cache: %w", err)
}
return connect.NewResponse(&emptypb.Empty{}), nil
}

func (s *Service) Clear(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[emptypb.Empty], error) {
err := s.storage.RemoveAllCaches()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to clear: %w", err)
}
return connect.NewResponse(&emptypb.Empty{}), nil
}

func (s *Service) CustomLeaderboard(ctx context.Context, req *connect.Request[service.CustomLeaderboardRequest]) (*connect.Response[service.CustomLeaderboardResponse], error) {
uncachedNodes, err := s.storage.ToBeCached()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get uncached nodes: %w", err)
}
if len(uncachedNodes) != 0 {
return nil, fmt.Errorf("cannot use sorted leaderboards without caching")
Expand Down Expand Up @@ -211,18 +239,18 @@ func (s *Service) CustomLeaderboard(ctx context.Context, req *connect.Request[se
func (s *Service) AllKeys(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[service.AllKeysResponse], error) {
keys, err := s.storage.GetAllKeys()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get all keys: %w", err)
}
nodes, err := s.storage.GetNodes(keys)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get nodes by keys: %w", err)
}

resultNodes := make([]*service.Node, 0, len(nodes))
for _, node := range nodes {
query, err := NodeToServiceNode(node)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert node to service node: %w", err)
}
resultNodes = append(resultNodes, query)
}
Expand All @@ -238,37 +266,37 @@ func (s *Service) Query(ctx context.Context, req *connect.Request[service.QueryR
}
keys, err := s.storage.GetAllKeys()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get all keys: %w", err)
}

nodes, err := s.storage.GetNodes(keys)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get nodes by keys: %w", err)
}

caches, err := s.storage.GetCaches(keys)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get caches by keys: %w", err)
}
cacheStack, err := s.storage.ToBeCached()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get to be cached nodes: %w", err)
}
result, err := graph.ParseAndExecute(req.Msg.Script, s.storage, "", nodes, caches, len(cacheStack) == 0)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse and execute script: %w", err)
}

outputNodes, err := s.storage.GetNodes(result.ToArray())
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get nodes by ids: %w", err)
}

resultNodes := make([]*service.Node, 0, len(outputNodes))
for _, node := range outputNodes {
query, err := NodeToServiceNode(node)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to convert node to service node: %w", err)
}
resultNodes = append(resultNodes, query)
}
Expand All @@ -287,23 +315,23 @@ func (s *Service) Check(ctx context.Context, req *connect.Request[emptypb.Empty]
func (s *Service) IngestSBOM(ctx context.Context, req *connect.Request[service.IngestSBOMRequest]) (*connect.Response[emptypb.Empty], error) {
err := ingest.SBOM(s.storage, req.Msg.Sbom)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to ingest sbom: %w", err)
}
return connect.NewResponse(&emptypb.Empty{}), nil
}

func (s *Service) IngestVulnerability(ctx context.Context, req *connect.Request[service.IngestVulnerabilityRequest]) (*connect.Response[emptypb.Empty], error) {
err := ingest.Vulnerabilities(s.storage, req.Msg.Vulnerability)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to ingest vulnerability: %w", err)
}
return connect.NewResponse(&emptypb.Empty{}), nil
}

func (s *Service) IngestScorecard(ctx context.Context, req *connect.Request[service.IngestScorecardRequest]) (*connect.Response[emptypb.Empty], error) {
err := ingest.Scorecards(s.storage, req.Msg.Scorecard)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to ingest scorecard: %w", err)
}
return connect.NewResponse(&emptypb.Empty{}), nil
}
Expand Down
15 changes: 15 additions & 0 deletions api/v1/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ message GetNodesByGlobResponse {
repeated Node nodes = 1;
}

message AddNodeRequest {
Node node = 1;
}

message AddNodeResponse {
Node node = 1;
}

message SetDependencyRequest {
uint32 nodeId = 1;
uint32 dependencyID = 2;
}

message IngestSBOMRequest {
bytes sbom = 1;
}
Expand Down Expand Up @@ -98,6 +111,8 @@ service GraphService {
rpc GetNode(GetNodeRequest) returns (GetNodeResponse) {}
rpc GetNodesByGlob(GetNodesByGlobRequest) returns (GetNodesByGlobResponse) {}
rpc GetNodeByName(GetNodeByNameRequest) returns (GetNodeByNameResponse) {}
rpc AddNode(AddNodeRequest) returns (AddNodeResponse) {}
rpc SetDependency(SetDependencyRequest) returns (google.protobuf.Empty) {}
}

service IngestService {
Expand Down
53 changes: 53 additions & 0 deletions api/v1/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,59 @@ func TestIngestScorecard(t *testing.T) {
require.NoError(t, err)
}

func TestAddNode(t *testing.T) {
s := setupService()
addNodeReq := connect.NewRequest(&service.AddNodeRequest{
Node: &service.Node{Name: "test_node", Type: "type1"},
})
_, err := s.AddNode(context.Background(), addNodeReq)
require.NoError(t, err)
getNodeReq := connect.NewRequest(&service.GetNodeByNameRequest{Name: "test_node"})
resp, err := s.GetNodeByName(context.Background(), getNodeReq)
require.NoError(t, err)
assert.NotNil(t, resp.Msg.Node)
assert.Equal(t, "test_node", resp.Msg.Node.Name)

getNodeReq = connect.NewRequest(&service.GetNodeByNameRequest{Name: "nonexistent_node"})
resp, err = s.GetNodeByName(context.Background(), getNodeReq)
require.Error(t, err)
assert.Nil(t, resp)
}

func TestSetDependency(t *testing.T) {
s := setupService()
addNodeReq := connect.NewRequest(&service.AddNodeRequest{
Node: &service.Node{Name: "test_node", Type: "type1"},
})
node1, err := s.AddNode(context.Background(), addNodeReq)
require.NoError(t, err)
addNodeReq2 := connect.NewRequest(&service.AddNodeRequest{
Node: &service.Node{Name: "test_node2", Type: "type1"},
})
node2, err := s.AddNode(context.Background(), addNodeReq2)
require.NoError(t, err)
setDependencyReq := connect.NewRequest(&service.SetDependencyRequest{
NodeId: node1.Msg.Node.Id,
DependencyID: node2.Msg.Node.Id,
})
_, err = s.SetDependency(context.Background(), setDependencyReq)
require.NoError(t, err)
getNodeReq := connect.NewRequest(&service.GetNodeByNameRequest{Name: "test_node"})
resp, err := s.GetNodeByName(context.Background(), getNodeReq)
require.NoError(t, err)
assert.NotNil(t, resp.Msg.Node)
assert.NotEmpty(t, resp.Msg.Node.Dependencies, "Dependencies slice should not be empty")
assert.Equal(t, resp.Msg.Node.Dependencies[0], node2.Msg.Node.Id)
t.Run("non-existent node", func(t *testing.T) {
invalidReq := connect.NewRequest(&service.SetDependencyRequest{
NodeId: 0,
DependencyID: node2.Msg.Node.Id,
})
_, err = s.SetDependency(context.Background(), invalidReq)
assert.Error(t, err)
})
}

func TestHealthCheck(t *testing.T) {
s := setupService()
req := connect.NewRequest(&emptypb.Empty{})
Expand Down
11 changes: 11 additions & 0 deletions cmd/query/getMetadata/getMetadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
"github.com/spf13/cobra"
"github.com/zeebo/assert"
"google.golang.org/protobuf/types/known/emptypb"
)

func TestFormatTable(t *testing.T) {
Expand Down Expand Up @@ -48,6 +49,8 @@ type mockGraphServiceClient struct {
GetNodesByGlobFunc func(ctx context.Context, req *connect.Request[apiv1.GetNodesByGlobRequest]) (*connect.Response[apiv1.GetNodesByGlobResponse], error)
GetNodeFunc func(ctx context.Context, req *connect.Request[apiv1.GetNodeRequest]) (*connect.Response[apiv1.GetNodeResponse], error)
GetNodeByNameFunc func(ctx context.Context, req *connect.Request[apiv1.GetNodeByNameRequest]) (*connect.Response[apiv1.GetNodeByNameResponse], error)
AddNodeFunc func(ctx context.Context, req *connect.Request[apiv1.AddNodeRequest]) (*connect.Response[apiv1.AddNodeResponse], error)
SetDependencyFunc func(ctx context.Context, req *connect.Request[apiv1.SetDependencyRequest]) (*connect.Response[emptypb.Empty], error)
}

func (m *mockGraphServiceClient) GetNodesByGlob(ctx context.Context, req *connect.Request[apiv1.GetNodesByGlobRequest]) (*connect.Response[apiv1.GetNodesByGlobResponse], error) {
Expand All @@ -62,6 +65,14 @@ func (m *mockGraphServiceClient) GetNodeByName(ctx context.Context, req *connect
return m.GetNodeByNameFunc(ctx, req)
}

func (m *mockGraphServiceClient) AddNode(ctx context.Context, req *connect.Request[apiv1.AddNodeRequest]) (*connect.Response[apiv1.AddNodeResponse], error) {
return m.AddNodeFunc(ctx, req)
}

func (m *mockGraphServiceClient) SetDependency(ctx context.Context, req *connect.Request[apiv1.SetDependencyRequest]) (*connect.Response[emptypb.Empty], error) {
return m.SetDependencyFunc(ctx, req)
}

func TestRun(t *testing.T) {
tests := []struct {
name string
Expand Down
10 changes: 10 additions & 0 deletions cmd/query/globsearch/globsearch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
"github.com/spf13/cobra"
"github.com/zeebo/assert"
"google.golang.org/protobuf/types/known/emptypb"
)

func TestFormatTable(t *testing.T) {
Expand Down Expand Up @@ -163,6 +164,8 @@ type mockGraphServiceClient struct {
GetNodesByGlobFunc func(ctx context.Context, req *connect.Request[apiv1.GetNodesByGlobRequest]) (*connect.Response[apiv1.GetNodesByGlobResponse], error)
GetNodeFunc func(ctx context.Context, req *connect.Request[apiv1.GetNodeRequest]) (*connect.Response[apiv1.GetNodeResponse], error)
GetNodeByNameFunc func(ctx context.Context, req *connect.Request[apiv1.GetNodeByNameRequest]) (*connect.Response[apiv1.GetNodeByNameResponse], error)
SetDependencyFunc func(ctx context.Context, req *connect.Request[apiv1.SetDependencyRequest]) (*connect.Response[emptypb.Empty], error)
AddNodeFunc func(ctx context.Context, req *connect.Request[apiv1.AddNodeRequest]) (*connect.Response[apiv1.AddNodeResponse], error)
}

func (m *mockGraphServiceClient) GetNodesByGlob(ctx context.Context, req *connect.Request[apiv1.GetNodesByGlobRequest]) (*connect.Response[apiv1.GetNodesByGlobResponse], error) {
Expand All @@ -177,6 +180,13 @@ func (m *mockGraphServiceClient) GetNodeByName(ctx context.Context, req *connect
return m.GetNodeByNameFunc(ctx, req)
}

func (m *mockGraphServiceClient) AddNode(ctx context.Context, req *connect.Request[apiv1.AddNodeRequest]) (*connect.Response[apiv1.AddNodeResponse], error) {
return m.AddNodeFunc(ctx, req)
}

func (m *mockGraphServiceClient) SetDependency(ctx context.Context, req *connect.Request[apiv1.SetDependencyRequest]) (*connect.Response[emptypb.Empty], error) {
return m.SetDependencyFunc(ctx, req)
}
func TestRun(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading
Loading