From cfca9d5efd1aaafd4ef82e102f7f6ece97c3cddd Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:57:36 -0500 Subject: [PATCH 1/8] Include pagination for some more commands Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- .../backends/keyvalue/certifyGood.go | 173 +++++++++++++++++- .../graphql/examples/artifact_builder.gql | 28 +-- .../graphql/examples/certify_bad.gql | 21 +++ .../graphql/examples/certify_good.gql | 21 +++ .../graphql/examples/certify_scorecard.gql | 21 +++ 5 files changed, 242 insertions(+), 22 deletions(-) diff --git a/pkg/assembler/backends/keyvalue/certifyGood.go b/pkg/assembler/backends/keyvalue/certifyGood.go index 4179e8e8e6..e5850685bd 100644 --- a/pkg/assembler/backends/keyvalue/certifyGood.go +++ b/pkg/assembler/backends/keyvalue/certifyGood.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "time" @@ -191,7 +192,157 @@ func (c *demoClient) ingestCertifyGood(ctx context.Context, subject model.Packag // Query CertifyGood func (c *demoClient) CertifyGoodList(ctx context.Context, certifyGoodSpec model.CertifyGoodSpec, after *string, first *int) (*model.CertifyGoodConnection, error) { - return nil, fmt.Errorf("not implemented: CertifyGoodList") + c.m.RLock() + defer c.m.RUnlock() + + funcName := "CertifyGood" + + if certifyGoodSpec.ID != nil { + link, err := byIDkv[*goodLink](ctx, *certifyGoodSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + + foundCertifyGood, err := c.buildCertifyGood(ctx, link, &certifyGoodSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.CertifyGoodConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundCertifyGood.ID), + EndCursor: ptrfrom.String(foundCertifyGood.ID), + }, + Edges: []*model.CertifyGoodEdge{ + { + Cursor: foundCertifyGood.ID, + Node: foundCertifyGood, + }, + }, + }, nil + } + + edges := make([]*model.CertifyGoodEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + // Cant really search for an exact Pkg, as these can be linked to either + // names or versions, and version could be empty. + var search []string + foundOne := false + if certifyGoodSpec.Subject != nil && certifyGoodSpec.Subject.Artifact != nil { + exactArtifact, err := c.artifactExact(ctx, certifyGoodSpec.Subject.Artifact) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.GoodLinks...) + foundOne = true + } + } + if !foundOne && certifyGoodSpec.Subject != nil && certifyGoodSpec.Subject.Source != nil { + exactSource, err := c.exactSource(ctx, certifyGoodSpec.Subject.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = append(search, exactSource.GoodLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*goodLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + cg, err := c.CGIfMatch(ctx, &certifyGoodSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.CertifyGoodEdge{ + Cursor: cg.ID, + Node: cg, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(cgCol) + for !done { + var cgKeys []string + var err error + cgKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(cgKeys) + + totalCount = len(cgKeys) + + for i, cgKey := range cgKeys { + link, err := byKeykv[*goodLink](ctx, cgCol, cgKey, c) + if err != nil { + return nil, err + } + cgOut, err := c.CGIfMatch(ctx, &certifyGoodSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if cgOut.ID == *after { + totalCount = len(cgKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyGoodEdge{ + Cursor: cgOut.ID, + Node: cgOut, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyGoodEdge{ + Cursor: cgOut.ID, + Node: cgOut, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.CertifyGoodConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) CertifyGood(ctx context.Context, filter *model.CertifyGoodSpec) ([]*model.CertifyGood, error) { @@ -245,10 +396,12 @@ func (c *demoClient) CertifyGood(ctx context.Context, filter *model.CertifyGoodS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addCGIfMatch(ctx, out, filter, link) + cg, err := c.CGIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + out = append(out, cg) } } else { var done bool @@ -265,19 +418,19 @@ func (c *demoClient) CertifyGood(ctx context.Context, filter *model.CertifyGoodS if err != nil { return nil, err } - out, err = c.addCGIfMatch(ctx, out, filter, link) + cg, err := c.CGIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + out = append(out, cg) } } } return out, nil } -func (c *demoClient) addCGIfMatch(ctx context.Context, out []*model.CertifyGood, - filter *model.CertifyGoodSpec, link *goodLink) ( - []*model.CertifyGood, error) { +func (c *demoClient) CGIfMatch(ctx context.Context, filter *model.CertifyGoodSpec, link *goodLink) (*model.CertifyGood, error) { if filter != nil { if noMatch(filter.Justification, link.Justification) || @@ -285,7 +438,7 @@ func (c *demoClient) addCGIfMatch(ctx context.Context, out []*model.CertifyGood, noMatch(filter.Origin, link.Origin) || noMatch(filter.DocumentRef, link.DocumentRef) || filter.KnownSince != nil && filter.KnownSince.After(link.KnownSince) { - return out, nil + return nil, nil } } @@ -294,9 +447,9 @@ func (c *demoClient) addCGIfMatch(ctx context.Context, out []*model.CertifyGood, return nil, err } if foundCertifyGood == nil { - return out, nil + return nil, nil } - return append(out, foundCertifyGood), nil + return foundCertifyGood, nil } func (c *demoClient) buildCertifyGood(ctx context.Context, link *goodLink, filter *model.CertifyGoodSpec, ingestOrIDProvided bool) (*model.CertifyGood, error) { diff --git a/pkg/assembler/graphql/examples/artifact_builder.gql b/pkg/assembler/graphql/examples/artifact_builder.gql index 93af0d88e6..770bc3dc79 100644 --- a/pkg/assembler/graphql/examples/artifact_builder.gql +++ b/pkg/assembler/graphql/examples/artifact_builder.gql @@ -4,6 +4,21 @@ fragment allArtifactTree on Artifact { digest } +fragment allArtifactPaginationTree on ArtifactConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + query ArtifactQ1 { artifacts(artifactSpec: {}) { ...allArtifactTree @@ -13,18 +28,7 @@ query ArtifactQ1 { query ArtifactPagination { artifactsList(artifactSpec: {}, first: 10) { - totalCount - edges { - cursor - node { - ...allArtifactTree - } - } - pageInfo { - startCursor - endCursor - hasNextPage - } + ...allArtifactPaginationTree } } diff --git a/pkg/assembler/graphql/examples/certify_bad.gql b/pkg/assembler/graphql/examples/certify_bad.gql index d394a49d40..1ecea96aa2 100644 --- a/pkg/assembler/graphql/examples/certify_bad.gql +++ b/pkg/assembler/graphql/examples/certify_bad.gql @@ -50,6 +50,27 @@ fragment allCertifyBadTree on CertifyBad { documentRef } +fragment allCertifyBadPaginationTree on CertifyBadConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query certifyBadPagination { + CertifyBadList(certifyBadSpec: {}, first: 10) { + ...allCertifyBadPaginationTree + } +} + query CertifactBadQ1 { CertifyBad(certifyBadSpec: {}) { ...allCertifyBadTree diff --git a/pkg/assembler/graphql/examples/certify_good.gql b/pkg/assembler/graphql/examples/certify_good.gql index b83148ede2..3ef5f2403f 100644 --- a/pkg/assembler/graphql/examples/certify_good.gql +++ b/pkg/assembler/graphql/examples/certify_good.gql @@ -50,6 +50,27 @@ fragment allCertifyGoodTree on CertifyGood { documentRef } +fragment allCertifyGoodPaginationTree on CertifyGoodConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query CertifyGoodPagination { + C(scorecardSpec: {}, first: 10) { + ...allCertifyScorecardPaginationTree + } +} + query CertifactGoodQ1 { CertifyGood(certifyGoodSpec: {}) { ...allCertifyGoodTree diff --git a/pkg/assembler/graphql/examples/certify_scorecard.gql b/pkg/assembler/graphql/examples/certify_scorecard.gql index beb4b52ba3..82686f67c5 100644 --- a/pkg/assembler/graphql/examples/certify_scorecard.gql +++ b/pkg/assembler/graphql/examples/certify_scorecard.gql @@ -33,6 +33,27 @@ fragment allCertifyScorecardTree on CertifyScorecard { } } +fragment allCertifyScorecardPaginationTree on CertifyScorecardConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query scorecardPagination { + scorecardsList(scorecardSpec: {}, first: 10) { + ...allCertifyScorecardPaginationTree + } +} + query ScorecardQ1 { scorecards(scorecardSpec: {}) { ...allCertifyScorecardTree From b612ced7ca6f0865ffe0c18bbfb7f246c344533b Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:03:32 -0500 Subject: [PATCH 2/8] Included pagination for certifyScorecard Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- pkg/assembler/backends/keyvalue/artifact.go | 7 + pkg/assembler/backends/keyvalue/builder.go | 3 +- .../backends/keyvalue/certifyScorecard.go | 186 +++++++++++++++++- 3 files changed, 193 insertions(+), 3 deletions(-) diff --git a/pkg/assembler/backends/keyvalue/artifact.go b/pkg/assembler/backends/keyvalue/artifact.go index 65bbd93b26..8311f053cd 100644 --- a/pkg/assembler/backends/keyvalue/artifact.go +++ b/pkg/assembler/backends/keyvalue/artifact.go @@ -260,20 +260,26 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif algorithm := strings.ToLower(nilToEmpty(artifactSpec.Algorithm)) digest := strings.ToLower(nilToEmpty(artifactSpec.Digest)) + var done bool scn := c.kv.Keys(artCol) + var artKeys []string + for !done { artKeys, done, err = scn.Scan(ctx) if err != nil { return nil, err } + sort.Strings(artKeys) + for i, ak := range artKeys { a, err := byKeykv[*artStruct](ctx, artCol, ak, c) if err != nil { return nil, err } + convArt := c.convArtifact(a) if after != nil && !currentPage { if convArt.ID == *after { @@ -283,6 +289,7 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif continue } } + if first != nil { if currentPage && count < *first { artEdge := createArtifactEdges(algorithm, a, digest, convArt) diff --git a/pkg/assembler/backends/keyvalue/builder.go b/pkg/assembler/backends/keyvalue/builder.go index d74dd45e51..aac104de5f 100644 --- a/pkg/assembler/backends/keyvalue/builder.go +++ b/pkg/assembler/backends/keyvalue/builder.go @@ -200,9 +200,8 @@ func (c *demoClient) BuildersList(ctx context.Context, builderSpec model.Builder EndCursor: ptrfrom.String(edges[count-1].Node.ID), }, Edges: edges}, nil - } else { - return nil, nil } + return nil, nil } func (c *demoClient) Builders(ctx context.Context, builderSpec *model.BuilderSpec) ([]*model.Builder, error) { diff --git a/pkg/assembler/backends/keyvalue/certifyScorecard.go b/pkg/assembler/backends/keyvalue/certifyScorecard.go index 8b5c6fb72f..03b0fee33e 100644 --- a/pkg/assembler/backends/keyvalue/certifyScorecard.go +++ b/pkg/assembler/backends/keyvalue/certifyScorecard.go @@ -19,7 +19,9 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "reflect" + "sort" "strings" "time" @@ -144,7 +146,154 @@ func (c *demoClient) certifyScorecard(ctx context.Context, source model.IDorSour // Query CertifyScorecard func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.CertifyScorecardSpec, after *string, first *int) (*model.CertifyScorecardConnection, error) { - return nil, fmt.Errorf("not implemented: ScorecardsList") + c.m.RLock() + defer c.m.RUnlock() + + funcName := "Scorecards" + + if &scorecardSpec != nil && scorecardSpec.ID != nil { + link, err := byIDkv[*scorecardLink](ctx, *scorecardSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + + exactCertifyScorecard, err := c.buildScorecard(ctx, link, &scorecardSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.CertifyScorecardConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(exactCertifyScorecard.ID), + EndCursor: ptrfrom.String(exactCertifyScorecard.ID), + }, + Edges: []*model.CertifyScorecardEdge{ + { + Cursor: exactCertifyScorecard.ID, + Node: exactCertifyScorecard, + }, + }, + }, nil + } + + edges := make([]*model.CertifyScorecardEdge, 0) + hasNextPage := false + var cscKeys []string + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + if &scorecardSpec != nil && &scorecardSpec.Source != nil { + exactSource, err := c.exactSource(ctx, scorecardSpec.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = exactSource.ScorecardLinks + foundOne = true + } + } + + if foundOne { + var out []*model.CertifyScorecard + for _, id := range search { + link, err := byIDkv[*scorecardLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + out, err = c.addSCIfMatch(ctx, out, &scorecardSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + for _, scorecardOut := range out { + edges = append(edges, &model.CertifyScorecardEdge{ + Cursor: scorecardOut.ID, + Node: scorecardOut, + }) + } + } + } else { + scn := c.kv.Keys(cscCol) + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + for !done { + var err error + cscKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(cscKeys) + + totalCount = len(cscKeys) + + for i, cscKey := range cscKeys { + link, err := byKeykv[*scorecardLink](ctx, cscCol, cscKey, c) + if err != nil { + return nil, err + } + + scorecardOut, err := c.SCIfMatch(ctx, &scorecardSpec, link) + + if err != nil { + return nil, err + } + + if scorecardOut == nil { + return nil, fmt.Errorf("the scorecard link doesn't match the specification") + } + + if after != nil && !currentPage { + if scorecardOut.ID == *after { + totalCount = len(cscKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyScorecardEdge{ + Cursor: scorecardOut.ID, + Node: scorecardOut, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyScorecardEdge{ + Cursor: scorecardOut.ID, + Node: scorecardOut, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.CertifyScorecardConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) Scorecards(ctx context.Context, filter *model.CertifyScorecardSpec) ([]*model.CertifyScorecard, error) { @@ -216,6 +365,41 @@ func (c *demoClient) Scorecards(ctx context.Context, filter *model.CertifyScorec return out, nil } +func (c *demoClient) SCIfMatch(ctx context.Context, filter *model.CertifyScorecardSpec, link *scorecardLink) ( + *model.CertifyScorecard, error) { + if filter != nil && filter.TimeScanned != nil && !filter.TimeScanned.Equal(link.TimeScanned) { + return nil, nil + } + if filter != nil && noMatchFloat(filter.AggregateScore, link.AggregateScore) { + return nil, nil + } + if filter != nil && noMatchChecks(filter.Checks, link.Checks) { + return nil, nil + } + if filter != nil && noMatch(filter.ScorecardVersion, link.ScorecardVersion) { + return nil, nil + } + if filter != nil && noMatch(filter.ScorecardCommit, link.ScorecardCommit) { + return nil, nil + } + if filter != nil && noMatch(filter.Origin, link.Origin) { + return nil, nil + } + if filter != nil && noMatch(filter.Collector, link.Collector) { + return nil, nil + } + if filter != nil && noMatch(filter.DocumentRef, link.DocumentRef) { + return nil, nil + } + + foundCertifyScorecard, err := c.buildScorecard(ctx, link, filter, false) + if err != nil { + return nil, err + } + + return foundCertifyScorecard, nil +} + func (c *demoClient) addSCIfMatch(ctx context.Context, out []*model.CertifyScorecard, filter *model.CertifyScorecardSpec, link *scorecardLink) ( []*model.CertifyScorecard, error) { From 82a48ef764f8817a3e777ada25dba184b4f78022 Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Fri, 26 Apr 2024 08:48:11 -0500 Subject: [PATCH 3/8] Included Pagination for certifyBad Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- pkg/assembler/backends/keyvalue/artifact.go | 10 +- pkg/assembler/backends/keyvalue/builder.go | 12 +- pkg/assembler/backends/keyvalue/certifyBad.go | 172 ++++++++++++++- .../backends/keyvalue/certifyLegal.go | 199 ++++++++++++++--- .../backends/keyvalue/certifyScorecard.go | 7 +- .../backends/keyvalue/certifyVEXStatement.go | 194 +++++++++++++++-- .../backends/keyvalue/certifyVuln.go | 201 ++++++++++++++++-- .../graphql/examples/certify_legal.gql | 20 ++ .../graphql/examples/certify_vex.gql | 21 ++ .../graphql/examples/certify_vuln.gql | 21 ++ 10 files changed, 776 insertions(+), 81 deletions(-) create mode 100644 pkg/assembler/graphql/examples/certify_legal.gql diff --git a/pkg/assembler/backends/keyvalue/artifact.go b/pkg/assembler/backends/keyvalue/artifact.go index 8311f053cd..693dbd30b8 100644 --- a/pkg/assembler/backends/keyvalue/artifact.go +++ b/pkg/assembler/backends/keyvalue/artifact.go @@ -257,6 +257,7 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif currentPage = true } hasNextPage := false + totalCount := 0 algorithm := strings.ToLower(nilToEmpty(artifactSpec.Algorithm)) digest := strings.ToLower(nilToEmpty(artifactSpec.Digest)) @@ -274,6 +275,8 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif sort.Strings(artKeys) + totalCount = len(artKeys) + for i, ak := range artKeys { a, err := byKeykv[*artStruct](ctx, artCol, ak, c) if err != nil { @@ -284,10 +287,9 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif if after != nil && !currentPage { if convArt.ID == *after { currentPage = true - continue - } else { - continue + totalCount = len(artKeys) - (i + 1) } + continue } if first != nil { @@ -313,7 +315,7 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif } if len(edges) > 0 { return &model.ArtifactConnection{ - TotalCount: len(artKeys), + TotalCount: totalCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), diff --git a/pkg/assembler/backends/keyvalue/builder.go b/pkg/assembler/backends/keyvalue/builder.go index aac104de5f..2589266a8a 100644 --- a/pkg/assembler/backends/keyvalue/builder.go +++ b/pkg/assembler/backends/keyvalue/builder.go @@ -146,6 +146,8 @@ func (c *demoClient) BuildersList(ctx context.Context, builderSpec model.Builder currentPage = true } hasNextPage := false + totalCount := 0 + var done bool scn := c.kv.Keys(builderCol) @@ -156,6 +158,9 @@ func (c *demoClient) BuildersList(ctx context.Context, builderSpec model.Builder return nil, err } sort.Strings(bKeys) + + totalCount = len(bKeys) + for i, bk := range bKeys { b, err := byKeykv[*builderStruct](ctx, builderCol, bk, c) if err != nil { @@ -165,10 +170,9 @@ func (c *demoClient) BuildersList(ctx context.Context, builderSpec model.Builder if after != nil && !currentPage { if convBuild.ID == *after { currentPage = true - continue - } else { - continue + totalCount = len(bKeys) - (i + 1) } + continue } if first != nil { if currentPage && count < *first { @@ -193,7 +197,7 @@ func (c *demoClient) BuildersList(ctx context.Context, builderSpec model.Builder } if len(edges) > 0 { return &model.BuilderConnection{ - TotalCount: len(bKeys), + TotalCount: totalCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), diff --git a/pkg/assembler/backends/keyvalue/certifyBad.go b/pkg/assembler/backends/keyvalue/certifyBad.go index dbb79902eb..820b158bfb 100644 --- a/pkg/assembler/backends/keyvalue/certifyBad.go +++ b/pkg/assembler/backends/keyvalue/certifyBad.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "time" @@ -192,7 +193,156 @@ func (c *demoClient) ingestCertifyBad(ctx context.Context, subject model.Package // Query CertifyBad func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.CertifyBadSpec, after *string, first *int) (*model.CertifyBadConnection, error) { - return nil, fmt.Errorf("not implemented: CertifyBadList") + c.m.RLock() + defer c.m.RUnlock() + + funcName := "CertifyBad" + + if certifyBadSpec.ID != nil { + link, err := byIDkv[*badLink](ctx, *certifyBadSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + foundCertifyBad, err := c.buildCertifyBad(ctx, link, &certifyBadSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.CertifyBadConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundCertifyBad.ID), + EndCursor: ptrfrom.String(foundCertifyBad.ID), + }, + Edges: []*model.CertifyBadEdge{ + { + Cursor: foundCertifyBad.ID, + Node: foundCertifyBad, + }, + }, + }, nil + } + + edges := make([]*model.CertifyBadEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + // Cant really search for an exact Pkg, as these can be linked to either + // names or versions, and version could be empty. + var search []string + foundOne := false + if certifyBadSpec.Subject != nil && certifyBadSpec.Subject.Artifact != nil { + exactArtifact, err := c.artifactExact(ctx, certifyBadSpec.Subject.Artifact) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.BadLinks...) + foundOne = true + } + } + if !foundOne && certifyBadSpec.Subject != nil && certifyBadSpec.Subject.Source != nil { + exactSource, err := c.exactSource(ctx, certifyBadSpec.Subject.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = append(search, exactSource.BadLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*badLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + cb, err := c.CBIfMatch(ctx, &certifyBadSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.CertifyBadEdge{ + Cursor: cb.ID, + Node: cb, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(cbCol) + for !done { + var cbKeys []string + var err error + + cbKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(cbKeys) + + totalCount = len(cbKeys) + + for i, cbk := range cbKeys { + link, err := byKeykv[*badLink](ctx, cbCol, cbk, c) + if err != nil { + return nil, err + } + cbOut, err := c.CBIfMatch(ctx, &certifyBadSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if cbOut.ID == *after { + totalCount = len(cbKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyBadEdge{ + Cursor: cbOut.ID, + Node: cbOut, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyBadEdge{ + Cursor: cbOut.ID, + Node: cbOut, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.CertifyBadConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) CertifyBad(ctx context.Context, filter *model.CertifyBadSpec) ([]*model.CertifyBad, error) { @@ -246,10 +396,12 @@ func (c *demoClient) CertifyBad(ctx context.Context, filter *model.CertifyBadSpe if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addCBIfMatch(ctx, out, filter, link) + cb, err := c.CBIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + out = append(out, cb) } } else { var done bool @@ -266,19 +418,19 @@ func (c *demoClient) CertifyBad(ctx context.Context, filter *model.CertifyBadSpe if err != nil { return nil, err } - out, err = c.addCBIfMatch(ctx, out, filter, link) + cb, err := c.CBIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + out = append(out, cb) } } } return out, nil } -func (c *demoClient) addCBIfMatch(ctx context.Context, out []*model.CertifyBad, - filter *model.CertifyBadSpec, link *badLink) ( - []*model.CertifyBad, error) { +func (c *demoClient) CBIfMatch(ctx context.Context, filter *model.CertifyBadSpec, link *badLink) (*model.CertifyBad, error) { if filter != nil { if noMatch(filter.Justification, link.Justification) || @@ -286,7 +438,7 @@ func (c *demoClient) addCBIfMatch(ctx context.Context, out []*model.CertifyBad, noMatch(filter.Origin, link.Origin) || noMatch(filter.DocumentRef, link.DocumentRef) || filter.KnownSince != nil && filter.KnownSince.After(link.KnownSince) { - return out, nil + return nil, nil } } @@ -295,9 +447,9 @@ func (c *demoClient) addCBIfMatch(ctx context.Context, out []*model.CertifyBad, return nil, err } if foundCertifyBad == nil { - return out, nil + return nil, nil } - return append(out, foundCertifyBad), nil + return foundCertifyBad, nil } func (c *demoClient) buildCertifyBad(ctx context.Context, link *badLink, filter *model.CertifyBadSpec, ingestOrIDProvided bool) (*model.CertifyBad, error) { diff --git a/pkg/assembler/backends/keyvalue/certifyLegal.go b/pkg/assembler/backends/keyvalue/certifyLegal.go index 8e3811ae05..a849f996e6 100644 --- a/pkg/assembler/backends/keyvalue/certifyLegal.go +++ b/pkg/assembler/backends/keyvalue/certifyLegal.go @@ -19,7 +19,9 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "slices" + "sort" "strings" "time" @@ -266,7 +268,166 @@ func (c *demoClient) convLegal(ctx context.Context, in *certifyLegalStruct) (*mo } func (c *demoClient) CertifyLegalList(ctx context.Context, certifyLegalSpec model.CertifyLegalSpec, after *string, first *int) (*model.CertifyLegalConnection, error) { - return nil, fmt.Errorf("not implemented: CertifyLegalList") + c.m.RLock() + defer c.m.RUnlock() + + funcName := "CertifyLegal" + + if certifyLegalSpec.ID != nil { + link, err := byIDkv[*certifyLegalStruct](ctx, *certifyLegalSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + foundCertifyLegal, err := c.convLegal(ctx, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.CertifyLegalConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundCertifyLegal.ID), + EndCursor: ptrfrom.String(foundCertifyLegal.ID), + }, + Edges: []*model.CertifyLegalEdge{ + { + Cursor: foundCertifyLegal.ID, + Node: foundCertifyLegal, + }, + }, + }, nil + } + + edges := make([]*model.CertifyLegalEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + if certifyLegalSpec.Subject != nil && certifyLegalSpec.Subject.Package != nil { + pkgs, err := c.findPackageVersion(ctx, certifyLegalSpec.Subject.Package) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + foundOne = len(pkgs) > 0 + for _, pkg := range pkgs { + search = append(search, pkg.CertifyLegals...) + } + } + if !foundOne && certifyLegalSpec.Subject != nil && certifyLegalSpec.Subject.Source != nil { + exactSource, err := c.exactSource(ctx, certifyLegalSpec.Subject.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = append(search, exactSource.CertifyLegals...) + foundOne = true + } + } + if !foundOne { + for _, lSpec := range certifyLegalSpec.DeclaredLicenses { + exactLicense, err := c.licenseExact(ctx, lSpec) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactLicense != nil { + search = append(search, exactLicense.CertifyLegals...) + foundOne = true + break + } + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*certifyLegalStruct](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + legal, err := c.legalIfMatch(ctx, &certifyLegalSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.CertifyLegalEdge{ + Cursor: legal.ID, + Node: legal, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(clCol) + for !done { + var clKeys []string + var err error + clKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(clKeys) + totalCount = len(clKeys) + + for i, clk := range clKeys { + link, err := byKeykv[*certifyLegalStruct](ctx, clCol, clk, c) + if err != nil { + return nil, err + } + legal, err := c.legalIfMatch(ctx, &certifyLegalSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if legal.ID == *after { + totalCount = len(clKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyLegalEdge{ + Cursor: legal.ID, + Node: legal, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyLegalEdge{ + Cursor: legal.ID, + Node: legal, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.CertifyLegalConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) CertifyLegal(ctx context.Context, filter *model.CertifyLegalSpec) ([]*model.CertifyLegal, error) { @@ -324,19 +485,6 @@ func (c *demoClient) CertifyLegal(ctx context.Context, filter *model.CertifyLega } } } - if !foundOne && filter != nil { - for _, lSpec := range filter.DiscoveredLicenses { - exactLicense, err := c.licenseExact(ctx, lSpec) - if err != nil { - return nil, gqlerror.Errorf("%v :: %v", funcName, err) - } - if exactLicense != nil { - search = append(search, exactLicense.CertifyLegals...) - foundOne = true - break - } - } - } var out []*model.CertifyLegal if foundOne { @@ -345,10 +493,11 @@ func (c *demoClient) CertifyLegal(ctx context.Context, filter *model.CertifyLega if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addLegalIfMatch(ctx, out, filter, link) + legal, err := c.legalIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, legal) } } else { var done bool @@ -365,19 +514,19 @@ func (c *demoClient) CertifyLegal(ctx context.Context, filter *model.CertifyLega if err != nil { return nil, err } - out, err = c.addLegalIfMatch(ctx, out, filter, link) + legal, err := c.legalIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, legal) } } } return out, nil } -func (c *demoClient) addLegalIfMatch(ctx context.Context, out []*model.CertifyLegal, - filter *model.CertifyLegalSpec, link *certifyLegalStruct) ( - []*model.CertifyLegal, error, +func (c *demoClient) legalIfMatch(ctx context.Context, filter *model.CertifyLegalSpec, link *certifyLegalStruct) ( + *model.CertifyLegal, error, ) { if noMatch(filter.DeclaredLicense, link.DeclaredLicense) || noMatch(filter.DiscoveredLicense, link.DiscoveredLicense) || @@ -389,30 +538,30 @@ func (c *demoClient) addLegalIfMatch(ctx context.Context, out []*model.CertifyLe (filter.TimeScanned != nil && !link.TimeScanned.Equal(*filter.TimeScanned)) || !c.matchLicenses(ctx, filter.DeclaredLicenses, link.DeclaredLicenses) || !c.matchLicenses(ctx, filter.DiscoveredLicenses, link.DiscoveredLicenses) { - return out, nil + return nil, nil } if filter.Subject != nil { if filter.Subject.Package != nil { if link.Pkg == "" { - return out, nil + return nil, nil } p, err := c.buildPackageResponse(ctx, link.Pkg, filter.Subject.Package) if err != nil { return nil, err } if p == nil { - return out, nil + return nil, nil } } else if filter.Subject.Source != nil { if link.Source == "" { - return out, nil + return nil, nil } s, err := c.buildSourceResponse(ctx, link.Source, filter.Subject.Source) if err != nil { return nil, err } if s == nil { - return out, nil + return nil, nil } } } @@ -420,7 +569,7 @@ func (c *demoClient) addLegalIfMatch(ctx context.Context, out []*model.CertifyLe if err != nil { return nil, err } - return append(out, o), nil + return o, nil } func (c *demoClient) matchLicenses(ctx context.Context, filter []*model.LicenseSpec, value []string) bool { diff --git a/pkg/assembler/backends/keyvalue/certifyScorecard.go b/pkg/assembler/backends/keyvalue/certifyScorecard.go index 03b0fee33e..64114e889e 100644 --- a/pkg/assembler/backends/keyvalue/certifyScorecard.go +++ b/pkg/assembler/backends/keyvalue/certifyScorecard.go @@ -151,7 +151,7 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer funcName := "Scorecards" - if &scorecardSpec != nil && scorecardSpec.ID != nil { + if scorecardSpec.ID != nil { link, err := byIDkv[*scorecardLink](ctx, *scorecardSpec.ID, c) if err != nil { // Not found @@ -181,13 +181,12 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer edges := make([]*model.CertifyScorecardEdge, 0) hasNextPage := false - var cscKeys []string numNodes := 0 totalCount := 0 var search []string foundOne := false - if &scorecardSpec != nil && &scorecardSpec.Source != nil { + if scorecardSpec.Source != nil { exactSource, err := c.exactSource(ctx, scorecardSpec.Source) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) @@ -229,6 +228,8 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer var done bool for !done { var err error + var cscKeys []string + cscKeys, done, err = scn.Scan(ctx) if err != nil { return nil, err diff --git a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go index 41766d96da..5db5f9a0f9 100644 --- a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go +++ b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "time" @@ -194,7 +195,165 @@ func (c *demoClient) ingestVEXStatement(ctx context.Context, subject model.Packa // Query CertifyVex func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStatementSpec model.CertifyVEXStatementSpec, after *string, first *int) (*model.VEXConnection, error) { - return nil, fmt.Errorf("not implemented: CertifyVEXStatementList") + c.m.RLock() + defer c.m.RUnlock() + funcName := "CertifyVEXStatement" + + if certifyVEXStatementSpec.ID != nil { + link, err := byIDkv[*vexLink](ctx, *certifyVEXStatementSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + foundCertifyVex, err := c.buildCertifyVEXStatement(ctx, link, &certifyVEXStatementSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.VEXConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundCertifyVex.ID), + EndCursor: ptrfrom.String(foundCertifyVex.ID), + }, + Edges: []*model.VEXEdge{ + { + Cursor: foundCertifyVex.ID, + Node: foundCertifyVex, + }, + }, + }, nil + } + + edges := make([]*model.VEXEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + + if certifyVEXStatementSpec.Subject != nil && certifyVEXStatementSpec.Subject.Artifact != nil { + exactArtifact, err := c.artifactExact(ctx, certifyVEXStatementSpec.Subject.Artifact) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.VexLinks...) + foundOne = true + } + } + if !foundOne && certifyVEXStatementSpec.Subject != nil && certifyVEXStatementSpec.Subject.Package != nil { + pkgs, err := c.findPackageVersion(ctx, certifyVEXStatementSpec.Subject.Package) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + foundOne = len(pkgs) > 0 + for _, pkg := range pkgs { + search = append(search, pkg.VexLinks...) + } + } + if !foundOne && certifyVEXStatementSpec.Vulnerability != nil { + exactVuln, err := c.exactVulnerability(ctx, certifyVEXStatementSpec.Vulnerability) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactVuln != nil { + search = append(search, exactVuln.VexLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*vexLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + vex, err := c.vexIfMatch(ctx, &certifyVEXStatementSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.VEXEdge{ + Cursor: vex.ID, + Node: vex, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(cVEXCol) + + for !done { + var keys []string + var err error + keys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(keys) + totalCount = len(keys) + + for i, key := range keys { + link, err := byKeykv[*vexLink](ctx, cVEXCol, key, c) + if err != nil { + return nil, err + } + vex, err := c.vexIfMatch(ctx, &certifyVEXStatementSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%vex :: %vex", funcName, err) + } + + if after != nil && !currentPage { + if vex.ID == *after { + totalCount = len(keys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VEXEdge{ + Cursor: vex.ID, + Node: vex, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VEXEdge{ + Cursor: vex.ID, + Node: vex, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.VEXConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) CertifyVEXStatement(ctx context.Context, filter *model.CertifyVEXStatementSpec) ([]*model.CertifyVEXStatement, error) { @@ -257,10 +416,11 @@ func (c *demoClient) CertifyVEXStatement(ctx context.Context, filter *model.Cert if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addVexIfMatch(ctx, out, filter, link) + v, err := c.vexIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, v) } } else { var done bool @@ -277,43 +437,43 @@ func (c *demoClient) CertifyVEXStatement(ctx context.Context, filter *model.Cert if err != nil { return nil, err } - out, err = c.addVexIfMatch(ctx, out, filter, link) + v, err := c.vexIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, v) } } } return out, nil } -func (c *demoClient) addVexIfMatch(ctx context.Context, out []*model.CertifyVEXStatement, - filter *model.CertifyVEXStatementSpec, link *vexLink) ( - []*model.CertifyVEXStatement, error) { +func (c *demoClient) vexIfMatch(ctx context.Context, filter *model.CertifyVEXStatementSpec, link *vexLink) ( + *model.CertifyVEXStatement, error) { if filter != nil && filter.KnownSince != nil && !filter.KnownSince.Equal(link.KnownSince) { - return out, nil + return nil, nil } if filter != nil && filter.VexJustification != nil && *filter.VexJustification != link.Justification { - return out, nil + return nil, nil } if filter != nil && filter.Status != nil && *filter.Status != link.Status { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Statement, link.Statement) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.StatusNotes, link.StatusNotes) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Collector, link.Collector) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Origin, link.Origin) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } foundCertifyVex, err := c.buildCertifyVEXStatement(ctx, link, filter, false) @@ -321,9 +481,9 @@ func (c *demoClient) addVexIfMatch(ctx context.Context, out []*model.CertifyVEXS return nil, err } if foundCertifyVex == nil { - return out, nil + return nil, nil } - return append(out, foundCertifyVex), nil + return foundCertifyVex, nil } diff --git a/pkg/assembler/backends/keyvalue/certifyVuln.go b/pkg/assembler/backends/keyvalue/certifyVuln.go index 2ad332b842..bee158e343 100644 --- a/pkg/assembler/backends/keyvalue/certifyVuln.go +++ b/pkg/assembler/backends/keyvalue/certifyVuln.go @@ -18,8 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" "reflect" + "sort" "strings" "time" @@ -158,7 +158,171 @@ func (c *demoClient) ingestVulnerability(ctx context.Context, packageArg model.I // Query CertifyVuln func (c *demoClient) CertifyVulnList(ctx context.Context, certifyVulnSpec model.CertifyVulnSpec, after *string, first *int) (*model.CertifyVulnConnection, error) { - return nil, fmt.Errorf("not implemented: CertifyVulnList") + c.m.RLock() + defer c.m.RUnlock() + funcName := "CertifyVuln" + + if certifyVulnSpec.ID != nil { + link, err := byIDkv[*certifyVulnerabilityLink](ctx, *certifyVulnSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + foundCertifyVuln, err := c.buildCertifyVulnerability(ctx, link, &certifyVulnSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.CertifyVulnConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundCertifyVuln.ID), + EndCursor: ptrfrom.String(foundCertifyVuln.ID), + }, + Edges: []*model.CertifyVulnEdge{ + { + Cursor: foundCertifyVuln.ID, + Node: foundCertifyVuln, + }, + }, + }, nil + } + + edges := make([]*model.CertifyVulnEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + + if certifyVulnSpec.Package != nil { + pkgs, err := c.findPackageVersion(ctx, certifyVulnSpec.Package) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + foundOne = len(pkgs) > 0 + for _, pkg := range pkgs { + search = append(search, pkg.CertifyVulnLinks...) + } + } + + if !foundOne && certifyVulnSpec.Vulnerability != nil && + certifyVulnSpec.Vulnerability.NoVuln != nil && *certifyVulnSpec.Vulnerability.NoVuln { + exactVuln, err := c.exactVulnerability(ctx, &model.VulnerabilitySpec{ + Type: ptrfrom.String(noVulnType), + VulnerabilityID: ptrfrom.String(""), + }) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactVuln != nil { + search = append(search, exactVuln.CertifyVulnLinks...) + foundOne = true + } + } else if !foundOne && certifyVulnSpec.Vulnerability != nil { + if certifyVulnSpec.Vulnerability.NoVuln != nil && !*certifyVulnSpec.Vulnerability.NoVuln { + if certifyVulnSpec.Vulnerability.Type != nil && *certifyVulnSpec.Vulnerability.Type == noVulnType { + return nil, gqlerror.Errorf("novuln boolean set to false, cannot specify vulnerability type to be novuln") + } + } + exactVuln, err := c.exactVulnerability(ctx, certifyVulnSpec.Vulnerability) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactVuln != nil { + search = append(search, exactVuln.CertifyVulnLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*certifyVulnerabilityLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + cv, err := c.certifyVulnIfMatch(ctx, &certifyVulnSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.CertifyVulnEdge{ + Cursor: cv.ID, + Node: cv, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + var done bool + scn := c.kv.Keys(cVulnCol) + for !done { + var keys []string + var err error + keys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(keys) + totalCount = len(keys) + + for i, key := range keys { + link, err := byKeykv[*certifyVulnerabilityLink](ctx, cVulnCol, key, c) + if err != nil { + return nil, err + } + cv, err := c.certifyVulnIfMatch(ctx, &certifyVulnSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if cv.ID == *after { + totalCount = len(keys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyVulnEdge{ + Cursor: cv.ID, + Node: cv, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyVulnEdge{ + Cursor: cv.ID, + Node: cv, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.CertifyVulnConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) CertifyVuln(ctx context.Context, filter *model.CertifyVulnSpec) ([]*model.CertifyVuln, error) { @@ -210,7 +374,7 @@ func (c *demoClient) CertifyVuln(ctx context.Context, filter *model.CertifyVulnS } else if !foundOne && filter != nil && filter.Vulnerability != nil { if filter.Vulnerability.NoVuln != nil && !*filter.Vulnerability.NoVuln { if filter.Vulnerability.Type != nil && *filter.Vulnerability.Type == noVulnType { - return []*model.CertifyVuln{}, gqlerror.Errorf("novuln boolean set to false, cannot specify vulnerability type to be novuln") + return nil, gqlerror.Errorf("novuln boolean set to false, cannot specify vulnerability type to be novuln") } } exactVuln, err := c.exactVulnerability(ctx, filter.Vulnerability) @@ -230,10 +394,11 @@ func (c *demoClient) CertifyVuln(ctx context.Context, filter *model.CertifyVulnS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addCVIfMatch(ctx, out, filter, link) + cv, err := c.certifyVulnIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, cv) } } else { var done bool @@ -250,10 +415,11 @@ func (c *demoClient) CertifyVuln(ctx context.Context, filter *model.CertifyVulnS if err != nil { return nil, err } - out, err = c.addCVIfMatch(ctx, out, filter, link) + cv, err := c.certifyVulnIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, cv) } } } @@ -261,32 +427,31 @@ func (c *demoClient) CertifyVuln(ctx context.Context, filter *model.CertifyVulnS return out, nil } -func (c *demoClient) addCVIfMatch(ctx context.Context, out []*model.CertifyVuln, - filter *model.CertifyVulnSpec, - link *certifyVulnerabilityLink) ([]*model.CertifyVuln, error) { +func (c *demoClient) certifyVulnIfMatch(ctx context.Context, filter *model.CertifyVulnSpec, link *certifyVulnerabilityLink) ( + *model.CertifyVuln, error) { if filter != nil && filter.TimeScanned != nil && !filter.TimeScanned.Equal(link.TimeScanned) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DbURI, link.DBURI) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DbVersion, link.DBVersion) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.ScannerURI, link.ScannerURI) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.ScannerVersion, link.ScannerVersion) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Collector, link.Collector) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Origin, link.Origin) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } foundCertifyVuln, err := c.buildCertifyVulnerability(ctx, link, filter, false) @@ -294,9 +459,9 @@ func (c *demoClient) addCVIfMatch(ctx context.Context, out []*model.CertifyVuln, return nil, err } if foundCertifyVuln == nil || reflect.ValueOf(foundCertifyVuln.Vulnerability).IsNil() { - return out, nil + return nil, nil } - return append(out, foundCertifyVuln), nil + return foundCertifyVuln, nil } func (c *demoClient) buildCertifyVulnerability(ctx context.Context, link *certifyVulnerabilityLink, filter *model.CertifyVulnSpec, ingestOrIDProvided bool) (*model.CertifyVuln, error) { diff --git a/pkg/assembler/graphql/examples/certify_legal.gql b/pkg/assembler/graphql/examples/certify_legal.gql new file mode 100644 index 0000000000..c7a8386575 --- /dev/null +++ b/pkg/assembler/graphql/examples/certify_legal.gql @@ -0,0 +1,20 @@ +fragment AllCertifyLegalPaginationTree on CertifyLegalConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query CertifyLegalPagination { + CertifyLegalList(certifyLegalSpec: {}, first: 10) { + ...AllCertifyLegalPaginationTree + } +} \ No newline at end of file diff --git a/pkg/assembler/graphql/examples/certify_vex.gql b/pkg/assembler/graphql/examples/certify_vex.gql index c77413f026..ed20262d07 100644 --- a/pkg/assembler/graphql/examples/certify_vex.gql +++ b/pkg/assembler/graphql/examples/certify_vex.gql @@ -47,6 +47,27 @@ fragment allCertifyVEXStatementTree on CertifyVEXStatement { documentRef } +fragment AllVexPaginationTree on VEXConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query VexPagination { + CertifyVEXStatementList(certifyVEXStatementSpec: {}, first: 10) { + ...AllVexPaginationTree + } +} + query CertifyVEXStatementQ1 { CertifyVEXStatement(certifyVEXStatementSpec: {}) { ...allCertifyVEXStatementTree diff --git a/pkg/assembler/graphql/examples/certify_vuln.gql b/pkg/assembler/graphql/examples/certify_vuln.gql index 3b5cbe3e53..7cea3b0a0d 100644 --- a/pkg/assembler/graphql/examples/certify_vuln.gql +++ b/pkg/assembler/graphql/examples/certify_vuln.gql @@ -41,6 +41,27 @@ fragment allCertifyVulnTree on CertifyVuln { } } +fragment AllCertifyVulnPaginationTree on CertifyVulnConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query CertifyVulnPagination { + CertifyVulnList(certifyVulnSpec: {}, first: 10) { + ...AllCertifyVulnPaginationTree + } +} + query CertifyVulnQ1 { CertifyVuln(certifyVulnSpec: {}) { ...allCertifyVulnTree From e1abf5a9f74f70c2f89be3fe36ab5126c0378384 Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Wed, 1 May 2024 09:23:04 -0500 Subject: [PATCH 4/8] Included pagination for more commands Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- .../backends/keyvalue/hasMetadata.go | 180 +++++++++++++++-- pkg/assembler/backends/keyvalue/hasSBOM.go | 176 +++++++++++++++-- pkg/assembler/backends/keyvalue/hasSLSA.go | 170 +++++++++++++++- .../backends/keyvalue/hasSourceAt.go | 164 +++++++++++++-- pkg/assembler/backends/keyvalue/hashEqual.go | 156 ++++++++++++++- .../backends/keyvalue/isDependency.go | 158 ++++++++++++++- .../backends/keyvalue/isOccurrence.go | 187 ++++++++++++++++-- pkg/assembler/backends/keyvalue/license.go | 104 +++++++++- pkg/assembler/backends/keyvalue/pkg.go | 131 +++++++++++- pkg/assembler/backends/keyvalue/src.go | 129 +++++++++++- .../graphql/examples/has_metadata.gql | 20 ++ pkg/assembler/graphql/examples/has_sbom.gql | 21 ++ pkg/assembler/graphql/examples/has_slsa.gql | 21 ++ .../graphql/examples/has_source_at.gql | 21 ++ pkg/assembler/graphql/examples/hash_equal.gql | 21 ++ .../graphql/examples/is_dependency.gql | 21 ++ .../graphql/examples/is_occurence.gql | 21 ++ pkg/assembler/graphql/examples/license.gql | 20 ++ pkg/assembler/graphql/examples/packages.gql | 21 ++ pkg/assembler/graphql/examples/source.gql | 21 ++ 20 files changed, 1676 insertions(+), 87 deletions(-) create mode 100644 pkg/assembler/graphql/examples/has_metadata.gql create mode 100644 pkg/assembler/graphql/examples/license.gql diff --git a/pkg/assembler/backends/keyvalue/hasMetadata.go b/pkg/assembler/backends/keyvalue/hasMetadata.go index bf4c623219..efc5b40d06 100644 --- a/pkg/assembler/backends/keyvalue/hasMetadata.go +++ b/pkg/assembler/backends/keyvalue/hasMetadata.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "time" @@ -201,7 +202,154 @@ func (c *demoClient) ingestHasMetadata(ctx context.Context, subject model.Packag // Query HasMetadata func (c *demoClient) HasMetadataList(ctx context.Context, hasMetadataSpec model.HasMetadataSpec, after *string, first *int) (*model.HasMetadataConnection, error) { - return nil, fmt.Errorf("not implemented: HasMetadataList") + funcName := "HasMetadata" + + c.m.RLock() + defer c.m.RUnlock() + + if hasMetadataSpec.ID != nil { + link, err := byIDkv[*hasMetadataLink](ctx, *hasMetadataSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + found, err := c.buildHasMetadata(ctx, link, &hasMetadataSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.HasMetadataConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(found.ID), + EndCursor: ptrfrom.String(found.ID), + }, + Edges: []*model.HasMetadataEdge{ + { + Cursor: found.ID, + Node: found, + }, + }, + }, nil + } + + edges := make([]*model.HasMetadataEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + // Cant really search for an exact Pkg, as these can be linked to either + // names or versions, and version could be empty. + var search []string + foundOne := false + if hasMetadataSpec.Subject != nil && hasMetadataSpec.Subject.Artifact != nil { + exactArtifact, err := c.artifactExact(ctx, hasMetadataSpec.Subject.Artifact) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.HasMetadataLinks...) + foundOne = true + } + } + if !foundOne && hasMetadataSpec.Subject != nil && hasMetadataSpec.Subject.Source != nil { + exactSource, err := c.exactSource(ctx, hasMetadataSpec.Subject.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = append(search, exactSource.HasMetadataLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*hasMetadataLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + hm, err := c.hasMetadataIfMatch(ctx, &hasMetadataSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.HasMetadataEdge{ + Cursor: hm.ID, + Node: hm, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(hasMDCol) + for !done { + var hmKeys []string + var err error + hmKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(hmKeys) + totalCount = len(hmKeys) + + for i, hmKey := range hmKeys { + link, err := byKeykv[*hasMetadataLink](ctx, hasMDCol, hmKey, c) + if err != nil { + return nil, err + } + hm, err := c.hasMetadataIfMatch(ctx, &hasMetadataSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if hm.ID == *after { + totalCount = len(hmKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasMetadataEdge{ + Cursor: hm.ID, + Node: hm, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasMetadataEdge{ + Cursor: hm.ID, + Node: hm, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.HasMetadataConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) HasMetadata(ctx context.Context, filter *model.HasMetadataSpec) ([]*model.HasMetadata, error) { @@ -255,10 +403,11 @@ func (c *demoClient) HasMetadata(ctx context.Context, filter *model.HasMetadataS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addHMIfMatch(ctx, out, filter, link) + hm, err := c.hasMetadataIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, hm) } } else { var done bool @@ -275,40 +424,41 @@ func (c *demoClient) HasMetadata(ctx context.Context, filter *model.HasMetadataS if err != nil { return nil, err } - out, err = c.addHMIfMatch(ctx, out, filter, link) + hm, err := c.hasMetadataIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, hm) } } } return out, nil } -func (c *demoClient) addHMIfMatch(ctx context.Context, out []*model.HasMetadata, filter *model.HasMetadataSpec, link *hasMetadataLink) ( - []*model.HasMetadata, error) { +func (c *demoClient) hasMetadataIfMatch(ctx context.Context, filter *model.HasMetadataSpec, link *hasMetadataLink) ( + *model.HasMetadata, error) { if filter != nil && noMatch(filter.Justification, link.Justification) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Collector, link.Collector) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Origin, link.Origin) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Key, link.MDKey) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Value, link.Value) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } // no match if filter time since is after the timestamp if filter != nil && filter.Since != nil && filter.Since.After(link.Timestamp) { - return out, nil + return nil, nil } found, err := c.buildHasMetadata(ctx, link, filter, false) @@ -316,9 +466,9 @@ func (c *demoClient) addHMIfMatch(ctx context.Context, out []*model.HasMetadata, return nil, err } if found == nil { - return out, nil + return nil, nil } - return append(out, found), nil + return found, nil } func (c *demoClient) buildHasMetadata(ctx context.Context, link *hasMetadataLink, filter *model.HasMetadataSpec, ingestOrIDProvided bool) (*model.HasMetadata, error) { diff --git a/pkg/assembler/backends/keyvalue/hasSBOM.go b/pkg/assembler/backends/keyvalue/hasSBOM.go index 7228c6ef49..9f77570573 100644 --- a/pkg/assembler/backends/keyvalue/hasSBOM.go +++ b/pkg/assembler/backends/keyvalue/hasSBOM.go @@ -19,6 +19,8 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "time" @@ -322,7 +324,152 @@ func (c *demoClient) convHasSBOM(ctx context.Context, in *hasSBOMStruct) (*model // Query HasSBOM func (c *demoClient) HasSBOMList(ctx context.Context, hasSBOMSpec model.HasSBOMSpec, after *string, first *int) (*model.HasSBOMConnection, error) { - return nil, fmt.Errorf("not implemented: HasSBOMList") + funcName := "HasSBOM" + c.m.RLock() + defer c.m.RUnlock() + + if hasSBOMSpec.ID != nil { + link, err := byIDkv[*hasSBOMStruct](ctx, *hasSBOMSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + hs, err := c.convHasSBOM(ctx, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.HasSBOMConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(hs.ID), + EndCursor: ptrfrom.String(hs.ID), + }, + Edges: []*model.HasSBOMEdge{ + { + Cursor: hs.ID, + Node: hs, + }, + }, + }, nil + } + + edges := make([]*model.HasSBOMEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + if hasSBOMSpec.Subject != nil && hasSBOMSpec.Subject.Package != nil { + pkgs, err := c.findPackageVersion(ctx, hasSBOMSpec.Subject.Package) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + foundOne = len(pkgs) > 0 + for _, pkg := range pkgs { + search = append(search, pkg.HasSBOMs...) + } + } + if !foundOne && hasSBOMSpec.Subject != nil && hasSBOMSpec.Subject.Artifact != nil { + exactArt, err := c.artifactExact(ctx, hasSBOMSpec.Subject.Artifact) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArt != nil { + search = exactArt.HasSBOMs + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*hasSBOMStruct](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + hs, err := c.hasSBOMIfMatch(ctx, &hasSBOMSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.HasSBOMEdge{ + Cursor: hs.ID, + Node: hs, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(hasSBOMCol) + for !done { + var hsKeys []string + var err error + hsKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(hsKeys) + totalCount = len(hsKeys) + + for i, hsKey := range hsKeys { + link, err := byKeykv[*hasSBOMStruct](ctx, hasSBOMCol, hsKey, c) + if err != nil { + return nil, err + } + hs, err := c.hasSBOMIfMatch(ctx, &hasSBOMSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if hs.ID == *after { + totalCount = len(hsKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasSBOMEdge{ + Cursor: hs.ID, + Node: hs, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasSBOMEdge{ + Cursor: hs.ID, + Node: hs, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.HasSBOMConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) HasSBOM(ctx context.Context, filter *model.HasSBOMSpec) ([]*model.HasSbom, error) { @@ -374,10 +521,11 @@ func (c *demoClient) HasSBOM(ctx context.Context, filter *model.HasSBOMSpec) ([] if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addHasSBOMIfMatch(ctx, out, filter, link) + hs, err := c.hasSBOMIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, hs) } } else { var done bool @@ -394,10 +542,11 @@ func (c *demoClient) HasSBOM(ctx context.Context, filter *model.HasSBOMSpec) ([] if err != nil { return nil, err } - out, err = c.addHasSBOMIfMatch(ctx, out, filter, link) + hs, err := c.hasSBOMIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, hs) } } } @@ -405,9 +554,8 @@ func (c *demoClient) HasSBOM(ctx context.Context, filter *model.HasSBOMSpec) ([] return out, nil } -func (c *demoClient) addHasSBOMIfMatch(ctx context.Context, out []*model.HasSbom, - filter *model.HasSBOMSpec, link *hasSBOMStruct) ( - []*model.HasSbom, error) { +func (c *demoClient) hasSBOMIfMatch(ctx context.Context, filter *model.HasSBOMSpec, link *hasSBOMStruct) ( + *model.HasSbom, error) { if filter != nil { if noMatch(filter.URI, link.URI) || @@ -418,39 +566,39 @@ func (c *demoClient) addHasSBOMIfMatch(ctx context.Context, out []*model.HasSbom noMatch(filter.Collector, link.Collector) || noMatch(filter.DocumentRef, link.DocumentRef) || (filter.KnownSince != nil && filter.KnownSince.After(link.KnownSince)) { - return out, nil + return nil, nil } // collect packages and artifacts from included software pkgs, artifacts, err := c.getPackageVersionAndArtifacts(ctx, link.IncludedSoftware) if err != nil { - return out, err + return nil, err } pkgFilters, artFilters := helper.GetPackageAndArtifactFilters(filter.IncludedSoftware) if !c.matchPackages(ctx, pkgFilters, pkgs) || !c.matchArtifacts(ctx, artFilters, artifacts) || !c.matchDependencies(ctx, filter.IncludedDependencies, link.IncludedDependencies) || !c.matchOccurrences(ctx, filter.IncludedOccurrences, link.IncludedOccurrences) { - return out, nil + return nil, nil } if filter.Subject != nil { if filter.Subject.Package != nil { if link.Pkg == "" { - return out, nil + return nil, nil } p, err := c.buildPackageResponse(ctx, link.Pkg, filter.Subject.Package) if err != nil { return nil, err } if p == nil { - return out, nil + return nil, nil } } else if filter.Subject.Artifact != nil { if link.Artifact == "" { - return out, nil + return nil, nil } if !c.artifactMatch(ctx, link.Artifact, filter.Subject.Artifact) { - return out, nil + return nil, nil } } } @@ -459,5 +607,5 @@ func (c *demoClient) addHasSBOMIfMatch(ctx context.Context, out []*model.HasSbom if err != nil { return nil, err } - return append(out, sb), nil + return sb, nil } diff --git a/pkg/assembler/backends/keyvalue/hasSLSA.go b/pkg/assembler/backends/keyvalue/hasSLSA.go index b7e6a679f6..0dbbb1ca13 100644 --- a/pkg/assembler/backends/keyvalue/hasSLSA.go +++ b/pkg/assembler/backends/keyvalue/hasSLSA.go @@ -19,6 +19,7 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "slices" "sort" "strings" @@ -92,7 +93,159 @@ func (n *hasSLSAStruct) BuildModelNode(ctx context.Context, c *demoClient) (mode // Query HasSlsa func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSASpec, after *string, first *int) (*model.HasSLSAConnection, error) { - return nil, fmt.Errorf("not implemented: HasSLSAList") + funcName := "HasSlsa" + c.m.RLock() + defer c.m.RUnlock() + if hasSLSASpec.ID != nil { + link, err := byIDkv[*hasSLSAStruct](ctx, *hasSLSASpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + hs, err := c.convSLSA(ctx, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.HasSLSAConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(hs.ID), + EndCursor: ptrfrom.String(hs.ID), + }, + Edges: []*model.HasSLSAEdge{ + { + Cursor: hs.ID, + Node: hs, + }, + }, + }, nil + } + + edges := make([]*model.HasSLSAEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + var arts []*model.ArtifactSpec + arts = append(arts, hasSLSASpec.Subject) + arts = append(arts, hasSLSASpec.BuiltFrom...) + + for _, a := range arts { + if !foundOne && a != nil { + exactArtifact, err := c.artifactExact(ctx, a) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.HasSLSAs...) + foundOne = true + break + } + } + } + if !foundOne && hasSLSASpec.BuiltBy != nil { + exactBuilder, err := c.exactBuilder(ctx, hasSLSASpec.BuiltBy) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactBuilder != nil { + search = append(search, exactBuilder.HasSLSAs...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*hasSLSAStruct](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + hs, err := c.addSLSAIfMatch(ctx, &hasSLSASpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.HasSLSAEdge{ + Cursor: hs.ID, + Node: hs, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(slsaCol) + for !done { + var slsaKeys []string + var err error + slsaKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(slsaKeys) + totalCount = len(slsaKeys) + + for i, slsak := range slsaKeys { + link, err := byKeykv[*hasSLSAStruct](ctx, slsaCol, slsak, c) + if err != nil { + return nil, err + } + hs, err := c.addSLSAIfMatch(ctx, &hasSLSASpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if hs.ID == *after { + totalCount = len(slsaKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasSLSAEdge{ + Cursor: hs.ID, + Node: hs, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasSLSAEdge{ + Cursor: hs.ID, + Node: hs, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.HasSLSAConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) HasSlsa(ctx context.Context, filter *model.HasSLSASpec) ([]*model.HasSlsa, error) { @@ -151,10 +304,11 @@ func (c *demoClient) HasSlsa(ctx context.Context, filter *model.HasSLSASpec) ([] if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addSLSAIfMatch(ctx, out, filter, link) + hs, err := c.addSLSAIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, hs) } } else { var done bool @@ -171,10 +325,11 @@ func (c *demoClient) HasSlsa(ctx context.Context, filter *model.HasSLSASpec) ([] if err != nil { return nil, err } - out, err = c.addSLSAIfMatch(ctx, out, filter, link) + hs, err := c.addSLSAIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, hs) } } } @@ -351,9 +506,8 @@ func (c *demoClient) convSLSA(ctx context.Context, in *hasSLSAStruct) (*model.Ha }, nil } -func (c *demoClient) addSLSAIfMatch(ctx context.Context, out []*model.HasSlsa, - filter *model.HasSLSASpec, link *hasSLSAStruct) ( - []*model.HasSlsa, error, +func (c *demoClient) addSLSAIfMatch(ctx context.Context, filter *model.HasSLSASpec, link *hasSLSAStruct) ( + *model.HasSlsa, error, ) { bb, err := byIDkv[*builderStruct](ctx, link.BuiltBy, c) if err != nil { @@ -371,11 +525,11 @@ func (c *demoClient) addSLSAIfMatch(ctx context.Context, out []*model.HasSlsa, !matchSLSAPreds(link.Predicates, filter.Predicate) || !c.matchArtifacts(ctx, []*model.ArtifactSpec{filter.Subject}, []string{link.Subject}) || !c.matchArtifacts(ctx, filter.BuiltFrom, link.BuiltFrom) { - return out, nil + return nil, nil } hs, err := c.convSLSA(ctx, link) if err != nil { return nil, err } - return append(out, hs), nil + return hs, nil } diff --git a/pkg/assembler/backends/keyvalue/hasSourceAt.go b/pkg/assembler/backends/keyvalue/hasSourceAt.go index bc687da60e..16c4a6f4e6 100644 --- a/pkg/assembler/backends/keyvalue/hasSourceAt.go +++ b/pkg/assembler/backends/keyvalue/hasSourceAt.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "time" @@ -151,7 +152,141 @@ func (c *demoClient) ingestHasSourceAt(ctx context.Context, packageArg model.IDo // Query HasSourceAt func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model.HasSourceAtSpec, after *string, first *int) (*model.HasSourceAtConnection, error) { - return nil, fmt.Errorf("not implemented: HasSourceAtList") + c.m.RLock() + defer c.m.RUnlock() + funcName := "HasSourceAt" + if hasSourceAtSpec.ID != nil { + link, err := byIDkv[*srcMapLink](ctx, *hasSourceAtSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + foundHasSourceAt, err := c.buildHasSourceAt(ctx, link, &hasSourceAtSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.HasSourceAtConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundHasSourceAt.ID), + EndCursor: ptrfrom.String(foundHasSourceAt.ID), + }, + Edges: []*model.HasSourceAtEdge{ + { + Cursor: foundHasSourceAt.ID, + Node: foundHasSourceAt, + }, + }, + }, nil + } + + edges := make([]*model.HasSourceAtEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + // Cant really search for an exact Pkg, as these can be linked to either + // names or versions, only search Source backedges. + var search []string + foundOne := false + if hasSourceAtSpec.Source != nil { + exactSource, err := c.exactSource(ctx, hasSourceAtSpec.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = append(search, exactSource.SrcMapLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*srcMapLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + src, err := c.srcIfMatch(ctx, &hasSourceAtSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.HasSourceAtEdge{ + Cursor: src.ID, + Node: src, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + var done bool + scn := c.kv.Keys(hsaCol) + for !done { + var hsaKeys []string + var err error + hsaKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(hsaKeys) + totalCount = len(hsaKeys) + + for i, hsak := range hsaKeys { + link, err := byKeykv[*srcMapLink](ctx, hsaCol, hsak, c) + if err != nil { + return nil, err + } + src, err := c.srcIfMatch(ctx, &hasSourceAtSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if src.ID == *after { + totalCount = len(hsaKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasSourceAtEdge{ + Cursor: src.ID, + Node: src, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasSourceAtEdge{ + Cursor: src.ID, + Node: src, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.HasSourceAtConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) HasSourceAt(ctx context.Context, filter *model.HasSourceAtSpec) ([]*model.HasSourceAt, error) { @@ -193,10 +328,11 @@ func (c *demoClient) HasSourceAt(ctx context.Context, filter *model.HasSourceAtS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addSrcIfMatch(ctx, out, filter, link) + src, err := c.srcIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, src) } } else { var done bool @@ -213,10 +349,11 @@ func (c *demoClient) HasSourceAt(ctx context.Context, filter *model.HasSourceAtS if err != nil { return nil, err } - out, err = c.addSrcIfMatch(ctx, out, filter, link) + src, err := c.srcIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, src) } } } @@ -273,30 +410,29 @@ func (c *demoClient) buildHasSourceAt(ctx context.Context, link *srcMapLink, fil return &newHSA, nil } -func (c *demoClient) addSrcIfMatch(ctx context.Context, out []*model.HasSourceAt, - filter *model.HasSourceAtSpec, link *srcMapLink) ( - []*model.HasSourceAt, error) { +func (c *demoClient) srcIfMatch(ctx context.Context, filter *model.HasSourceAtSpec, link *srcMapLink) ( + *model.HasSourceAt, error) { if filter != nil && noMatch(filter.Justification, link.Justification) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Origin, link.Origin) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Collector, link.Collector) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } if filter != nil && filter.KnownSince != nil && !filter.KnownSince.Equal(link.KnownSince) { - return out, nil + return nil, nil } foundHasSourceAt, err := c.buildHasSourceAt(ctx, link, filter, false) if err != nil { return nil, err } if foundHasSourceAt == nil { - return out, nil + return nil, nil } - return append(out, foundHasSourceAt), nil + return foundHasSourceAt, nil } diff --git a/pkg/assembler/backends/keyvalue/hashEqual.go b/pkg/assembler/backends/keyvalue/hashEqual.go index 76a09a97d6..6a664743ea 100644 --- a/pkg/assembler/backends/keyvalue/hashEqual.go +++ b/pkg/assembler/backends/keyvalue/hashEqual.go @@ -19,7 +19,9 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "slices" + "sort" "strings" "github.com/guacsec/guac/pkg/assembler/graphql/model" @@ -184,7 +186,144 @@ func (c *demoClient) matchArtifacts(ctx context.Context, filter []*model.Artifac // Query HashEqual func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.HashEqualSpec, after *string, first *int) (*model.HashEqualConnection, error) { - return nil, fmt.Errorf("not implemented: HashEqualList") + funcName := "HashEqual" + c.m.RLock() + defer c.m.RUnlock() + if hashEqualSpec.ID != nil { + link, err := byIDkv[*hashEqualStruct](ctx, *hashEqualSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + he, err := c.convHashEqual(ctx, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.HashEqualConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(he.ID), + EndCursor: ptrfrom.String(he.ID), + }, + Edges: []*model.HashEqualEdge{ + { + Cursor: he.ID, + Node: he, + }, + }, + }, nil + } + + edges := make([]*model.HashEqualEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + for _, a := range hashEqualSpec.Artifacts { + if !foundOne && a != nil { + exactArtifact, err := c.artifactExact(ctx, a) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.HashEquals...) + foundOne = true + break + } + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*hashEqualStruct](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + he, err := c.hashEqualsIfMatch(ctx, &hashEqualSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.HashEqualEdge{ + Cursor: he.ID, + Node: he, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(hashEqCol) + for !done { + var heKeys []string + var err error + heKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(heKeys) + totalCount = len(heKeys) + + for i, hek := range heKeys { + link, err := byKeykv[*hashEqualStruct](ctx, hashEqCol, hek, c) + if err != nil { + return nil, err + } + he, err := c.hashEqualsIfMatch(ctx, &hashEqualSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if he.ID == *after { + totalCount = len(heKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HashEqualEdge{ + Cursor: he.ID, + Node: he, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HashEqualEdge{ + Cursor: he.ID, + Node: he, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.HashEqualConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) HashEqual(ctx context.Context, filter *model.HashEqualSpec) ([]*model.HashEqual, error) { @@ -228,10 +367,11 @@ func (c *demoClient) HashEqual(ctx context.Context, filter *model.HashEqualSpec) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addHEIfMatch(ctx, out, filter, link) + he, err := c.hashEqualsIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, he) } } else { var done bool @@ -248,10 +388,11 @@ func (c *demoClient) HashEqual(ctx context.Context, filter *model.HashEqualSpec) if err != nil { return nil, err } - out, err = c.addHEIfMatch(ctx, out, filter, link) + he, err := c.hashEqualsIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, he) } } } @@ -277,20 +418,19 @@ func (c *demoClient) convHashEqual(ctx context.Context, h *hashEqualStruct) (*mo }, nil } -func (c *demoClient) addHEIfMatch(ctx context.Context, out []*model.HashEqual, - filter *model.HashEqualSpec, link *hashEqualStruct) ( - []*model.HashEqual, error, +func (c *demoClient) hashEqualsIfMatch(ctx context.Context, filter *model.HashEqualSpec, link *hashEqualStruct) ( + *model.HashEqual, error, ) { if noMatch(filter.Justification, link.Justification) || noMatch(filter.Origin, link.Origin) || noMatch(filter.Collector, link.Collector) || noMatch(filter.DocumentRef, link.DocumentRef) || !c.matchArtifacts(ctx, filter.Artifacts, link.Artifacts) { - return out, nil + return nil, nil } he, err := c.convHashEqual(ctx, link) if err != nil { return nil, err } - return append(out, he), nil + return he, nil } diff --git a/pkg/assembler/backends/keyvalue/isDependency.go b/pkg/assembler/backends/keyvalue/isDependency.go index e7f5909ae1..71c245b6e0 100644 --- a/pkg/assembler/backends/keyvalue/isDependency.go +++ b/pkg/assembler/backends/keyvalue/isDependency.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "github.com/vektah/gqlparser/v2/gqlerror" @@ -152,7 +153,143 @@ func (c *demoClient) ingestDependency(ctx context.Context, packageArg model.IDor // Query IsDependency func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec model.IsDependencySpec, after *string, first *int) (*model.IsDependencyConnection, error) { - return nil, fmt.Errorf("not implemented: IsDependencyList") + c.m.RLock() + defer c.m.RUnlock() + funcName := "IsDependency" + + if isDependencySpec.ID != nil { + link, err := byIDkv[*isDependencyLink](ctx, *isDependencySpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + foundIsDependency, err := c.buildIsDependency(ctx, link, &isDependencySpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.IsDependencyConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundIsDependency.ID), + EndCursor: ptrfrom.String(foundIsDependency.ID), + }, + Edges: []*model.IsDependencyEdge{ + { + Cursor: foundIsDependency.ID, + Node: foundIsDependency, + }, + }, + }, nil + } + + edges := make([]*model.IsDependencyEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + if isDependencySpec.Package != nil { + pkgs, err := c.findPackageVersion(ctx, isDependencySpec.Package) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + foundOne = len(pkgs) > 0 + for _, pkg := range pkgs { + search = append(search, pkg.IsDependencyLinks...) + } + } + // Dont search on DependencyPackage as it can be either package-name or package-version + + if foundOne { + for _, id := range search { + link, err := byIDkv[*isDependencyLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + dep, err := c.depIfMatch(ctx, &isDependencySpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.IsDependencyEdge{ + Cursor: dep.ID, + Node: dep, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(isDepCol) + for !done { + var depKeys []string + var err error + depKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(depKeys) + totalCount = len(depKeys) + + for i, depKey := range depKeys { + link, err := byKeykv[*isDependencyLink](ctx, isDepCol, depKey, c) + if err != nil { + return nil, err + } + dep, err := c.depIfMatch(ctx, &isDependencySpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if dep.ID == *after { + totalCount = len(depKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.IsDependencyEdge{ + Cursor: dep.ID, + Node: dep, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.IsDependencyEdge{ + Cursor: dep.ID, + Node: dep, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.IsDependencyConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) IsDependency(ctx context.Context, filter *model.IsDependencySpec) ([]*model.IsDependency, error) { @@ -194,10 +331,11 @@ func (c *demoClient) IsDependency(ctx context.Context, filter *model.IsDependenc if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addDepIfMatch(ctx, out, filter, link) + dep, err := c.depIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, dep) } } else { var done bool @@ -214,10 +352,11 @@ func (c *demoClient) IsDependency(ctx context.Context, filter *model.IsDependenc if err != nil { return nil, err } - out, err = c.addDepIfMatch(ctx, out, filter, link) + dep, err := c.depIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, dep) } } } @@ -273,11 +412,10 @@ func (c *demoClient) buildIsDependency(ctx context.Context, link *isDependencyLi return &foundIsDependency, nil } -func (c *demoClient) addDepIfMatch(ctx context.Context, out []*model.IsDependency, - filter *model.IsDependencySpec, link *isDependencyLink) ( - []*model.IsDependency, error) { +func (c *demoClient) depIfMatch(ctx context.Context, filter *model.IsDependencySpec, link *isDependencyLink) ( + *model.IsDependency, error) { if noMatchIsDep(filter, link) { - return out, nil + return nil, nil } foundIsDependency, err := c.buildIsDependency(ctx, link, filter, false) @@ -285,9 +423,9 @@ func (c *demoClient) addDepIfMatch(ctx context.Context, out []*model.IsDependenc return nil, err } if foundIsDependency == nil { - return out, nil + return nil, nil } - return append(out, foundIsDependency), nil + return foundIsDependency, nil } func noMatchIsDep(filter *model.IsDependencySpec, link *isDependencyLink) bool { diff --git a/pkg/assembler/backends/keyvalue/isOccurrence.go b/pkg/assembler/backends/keyvalue/isOccurrence.go index 551f1e04d7..a8800f1b98 100644 --- a/pkg/assembler/backends/keyvalue/isOccurrence.go +++ b/pkg/assembler/backends/keyvalue/isOccurrence.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "github.com/vektah/gqlparser/v2/gqlerror" @@ -237,7 +238,164 @@ func (c *demoClient) artifactMatch(ctx context.Context, aID string, artifactSpec // Query IsOccurrence func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec model.IsOccurrenceSpec, after *string, first *int) (*model.IsOccurrenceConnection, error) { - return nil, fmt.Errorf("not implemented: IsOccurrenceList") + funcName := "IsOccurrence" + + c.m.RLock() + defer c.m.RUnlock() + + if isOccurrenceSpec.ID != nil { + link, err := byIDkv[*isOccurrenceStruct](ctx, *isOccurrenceSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + o, err := c.convOccurrence(ctx, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.IsOccurrenceConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(o.ID), + EndCursor: ptrfrom.String(o.ID), + }, + Edges: []*model.IsOccurrenceEdge{ + { + Cursor: o.ID, + Node: o, + }, + }, + }, nil + } + + edges := make([]*model.IsOccurrenceEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + if isOccurrenceSpec.Artifact != nil { + exactArtifact, err := c.artifactExact(ctx, isOccurrenceSpec.Artifact) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.Occurrences...) + foundOne = true + } + } + if !foundOne && isOccurrenceSpec.Subject != nil && isOccurrenceSpec.Subject.Package != nil { + pkgs, err := c.findPackageVersion(ctx, isOccurrenceSpec.Subject.Package) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + foundOne = len(pkgs) > 0 + for _, pkg := range pkgs { + search = append(search, pkg.Occurrences...) + } + } + if !foundOne && isOccurrenceSpec.Subject != nil && isOccurrenceSpec.Subject.Source != nil { + exactSource, err := c.exactSource(ctx, isOccurrenceSpec.Subject.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = append(search, exactSource.Occurrences...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*isOccurrenceStruct](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + occ, err := c.occIfMatch(ctx, &isOccurrenceSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.IsOccurrenceEdge{ + Cursor: occ.ID, + Node: occ, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(occCol) + for !done { + var occKeys []string + var err error + occKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(occKeys) + totalCount = len(occKeys) + + for i, ok := range occKeys { + link, err := byKeykv[*isOccurrenceStruct](ctx, occCol, ok, c) + if err != nil { + return nil, err + } + occ, err := c.occIfMatch(ctx, &isOccurrenceSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if occ.ID == *after { + totalCount = len(occKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.IsOccurrenceEdge{ + Cursor: occ.ID, + Node: occ, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.IsOccurrenceEdge{ + Cursor: occ.ID, + Node: occ, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.IsOccurrenceConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) IsOccurrence(ctx context.Context, filter *model.IsOccurrenceSpec) ([]*model.IsOccurrence, error) { @@ -300,10 +458,11 @@ func (c *demoClient) IsOccurrence(ctx context.Context, filter *model.IsOccurrenc if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addOccIfMatch(ctx, out, filter, link) + occ, err := c.occIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, occ) } } else { var done bool @@ -320,51 +479,51 @@ func (c *demoClient) IsOccurrence(ctx context.Context, filter *model.IsOccurrenc if err != nil { return nil, err } - out, err = c.addOccIfMatch(ctx, out, filter, link) + occ, err := c.occIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, occ) } } } return out, nil } -func (c *demoClient) addOccIfMatch(ctx context.Context, out []*model.IsOccurrence, - filter *model.IsOccurrenceSpec, link *isOccurrenceStruct) ( - []*model.IsOccurrence, error) { +func (c *demoClient) occIfMatch(ctx context.Context, filter *model.IsOccurrenceSpec, link *isOccurrenceStruct) ( + *model.IsOccurrence, error) { if noMatch(filter.Justification, link.Justification) || noMatch(filter.Origin, link.Origin) || noMatch(filter.Collector, link.Collector) || noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } if filter.Artifact != nil && !c.artifactMatch(ctx, link.Artifact, filter.Artifact) { - return out, nil + return nil, nil } if filter.Subject != nil { if filter.Subject.Package != nil { if link.Pkg == "" { - return out, nil + return nil, nil } p, err := c.buildPackageResponse(ctx, link.Pkg, filter.Subject.Package) if err != nil { return nil, err } if p == nil { - return out, nil + return nil, nil } } else if filter.Subject.Source != nil { if link.Source == "" { - return out, nil + return nil, nil } s, err := c.buildSourceResponse(ctx, link.Source, filter.Subject.Source) if err != nil { return nil, err } if s == nil { - return out, nil + return nil, nil } } } @@ -372,7 +531,7 @@ func (c *demoClient) addOccIfMatch(ctx context.Context, out []*model.IsOccurrenc if err != nil { return nil, err } - return append(out, o), nil + return o, nil } func (c *demoClient) matchOccurrences(ctx context.Context, filters []*model.IsOccurrenceSpec, occLinkIDs []string) bool { diff --git a/pkg/assembler/backends/keyvalue/license.go b/pkg/assembler/backends/keyvalue/license.go index 8d6b197cf1..5430694cf3 100644 --- a/pkg/assembler/backends/keyvalue/license.go +++ b/pkg/assembler/backends/keyvalue/license.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "github.com/vektah/gqlparser/v2/gqlerror" @@ -159,7 +160,106 @@ func (c *demoClient) licenseExact(ctx context.Context, licenseSpec *model.Licens // Query Licenses func (c *demoClient) LicenseList(ctx context.Context, licenseSpec model.LicenseSpec, after *string, first *int) (*model.LicenseConnection, error) { - return nil, fmt.Errorf("not implemented: LicenseList") + c.m.RLock() + defer c.m.RUnlock() + a, err := c.licenseExact(ctx, &licenseSpec) + if err != nil { + return nil, gqlerror.Errorf("Licenses :: invalid spec %s", err) + } + if a != nil { + license := c.convLicense(a) + return &model.LicenseConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(license.ID), + EndCursor: ptrfrom.String(license.ID), + }, + Edges: []*model.LicenseEdge{ + { + Cursor: license.ID, + Node: license, + }, + }, + }, nil + } + + edges := make([]*model.LicenseEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var done bool + scn := c.kv.Keys(licenseCol) + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + for !done { + var lKeys []string + lKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(lKeys) + totalCount = len(lKeys) + + for i, lk := range lKeys { + l, err := byKeykv[*licStruct](ctx, licenseCol, lk, c) + if err != nil { + return nil, err + } + if noMatch(licenseSpec.Name, l.Name) || + noMatch(licenseSpec.ListVersion, l.ListVersion) || + noMatch(licenseSpec.Inline, l.Inline) { + continue + } + + license := c.convLicense(l) + + if after != nil && !currentPage { + if license.ID == *after { + totalCount = len(lKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.LicenseEdge{ + Cursor: license.ID, + Node: license, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.LicenseEdge{ + Cursor: license.ID, + Node: license, + }) + } + } + } + + if len(edges) != 0 { + return &model.LicenseConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) Licenses(ctx context.Context, licenseSpec *model.LicenseSpec) ([]*model.License, error) { diff --git a/pkg/assembler/backends/keyvalue/pkg.go b/pkg/assembler/backends/keyvalue/pkg.go index ea126fe06f..25cd5ba9e3 100644 --- a/pkg/assembler/backends/keyvalue/pkg.go +++ b/pkg/assembler/backends/keyvalue/pkg.go @@ -19,8 +19,10 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "reflect" "slices" + "sort" "strings" "github.com/vektah/gqlparser/v2/gqlerror" @@ -523,7 +525,134 @@ func hashVersionHelper(version string, subpath string, qualifiers map[string]str // Query Package func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, after *string, first *int) (*model.PackageConnection, error) { - return nil, fmt.Errorf("not implemented: PackagesList") + c.m.RLock() + defer c.m.RUnlock() + if pkgSpec.ID != nil { + p, err := c.buildPackageResponse(ctx, *pkgSpec.ID, &pkgSpec) + if err != nil { + if errors.Is(err, errNotFound) { + // not found + return nil, nil + } + return nil, err + } + + return &model.PackageConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(p.ID), + EndCursor: ptrfrom.String(p.ID), + }, + Edges: []*model.PackageEdge{ + { + Cursor: p.ID, + Node: p, + }, + }, + }, nil + } + + edges := make([]*model.PackageEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + if pkgSpec.Type != nil { + inType := &pkgType{ + Type: *pkgSpec.Type, + } + pkgTypeNode, err := byKeykv[*pkgType](ctx, pkgTypeCol, inType.Key(), c) + if err == nil { + pNamespaces := c.buildPkgNamespace(ctx, pkgTypeNode, &pkgSpec) + if len(pNamespaces) > 0 { + edges = append(edges, &model.PackageEdge{ + Cursor: pNamespaces[0].Names[0].Versions[0].ID, + Node: &model.Package{ + ID: pkgTypeNode.ThisID, + Type: pkgTypeNode.Type, + Namespaces: pNamespaces, + }, + }) + } + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(pkgTypeCol) + for !done { + var typeKeys []string + var err error + typeKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(typeKeys) + totalCount = len(typeKeys) + + for i, tk := range typeKeys { + pkgTypeNode, err := byKeykv[*pkgType](ctx, pkgTypeCol, tk, c) + if err != nil { + return nil, err + } + pNamespaces := c.buildPkgNamespace(ctx, pkgTypeNode, &pkgSpec) + if len(pNamespaces) == 0 { + continue + } + + p := &model.Package{ + ID: pkgTypeNode.ThisID, + Type: pkgTypeNode.Type, + Namespaces: pNamespaces, + } + + if after != nil && !currentPage { + if p.Namespaces[0].Names[0].Versions[0].ID == *after { + totalCount = len(typeKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.PackageEdge{ + Cursor: p.Namespaces[0].Names[0].Versions[0].ID, + Node: p, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.PackageEdge{ + Cursor: p.Namespaces[0].Names[0].Versions[0].ID, + Node: p, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.PackageConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) Packages(ctx context.Context, filter *model.PkgSpec) ([]*model.Package, error) { diff --git a/pkg/assembler/backends/keyvalue/src.go b/pkg/assembler/backends/keyvalue/src.go index caa2d011d5..dd0cb4f200 100644 --- a/pkg/assembler/backends/keyvalue/src.go +++ b/pkg/assembler/backends/keyvalue/src.go @@ -19,6 +19,8 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "github.com/vektah/gqlparser/v2/gqlerror" @@ -312,7 +314,132 @@ func (c *demoClient) IngestSource(ctx context.Context, input model.IDorSourceInp // Query Source func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpec, after *string, first *int) (*model.SourceConnection, error) { - return nil, fmt.Errorf("not implemented: SourcesList") + c.m.RLock() + defer c.m.RUnlock() + if sourceSpec.ID != nil { + s, err := c.buildSourceResponse(ctx, *sourceSpec.ID, &sourceSpec) + if err != nil { + if errors.Is(err, errNotFound) { + // not found + return nil, nil + } + return nil, err + } + + return &model.SourceConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(s.ID), + EndCursor: ptrfrom.String(s.ID), + }, + Edges: []*model.SourceEdge{ + { + Cursor: s.ID, + Node: s, + }, + }, + }, nil + } + + edges := make([]*model.SourceEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + if sourceSpec.Type != nil { + inType := &srcType{ + Type: *sourceSpec.Type, + } + srcTypeNode, err := byKeykv[*srcType](ctx, srcTypeCol, inType.Key(), c) + if err == nil { + sNamespaces := c.buildSourceNamespace(ctx, srcTypeNode, &sourceSpec) + if len(sNamespaces) > 0 { + edges = append(edges, &model.SourceEdge{ + Cursor: sNamespaces[0].Names[0].ID, + Node: &model.Source{ + ID: srcTypeNode.ThisID, + Type: srcTypeNode.Type, + Namespaces: sNamespaces, + }, + }) + } + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(srcTypeCol) + for !done { + var typeKeys []string + var err error + typeKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(typeKeys) + totalCount = len(typeKeys) + + for i, tk := range typeKeys { + srcTypeNode, err := byKeykv[*srcType](ctx, srcTypeCol, tk, c) + if err != nil { + return nil, err + } + sNamespaces := c.buildSourceNamespace(ctx, srcTypeNode, &sourceSpec) + if len(sNamespaces) > 0 { + s := &model.Source{ + ID: srcTypeNode.ThisID, + Type: srcTypeNode.Type, + Namespaces: sNamespaces, + } + + if after != nil && !currentPage { + if s.Namespaces[0].Names[0].ID == *after { + totalCount = len(typeKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.SourceEdge{ + Cursor: s.Namespaces[0].Names[0].ID, + Node: s, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.SourceEdge{ + Cursor: s.Namespaces[0].Names[0].ID, + Node: s, + }) + } + } + } + } + } + + if len(edges) != 0 { + return &model.SourceConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) Sources(ctx context.Context, filter *model.SourceSpec) ([]*model.Source, error) { diff --git a/pkg/assembler/graphql/examples/has_metadata.gql b/pkg/assembler/graphql/examples/has_metadata.gql new file mode 100644 index 0000000000..ae4d48c6e3 --- /dev/null +++ b/pkg/assembler/graphql/examples/has_metadata.gql @@ -0,0 +1,20 @@ +fragment AllHasMetadataPaginationTree on HasMetadataConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query HasMetadataPagination { + HasMetadataList(hasMetadataSpec: {}, first: 10) { + ...AllHasMetadataPaginationTree + } +} \ No newline at end of file diff --git a/pkg/assembler/graphql/examples/has_sbom.gql b/pkg/assembler/graphql/examples/has_sbom.gql index 053782fc28..a7b1614533 100644 --- a/pkg/assembler/graphql/examples/has_sbom.gql +++ b/pkg/assembler/graphql/examples/has_sbom.gql @@ -39,6 +39,27 @@ fragment allHasSBOMTree on HasSBOM { documentRef } +fragment AllHasSBOMPaginationTree on HasSBOMConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query HasSBOMPagination { + HasSBOMList(hasSBOMSpec: {}, first: 10) { + ...AllHasSBOMPaginationTree + } +} + query HasSBOMQ1 { HasSBOM(hasSBOMSpec: {}) { ...allHasSBOMTree diff --git a/pkg/assembler/graphql/examples/has_slsa.gql b/pkg/assembler/graphql/examples/has_slsa.gql index 4767b06597..fb6c9e03c7 100644 --- a/pkg/assembler/graphql/examples/has_slsa.gql +++ b/pkg/assembler/graphql/examples/has_slsa.gql @@ -29,6 +29,27 @@ fragment allHasSLSATree on HasSLSA { } } +fragment AllHasSLSAPaginationTree on HasSLSAConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query HasSLSAPagination { + HasSLSAList(hasSLSASpec: {}, first: 10) { + ...AllHasSLSAPaginationTree + } +} + query SLSAQ1 { HasSLSA(hasSLSASpec: {}) { ...allHasSLSATree diff --git a/pkg/assembler/graphql/examples/has_source_at.gql b/pkg/assembler/graphql/examples/has_source_at.gql index 0859502426..22bfa73926 100644 --- a/pkg/assembler/graphql/examples/has_source_at.gql +++ b/pkg/assembler/graphql/examples/has_source_at.gql @@ -42,6 +42,27 @@ fragment allHasSourceAtTree on HasSourceAt { documentRef } +fragment AllHasSourceAtPaginationTree on HasSourceAtConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query HasSourceAtPagination { + HasSourceAtList(hasSourceAtSpec: {}, first: 10) { + ...AllHasSourceAtPaginationTree + } +} + query HasSourceAtQ1 { HasSourceAt(hasSourceAtSpec: {}) { ...allHasSourceAtTree diff --git a/pkg/assembler/graphql/examples/hash_equal.gql b/pkg/assembler/graphql/examples/hash_equal.gql index 07d90f8dd7..ddf87d9074 100644 --- a/pkg/assembler/graphql/examples/hash_equal.gql +++ b/pkg/assembler/graphql/examples/hash_equal.gql @@ -11,6 +11,27 @@ fragment allHashEqualTree on HashEqual { documentRef } +fragment AllHashEqualsPaginationTree on HashEqualConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query HashEqualsPagination { + HashEqualList(hashEqualSpec: {}, first: 10) { + ...AllHashEqualsPaginationTree + } +} + query HashEqualQ1 { HashEqual(hashEqualSpec: {}) { ...allHashEqualTree diff --git a/pkg/assembler/graphql/examples/is_dependency.gql b/pkg/assembler/graphql/examples/is_dependency.gql index 4f2671c278..b91199bb5d 100644 --- a/pkg/assembler/graphql/examples/is_dependency.gql +++ b/pkg/assembler/graphql/examples/is_dependency.gql @@ -50,6 +50,27 @@ fragment allIsDependencyTree on IsDependency { documentRef } +fragment AllIsDependencyPaginationTree on IsDependencyConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query IsDependencyPagination { + IsDependencyList(isDependencySpec: {}, first: 10) { + ...AllIsDependencyPaginationTree + } +} + query IsDependencyQ1 { IsDependency(isDependencySpec: {}) { ...allIsDependencyTree diff --git a/pkg/assembler/graphql/examples/is_occurence.gql b/pkg/assembler/graphql/examples/is_occurence.gql index 9c67d2e290..dc36f95c30 100644 --- a/pkg/assembler/graphql/examples/is_occurence.gql +++ b/pkg/assembler/graphql/examples/is_occurence.gql @@ -49,6 +49,27 @@ fragment allIsOccurrencesTree on IsOccurrence { documentRef } +fragment AllIsOccurrencePaginationTree on IsOccurrenceConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query IsOccurrencePagination { + IsOccurrenceList(isOccurrenceSpec: {}, first: 10) { + ...AllIsOccurrencePaginationTree + } +} + query IsOccurrenceQ1 { IsOccurrence(isOccurrenceSpec: {}) { ...allIsOccurrencesTree diff --git a/pkg/assembler/graphql/examples/license.gql b/pkg/assembler/graphql/examples/license.gql new file mode 100644 index 0000000000..69d2810f7b --- /dev/null +++ b/pkg/assembler/graphql/examples/license.gql @@ -0,0 +1,20 @@ +fragment AllLicensePaginationTree on LicenseConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query LicensePagination { + licenseList(licenseSpec: {}, first: 10) { + ...AllLicensePaginationTree + } +} \ No newline at end of file diff --git a/pkg/assembler/graphql/examples/packages.gql b/pkg/assembler/graphql/examples/packages.gql index 5edee3b954..bc0aa0f662 100644 --- a/pkg/assembler/graphql/examples/packages.gql +++ b/pkg/assembler/graphql/examples/packages.gql @@ -20,6 +20,27 @@ fragment allPkgTree on Package { } } +fragment AllPkgPaginationTree on PackageConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query PkgPagination { + packagesList(pkgSpec: {}, first: 10) { + ...AllPkgPaginationTree + } +} + query PkgQ1 { packages(pkgSpec: {}) { type diff --git a/pkg/assembler/graphql/examples/source.gql b/pkg/assembler/graphql/examples/source.gql index 77cf7ef4c7..e9fcc9bc5e 100644 --- a/pkg/assembler/graphql/examples/source.gql +++ b/pkg/assembler/graphql/examples/source.gql @@ -13,6 +13,27 @@ fragment allSrcTree on Source { } } +fragment AllSourcePaginationTree on SourceConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query SourcePagination { + sourcesList(sourceSpec: {}, first: 10) { + ...AllSourcePaginationTree + } +} + query SrcQ1 { sources(sourceSpec: {}) { namespaces { From 94e198064d0dce9de61c55396b419600e0bfa813 Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Thu, 2 May 2024 10:39:24 -0500 Subject: [PATCH 5/8] Added pagination for more queries Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- pkg/assembler/backends/keyvalue/pkgEqual.go | 152 ++++++++++++++- .../backends/keyvalue/pointOfContact.go | 180 ++++++++++++++++-- pkg/assembler/backends/keyvalue/vulnEqual.go | 158 ++++++++++++++- .../backends/keyvalue/vulnMetadata.go | 173 +++++++++++++++-- .../backends/keyvalue/vulnerability.go | 139 +++++++++++++- pkg/assembler/graphql/examples/pkg_equal.gql | 21 ++ .../graphql/examples/point_of_contact.gql | 20 ++ pkg/assembler/graphql/examples/vulnEqual.gql | 21 ++ .../graphql/examples/vuln_metadata.gql | 21 ++ .../graphql/examples/vulnerability.gql | 21 ++ 10 files changed, 855 insertions(+), 51 deletions(-) create mode 100644 pkg/assembler/graphql/examples/point_of_contact.gql diff --git a/pkg/assembler/backends/keyvalue/pkgEqual.go b/pkg/assembler/backends/keyvalue/pkgEqual.go index 404cc2be82..9762838df6 100644 --- a/pkg/assembler/backends/keyvalue/pkgEqual.go +++ b/pkg/assembler/backends/keyvalue/pkgEqual.go @@ -19,7 +19,9 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "slices" + "sort" "strings" "github.com/guacsec/guac/pkg/assembler/graphql/model" @@ -154,7 +156,138 @@ func (c *demoClient) ingestPkgEqual(ctx context.Context, pkg model.IDorPkgInput, // Query PkgEqual func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqualSpec, after *string, first *int) (*model.PkgEqualConnection, error) { - return nil, fmt.Errorf("not implemented: PkgEqualList") + funcName := "PkgEqual" + c.m.RLock() + defer c.m.RUnlock() + if pkgEqualSpec.ID != nil { + link, err := byIDkv[*pkgEqualStruct](ctx, *pkgEqualSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + pe, err := c.convPkgEqual(ctx, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.PkgEqualConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(pe.ID), + EndCursor: ptrfrom.String(pe.ID), + }, + Edges: []*model.PkgEqualEdge{ + { + Cursor: pe.ID, + Node: pe, + }, + }, + }, nil + } + + edges := make([]*model.PkgEqualEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + for _, p := range pkgEqualSpec.Packages { + pkgs, err := c.findPackageVersion(ctx, p) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + for _, pkg := range pkgs { + search = append(search, pkg.PkgEquals...) + } + } + + if len(search) > 0 { + for _, id := range search { + link, err := byIDkv[*pkgEqualStruct](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + pe, err := c.peIfMatch(ctx, &pkgEqualSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.PkgEqualEdge{ + Cursor: pe.ID, + Node: pe, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(pkgEqCol) + for !done { + var peKeys []string + var err error + peKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(peKeys) + totalCount = len(peKeys) + + for i, pek := range peKeys { + link, err := byKeykv[*pkgEqualStruct](ctx, pkgEqCol, pek, c) + if err != nil { + return nil, err + } + pe, err := c.peIfMatch(ctx, &pkgEqualSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if after != nil && !currentPage { + if pe.ID == *after { + totalCount = len(peKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.PkgEqualEdge{ + Cursor: pe.ID, + Node: pe, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.PkgEqualEdge{ + Cursor: pe.ID, + Node: pe, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.PkgEqualConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) PkgEqual(ctx context.Context, filter *model.PkgEqualSpec) ([]*model.PkgEqual, error) { @@ -193,10 +326,11 @@ func (c *demoClient) PkgEqual(ctx context.Context, filter *model.PkgEqualSpec) ( if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addCPIfMatch(ctx, out, filter, link) + pe, err := c.peIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, pe) } } else { var done bool @@ -213,25 +347,25 @@ func (c *demoClient) PkgEqual(ctx context.Context, filter *model.PkgEqualSpec) ( if err != nil { return nil, err } - out, err = c.addCPIfMatch(ctx, out, filter, link) + pe, err := c.peIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, pe) } } } return out, nil } -func (c *demoClient) addCPIfMatch(ctx context.Context, out []*model.PkgEqual, - filter *model.PkgEqualSpec, link *pkgEqualStruct) ( - []*model.PkgEqual, error, +func (c *demoClient) peIfMatch(ctx context.Context, filter *model.PkgEqualSpec, link *pkgEqualStruct) ( + *model.PkgEqual, error, ) { if noMatch(filter.Justification, link.Justification) || noMatch(filter.Origin, link.Origin) || noMatch(filter.Collector, link.Collector) || noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } for _, ps := range filter.Packages { if ps == nil { @@ -250,12 +384,12 @@ func (c *demoClient) addCPIfMatch(ctx context.Context, out []*model.PkgEqual, } } if !found { - return out, nil + return nil, nil } } pe, err := c.convPkgEqual(ctx, link) if err != nil { return nil, err } - return append(out, pe), nil + return pe, nil } diff --git a/pkg/assembler/backends/keyvalue/pointOfContact.go b/pkg/assembler/backends/keyvalue/pointOfContact.go index 1dda7e3e4a..e4e5d826b6 100644 --- a/pkg/assembler/backends/keyvalue/pointOfContact.go +++ b/pkg/assembler/backends/keyvalue/pointOfContact.go @@ -18,7 +18,8 @@ package keyvalue import ( "context" "errors" - "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" + "sort" "strings" "time" @@ -199,7 +200,154 @@ func (c *demoClient) ingestPointOfContact(ctx context.Context, subject model.Pac // Query PointOfContact func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec model.PointOfContactSpec, after *string, first *int) (*model.PointOfContactConnection, error) { - return nil, fmt.Errorf("not implemented: PointOfContactList") + funcName := "PointOfContact" + + c.m.RLock() + defer c.m.RUnlock() + + if pointOfContactSpec.ID != nil { + link, err := byIDkv[*pointOfContactLink](ctx, *pointOfContactSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + found, err := c.buildPointOfContact(ctx, link, &pointOfContactSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.PointOfContactConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(found.ID), + EndCursor: ptrfrom.String(found.ID), + }, + Edges: []*model.PointOfContactEdge{ + { + Cursor: found.ID, + Node: found, + }, + }, + }, nil + } + + edges := make([]*model.PointOfContactEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + // Cant really search for an exact Pkg, as these can be linked to either + // names or versions, and version could be empty. + var search []string + foundOne := false + if pointOfContactSpec.Subject != nil && pointOfContactSpec.Subject.Artifact != nil { + exactArtifact, err := c.artifactExact(ctx, pointOfContactSpec.Subject.Artifact) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactArtifact != nil { + search = append(search, exactArtifact.PointOfContactLinks...) + foundOne = true + } + } + if !foundOne && pointOfContactSpec.Subject != nil && pointOfContactSpec.Subject.Source != nil { + exactSource, err := c.exactSource(ctx, pointOfContactSpec.Subject.Source) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactSource != nil { + search = append(search, exactSource.PointOfContactLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*pointOfContactLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + poc, err := c.pointOfContactIfMatch(ctx, &pointOfContactSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.PointOfContactEdge{ + Cursor: poc.ID, + Node: poc, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(pocCol) + for !done { + var pocKeys []string + var err error + pocKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(pocKeys) + totalCount = len(pocKeys) + + for i, pk := range pocKeys { + link, err := byKeykv[*pointOfContactLink](ctx, pocCol, pk, c) + if err != nil { + return nil, err + } + poc, err := c.pointOfContactIfMatch(ctx, &pointOfContactSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if after != nil && !currentPage { + if poc.ID == *after { + totalCount = len(pocKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.PointOfContactEdge{ + Cursor: poc.ID, + Node: poc, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.PointOfContactEdge{ + Cursor: poc.ID, + Node: poc, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.PointOfContactConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) PointOfContact(ctx context.Context, filter *model.PointOfContactSpec) ([]*model.PointOfContact, error) { @@ -253,10 +401,11 @@ func (c *demoClient) PointOfContact(ctx context.Context, filter *model.PointOfCo if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addPOCIfMatch(ctx, out, filter, link) + poc, err := c.pointOfContactIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, poc) } } else { var done bool @@ -273,40 +422,41 @@ func (c *demoClient) PointOfContact(ctx context.Context, filter *model.PointOfCo if err != nil { return nil, err } - out, err = c.addPOCIfMatch(ctx, out, filter, link) + poc, err := c.pointOfContactIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, poc) } } } return out, nil } -func (c *demoClient) addPOCIfMatch(ctx context.Context, out []*model.PointOfContact, filter *model.PointOfContactSpec, link *pointOfContactLink) ( - []*model.PointOfContact, error) { +func (c *demoClient) pointOfContactIfMatch(ctx context.Context, filter *model.PointOfContactSpec, link *pointOfContactLink) ( + *model.PointOfContact, error) { if filter != nil && noMatch(filter.Justification, link.Justification) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Collector, link.Collector) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Origin, link.Origin) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Email, link.Email) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Info, link.Info) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } // no match if filter time since is after the timestamp if filter != nil && filter.Since != nil && filter.Since.After(link.Since) { - return out, nil + return nil, nil } found, err := c.buildPointOfContact(ctx, link, filter, false) @@ -314,9 +464,9 @@ func (c *demoClient) addPOCIfMatch(ctx context.Context, out []*model.PointOfCont return nil, err } if found == nil { - return out, nil + return nil, nil } - return append(out, found), nil + return found, nil } func (c *demoClient) buildPointOfContact(ctx context.Context, link *pointOfContactLink, filter *model.PointOfContactSpec, ingestOrIDProvided bool) (*model.PointOfContact, error) { diff --git a/pkg/assembler/backends/keyvalue/vulnEqual.go b/pkg/assembler/backends/keyvalue/vulnEqual.go index 683639ea6b..d6e09cf55c 100644 --- a/pkg/assembler/backends/keyvalue/vulnEqual.go +++ b/pkg/assembler/backends/keyvalue/vulnEqual.go @@ -19,7 +19,9 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "slices" + "sort" "strings" "github.com/guacsec/guac/pkg/assembler/graphql/model" @@ -155,7 +157,144 @@ func (c *demoClient) convVulnEqual(ctx context.Context, in *vulnerabilityEqualLi // Query VulnEqual func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.VulnEqualSpec, after *string, first *int) (*model.VulnEqualConnection, error) { - return nil, fmt.Errorf("not implemented: VulnEqualList") + funcName := "VulnEqual" + c.m.RLock() + defer c.m.RUnlock() + if vulnEqualSpec.ID != nil { + link, err := byIDkv[*vulnerabilityEqualLink](ctx, *vulnEqualSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + ve, err := c.convVulnEqual(ctx, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + return &model.VulnEqualConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(ve.ID), + EndCursor: ptrfrom.String(ve.ID), + }, + Edges: []*model.VulnEqualEdge{ + { + Cursor: ve.ID, + Node: ve, + }, + }, + }, nil + } + + edges := make([]*model.VulnEqualEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + for _, v := range vulnEqualSpec.Vulnerabilities { + if !foundOne { + exactVuln, err := c.exactVulnerability(ctx, v) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactVuln != nil { + search = append(search, exactVuln.VulnEqualLinks...) + foundOne = true + break + } + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*vulnerabilityEqualLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + ve, err := c.vulnIfMatch(ctx, &vulnEqualSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.VulnEqualEdge{ + Cursor: ve.ID, + Node: ve, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(vulnEqCol) + for !done { + var veKeys []string + var err error + veKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(veKeys) + totalCount = len(veKeys) + + for i, vek := range veKeys { + link, err := byKeykv[*vulnerabilityEqualLink](ctx, vulnEqCol, vek, c) + if err != nil { + return nil, err + } + ve, err := c.vulnIfMatch(ctx, &vulnEqualSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if ve.ID == *after { + totalCount = len(veKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VulnEqualEdge{ + Cursor: ve.ID, + Node: ve, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VulnEqualEdge{ + Cursor: ve.ID, + Node: ve, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.VulnEqualConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) VulnEqual(ctx context.Context, filter *model.VulnEqualSpec) ([]*model.VulnEqual, error) { @@ -199,10 +338,11 @@ func (c *demoClient) VulnEqual(ctx context.Context, filter *model.VulnEqualSpec) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addVulnIfMatch(ctx, out, filter, link) + ve, err := c.vulnIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, ve) } } else { var done bool @@ -219,25 +359,25 @@ func (c *demoClient) VulnEqual(ctx context.Context, filter *model.VulnEqualSpec) if err != nil { return nil, err } - out, err = c.addVulnIfMatch(ctx, out, filter, link) + ve, err := c.vulnIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, ve) } } } return out, nil } -func (c *demoClient) addVulnIfMatch(ctx context.Context, out []*model.VulnEqual, - filter *model.VulnEqualSpec, link *vulnerabilityEqualLink) ( - []*model.VulnEqual, error, +func (c *demoClient) vulnIfMatch(ctx context.Context, filter *model.VulnEqualSpec, link *vulnerabilityEqualLink) ( + *model.VulnEqual, error, ) { if noMatch(filter.Justification, link.Justification) || noMatch(filter.Origin, link.Origin) || noMatch(filter.Collector, link.Collector) || noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } for _, vs := range filter.Vulnerabilities { if vs == nil { @@ -254,12 +394,12 @@ func (c *demoClient) addVulnIfMatch(ctx context.Context, out []*model.VulnEqual, } } if !found { - return out, nil + return nil, nil } } ve, err := c.convVulnEqual(ctx, link) if err != nil { return nil, err } - return append(out, ve), nil + return ve, nil } diff --git a/pkg/assembler/backends/keyvalue/vulnMetadata.go b/pkg/assembler/backends/keyvalue/vulnMetadata.go index 7145ce1a04..52ed65bb7d 100644 --- a/pkg/assembler/backends/keyvalue/vulnMetadata.go +++ b/pkg/assembler/backends/keyvalue/vulnMetadata.go @@ -19,7 +19,9 @@ import ( "context" "errors" "fmt" + "github.com/guacsec/guac/internal/testing/ptrfrom" "reflect" + "sort" "strings" "time" @@ -133,7 +135,143 @@ func (c *demoClient) ingestVulnerabilityMetadata(ctx context.Context, vulnerabil // Query VulnerabilityMetadata func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilityMetadataSpec model.VulnerabilityMetadataSpec, after *string, first *int) (*model.VulnerabilityMetadataConnection, error) { - return nil, fmt.Errorf("not implemented: CertifyBadList") + c.m.RLock() + defer c.m.RUnlock() + funcName := "VulnerabilityMetadata" + + if vulnerabilityMetadataSpec.ID != nil { + link, err := byIDkv[*vulnerabilityMetadataLink](ctx, *vulnerabilityMetadataSpec.ID, c) + if err != nil { + // Not found + return nil, nil + } + // If found by id, ignore rest of fields in spec and return as a match + foundVulnMetadata, err := c.buildVulnerabilityMetadata(ctx, link, &vulnerabilityMetadataSpec, true) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + return &model.VulnerabilityMetadataConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(foundVulnMetadata.ID), + EndCursor: ptrfrom.String(foundVulnMetadata.ID), + }, + Edges: []*model.VulnerabilityMetadataEdge{ + { + Cursor: foundVulnMetadata.ID, + Node: foundVulnMetadata, + }, + }, + }, nil + } + + edges := make([]*model.VulnerabilityMetadataEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + var search []string + foundOne := false + if !foundOne && vulnerabilityMetadataSpec.Vulnerability != nil { + exactVuln, err := c.exactVulnerability(ctx, vulnerabilityMetadataSpec.Vulnerability) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + if exactVuln != nil { + search = append(search, exactVuln.VulnMetadataLinks...) + foundOne = true + } + } + + if foundOne { + for _, id := range search { + link, err := byIDkv[*vulnerabilityMetadataLink](ctx, id, c) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + vmd, err := c.vulnMetadataIfMatch(ctx, &vulnerabilityMetadataSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + edges = append(edges, &model.VulnerabilityMetadataEdge{ + Cursor: vmd.ID, + Node: vmd, + }) + } + } else { + currentPage := false + + // If no cursor present start from the top + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(vulnMDCol) + for !done { + var vmdKeys []string + var err error + vmdKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(vmdKeys) + totalCount = len(vmdKeys) + + for i, vmdk := range vmdKeys { + link, err := byKeykv[*vulnerabilityMetadataLink](ctx, vulnMDCol, vmdk, c) + if err != nil { + return nil, err + } + vmd, err := c.vulnMetadataIfMatch(ctx, &vulnerabilityMetadataSpec, link) + if err != nil { + return nil, gqlerror.Errorf("%v :: %v", funcName, err) + } + + if after != nil && !currentPage { + if vmd.ID == *after { + totalCount = len(vmdKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VulnerabilityMetadataEdge{ + Cursor: vmd.ID, + Node: vmd, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VulnerabilityMetadataEdge{ + Cursor: vmd.ID, + Node: vmd, + }) + } + } + } + } + + if len(edges) != 0 { + return &model.VulnerabilityMetadataConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) VulnerabilityMetadata(ctx context.Context, filter *model.VulnerabilityMetadataSpec) ([]*model.VulnerabilityMetadata, error) { @@ -176,10 +314,11 @@ func (c *demoClient) VulnerabilityMetadata(ctx context.Context, filter *model.Vu if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - out, err = c.addVulnMetadataMatch(ctx, out, filter, link) + vmd, err := c.vulnMetadataIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, vmd) } } else { var done bool @@ -196,10 +335,11 @@ func (c *demoClient) VulnerabilityMetadata(ctx context.Context, filter *model.Vu if err != nil { return nil, err } - out, err = c.addVulnMetadataMatch(ctx, out, filter, link) + vmd, err := c.vulnMetadataIfMatch(ctx, filter, link) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + out = append(out, vmd) } } } @@ -207,44 +347,43 @@ func (c *demoClient) VulnerabilityMetadata(ctx context.Context, filter *model.Vu return out, nil } -func (c *demoClient) addVulnMetadataMatch(ctx context.Context, out []*model.VulnerabilityMetadata, - filter *model.VulnerabilityMetadataSpec, - link *vulnerabilityMetadataLink) ([]*model.VulnerabilityMetadata, error) { +func (c *demoClient) vulnMetadataIfMatch(ctx context.Context, filter *model.VulnerabilityMetadataSpec, + link *vulnerabilityMetadataLink) (*model.VulnerabilityMetadata, error) { if filter != nil && filter.Comparator != nil { if filter.ScoreValue == nil { - return out, gqlerror.Errorf("comparator set without a vulnerability score being specified") + return nil, gqlerror.Errorf("comparator set without a vulnerability score being specified") } switch *filter.Comparator { case model.ComparatorEqual: if link.ScoreValue != *filter.ScoreValue { - return out, nil + return nil, nil } case model.ComparatorGreater, model.ComparatorGreaterEqual: if link.ScoreValue < *filter.ScoreValue { - return out, nil + return nil, nil } case model.ComparatorLess, model.ComparatorLessEqual: if link.ScoreValue > *filter.ScoreValue { - return out, nil + return nil, nil } } } else { if filter != nil && noMatchFloat(filter.ScoreValue, link.ScoreValue) { - return out, nil + return nil, nil } } if filter != nil && filter.ScoreType != nil && *filter.ScoreType != link.ScoreType { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Collector, link.Collector) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.Origin, link.Origin) { - return out, nil + return nil, nil } if filter != nil && noMatch(filter.DocumentRef, link.DocumentRef) { - return out, nil + return nil, nil } foundVulnMetadata, err := c.buildVulnerabilityMetadata(ctx, link, filter, false) @@ -252,9 +391,9 @@ func (c *demoClient) addVulnMetadataMatch(ctx context.Context, out []*model.Vuln return nil, fmt.Errorf("failed to build vuln metadata node from link") } if foundVulnMetadata == nil || reflect.ValueOf(foundVulnMetadata.Vulnerability).IsNil() { - return out, nil + return nil, nil } - return append(out, foundVulnMetadata), nil + return foundVulnMetadata, nil } func (c *demoClient) buildVulnerabilityMetadata(ctx context.Context, link *vulnerabilityMetadataLink, filter *model.VulnerabilityMetadataSpec, ingestOrIDProvided bool) (*model.VulnerabilityMetadata, error) { diff --git a/pkg/assembler/backends/keyvalue/vulnerability.go b/pkg/assembler/backends/keyvalue/vulnerability.go index 91b5ae7a13..37513e84e0 100644 --- a/pkg/assembler/backends/keyvalue/vulnerability.go +++ b/pkg/assembler/backends/keyvalue/vulnerability.go @@ -19,6 +19,7 @@ import ( "context" "errors" "fmt" + "sort" "strings" "github.com/vektah/gqlparser/v2/gqlerror" @@ -215,7 +216,143 @@ func (c *demoClient) IngestVulnerability(ctx context.Context, input model.IDorVu // Query Vulnerabilities func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.VulnerabilitySpec, after *string, first *int) (*model.VulnerabilityConnection, error) { - return nil, fmt.Errorf("not implemented: VulnerabilityList") + c.m.RLock() + defer c.m.RUnlock() + if vulnSpec.ID != nil { + v, err := c.buildVulnResponse(ctx, *vulnSpec.ID, &vulnSpec) + if err != nil { + if errors.Is(err, errNotFound) { + // not found + return nil, nil + } + return nil, err + } + + return &model.VulnerabilityConnection{ + TotalCount: 1, + PageInfo: &model.PageInfo{ + HasNextPage: false, + StartCursor: ptrfrom.String(v.ID), + EndCursor: ptrfrom.String(v.ID), + }, + Edges: []*model.VulnerabilityEdge{ + { + Cursor: v.ID, + Node: v, + }, + }, + }, nil + } + + edges := make([]*model.VulnerabilityEdge, 0) + hasNextPage := false + numNodes := 0 + totalCount := 0 + + if vulnSpec.NoVuln != nil && !*vulnSpec.NoVuln { + if vulnSpec.Type != nil && *vulnSpec.Type == noVulnType { + return nil, gqlerror.Errorf("novuln boolean set to false, cannot specify vulnerability type to be novuln") + } + } + + // if novuln is specified, retrieve all "novuln" type nodes + if vulnSpec.NoVuln != nil && *vulnSpec.NoVuln { + vulnSpec.Type = ptrfrom.String(noVulnType) + vulnSpec.VulnerabilityID = ptrfrom.String("") + } + + if vulnSpec.Type != nil { + inType := &vulnTypeStruct{ + Type: strings.ToLower(*vulnSpec.Type), + } + typeStruct, err := byKeykv[*vulnTypeStruct](ctx, vulnTypeCol, inType.Key(), c) + if err == nil { + vulnIDs := c.buildVulnID(ctx, typeStruct, &vulnSpec) + if len(vulnIDs) > 0 { + edges = append(edges, &model.VulnerabilityEdge{ + Cursor: vulnIDs[0].ID, + Node: &model.Vulnerability{ + ID: typeStruct.ThisID, + Type: typeStruct.Type, + VulnerabilityIDs: vulnIDs, + }, + }) + } + } + } else { + currentPage := false + + if after == nil { + currentPage = true + } + + var done bool + scn := c.kv.Keys(vulnTypeCol) + for !done { + var typeKeys []string + var err error + typeKeys, done, err = scn.Scan(ctx) + if err != nil { + return nil, err + } + + sort.Strings(typeKeys) + totalCount = len(typeKeys) + + for i, tk := range typeKeys { + typeStruct, err := byKeykv[*vulnTypeStruct](ctx, vulnTypeCol, tk, c) + if err != nil { + return nil, err + } + vulnIDs := c.buildVulnID(ctx, typeStruct, &vulnSpec) + if len(vulnIDs) > 0 { + v := &model.Vulnerability{ + ID: typeStruct.ThisID, + Type: typeStruct.Type, + VulnerabilityIDs: vulnIDs, + } + + if after != nil && !currentPage { + if v.VulnerabilityIDs[0].VulnerabilityID == *after { + totalCount = len(typeKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VulnerabilityEdge{ + Cursor: v.VulnerabilityIDs[0].VulnerabilityID, + Node: v, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VulnerabilityEdge{ + Cursor: v.VulnerabilityIDs[0].VulnerabilityID, + Node: v, + }) + } + } + } + } + } + + if len(edges) != 0 { + return &model.VulnerabilityConnection{ + TotalCount: totalCount, + PageInfo: &model.PageInfo{ + HasNextPage: hasNextPage, + StartCursor: ptrfrom.String(edges[0].Node.ID), + EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + }, + Edges: edges, + }, nil + } + return nil, nil } func (c *demoClient) Vulnerabilities(ctx context.Context, filter *model.VulnerabilitySpec) ([]*model.Vulnerability, error) { diff --git a/pkg/assembler/graphql/examples/pkg_equal.gql b/pkg/assembler/graphql/examples/pkg_equal.gql index 375b88780a..1a32f9d544 100644 --- a/pkg/assembler/graphql/examples/pkg_equal.gql +++ b/pkg/assembler/graphql/examples/pkg_equal.gql @@ -27,6 +27,27 @@ fragment allPkgEqualTree on PkgEqual { documentRef } +fragment AllPkgEqualPaginationTree on PkgEqualConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query PkgEqualPagination { + PkgEqualList(pkgEqualSpec: {}, first: 10) { + ...AllPkgEqualPaginationTree + } +} + query PkgEqualQ1 { PkgEqual(pkgEqualSpec: {}) { ...allPkgEqualTree diff --git a/pkg/assembler/graphql/examples/point_of_contact.gql b/pkg/assembler/graphql/examples/point_of_contact.gql new file mode 100644 index 0000000000..ac32fc6693 --- /dev/null +++ b/pkg/assembler/graphql/examples/point_of_contact.gql @@ -0,0 +1,20 @@ +fragment AllPointOfContactPaginationTree on PointOfContactConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query PointOfContactPagination { + PointOfContactList(pointOfContactSpec: {}, first: 10) { + ...AllPointOfContactPaginationTree + } +} \ No newline at end of file diff --git a/pkg/assembler/graphql/examples/vulnEqual.gql b/pkg/assembler/graphql/examples/vulnEqual.gql index 14fec5c018..3ac66f8a10 100644 --- a/pkg/assembler/graphql/examples/vulnEqual.gql +++ b/pkg/assembler/graphql/examples/vulnEqual.gql @@ -14,6 +14,27 @@ fragment allVulnEqualTree on VulnEqual { documentRef } +fragment AllVulnEqualPaginationTree on VulnEqualConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query VulnEqualPagination { + vulnEqualList(vulnEqualSpec: {}, first: 10) { + ...AllVulnEqualPaginationTree + } +} + query IsVulnerabilityQ1 { vulnEqual(vulnEqualSpec: {}) { ...allVulnEqualTree diff --git a/pkg/assembler/graphql/examples/vuln_metadata.gql b/pkg/assembler/graphql/examples/vuln_metadata.gql index d045580cf5..c9944936b9 100644 --- a/pkg/assembler/graphql/examples/vuln_metadata.gql +++ b/pkg/assembler/graphql/examples/vuln_metadata.gql @@ -16,6 +16,27 @@ fragment allVulnMetadataTree on VulnerabilityMetadata { documentRef } +fragment AllVulnerabilityMetadataPaginationTree on VulnerabilityMetadataConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query VulnerabilityMetadataPagination { + vulnerabilityMetadataList(vulnerabilityMetadataSpec: {}, first: 10) { + ...AllVulnerabilityMetadataPaginationTree + } +} + query VulnMetadataQ1 { vulnerabilityMetadata(vulnerabilityMetadataSpec: {}) { ...allVulnMetadataTree diff --git a/pkg/assembler/graphql/examples/vulnerability.gql b/pkg/assembler/graphql/examples/vulnerability.gql index fad82c70b5..dd9c7cc2b3 100644 --- a/pkg/assembler/graphql/examples/vulnerability.gql +++ b/pkg/assembler/graphql/examples/vulnerability.gql @@ -7,6 +7,27 @@ fragment allVulnerabilityTree on Vulnerability { } } +fragment AllVulnerabilityPaginationTree on VulnerabilityConnection { + totalCount + edges { + cursor + node { + id + } + } + pageInfo { + startCursor + endCursor + hasNextPage + } +} + +query VulnerabilityPagination { + vulnerabilityList(vulnSpec: {}, first: 10) { + ...AllVulnerabilityPaginationTree + } +} + query CVEQ1 { vulnerabilities(vulnSpec: {}) { ...allVulnerabilityTree From 00954b434ef42d74e4adb2b953808dbe337f60db Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Mon, 6 May 2024 09:18:04 -0500 Subject: [PATCH 6/8] Fixed tests Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- internal/testing/backend/helpers_test.go | 5 + internal/testing/backend/main_test.go | 82 ++++++------- pkg/assembler/backends/keyvalue/artifact.go | 26 ++-- pkg/assembler/backends/keyvalue/builder.go | 7 +- pkg/assembler/backends/keyvalue/certifyBad.go | 8 +- .../backends/keyvalue/certifyGood.go | 6 +- .../backends/keyvalue/certifyLegal.go | 6 +- .../backends/keyvalue/certifyScorecard.go | 4 +- .../backends/keyvalue/certifyVEXStatement.go | 9 +- .../backends/keyvalue/certifyVuln.go | 6 +- .../backends/keyvalue/hasMetadata.go | 6 +- pkg/assembler/backends/keyvalue/hasSBOM.go | 6 +- pkg/assembler/backends/keyvalue/hasSLSA.go | 10 +- .../backends/keyvalue/hasSourceAt.go | 10 +- pkg/assembler/backends/keyvalue/hashEqual.go | 9 +- .../backends/keyvalue/isDependency.go | 9 +- .../backends/keyvalue/isOccurrence.go | 9 +- pkg/assembler/backends/keyvalue/license.go | 6 +- pkg/assembler/backends/keyvalue/pkg.go | 115 ++++++++++++------ pkg/assembler/backends/keyvalue/pkgEqual.go | 10 +- .../backends/keyvalue/pointOfContact.go | 10 +- pkg/assembler/backends/keyvalue/src.go | 85 ++++++++----- pkg/assembler/backends/keyvalue/vulnEqual.go | 9 +- .../backends/keyvalue/vulnMetadata.go | 9 +- .../backends/keyvalue/vulnerability.go | 65 +++++----- 25 files changed, 353 insertions(+), 174 deletions(-) diff --git a/internal/testing/backend/helpers_test.go b/internal/testing/backend/helpers_test.go index 1e454c2ee6..89a13b0303 100644 --- a/internal/testing/backend/helpers_test.go +++ b/internal/testing/backend/helpers_test.go @@ -59,6 +59,7 @@ var commonOpts = cmp.Options{ cmpopts.SortSlices(lessHM), cmpopts.SortSlices(lessPackageOrArtifact), cmpopts.SortSlices(lessSLSAPred), + cmpopts.SortSlices(hasSlsaLess), cmpopts.SortSlices(lessHSA), cmpopts.SortSlices(lessIsDep), cmpopts.SortSlices(lessIsOcc), @@ -586,6 +587,10 @@ func lessSLSAPred(a, b *model.SLSAPredicate) bool { return false } +func hasSlsaLess(a, b *model.HasSlsa) bool { + return cmpArt(a.Subject, b.Subject) < 0 +} + func lessPackageOrArtifact(a, b model.PackageOrArtifact) bool { return cmpPackageOrArtifact(a, b) < 0 } diff --git a/internal/testing/backend/main_test.go b/internal/testing/backend/main_test.go index f712ab1975..725b1f6e84 100644 --- a/internal/testing/backend/main_test.go +++ b/internal/testing/backend/main_test.go @@ -38,40 +38,40 @@ const ( var skipMatrix = map[string]map[string]bool{ // pagination not implemented - "TestArtifacts": {arango: true, memmap: true, redis: true, tikv: true}, - "TestBuilder": {arango: true, memmap: true, redis: true, tikv: true}, - "TestBuilders": {arango: true, memmap: true, redis: true, tikv: true}, - "TestCertifyBad": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestCertifyBads": {arango: true, memmap: true, redis: true, tikv: true}, - "TestCertifyGood": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestCertifyGoods": {arango: true, memmap: true, redis: true, tikv: true}, - "TestLegal": {arango: true, memmap: true, redis: true, tikv: true}, - "TestLegals": {arango: true, memmap: true, redis: true, tikv: true}, - "TestCertifyScorecard": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestScorecards": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestCertifyVulnerability": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestCertifyVulns": {arango: true, memmap: true, redis: true, tikv: true}, - "TestHasMetadata": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestBulkHasMetadata": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestHasSBOMs": {arango: true, memmap: true, redis: true, tikv: true}, - "TestHasSLSA": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestHasSLSAs": {arango: true, memmap: true, redis: true, tikv: true}, - "TestHasSourceAt": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestHasSourceAts": {arango: true, memmap: true, redis: true, tikv: true}, - "TestHashEqual": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestHashEquals": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIsDependencies": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestOccurrences": {arango: true, memmap: true, redis: true, tikv: true}, - "TestLicenses": {arango: true, memmap: true, redis: true, tikv: true}, - "TestLicensesBulk": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestPkgEquals": {arango: true, memmap: true, redis: true, tikv: true}, - "TestPackages": {arango: true, memmap: true, redis: true, tikv: true}, - "TestPointOfContact": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestPointOfContacts": {arango: true, memmap: true, redis: true, tikv: true}, - "TestSources": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestVulnEquals": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestVulnMetadata": {arango: true, memmap: true, redis: true, tikv: true}, - "TestIngestVulnMetadatas": {arango: true, memmap: true, redis: true, tikv: true}, + "TestArtifacts": {arango: true, redis: true, tikv: true}, + "TestBuilder": {arango: true, redis: true, tikv: true}, + "TestBuilders": {arango: true, redis: true, tikv: true}, + "TestCertifyBad": {arango: true, redis: true, tikv: true}, + "TestIngestCertifyBads": {arango: true, redis: true, tikv: true}, + "TestCertifyGood": {arango: true, redis: true, tikv: true}, + "TestIngestCertifyGoods": {arango: true, redis: true, tikv: true}, + "TestLegal": {arango: true, redis: true, tikv: true}, + "TestLegals": {arango: true, redis: true, tikv: true}, + "TestCertifyScorecard": {arango: true, redis: true, tikv: true}, + "TestIngestScorecards": {arango: true, redis: true, tikv: true}, + "TestIngestCertifyVulnerability": {arango: true, redis: true, tikv: true}, + "TestIngestCertifyVulns": {arango: true, redis: true, tikv: true}, + "TestHasMetadata": {arango: true, redis: true, tikv: true}, + "TestIngestBulkHasMetadata": {arango: true, redis: true, tikv: true}, + "TestIngestHasSBOMs": {arango: true, redis: true, tikv: true}, + "TestHasSLSA": {arango: true, redis: true, tikv: true}, + "TestIngestHasSLSAs": {arango: true, redis: true, tikv: true}, + "TestHasSourceAt": {arango: true, redis: true, tikv: true}, + "TestIngestHasSourceAts": {arango: true, redis: true, tikv: true}, + "TestHashEqual": {arango: true, redis: true, tikv: true}, + "TestIngestHashEquals": {arango: true, redis: true, tikv: true}, + "TestIsDependencies": {arango: true, redis: true, tikv: true}, + "TestIngestOccurrences": {arango: true, redis: true, tikv: true}, + "TestLicenses": {arango: true, redis: true, tikv: true}, + "TestLicensesBulk": {arango: true, redis: true, tikv: true}, + "TestIngestPkgEquals": {arango: true, redis: true, tikv: true}, + "TestPackages": {arango: true, redis: true, tikv: true}, + "TestPointOfContact": {arango: true, redis: true, tikv: true}, + "TestIngestPointOfContacts": {arango: true, redis: true, tikv: true}, + "TestSources": {arango: true, redis: true, tikv: true}, + "TestIngestVulnEquals": {arango: true, redis: true, tikv: true}, + "TestIngestVulnMetadata": {arango: true, redis: true, tikv: true}, + "TestIngestVulnMetadatas": {arango: true, redis: true, tikv: true}, // arango fails IncludedOccurrences_-_Valid_Included_ID and IncludedDependencies_-_Valid_Included_ID "TestHasSBOM": {arango: true}, @@ -87,11 +87,11 @@ var skipMatrix = map[string]map[string]bool{ "TestPkgEqual": {arango: true, memmap: true, redis: true, tikv: true}, // keyvalue: Query_on_OSV_and_novuln_(return_nothing_as_not_valid) fails // arango: errors when ID is not found - "TestVulnEqual": {memmap: true, redis: true, tikv: true, arango: true}, + "TestVulnEqual": {redis: true, memmap: true, tikv: true, arango: true}, // arango: errors when ID is not found - "TestVulnerability": {arango: true}, + "TestVulnerability": {arango: true, redis: true, tikv: true}, // redis order issues - "TestVEX": {arango: true, redis: true}, + "TestVEX": {arango: true, redis: true, tikv: true}, // redis order issues "TestVEXBulkIngest": {arango: true, redis: true}, "TestFindSoftware": {redis: true, arango: true}, @@ -105,11 +105,11 @@ type backend interface { } var testBackends = map[string]backend{ - // memmap: newMemMap(), + memmap: newMemMap(), arango: newArango(), - // redis: newRedis(), - ent: newEnt(), - // tikv: newTikv(), + redis: newRedis(), + ent: newEnt(), + tikv: newTikv(), } var currentBackend string diff --git a/pkg/assembler/backends/keyvalue/artifact.go b/pkg/assembler/backends/keyvalue/artifact.go index 693dbd30b8..da54ad4d54 100644 --- a/pkg/assembler/backends/keyvalue/artifact.go +++ b/pkg/assembler/backends/keyvalue/artifact.go @@ -292,24 +292,28 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif continue } + if convArt == nil { + continue + } + + artEdge := createArtifactEdges(algorithm, a, digest, convArt) + + if artEdge == nil { + continue + } + if first != nil { if currentPage && count < *first { - artEdge := createArtifactEdges(algorithm, a, digest, convArt) - if artEdge != nil { - edges = append(edges, artEdge) - count++ - } + edges = append(edges, artEdge) + count++ } // If there are any elements left after the current page we indicate that in the response if count == *first && i < len(artKeys) { hasNextPage = true } } else { - artEdge := createArtifactEdges(algorithm, a, digest, convArt) - if artEdge != nil { - edges = append(edges, artEdge) - count++ - } + edges = append(edges, artEdge) + count++ } } } @@ -319,7 +323,7 @@ func (c *demoClient) ArtifactsList(ctx context.Context, artifactSpec model.Artif PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[count-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(count-1, 0)].Node.ID), }, Edges: edges}, nil } else { diff --git a/pkg/assembler/backends/keyvalue/builder.go b/pkg/assembler/backends/keyvalue/builder.go index 2589266a8a..d79737b426 100644 --- a/pkg/assembler/backends/keyvalue/builder.go +++ b/pkg/assembler/backends/keyvalue/builder.go @@ -167,6 +167,11 @@ func (c *demoClient) BuildersList(ctx context.Context, builderSpec model.Builder return nil, err } convBuild := c.convBuilder(b) + + if convBuild == nil { + continue + } + if after != nil && !currentPage { if convBuild.ID == *after { currentPage = true @@ -201,7 +206,7 @@ func (c *demoClient) BuildersList(ctx context.Context, builderSpec model.Builder PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[count-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(0, count-1)].Node.ID), }, Edges: edges}, nil } diff --git a/pkg/assembler/backends/keyvalue/certifyBad.go b/pkg/assembler/backends/keyvalue/certifyBad.go index 820b158bfb..669189617e 100644 --- a/pkg/assembler/backends/keyvalue/certifyBad.go +++ b/pkg/assembler/backends/keyvalue/certifyBad.go @@ -303,6 +303,10 @@ func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.Ce return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cbOut == nil { + continue + } + if after != nil && !currentPage { if cbOut.ID == *after { totalCount = len(cbKeys) - (i + 1) @@ -312,7 +316,7 @@ func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.Ce } if first != nil { - if numNodes < *first { + if numNodes < *first && currentPage { edges = append(edges, &model.CertifyBadEdge{ Cursor: cbOut.ID, Node: cbOut, @@ -337,7 +341,7 @@ func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.Ce PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(0, numNodes-1)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/certifyGood.go b/pkg/assembler/backends/keyvalue/certifyGood.go index e5850685bd..f2c6b663f6 100644 --- a/pkg/assembler/backends/keyvalue/certifyGood.go +++ b/pkg/assembler/backends/keyvalue/certifyGood.go @@ -303,6 +303,10 @@ func (c *demoClient) CertifyGoodList(ctx context.Context, certifyGoodSpec model. return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cgOut == nil { + continue + } + if after != nil && !currentPage { if cgOut.ID == *after { totalCount = len(cgKeys) - (i + 1) @@ -337,7 +341,7 @@ func (c *demoClient) CertifyGoodList(ctx context.Context, certifyGoodSpec model. PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(0, numNodes-1)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/certifyLegal.go b/pkg/assembler/backends/keyvalue/certifyLegal.go index a849f996e6..4051fb8e8a 100644 --- a/pkg/assembler/backends/keyvalue/certifyLegal.go +++ b/pkg/assembler/backends/keyvalue/certifyLegal.go @@ -388,6 +388,10 @@ func (c *demoClient) CertifyLegalList(ctx context.Context, certifyLegalSpec mode return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if legal == nil { + continue + } + if after != nil && !currentPage { if legal.ID == *after { totalCount = len(clKeys) - (i + 1) @@ -422,7 +426,7 @@ func (c *demoClient) CertifyLegalList(ctx context.Context, certifyLegalSpec mode PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/certifyScorecard.go b/pkg/assembler/backends/keyvalue/certifyScorecard.go index 64114e889e..d0300baa83 100644 --- a/pkg/assembler/backends/keyvalue/certifyScorecard.go +++ b/pkg/assembler/backends/keyvalue/certifyScorecard.go @@ -252,7 +252,7 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer } if scorecardOut == nil { - return nil, fmt.Errorf("the scorecard link doesn't match the specification") + continue } if after != nil && !currentPage { @@ -289,7 +289,7 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go index 5db5f9a0f9..ab1f9072d5 100644 --- a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go +++ b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go @@ -276,6 +276,9 @@ func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStat if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if vex == nil { + continue + } edges = append(edges, &model.VEXEdge{ Cursor: vex.ID, @@ -314,6 +317,10 @@ func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStat return nil, gqlerror.Errorf("%vex :: %vex", funcName, err) } + if vex == nil { + continue + } + if after != nil && !currentPage { if vex.ID == *after { totalCount = len(keys) - (i + 1) @@ -348,7 +355,7 @@ func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStat PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/certifyVuln.go b/pkg/assembler/backends/keyvalue/certifyVuln.go index bee158e343..8c3069f403 100644 --- a/pkg/assembler/backends/keyvalue/certifyVuln.go +++ b/pkg/assembler/backends/keyvalue/certifyVuln.go @@ -283,6 +283,10 @@ func (c *demoClient) CertifyVulnList(ctx context.Context, certifyVulnSpec model. return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cv == nil { + continue + } + if after != nil && !currentPage { if cv.ID == *after { totalCount = len(keys) - (i + 1) @@ -317,7 +321,7 @@ func (c *demoClient) CertifyVulnList(ctx context.Context, certifyVulnSpec model. PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/hasMetadata.go b/pkg/assembler/backends/keyvalue/hasMetadata.go index efc5b40d06..52a006aed0 100644 --- a/pkg/assembler/backends/keyvalue/hasMetadata.go +++ b/pkg/assembler/backends/keyvalue/hasMetadata.go @@ -310,6 +310,10 @@ func (c *demoClient) HasMetadataList(ctx context.Context, hasMetadataSpec model. return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hm == nil { + continue + } + if after != nil && !currentPage { if hm.ID == *after { totalCount = len(hmKeys) - (i + 1) @@ -344,7 +348,7 @@ func (c *demoClient) HasMetadataList(ctx context.Context, hasMetadataSpec model. PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/hasSBOM.go b/pkg/assembler/backends/keyvalue/hasSBOM.go index 9f77570573..4a5838d052 100644 --- a/pkg/assembler/backends/keyvalue/hasSBOM.go +++ b/pkg/assembler/backends/keyvalue/hasSBOM.go @@ -430,6 +430,10 @@ func (c *demoClient) HasSBOMList(ctx context.Context, hasSBOMSpec model.HasSBOMS return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hs == nil { + continue + } + if after != nil && !currentPage { if hs.ID == *after { totalCount = len(hsKeys) - (i + 1) @@ -464,7 +468,7 @@ func (c *demoClient) HasSBOMList(ctx context.Context, hasSBOMSpec model.HasSBOMS PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/hasSLSA.go b/pkg/assembler/backends/keyvalue/hasSLSA.go index 0dbbb1ca13..82650158c2 100644 --- a/pkg/assembler/backends/keyvalue/hasSLSA.go +++ b/pkg/assembler/backends/keyvalue/hasSLSA.go @@ -196,8 +196,8 @@ func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSAS sort.Strings(slsaKeys) totalCount = len(slsaKeys) - for i, slsak := range slsaKeys { - link, err := byKeykv[*hasSLSAStruct](ctx, slsaCol, slsak, c) + for i, slsaKey := range slsaKeys { + link, err := byKeykv[*hasSLSAStruct](ctx, slsaCol, slsaKey, c) if err != nil { return nil, err } @@ -206,6 +206,10 @@ func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSAS return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hs == nil { + continue + } + if after != nil && !currentPage { if hs.ID == *after { totalCount = len(slsaKeys) - (i + 1) @@ -240,7 +244,7 @@ func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSAS PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/hasSourceAt.go b/pkg/assembler/backends/keyvalue/hasSourceAt.go index 16c4a6f4e6..30a1dc2790 100644 --- a/pkg/assembler/backends/keyvalue/hasSourceAt.go +++ b/pkg/assembler/backends/keyvalue/hasSourceAt.go @@ -212,6 +212,10 @@ func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model. return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if src == nil { + continue + } + edges = append(edges, &model.HasSourceAtEdge{ Cursor: src.ID, Node: src, @@ -247,6 +251,10 @@ func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model. return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if src == nil { + continue + } + if after != nil && !currentPage { if src.ID == *after { totalCount = len(hsaKeys) - (i + 1) @@ -281,7 +289,7 @@ func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model. PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/hashEqual.go b/pkg/assembler/backends/keyvalue/hashEqual.go index 6a664743ea..8b402d457c 100644 --- a/pkg/assembler/backends/keyvalue/hashEqual.go +++ b/pkg/assembler/backends/keyvalue/hashEqual.go @@ -247,6 +247,9 @@ func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.Hash if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if he == nil { + continue + } edges = append(edges, &model.HashEqualEdge{ Cursor: he.ID, @@ -284,6 +287,10 @@ func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.Hash return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if he == nil { + continue + } + if after != nil && !currentPage { if he.ID == *after { totalCount = len(heKeys) - (i + 1) @@ -318,7 +325,7 @@ func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.Hash PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/isDependency.go b/pkg/assembler/backends/keyvalue/isDependency.go index 71c245b6e0..702c14af7d 100644 --- a/pkg/assembler/backends/keyvalue/isDependency.go +++ b/pkg/assembler/backends/keyvalue/isDependency.go @@ -213,6 +213,9 @@ func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec mode if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if dep == nil { + continue + } edges = append(edges, &model.IsDependencyEdge{ Cursor: dep.ID, @@ -250,6 +253,10 @@ func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec mode return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if dep == nil { + continue + } + if after != nil && !currentPage { if dep.ID == *after { totalCount = len(depKeys) - (i + 1) @@ -284,7 +291,7 @@ func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec mode PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/isOccurrence.go b/pkg/assembler/backends/keyvalue/isOccurrence.go index a8800f1b98..72e2af2610 100644 --- a/pkg/assembler/backends/keyvalue/isOccurrence.go +++ b/pkg/assembler/backends/keyvalue/isOccurrence.go @@ -319,6 +319,9 @@ func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec mode if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if occ == nil { + continue + } edges = append(edges, &model.IsOccurrenceEdge{ Cursor: occ.ID, @@ -356,6 +359,10 @@ func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec mode return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if occ == nil { + continue + } + if after != nil && !currentPage { if occ.ID == *after { totalCount = len(occKeys) - (i + 1) @@ -390,7 +397,7 @@ func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec mode PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/license.go b/pkg/assembler/backends/keyvalue/license.go index 5430694cf3..490f0d03c8 100644 --- a/pkg/assembler/backends/keyvalue/license.go +++ b/pkg/assembler/backends/keyvalue/license.go @@ -221,6 +221,10 @@ func (c *demoClient) LicenseList(ctx context.Context, licenseSpec model.LicenseS license := c.convLicense(l) + if license == nil { + continue + } + if after != nil && !currentPage { if license.ID == *after { totalCount = len(lKeys) - (i + 1) @@ -254,7 +258,7 @@ func (c *demoClient) LicenseList(ctx context.Context, licenseSpec model.LicenseS PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/pkg.go b/pkg/assembler/backends/keyvalue/pkg.go index 25cd5ba9e3..5e63478a7f 100644 --- a/pkg/assembler/backends/keyvalue/pkg.go +++ b/pkg/assembler/backends/keyvalue/pkg.go @@ -565,15 +565,34 @@ func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, af pkgTypeNode, err := byKeykv[*pkgType](ctx, pkgTypeCol, inType.Key(), c) if err == nil { pNamespaces := c.buildPkgNamespace(ctx, pkgTypeNode, &pkgSpec) - if len(pNamespaces) > 0 { - edges = append(edges, &model.PackageEdge{ - Cursor: pNamespaces[0].Names[0].Versions[0].ID, - Node: &model.Package{ - ID: pkgTypeNode.ThisID, - Type: pkgTypeNode.Type, - Namespaces: pNamespaces, - }, - }) + for _, namespace := range pNamespaces { + for _, name := range namespace.Names { + for _, version := range name.Versions { + p := &model.Package{ + ID: pkgTypeNode.ThisID, + Type: pkgTypeNode.Type, + Namespaces: []*model.PackageNamespace{ + { + ID: namespace.ID, + Namespace: namespace.Namespace, + Names: []*model.PackageName{ + { + ID: name.ID, + Name: name.Name, + Versions: []*model.PackageVersion{ + version, + }, + }, + }, + }, + }, + } + edges = append(edges, &model.PackageEdge{ + Cursor: pNamespaces[0].Names[0].Versions[0].ID, + Node: p, + }) + } + } } } } else { @@ -607,35 +626,55 @@ func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, af continue } - p := &model.Package{ - ID: pkgTypeNode.ThisID, - Type: pkgTypeNode.Type, - Namespaces: pNamespaces, - } - - if after != nil && !currentPage { - if p.Namespaces[0].Names[0].Versions[0].ID == *after { - totalCount = len(typeKeys) - (i + 1) - currentPage = true - } - continue - } - - if first != nil { - if numNodes < *first { - edges = append(edges, &model.PackageEdge{ - Cursor: p.Namespaces[0].Names[0].Versions[0].ID, - Node: p, - }) - numNodes++ - } else if numNodes == *first { - hasNextPage = true + for _, namespace := range pNamespaces { + for _, name := range namespace.Names { + for _, version := range name.Versions { + p := &model.Package{ + ID: pkgTypeNode.ThisID, + Type: pkgTypeNode.Type, + Namespaces: []*model.PackageNamespace{ + { + ID: namespace.ID, + Namespace: namespace.Namespace, + Names: []*model.PackageName{ + { + ID: name.ID, + Name: name.Name, + Versions: []*model.PackageVersion{ + version, + }, + }, + }, + }, + }, + } + + if after != nil && !currentPage { + if p.Namespaces[0].Names[0].Versions[0].ID == *after { + totalCount = len(typeKeys) - (i + 1) + currentPage = true + } + continue + } + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.PackageEdge{ + Cursor: p.Namespaces[0].Names[0].Versions[0].ID, + Node: p, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.PackageEdge{ + Cursor: p.Namespaces[0].Names[0].Versions[0].ID, + Node: p, + }) + } + } } - } else { - edges = append(edges, &model.PackageEdge{ - Cursor: p.Namespaces[0].Names[0].Versions[0].ID, - Node: p, - }) } } } @@ -647,7 +686,7 @@ func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, af PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/pkgEqual.go b/pkg/assembler/backends/keyvalue/pkgEqual.go index 9762838df6..89b636e439 100644 --- a/pkg/assembler/backends/keyvalue/pkgEqual.go +++ b/pkg/assembler/backends/keyvalue/pkgEqual.go @@ -212,6 +212,9 @@ func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqu if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if pe == nil { + continue + } edges = append(edges, &model.PkgEqualEdge{ Cursor: pe.ID, @@ -248,6 +251,11 @@ func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqu if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if pe == nil { + continue + } + if after != nil && !currentPage { if pe.ID == *after { totalCount = len(peKeys) - (i + 1) @@ -282,7 +290,7 @@ func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqu PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/pointOfContact.go b/pkg/assembler/backends/keyvalue/pointOfContact.go index e4e5d826b6..dd917e0422 100644 --- a/pkg/assembler/backends/keyvalue/pointOfContact.go +++ b/pkg/assembler/backends/keyvalue/pointOfContact.go @@ -272,6 +272,9 @@ func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if poc == nil { + continue + } edges = append(edges, &model.PointOfContactEdge{ Cursor: poc.ID, @@ -308,6 +311,11 @@ func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if poc == nil { + continue + } + if after != nil && !currentPage { if poc.ID == *after { totalCount = len(pocKeys) - (i + 1) @@ -342,7 +350,7 @@ func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/src.go b/pkg/assembler/backends/keyvalue/src.go index dd0cb4f200..2bafb1523e 100644 --- a/pkg/assembler/backends/keyvalue/src.go +++ b/pkg/assembler/backends/keyvalue/src.go @@ -354,15 +354,26 @@ func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpe srcTypeNode, err := byKeykv[*srcType](ctx, srcTypeCol, inType.Key(), c) if err == nil { sNamespaces := c.buildSourceNamespace(ctx, srcTypeNode, &sourceSpec) - if len(sNamespaces) > 0 { - edges = append(edges, &model.SourceEdge{ - Cursor: sNamespaces[0].Names[0].ID, - Node: &model.Source{ - ID: srcTypeNode.ThisID, - Type: srcTypeNode.Type, - Namespaces: sNamespaces, - }, - }) + for _, namespace := range sNamespaces { + for _, name := range namespace.Names { + s := &model.Source{ + ID: srcTypeNode.ThisID, + Type: srcTypeNode.Type, + Namespaces: []*model.SourceNamespace{ + { + ID: namespace.ID, + Namespace: namespace.Namespace, + Names: []*model.SourceName{ + name, + }, + }, + }, + } + edges = append(edges, &model.SourceEdge{ + Cursor: sNamespaces[0].Names[0].ID, + Node: s, + }) + } } } } else { @@ -392,36 +403,46 @@ func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpe return nil, err } sNamespaces := c.buildSourceNamespace(ctx, srcTypeNode, &sourceSpec) - if len(sNamespaces) > 0 { - s := &model.Source{ - ID: srcTypeNode.ThisID, - Type: srcTypeNode.Type, - Namespaces: sNamespaces, - } + for _, namespace := range sNamespaces { + for _, name := range namespace.Names { + s := &model.Source{ + ID: srcTypeNode.ThisID, + Type: srcTypeNode.Type, + Namespaces: []*model.SourceNamespace{ + { + ID: namespace.ID, + Namespace: namespace.Namespace, + Names: []*model.SourceName{ + name, + }, + }, + }, + } - if after != nil && !currentPage { - if s.Namespaces[0].Names[0].ID == *after { - totalCount = len(typeKeys) - (i + 1) - currentPage = true + if after != nil && !currentPage { + if s.Namespaces[0].Names[0].ID == *after { + totalCount = len(typeKeys) - (i + 1) + currentPage = true + } + continue } - continue - } - if first != nil { - if numNodes < *first { + if first != nil { + if numNodes < *first { + edges = append(edges, &model.SourceEdge{ + Cursor: s.Namespaces[0].Names[0].ID, + Node: s, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { edges = append(edges, &model.SourceEdge{ Cursor: s.Namespaces[0].Names[0].ID, Node: s, }) - numNodes++ - } else if numNodes == *first { - hasNextPage = true } - } else { - edges = append(edges, &model.SourceEdge{ - Cursor: s.Namespaces[0].Names[0].ID, - Node: s, - }) } } } @@ -434,7 +455,7 @@ func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpe PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/vulnEqual.go b/pkg/assembler/backends/keyvalue/vulnEqual.go index d6e09cf55c..924112bd8a 100644 --- a/pkg/assembler/backends/keyvalue/vulnEqual.go +++ b/pkg/assembler/backends/keyvalue/vulnEqual.go @@ -218,6 +218,9 @@ func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.Vuln if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if ve == nil { + continue + } edges = append(edges, &model.VulnEqualEdge{ Cursor: ve.ID, @@ -255,6 +258,10 @@ func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.Vuln return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if ve == nil { + continue + } + if after != nil && !currentPage { if ve.ID == *after { totalCount = len(veKeys) - (i + 1) @@ -289,7 +296,7 @@ func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.Vuln PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/vulnMetadata.go b/pkg/assembler/backends/keyvalue/vulnMetadata.go index 52ed65bb7d..809325060a 100644 --- a/pkg/assembler/backends/keyvalue/vulnMetadata.go +++ b/pkg/assembler/backends/keyvalue/vulnMetadata.go @@ -195,6 +195,9 @@ func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilit if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if vmd == nil { + continue + } edges = append(edges, &model.VulnerabilityMetadataEdge{ Cursor: vmd.ID, @@ -232,6 +235,10 @@ func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilit return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if vmd == nil { + continue + } + if after != nil && !currentPage { if vmd.ID == *after { totalCount = len(vmdKeys) - (i + 1) @@ -266,7 +273,7 @@ func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilit PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil diff --git a/pkg/assembler/backends/keyvalue/vulnerability.go b/pkg/assembler/backends/keyvalue/vulnerability.go index 37513e84e0..5877342278 100644 --- a/pkg/assembler/backends/keyvalue/vulnerability.go +++ b/pkg/assembler/backends/keyvalue/vulnerability.go @@ -268,14 +268,17 @@ func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.Vulne typeStruct, err := byKeykv[*vulnTypeStruct](ctx, vulnTypeCol, inType.Key(), c) if err == nil { vulnIDs := c.buildVulnID(ctx, typeStruct, &vulnSpec) - if len(vulnIDs) > 0 { + for _, id := range vulnIDs { + v := &model.Vulnerability{ + ID: typeStruct.ThisID, + Type: typeStruct.Type, + VulnerabilityIDs: []*model.VulnerabilityID{ + id, + }, + } edges = append(edges, &model.VulnerabilityEdge{ Cursor: vulnIDs[0].ID, - Node: &model.Vulnerability{ - ID: typeStruct.ThisID, - Type: typeStruct.Type, - VulnerabilityIDs: vulnIDs, - }, + Node: v, }) } } @@ -306,35 +309,39 @@ func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.Vulne } vulnIDs := c.buildVulnID(ctx, typeStruct, &vulnSpec) if len(vulnIDs) > 0 { - v := &model.Vulnerability{ - ID: typeStruct.ThisID, - Type: typeStruct.Type, - VulnerabilityIDs: vulnIDs, - } + for _, id := range vulnIDs { + v := &model.Vulnerability{ + ID: typeStruct.ThisID, + Type: typeStruct.Type, + VulnerabilityIDs: []*model.VulnerabilityID{ + id, + }, + } - if after != nil && !currentPage { - if v.VulnerabilityIDs[0].VulnerabilityID == *after { - totalCount = len(typeKeys) - (i + 1) - currentPage = true + if after != nil && !currentPage { + if id.ID == *after { + totalCount = len(typeKeys) - (i + 1) + currentPage = true + } + continue } - continue - } - if first != nil { - if numNodes < *first { + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VulnerabilityEdge{ + Cursor: id.ID, + Node: v, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { edges = append(edges, &model.VulnerabilityEdge{ - Cursor: v.VulnerabilityIDs[0].VulnerabilityID, + Cursor: id.ID, Node: v, }) - numNodes++ - } else if numNodes == *first { - hasNextPage = true } - } else { - edges = append(edges, &model.VulnerabilityEdge{ - Cursor: v.VulnerabilityIDs[0].VulnerabilityID, - Node: v, - }) } } } @@ -347,7 +354,7 @@ func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.Vulne PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), - EndCursor: ptrfrom.String(edges[numNodes-1].Node.ID), + EndCursor: ptrfrom.String(edges[max(numNodes-1, 0)].Node.ID), }, Edges: edges, }, nil From 43901232a0ebe138352f2e9a414bc5d297bcf8b0 Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Thu, 16 May 2024 09:36:41 -0500 Subject: [PATCH 7/8] Updated based on code review Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- internal/testing/backend/main_test.go | 8 +++---- pkg/assembler/backends/keyvalue/certifyBad.go | 17 ++++++++++++- .../backends/keyvalue/certifyGood.go | 16 ++++++++++++- .../backends/keyvalue/certifyLegal.go | 18 +++++++++++++- .../backends/keyvalue/certifyScorecard.go | 10 +++++++- .../backends/keyvalue/certifyVEXStatement.go | 20 +++++++++++++++- .../backends/keyvalue/certifyVuln.go | 24 ++++++++++++++++++- .../backends/keyvalue/hasMetadata.go | 23 +++++++++++++++++- pkg/assembler/backends/keyvalue/hasSBOM.go | 23 +++++++++++++++++- pkg/assembler/backends/keyvalue/hasSLSA.go | 21 +++++++++++++++- .../backends/keyvalue/hasSourceAt.go | 19 +++++++++++++-- pkg/assembler/backends/keyvalue/hashEqual.go | 20 +++++++++++++++- .../backends/keyvalue/isDependency.go | 18 +++++++++++++- .../backends/keyvalue/isOccurrence.go | 18 +++++++++++++- pkg/assembler/backends/keyvalue/pkg.go | 11 ++++++++- pkg/assembler/backends/keyvalue/pkgEqual.go | 18 +++++++++++++- .../backends/keyvalue/pointOfContact.go | 18 +++++++++++++- pkg/assembler/backends/keyvalue/src.go | 11 ++++++++- pkg/assembler/backends/keyvalue/vulnEqual.go | 18 +++++++++++++- .../backends/keyvalue/vulnMetadata.go | 18 +++++++++++++- .../backends/keyvalue/vulnerability.go | 11 ++++++++- 21 files changed, 335 insertions(+), 25 deletions(-) diff --git a/internal/testing/backend/main_test.go b/internal/testing/backend/main_test.go index 725b1f6e84..1670ef2a54 100644 --- a/internal/testing/backend/main_test.go +++ b/internal/testing/backend/main_test.go @@ -106,10 +106,10 @@ type backend interface { var testBackends = map[string]backend{ memmap: newMemMap(), - arango: newArango(), - redis: newRedis(), - ent: newEnt(), - tikv: newTikv(), + //arango: newArango(), + //redis: newRedis(), + //ent: newEnt(), + //tikv: newTikv(), } var currentBackend string diff --git a/pkg/assembler/backends/keyvalue/certifyBad.go b/pkg/assembler/backends/keyvalue/certifyBad.go index 669189617e..d4330c9414 100644 --- a/pkg/assembler/backends/keyvalue/certifyBad.go +++ b/pkg/assembler/backends/keyvalue/certifyBad.go @@ -228,6 +228,7 @@ func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.Ce hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 // Cant really search for an exact Pkg, as these can be linked to either // names or versions, and version could be empty. @@ -264,6 +265,16 @@ func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.Ce if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cb == nil { + continue + } + + if after != nil { + if cb.ID > *after { + addToCount += 1 + } + continue + } edges = append(edges, &model.CertifyBadEdge{ Cursor: cb.ID, @@ -337,7 +348,7 @@ func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.Ce if len(edges) != 0 { return &model.CertifyBadConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -405,6 +416,10 @@ func (c *demoClient) CertifyBad(ctx context.Context, filter *model.CertifyBadSpe return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cb == nil { + continue + } + out = append(out, cb) } } else { diff --git a/pkg/assembler/backends/keyvalue/certifyGood.go b/pkg/assembler/backends/keyvalue/certifyGood.go index f2c6b663f6..8251460717 100644 --- a/pkg/assembler/backends/keyvalue/certifyGood.go +++ b/pkg/assembler/backends/keyvalue/certifyGood.go @@ -229,6 +229,7 @@ func (c *demoClient) CertifyGoodList(ctx context.Context, certifyGoodSpec model. hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 // Cant really search for an exact Pkg, as these can be linked to either // names or versions, and version could be empty. @@ -265,6 +266,16 @@ func (c *demoClient) CertifyGoodList(ctx context.Context, certifyGoodSpec model. if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cg == nil { + continue + } + + if after != nil { + if cg.ID > *after { + addToCount += 1 + } + continue + } edges = append(edges, &model.CertifyGoodEdge{ Cursor: cg.ID, @@ -337,7 +348,7 @@ func (c *demoClient) CertifyGoodList(ctx context.Context, certifyGoodSpec model. if len(edges) != 0 { return &model.CertifyGoodConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -404,6 +415,9 @@ func (c *demoClient) CertifyGood(ctx context.Context, filter *model.CertifyGoodS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cg == nil { + continue + } out = append(out, cg) } diff --git a/pkg/assembler/backends/keyvalue/certifyLegal.go b/pkg/assembler/backends/keyvalue/certifyLegal.go index 4051fb8e8a..7d3836e9d6 100644 --- a/pkg/assembler/backends/keyvalue/certifyLegal.go +++ b/pkg/assembler/backends/keyvalue/certifyLegal.go @@ -304,6 +304,7 @@ func (c *demoClient) CertifyLegalList(ctx context.Context, certifyLegalSpec mode hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -351,6 +352,16 @@ func (c *demoClient) CertifyLegalList(ctx context.Context, certifyLegalSpec mode if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if legal == nil { + continue + } + + if after != nil { + if legal.ID > *after { + addToCount += 1 + } + continue + } edges = append(edges, &model.CertifyLegalEdge{ Cursor: legal.ID, @@ -422,7 +433,7 @@ func (c *demoClient) CertifyLegalList(ctx context.Context, certifyLegalSpec mode if len(edges) != 0 { return &model.CertifyLegalConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -501,6 +512,11 @@ func (c *demoClient) CertifyLegal(ctx context.Context, filter *model.CertifyLega if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if legal == nil { + continue + } + out = append(out, legal) } } else { diff --git a/pkg/assembler/backends/keyvalue/certifyScorecard.go b/pkg/assembler/backends/keyvalue/certifyScorecard.go index d0300baa83..230fe2fa4d 100644 --- a/pkg/assembler/backends/keyvalue/certifyScorecard.go +++ b/pkg/assembler/backends/keyvalue/certifyScorecard.go @@ -183,6 +183,7 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -210,6 +211,13 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer } for _, scorecardOut := range out { + if after != nil { + if scorecardOut.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.CertifyScorecardEdge{ Cursor: scorecardOut.ID, Node: scorecardOut, @@ -285,7 +293,7 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer if len(edges) != 0 { return &model.CertifyScorecardConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), diff --git a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go index ab1f9072d5..ec55495cc9 100644 --- a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go +++ b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go @@ -231,6 +231,7 @@ func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStat hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -280,6 +281,13 @@ func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStat continue } + if after != nil { + if vex.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.VEXEdge{ Cursor: vex.ID, Node: vex, @@ -351,7 +359,7 @@ func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStat if len(edges) != 0 { return &model.VEXConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -427,6 +435,11 @@ func (c *demoClient) CertifyVEXStatement(ctx context.Context, filter *model.Cert if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if v == nil { + continue + } + out = append(out, v) } } else { @@ -448,6 +461,11 @@ func (c *demoClient) CertifyVEXStatement(ctx context.Context, filter *model.Cert if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if v == nil { + continue + } + out = append(out, v) } } diff --git a/pkg/assembler/backends/keyvalue/certifyVuln.go b/pkg/assembler/backends/keyvalue/certifyVuln.go index 8c3069f403..49806a1bc5 100644 --- a/pkg/assembler/backends/keyvalue/certifyVuln.go +++ b/pkg/assembler/backends/keyvalue/certifyVuln.go @@ -193,6 +193,7 @@ func (c *demoClient) CertifyVulnList(ctx context.Context, certifyVulnSpec model. hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -248,6 +249,17 @@ func (c *demoClient) CertifyVulnList(ctx context.Context, certifyVulnSpec model. return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if cv == nil { + continue + } + + if after != nil { + if cv.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.CertifyVulnEdge{ Cursor: cv.ID, Node: cv, @@ -317,7 +329,7 @@ func (c *demoClient) CertifyVulnList(ctx context.Context, certifyVulnSpec model. if len(edges) != 0 { return &model.CertifyVulnConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -402,6 +414,11 @@ func (c *demoClient) CertifyVuln(ctx context.Context, filter *model.CertifyVulnS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if cv == nil { + continue + } + out = append(out, cv) } } else { @@ -423,6 +440,11 @@ func (c *demoClient) CertifyVuln(ctx context.Context, filter *model.CertifyVulnS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if cv == nil { + continue + } + out = append(out, cv) } } diff --git a/pkg/assembler/backends/keyvalue/hasMetadata.go b/pkg/assembler/backends/keyvalue/hasMetadata.go index 52a006aed0..b2b3992c86 100644 --- a/pkg/assembler/backends/keyvalue/hasMetadata.go +++ b/pkg/assembler/backends/keyvalue/hasMetadata.go @@ -237,6 +237,7 @@ func (c *demoClient) HasMetadataList(ctx context.Context, hasMetadataSpec model. hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 // Cant really search for an exact Pkg, as these can be linked to either // names or versions, and version could be empty. @@ -273,6 +274,16 @@ func (c *demoClient) HasMetadataList(ctx context.Context, hasMetadataSpec model. if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hm == nil { + continue + } + + if after != nil { + if hm.ID > *after { + addToCount += 1 + } + continue + } edges = append(edges, &model.HasMetadataEdge{ Cursor: hm.ID, @@ -344,7 +355,7 @@ func (c *demoClient) HasMetadataList(ctx context.Context, hasMetadataSpec model. if len(edges) != 0 { return &model.HasMetadataConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -411,6 +422,11 @@ func (c *demoClient) HasMetadata(ctx context.Context, filter *model.HasMetadataS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if hm == nil { + continue + } + out = append(out, hm) } } else { @@ -432,6 +448,11 @@ func (c *demoClient) HasMetadata(ctx context.Context, filter *model.HasMetadataS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if hm == nil { + continue + } + out = append(out, hm) } } diff --git a/pkg/assembler/backends/keyvalue/hasSBOM.go b/pkg/assembler/backends/keyvalue/hasSBOM.go index 4a5838d052..638ccc1e9e 100644 --- a/pkg/assembler/backends/keyvalue/hasSBOM.go +++ b/pkg/assembler/backends/keyvalue/hasSBOM.go @@ -359,6 +359,7 @@ func (c *demoClient) HasSBOMList(ctx context.Context, hasSBOMSpec model.HasSBOMS hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -393,6 +394,16 @@ func (c *demoClient) HasSBOMList(ctx context.Context, hasSBOMSpec model.HasSBOMS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hs == nil { + continue + } + + if after != nil { + if hs.ID > *after { + addToCount += 1 + } + continue + } edges = append(edges, &model.HasSBOMEdge{ Cursor: hs.ID, @@ -464,7 +475,7 @@ func (c *demoClient) HasSBOMList(ctx context.Context, hasSBOMSpec model.HasSBOMS if len(edges) != 0 { return &model.HasSBOMConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -529,6 +540,11 @@ func (c *demoClient) HasSBOM(ctx context.Context, filter *model.HasSBOMSpec) ([] if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if hs == nil { + continue + } + out = append(out, hs) } } else { @@ -550,6 +566,11 @@ func (c *demoClient) HasSBOM(ctx context.Context, filter *model.HasSBOMSpec) ([] if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if hs == nil { + continue + } + out = append(out, hs) } } diff --git a/pkg/assembler/backends/keyvalue/hasSLSA.go b/pkg/assembler/backends/keyvalue/hasSLSA.go index 82650158c2..23ea685af4 100644 --- a/pkg/assembler/backends/keyvalue/hasSLSA.go +++ b/pkg/assembler/backends/keyvalue/hasSLSA.go @@ -128,6 +128,7 @@ func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSAS hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -169,6 +170,16 @@ func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSAS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hs == nil { + continue + } + + if after != nil { + if hs.ID > *after { + addToCount += 1 + } + continue + } edges = append(edges, &model.HasSLSAEdge{ Cursor: hs.ID, @@ -240,7 +251,7 @@ func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSAS if len(edges) != 0 { return &model.HasSLSAConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -312,6 +323,10 @@ func (c *demoClient) HasSlsa(ctx context.Context, filter *model.HasSLSASpec) ([] if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hs == nil { + continue + } + out = append(out, hs) } } else { @@ -333,6 +348,10 @@ func (c *demoClient) HasSlsa(ctx context.Context, filter *model.HasSLSASpec) ([] if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if hs == nil { + continue + } + out = append(out, hs) } } diff --git a/pkg/assembler/backends/keyvalue/hasSourceAt.go b/pkg/assembler/backends/keyvalue/hasSourceAt.go index 30a1dc2790..9f6f049fb3 100644 --- a/pkg/assembler/backends/keyvalue/hasSourceAt.go +++ b/pkg/assembler/backends/keyvalue/hasSourceAt.go @@ -185,6 +185,7 @@ func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model. hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 // Cant really search for an exact Pkg, as these can be linked to either // names or versions, only search Source backedges. @@ -211,11 +212,17 @@ func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model. if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } - if src == nil { continue } + if after != nil { + if src.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.HasSourceAtEdge{ Cursor: src.ID, Node: src, @@ -285,7 +292,7 @@ func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model. if len(edges) != 0 { return &model.HasSourceAtConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -340,6 +347,10 @@ func (c *demoClient) HasSourceAt(ctx context.Context, filter *model.HasSourceAtS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if src == nil { + continue + } + out = append(out, src) } } else { @@ -361,6 +372,10 @@ func (c *demoClient) HasSourceAt(ctx context.Context, filter *model.HasSourceAtS if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if src == nil { + continue + } + out = append(out, src) } } diff --git a/pkg/assembler/backends/keyvalue/hashEqual.go b/pkg/assembler/backends/keyvalue/hashEqual.go index 8b402d457c..d834a81c5d 100644 --- a/pkg/assembler/backends/keyvalue/hashEqual.go +++ b/pkg/assembler/backends/keyvalue/hashEqual.go @@ -220,6 +220,7 @@ func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.Hash hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -251,6 +252,13 @@ func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.Hash continue } + if after != nil { + if he.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.HashEqualEdge{ Cursor: he.ID, Node: he, @@ -321,7 +329,7 @@ func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.Hash if len(edges) != 0 { return &model.HashEqualConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -378,6 +386,11 @@ func (c *demoClient) HashEqual(ctx context.Context, filter *model.HashEqualSpec) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if he == nil { + continue + } + out = append(out, he) } } else { @@ -399,6 +412,11 @@ func (c *demoClient) HashEqual(ctx context.Context, filter *model.HashEqualSpec) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + + if he == nil { + continue + } + out = append(out, he) } } diff --git a/pkg/assembler/backends/keyvalue/isDependency.go b/pkg/assembler/backends/keyvalue/isDependency.go index 702c14af7d..b2309d8eb0 100644 --- a/pkg/assembler/backends/keyvalue/isDependency.go +++ b/pkg/assembler/backends/keyvalue/isDependency.go @@ -188,6 +188,7 @@ func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec mode hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -217,6 +218,13 @@ func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec mode continue } + if after != nil { + if dep.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.IsDependencyEdge{ Cursor: dep.ID, Node: dep, @@ -287,7 +295,7 @@ func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec mode if len(edges) != 0 { return &model.IsDependencyConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -342,6 +350,10 @@ func (c *demoClient) IsDependency(ctx context.Context, filter *model.IsDependenc if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if dep == nil { + continue + } + out = append(out, dep) } } else { @@ -363,6 +375,10 @@ func (c *demoClient) IsDependency(ctx context.Context, filter *model.IsDependenc if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if dep == nil { + continue + } + out = append(out, dep) } } diff --git a/pkg/assembler/backends/keyvalue/isOccurrence.go b/pkg/assembler/backends/keyvalue/isOccurrence.go index 72e2af2610..f35c88b435 100644 --- a/pkg/assembler/backends/keyvalue/isOccurrence.go +++ b/pkg/assembler/backends/keyvalue/isOccurrence.go @@ -275,6 +275,7 @@ func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec mode hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -323,6 +324,13 @@ func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec mode continue } + if after != nil { + if occ.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.IsOccurrenceEdge{ Cursor: occ.ID, Node: occ, @@ -393,7 +401,7 @@ func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec mode if len(edges) != 0 { return &model.IsOccurrenceConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -469,6 +477,10 @@ func (c *demoClient) IsOccurrence(ctx context.Context, filter *model.IsOccurrenc if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if occ == nil { + continue + } + out = append(out, occ) } } else { @@ -490,6 +502,10 @@ func (c *demoClient) IsOccurrence(ctx context.Context, filter *model.IsOccurrenc if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if occ == nil { + continue + } + out = append(out, occ) } } diff --git a/pkg/assembler/backends/keyvalue/pkg.go b/pkg/assembler/backends/keyvalue/pkg.go index 5e63478a7f..892d0738bd 100644 --- a/pkg/assembler/backends/keyvalue/pkg.go +++ b/pkg/assembler/backends/keyvalue/pkg.go @@ -557,6 +557,7 @@ func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, af hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 if pkgSpec.Type != nil { inType := &pkgType{ @@ -587,6 +588,14 @@ func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, af }, }, } + + if after != nil { + if pNamespaces[0].Names[0].Versions[0].ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.PackageEdge{ Cursor: pNamespaces[0].Names[0].Versions[0].ID, Node: p, @@ -682,7 +691,7 @@ func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, af if len(edges) != 0 { return &model.PackageConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), diff --git a/pkg/assembler/backends/keyvalue/pkgEqual.go b/pkg/assembler/backends/keyvalue/pkgEqual.go index 89b636e439..d71a8b4049 100644 --- a/pkg/assembler/backends/keyvalue/pkgEqual.go +++ b/pkg/assembler/backends/keyvalue/pkgEqual.go @@ -190,6 +190,7 @@ func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqu hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string for _, p := range pkgEqualSpec.Packages { @@ -216,6 +217,13 @@ func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqu continue } + if after != nil { + if pe.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.PkgEqualEdge{ Cursor: pe.ID, Node: pe, @@ -286,7 +294,7 @@ func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqu if len(edges) != 0 { return &model.PkgEqualConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -338,6 +346,10 @@ func (c *demoClient) PkgEqual(ctx context.Context, filter *model.PkgEqualSpec) ( if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if pe == nil { + continue + } + out = append(out, pe) } } else { @@ -359,6 +371,10 @@ func (c *demoClient) PkgEqual(ctx context.Context, filter *model.PkgEqualSpec) ( if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if pe == nil { + continue + } + out = append(out, pe) } } diff --git a/pkg/assembler/backends/keyvalue/pointOfContact.go b/pkg/assembler/backends/keyvalue/pointOfContact.go index dd917e0422..75299a3bc4 100644 --- a/pkg/assembler/backends/keyvalue/pointOfContact.go +++ b/pkg/assembler/backends/keyvalue/pointOfContact.go @@ -236,6 +236,7 @@ func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 // Cant really search for an exact Pkg, as these can be linked to either // names or versions, and version could be empty. @@ -276,6 +277,13 @@ func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec continue } + if after != nil { + if poc.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.PointOfContactEdge{ Cursor: poc.ID, Node: poc, @@ -346,7 +354,7 @@ func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec if len(edges) != 0 { return &model.PointOfContactConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -413,6 +421,10 @@ func (c *demoClient) PointOfContact(ctx context.Context, filter *model.PointOfCo if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if poc == nil { + continue + } + out = append(out, poc) } } else { @@ -434,6 +446,10 @@ func (c *demoClient) PointOfContact(ctx context.Context, filter *model.PointOfCo if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if poc == nil { + continue + } + out = append(out, poc) } } diff --git a/pkg/assembler/backends/keyvalue/src.go b/pkg/assembler/backends/keyvalue/src.go index 2bafb1523e..f7077598cf 100644 --- a/pkg/assembler/backends/keyvalue/src.go +++ b/pkg/assembler/backends/keyvalue/src.go @@ -346,6 +346,7 @@ func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpe hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 if sourceSpec.Type != nil { inType := &srcType{ @@ -369,6 +370,14 @@ func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpe }, }, } + + if after != nil { + if sNamespaces[0].Names[0].ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.SourceEdge{ Cursor: sNamespaces[0].Names[0].ID, Node: s, @@ -451,7 +460,7 @@ func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpe if len(edges) != 0 { return &model.SourceConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), diff --git a/pkg/assembler/backends/keyvalue/vulnEqual.go b/pkg/assembler/backends/keyvalue/vulnEqual.go index 924112bd8a..fe0c4a7e4b 100644 --- a/pkg/assembler/backends/keyvalue/vulnEqual.go +++ b/pkg/assembler/backends/keyvalue/vulnEqual.go @@ -191,6 +191,7 @@ func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.Vuln hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -222,6 +223,13 @@ func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.Vuln continue } + if after != nil { + if ve.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.VulnEqualEdge{ Cursor: ve.ID, Node: ve, @@ -292,7 +300,7 @@ func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.Vuln if len(edges) != 0 { return &model.VulnEqualConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -349,6 +357,10 @@ func (c *demoClient) VulnEqual(ctx context.Context, filter *model.VulnEqualSpec) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if ve == nil { + continue + } + out = append(out, ve) } } else { @@ -370,6 +382,10 @@ func (c *demoClient) VulnEqual(ctx context.Context, filter *model.VulnEqualSpec) if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if ve == nil { + continue + } + out = append(out, ve) } } diff --git a/pkg/assembler/backends/keyvalue/vulnMetadata.go b/pkg/assembler/backends/keyvalue/vulnMetadata.go index 809325060a..2be2838b63 100644 --- a/pkg/assembler/backends/keyvalue/vulnMetadata.go +++ b/pkg/assembler/backends/keyvalue/vulnMetadata.go @@ -171,6 +171,7 @@ func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilit hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 var search []string foundOne := false @@ -199,6 +200,13 @@ func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilit continue } + if after != nil { + if vmd.ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.VulnerabilityMetadataEdge{ Cursor: vmd.ID, Node: vmd, @@ -269,7 +277,7 @@ func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilit if len(edges) != 0 { return &model.VulnerabilityMetadataConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), @@ -325,6 +333,10 @@ func (c *demoClient) VulnerabilityMetadata(ctx context.Context, filter *model.Vu if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if vmd == nil { + continue + } + out = append(out, vmd) } } else { @@ -346,6 +358,10 @@ func (c *demoClient) VulnerabilityMetadata(ctx context.Context, filter *model.Vu if err != nil { return nil, gqlerror.Errorf("%v :: %v", funcName, err) } + if vmd == nil { + continue + } + out = append(out, vmd) } } diff --git a/pkg/assembler/backends/keyvalue/vulnerability.go b/pkg/assembler/backends/keyvalue/vulnerability.go index 5877342278..e93bf9109e 100644 --- a/pkg/assembler/backends/keyvalue/vulnerability.go +++ b/pkg/assembler/backends/keyvalue/vulnerability.go @@ -248,6 +248,7 @@ func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.Vulne hasNextPage := false numNodes := 0 totalCount := 0 + addToCount := 0 if vulnSpec.NoVuln != nil && !*vulnSpec.NoVuln { if vulnSpec.Type != nil && *vulnSpec.Type == noVulnType { @@ -276,6 +277,14 @@ func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.Vulne id, }, } + + if after != nil { + if vulnIDs[0].ID > *after { + addToCount += 1 + } + continue + } + edges = append(edges, &model.VulnerabilityEdge{ Cursor: vulnIDs[0].ID, Node: v, @@ -350,7 +359,7 @@ func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.Vulne if len(edges) != 0 { return &model.VulnerabilityConnection{ - TotalCount: totalCount, + TotalCount: totalCount + addToCount, PageInfo: &model.PageInfo{ HasNextPage: hasNextPage, StartCursor: ptrfrom.String(edges[0].Node.ID), From 8695bdc9c0f1479f49676292571cb195680b6b15 Mon Sep 17 00:00:00 2001 From: nathannaveen <42319948+nathannaveen@users.noreply.github.com> Date: Mon, 20 May 2024 11:16:18 -0500 Subject: [PATCH 8/8] Updated based on code review Signed-off-by: nathannaveen <42319948+nathannaveen@users.noreply.github.com> --- internal/testing/backend/main_test.go | 8 +++--- pkg/assembler/backends/keyvalue/certifyBad.go | 27 ++++++++++++------- .../backends/keyvalue/certifyGood.go | 27 ++++++++++++------- .../backends/keyvalue/certifyLegal.go | 27 ++++++++++++------- .../backends/keyvalue/certifyScorecard.go | 27 ++++++++++++------- .../backends/keyvalue/certifyVEXStatement.go | 27 ++++++++++++------- .../backends/keyvalue/certifyVuln.go | 27 ++++++++++++------- .../backends/keyvalue/hasMetadata.go | 27 ++++++++++++------- pkg/assembler/backends/keyvalue/hasSBOM.go | 27 ++++++++++++------- pkg/assembler/backends/keyvalue/hasSLSA.go | 27 ++++++++++++------- .../backends/keyvalue/hasSourceAt.go | 27 ++++++++++++------- pkg/assembler/backends/keyvalue/hashEqual.go | 27 ++++++++++++------- .../backends/keyvalue/isDependency.go | 27 ++++++++++++------- .../backends/keyvalue/isOccurrence.go | 27 ++++++++++++------- pkg/assembler/backends/keyvalue/pkg.go | 27 ++++++++++++------- pkg/assembler/backends/keyvalue/pkgEqual.go | 27 ++++++++++++------- .../backends/keyvalue/pointOfContact.go | 27 ++++++++++++------- pkg/assembler/backends/keyvalue/src.go | 27 ++++++++++++------- pkg/assembler/backends/keyvalue/vulnEqual.go | 27 ++++++++++++------- .../backends/keyvalue/vulnMetadata.go | 27 ++++++++++++------- .../backends/keyvalue/vulnerability.go | 27 ++++++++++++------- 21 files changed, 364 insertions(+), 184 deletions(-) diff --git a/internal/testing/backend/main_test.go b/internal/testing/backend/main_test.go index 1670ef2a54..725b1f6e84 100644 --- a/internal/testing/backend/main_test.go +++ b/internal/testing/backend/main_test.go @@ -106,10 +106,10 @@ type backend interface { var testBackends = map[string]backend{ memmap: newMemMap(), - //arango: newArango(), - //redis: newRedis(), - //ent: newEnt(), - //tikv: newTikv(), + arango: newArango(), + redis: newRedis(), + ent: newEnt(), + tikv: newTikv(), } var currentBackend string diff --git a/pkg/assembler/backends/keyvalue/certifyBad.go b/pkg/assembler/backends/keyvalue/certifyBad.go index d4330c9414..60259a8459 100644 --- a/pkg/assembler/backends/keyvalue/certifyBad.go +++ b/pkg/assembler/backends/keyvalue/certifyBad.go @@ -269,17 +269,26 @@ func (c *demoClient) CertifyBadList(ctx context.Context, certifyBadSpec model.Ce continue } - if after != nil { - if cb.ID > *after { - addToCount += 1 + if (after != nil && cb.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyBadEdge{ + Cursor: cb.ID, + Node: cb, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyBadEdge{ + Cursor: cb.ID, + Node: cb, + }) } - continue } - - edges = append(edges, &model.CertifyBadEdge{ - Cursor: cb.ID, - Node: cb, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/certifyGood.go b/pkg/assembler/backends/keyvalue/certifyGood.go index 8251460717..617185e530 100644 --- a/pkg/assembler/backends/keyvalue/certifyGood.go +++ b/pkg/assembler/backends/keyvalue/certifyGood.go @@ -270,17 +270,26 @@ func (c *demoClient) CertifyGoodList(ctx context.Context, certifyGoodSpec model. continue } - if after != nil { - if cg.ID > *after { - addToCount += 1 + if (after != nil && cg.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyGoodEdge{ + Cursor: cg.ID, + Node: cg, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyGoodEdge{ + Cursor: cg.ID, + Node: cg, + }) } - continue } - - edges = append(edges, &model.CertifyGoodEdge{ - Cursor: cg.ID, - Node: cg, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/certifyLegal.go b/pkg/assembler/backends/keyvalue/certifyLegal.go index 7d3836e9d6..4d879c86fb 100644 --- a/pkg/assembler/backends/keyvalue/certifyLegal.go +++ b/pkg/assembler/backends/keyvalue/certifyLegal.go @@ -356,17 +356,26 @@ func (c *demoClient) CertifyLegalList(ctx context.Context, certifyLegalSpec mode continue } - if after != nil { - if legal.ID > *after { - addToCount += 1 + if (after != nil && legal.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyLegalEdge{ + Cursor: legal.ID, + Node: legal, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyLegalEdge{ + Cursor: legal.ID, + Node: legal, + }) } - continue } - - edges = append(edges, &model.CertifyLegalEdge{ - Cursor: legal.ID, - Node: legal, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/certifyScorecard.go b/pkg/assembler/backends/keyvalue/certifyScorecard.go index 230fe2fa4d..8f717bcbfc 100644 --- a/pkg/assembler/backends/keyvalue/certifyScorecard.go +++ b/pkg/assembler/backends/keyvalue/certifyScorecard.go @@ -211,17 +211,26 @@ func (c *demoClient) ScorecardsList(ctx context.Context, scorecardSpec model.Cer } for _, scorecardOut := range out { - if after != nil { - if scorecardOut.ID > *after { - addToCount += 1 + if (after != nil && scorecardOut.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyScorecardEdge{ + Cursor: scorecardOut.ID, + Node: scorecardOut, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyScorecardEdge{ + Cursor: scorecardOut.ID, + Node: scorecardOut, + }) } - continue } - - edges = append(edges, &model.CertifyScorecardEdge{ - Cursor: scorecardOut.ID, - Node: scorecardOut, - }) } } } else { diff --git a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go index ec55495cc9..71bff0ae78 100644 --- a/pkg/assembler/backends/keyvalue/certifyVEXStatement.go +++ b/pkg/assembler/backends/keyvalue/certifyVEXStatement.go @@ -281,17 +281,26 @@ func (c *demoClient) CertifyVEXStatementList(ctx context.Context, certifyVEXStat continue } - if after != nil { - if vex.ID > *after { - addToCount += 1 + if (after != nil && vex.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VEXEdge{ + Cursor: vex.ID, + Node: vex, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VEXEdge{ + Cursor: vex.ID, + Node: vex, + }) } - continue } - - edges = append(edges, &model.VEXEdge{ - Cursor: vex.ID, - Node: vex, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/certifyVuln.go b/pkg/assembler/backends/keyvalue/certifyVuln.go index 49806a1bc5..3307755897 100644 --- a/pkg/assembler/backends/keyvalue/certifyVuln.go +++ b/pkg/assembler/backends/keyvalue/certifyVuln.go @@ -253,17 +253,26 @@ func (c *demoClient) CertifyVulnList(ctx context.Context, certifyVulnSpec model. continue } - if after != nil { - if cv.ID > *after { - addToCount += 1 + if (after != nil && cv.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.CertifyVulnEdge{ + Cursor: cv.ID, + Node: cv, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.CertifyVulnEdge{ + Cursor: cv.ID, + Node: cv, + }) } - continue } - - edges = append(edges, &model.CertifyVulnEdge{ - Cursor: cv.ID, - Node: cv, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/hasMetadata.go b/pkg/assembler/backends/keyvalue/hasMetadata.go index b2b3992c86..6d0e4d9af7 100644 --- a/pkg/assembler/backends/keyvalue/hasMetadata.go +++ b/pkg/assembler/backends/keyvalue/hasMetadata.go @@ -278,17 +278,26 @@ func (c *demoClient) HasMetadataList(ctx context.Context, hasMetadataSpec model. continue } - if after != nil { - if hm.ID > *after { - addToCount += 1 + if (after != nil && hm.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasMetadataEdge{ + Cursor: hm.ID, + Node: hm, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasMetadataEdge{ + Cursor: hm.ID, + Node: hm, + }) } - continue } - - edges = append(edges, &model.HasMetadataEdge{ - Cursor: hm.ID, - Node: hm, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/hasSBOM.go b/pkg/assembler/backends/keyvalue/hasSBOM.go index 638ccc1e9e..b7caac0e91 100644 --- a/pkg/assembler/backends/keyvalue/hasSBOM.go +++ b/pkg/assembler/backends/keyvalue/hasSBOM.go @@ -398,17 +398,26 @@ func (c *demoClient) HasSBOMList(ctx context.Context, hasSBOMSpec model.HasSBOMS continue } - if after != nil { - if hs.ID > *after { - addToCount += 1 + if (after != nil && hs.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasSBOMEdge{ + Cursor: hs.ID, + Node: hs, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasSBOMEdge{ + Cursor: hs.ID, + Node: hs, + }) } - continue } - - edges = append(edges, &model.HasSBOMEdge{ - Cursor: hs.ID, - Node: hs, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/hasSLSA.go b/pkg/assembler/backends/keyvalue/hasSLSA.go index 23ea685af4..c9ed00831f 100644 --- a/pkg/assembler/backends/keyvalue/hasSLSA.go +++ b/pkg/assembler/backends/keyvalue/hasSLSA.go @@ -174,17 +174,26 @@ func (c *demoClient) HasSLSAList(ctx context.Context, hasSLSASpec model.HasSLSAS continue } - if after != nil { - if hs.ID > *after { - addToCount += 1 + if (after != nil && hs.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasSLSAEdge{ + Cursor: hs.ID, + Node: hs, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasSLSAEdge{ + Cursor: hs.ID, + Node: hs, + }) } - continue } - - edges = append(edges, &model.HasSLSAEdge{ - Cursor: hs.ID, - Node: hs, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/hasSourceAt.go b/pkg/assembler/backends/keyvalue/hasSourceAt.go index 9f6f049fb3..b5ddc8e5ba 100644 --- a/pkg/assembler/backends/keyvalue/hasSourceAt.go +++ b/pkg/assembler/backends/keyvalue/hasSourceAt.go @@ -216,17 +216,26 @@ func (c *demoClient) HasSourceAtList(ctx context.Context, hasSourceAtSpec model. continue } - if after != nil { - if src.ID > *after { - addToCount += 1 + if (after != nil && src.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HasSourceAtEdge{ + Cursor: src.ID, + Node: src, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HasSourceAtEdge{ + Cursor: src.ID, + Node: src, + }) } - continue } - - edges = append(edges, &model.HasSourceAtEdge{ - Cursor: src.ID, - Node: src, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/hashEqual.go b/pkg/assembler/backends/keyvalue/hashEqual.go index d834a81c5d..99ff4f3885 100644 --- a/pkg/assembler/backends/keyvalue/hashEqual.go +++ b/pkg/assembler/backends/keyvalue/hashEqual.go @@ -252,17 +252,26 @@ func (c *demoClient) HashEqualList(ctx context.Context, hashEqualSpec model.Hash continue } - if after != nil { - if he.ID > *after { - addToCount += 1 + if (after != nil && he.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.HashEqualEdge{ + Cursor: he.ID, + Node: he, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.HashEqualEdge{ + Cursor: he.ID, + Node: he, + }) } - continue } - - edges = append(edges, &model.HashEqualEdge{ - Cursor: he.ID, - Node: he, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/isDependency.go b/pkg/assembler/backends/keyvalue/isDependency.go index b2309d8eb0..68a9862d26 100644 --- a/pkg/assembler/backends/keyvalue/isDependency.go +++ b/pkg/assembler/backends/keyvalue/isDependency.go @@ -218,17 +218,26 @@ func (c *demoClient) IsDependencyList(ctx context.Context, isDependencySpec mode continue } - if after != nil { - if dep.ID > *after { - addToCount += 1 + if (after != nil && dep.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.IsDependencyEdge{ + Cursor: dep.ID, + Node: dep, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.IsDependencyEdge{ + Cursor: dep.ID, + Node: dep, + }) } - continue } - - edges = append(edges, &model.IsDependencyEdge{ - Cursor: dep.ID, - Node: dep, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/isOccurrence.go b/pkg/assembler/backends/keyvalue/isOccurrence.go index f35c88b435..9fb9148d19 100644 --- a/pkg/assembler/backends/keyvalue/isOccurrence.go +++ b/pkg/assembler/backends/keyvalue/isOccurrence.go @@ -324,17 +324,26 @@ func (c *demoClient) IsOccurrenceList(ctx context.Context, isOccurrenceSpec mode continue } - if after != nil { - if occ.ID > *after { - addToCount += 1 + if (after != nil && occ.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.IsOccurrenceEdge{ + Cursor: occ.ID, + Node: occ, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.IsOccurrenceEdge{ + Cursor: occ.ID, + Node: occ, + }) } - continue } - - edges = append(edges, &model.IsOccurrenceEdge{ - Cursor: occ.ID, - Node: occ, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/pkg.go b/pkg/assembler/backends/keyvalue/pkg.go index 892d0738bd..b80b261fce 100644 --- a/pkg/assembler/backends/keyvalue/pkg.go +++ b/pkg/assembler/backends/keyvalue/pkg.go @@ -589,17 +589,26 @@ func (c *demoClient) PackagesList(ctx context.Context, pkgSpec model.PkgSpec, af }, } - if after != nil { - if pNamespaces[0].Names[0].Versions[0].ID > *after { - addToCount += 1 + if (after != nil && pNamespaces[0].Names[0].Versions[0].ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.PackageEdge{ + Cursor: pNamespaces[0].Names[0].Versions[0].ID, + Node: p, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.PackageEdge{ + Cursor: pNamespaces[0].Names[0].Versions[0].ID, + Node: p, + }) } - continue } - - edges = append(edges, &model.PackageEdge{ - Cursor: pNamespaces[0].Names[0].Versions[0].ID, - Node: p, - }) } } } diff --git a/pkg/assembler/backends/keyvalue/pkgEqual.go b/pkg/assembler/backends/keyvalue/pkgEqual.go index d71a8b4049..905ca4a984 100644 --- a/pkg/assembler/backends/keyvalue/pkgEqual.go +++ b/pkg/assembler/backends/keyvalue/pkgEqual.go @@ -217,17 +217,26 @@ func (c *demoClient) PkgEqualList(ctx context.Context, pkgEqualSpec model.PkgEqu continue } - if after != nil { - if pe.ID > *after { - addToCount += 1 + if (after != nil && pe.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.PkgEqualEdge{ + Cursor: pe.ID, + Node: pe, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.PkgEqualEdge{ + Cursor: pe.ID, + Node: pe, + }) } - continue } - - edges = append(edges, &model.PkgEqualEdge{ - Cursor: pe.ID, - Node: pe, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/pointOfContact.go b/pkg/assembler/backends/keyvalue/pointOfContact.go index 75299a3bc4..c32fbeff80 100644 --- a/pkg/assembler/backends/keyvalue/pointOfContact.go +++ b/pkg/assembler/backends/keyvalue/pointOfContact.go @@ -277,17 +277,26 @@ func (c *demoClient) PointOfContactList(ctx context.Context, pointOfContactSpec continue } - if after != nil { - if poc.ID > *after { - addToCount += 1 + if (after != nil && poc.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.PointOfContactEdge{ + Cursor: poc.ID, + Node: poc, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.PointOfContactEdge{ + Cursor: poc.ID, + Node: poc, + }) } - continue } - - edges = append(edges, &model.PointOfContactEdge{ - Cursor: poc.ID, - Node: poc, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/src.go b/pkg/assembler/backends/keyvalue/src.go index f7077598cf..5a8c40b111 100644 --- a/pkg/assembler/backends/keyvalue/src.go +++ b/pkg/assembler/backends/keyvalue/src.go @@ -371,17 +371,26 @@ func (c *demoClient) SourcesList(ctx context.Context, sourceSpec model.SourceSpe }, } - if after != nil { - if sNamespaces[0].Names[0].ID > *after { - addToCount += 1 + if (after != nil && sNamespaces[0].Names[0].ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.SourceEdge{ + Cursor: sNamespaces[0].Names[0].ID, + Node: s, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.SourceEdge{ + Cursor: sNamespaces[0].Names[0].ID, + Node: s, + }) } - continue } - - edges = append(edges, &model.SourceEdge{ - Cursor: sNamespaces[0].Names[0].ID, - Node: s, - }) } } } diff --git a/pkg/assembler/backends/keyvalue/vulnEqual.go b/pkg/assembler/backends/keyvalue/vulnEqual.go index fe0c4a7e4b..4a74fb616d 100644 --- a/pkg/assembler/backends/keyvalue/vulnEqual.go +++ b/pkg/assembler/backends/keyvalue/vulnEqual.go @@ -223,17 +223,26 @@ func (c *demoClient) VulnEqualList(ctx context.Context, vulnEqualSpec model.Vuln continue } - if after != nil { - if ve.ID > *after { - addToCount += 1 + if (after != nil && ve.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VulnEqualEdge{ + Cursor: ve.ID, + Node: ve, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VulnEqualEdge{ + Cursor: ve.ID, + Node: ve, + }) } - continue } - - edges = append(edges, &model.VulnEqualEdge{ - Cursor: ve.ID, - Node: ve, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/vulnMetadata.go b/pkg/assembler/backends/keyvalue/vulnMetadata.go index 2be2838b63..ed63f2b667 100644 --- a/pkg/assembler/backends/keyvalue/vulnMetadata.go +++ b/pkg/assembler/backends/keyvalue/vulnMetadata.go @@ -200,17 +200,26 @@ func (c *demoClient) VulnerabilityMetadataList(ctx context.Context, vulnerabilit continue } - if after != nil { - if vmd.ID > *after { - addToCount += 1 + if (after != nil && vmd.ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VulnerabilityMetadataEdge{ + Cursor: vmd.ID, + Node: vmd, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VulnerabilityMetadataEdge{ + Cursor: vmd.ID, + Node: vmd, + }) } - continue } - - edges = append(edges, &model.VulnerabilityMetadataEdge{ - Cursor: vmd.ID, - Node: vmd, - }) } } else { currentPage := false diff --git a/pkg/assembler/backends/keyvalue/vulnerability.go b/pkg/assembler/backends/keyvalue/vulnerability.go index e93bf9109e..0800126310 100644 --- a/pkg/assembler/backends/keyvalue/vulnerability.go +++ b/pkg/assembler/backends/keyvalue/vulnerability.go @@ -278,17 +278,26 @@ func (c *demoClient) VulnerabilityList(ctx context.Context, vulnSpec model.Vulne }, } - if after != nil { - if vulnIDs[0].ID > *after { - addToCount += 1 + if (after != nil && vulnIDs[0].ID > *after) || after == nil { + addToCount += 1 + + if first != nil { + if numNodes < *first { + edges = append(edges, &model.VulnerabilityEdge{ + Cursor: vulnIDs[0].ID, + Node: v, + }) + numNodes++ + } else if numNodes == *first { + hasNextPage = true + } + } else { + edges = append(edges, &model.VulnerabilityEdge{ + Cursor: vulnIDs[0].ID, + Node: v, + }) } - continue } - - edges = append(edges, &model.VulnerabilityEdge{ - Cursor: vulnIDs[0].ID, - Node: v, - }) } } } else {