From b9971763633f8063945a6c5cd691af4ead95985f Mon Sep 17 00:00:00 2001 From: Andrei Aaron Date: Thu, 19 Jan 2023 00:20:55 +0200 Subject: [PATCH] feat(repodb): add PageInfo to GlobalSearch and RepoListWithNewestImage results (#1121) Signed-off-by: Alex Stan add page info to dynamo-> feat(repodb): add PageInfo to GlobalSearch and RepoListWithNewestImage results (cherry picked from commit 4fed42bb4bbc68199281d9d9a4e09b97fbd4759b) Signed-off-by: Andrei Aaron Signed-off-by: Alex Stan Signed-off-by: Andrei Aaron Co-authored-by: Alex Stan --- pkg/extensions/search/common/common_test.go | 496 +++++++++++++----- pkg/extensions/search/convert/convert_test.go | 3 +- .../search/gql_generated/generated.go | 478 +++++++++++++---- .../search/gql_generated/models_gen.go | 16 +- pkg/extensions/search/resolver.go | 42 +- pkg/extensions/search/resolver_test.go | 76 +-- pkg/extensions/search/schema.graphql | 22 +- pkg/extensions/search/schema.resolvers.go | 13 +- .../repodb/boltdb-wrapper/boltdb_wrapper.go | 24 +- .../boltdb-wrapper/boltdb_wrapper_test.go | 18 +- .../repodb/dynamodb-wrapper/dynamo_test.go | 12 +- .../repodb/dynamodb-wrapper/dynamo_wrapper.go | 40 +- pkg/meta/repodb/pagination.go | 46 +- pkg/meta/repodb/pagination_test.go | 74 ++- pkg/meta/repodb/repodb.go | 9 +- pkg/meta/repodb/repodb_test.go | 80 +-- pkg/test/mocks/repo_db_mock.go | 12 +- 17 files changed, 1073 insertions(+), 388 deletions(-) diff --git a/pkg/extensions/search/common/common_test.go b/pkg/extensions/search/common/common_test.go index 79905f41..b11fe62d 100644 --- a/pkg/extensions/search/common/common_test.go +++ b/pkg/extensions/search/common/common_test.go @@ -116,15 +116,21 @@ type GlobalSearch struct { Images []common.ImageSummary `json:"images"` Repos []common.RepoSummary `json:"repos"` Layers []common.LayerSummary `json:"layers"` + Page repodb.PageInfo `json:"page"` } type ExpandedRepoInfo struct { RepoInfo common.RepoInfo `json:"expandedRepoInfo"` } +type PaginatedReposResult struct { + Results []common.RepoSummary `json:"results"` + Page repodb.PageInfo `json:"page"` +} + //nolint:tagliatelle // graphQL schema type RepoListWithNewestImage struct { - Repos []common.RepoSummary `json:"RepoListWithNewestImage"` + PaginatedReposResult `json:"RepoListWithNewestImage"` } type ErrorGQL struct { @@ -332,136 +338,241 @@ func TestRepoListWithNewestImage(t *testing.T) { So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, 422) - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) - - var responseStruct RepoWithNewestImageResponse - err = json.Unmarshal(resp.Body(), &responseStruct) - So(err, ShouldBeNil) - So(len(responseStruct.RepoListWithNewestImage.Repos), ShouldEqual, 4) - - images := responseStruct.RepoListWithNewestImage.Repos - So(images[0].NewestImage.Tag, ShouldEqual, "0.0.1") - - query := `{ - RepoListWithNewestImage(requestedPage: { - limit:1 - offset:0 - sortBy: UPDATE_TIME - }){ - Name - NewestImage{ - Tag + Convey("Test repoListWithNewestImage with pagination", func() { + query := `{ + RepoListWithNewestImage(requestedPage:{ + limit: 2 + offset: 0 + sortBy: UPDATE_TIME + }){ + Page{ + ItemCount + TotalCount + } + Results{ + Name + NewestImage{ + Tag + } + } } + }` + + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + var responseStruct RepoWithNewestImageResponse + err = json.Unmarshal(resp.Body(), &responseStruct) + So(err, ShouldBeNil) + So(len(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results), ShouldEqual, 2) + So(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Page.ItemCount, ShouldEqual, 2) + So(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Page.TotalCount, ShouldEqual, 4) + }) + + Convey("Test repoListWithNewestImage with pagination, no limit or offset", func() { + query := `{ + RepoListWithNewestImage(requestedPage:{ + limit: 0 + offset: 0 + sortBy: UPDATE_TIME + }){ + Page{ + ItemCount + TotalCount + } + Results{ + Name + NewestImage{ + Tag + } + } + } + }` + + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + var responseStruct RepoWithNewestImageResponse + err = json.Unmarshal(resp.Body(), &responseStruct) + So(err, ShouldBeNil) + So(len(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results), ShouldEqual, 4) + So(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Page.ItemCount, ShouldEqual, 4) + So(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Page.TotalCount, ShouldEqual, 4) + }) + + Convey("Test repoListWithNewestImage multiple", func() { + query := `{RepoListWithNewestImage{ + Results{ + Name + NewestImage{ + Tag + } + } + }}` + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + var responseStruct RepoWithNewestImageResponse + err = json.Unmarshal(resp.Body(), &responseStruct) + So(err, ShouldBeNil) + So(len(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results), ShouldEqual, 4) + + images := responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results + So(images[0].NewestImage.Tag, ShouldEqual, "0.0.1") + + query = `{ + RepoListWithNewestImage(requestedPage: { + limit: 1 + offset: 0 + sortBy: UPDATE_TIME + }){ + Results{ + Name + NewestImage{ + Tag + } + } + } + }` + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + err = json.Unmarshal(resp.Body(), &responseStruct) + So(err, ShouldBeNil) + So(len(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results), ShouldEqual, 1) + + repos := responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results + So(repos[0].NewestImage.Tag, ShouldEqual, "0.0.1") + + query = `{ + RepoListWithNewestImage{ + Results{ + Name + NewestImage{ + Tag + Vulnerabilities{ + MaxSeverity + Count + } + } + } + } + }` + + // Verify we don't return any vulnerabilities if CVE scanning is disabled + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + err = json.Unmarshal(resp.Body(), &responseStruct) + So(err, ShouldBeNil) + So(len(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results), ShouldEqual, 4) + + images = responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results + So(images[0].NewestImage.Tag, ShouldEqual, "0.0.1") + So(images[0].NewestImage.Vulnerabilities.Count, ShouldEqual, 0) + So(images[0].NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "") + + query = `{ + RepoListWithNewestImage{ + Results{ + Name + NewestImage{ + Tag + } + } + } + }` + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + + err = os.Chmod(rootDir, 0o000) + if err != nil { + panic(err) } - }` - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query=" + url.QueryEscape(query)) - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) - err = json.Unmarshal(resp.Body(), &responseStruct) - So(err, ShouldBeNil) - So(len(responseStruct.RepoListWithNewestImage.Repos), ShouldEqual, 1) + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) - repos := responseStruct.RepoListWithNewestImage.Repos - So(repos[0].NewestImage.Tag, ShouldEqual, "0.0.1") + err = json.Unmarshal(resp.Body(), &responseStruct) + So(err, ShouldBeNil) + So(responseStruct.Errors, ShouldBeNil) // Even if permissions fail data is coming from the DB - // Verify we don't return any vulnerabilities if CVE scanning is disabled - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag%20Vulnerabilities{MaxSeverity%20Count}}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) + err = os.Chmod(rootDir, 0o755) + if err != nil { + panic(err) + } - err = json.Unmarshal(resp.Body(), &responseStruct) - So(err, ShouldBeNil) - So(len(responseStruct.RepoListWithNewestImage.Repos), ShouldEqual, 4) + var manifestDigest godigest.Digest + var configDigest godigest.Digest + manifestDigest, configDigest, _ = GetOciLayoutDigests("../../../../test/data/zot-test") - images = responseStruct.RepoListWithNewestImage.Repos - So(images[0].NewestImage.Tag, ShouldEqual, "0.0.1") - So(images[0].NewestImage.Vulnerabilities.Count, ShouldEqual, 0) - So(images[0].NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "") + // Delete config blob and try. + err = os.Remove(path.Join(subRootDir, "a/zot-test/blobs/sha256", configDigest.Encoded())) + if err != nil { + panic(err) + } - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) - err = os.Chmod(rootDir, 0o000) - if err != nil { - panic(err) - } + err = os.Remove(path.Join(subRootDir, "a/zot-test/blobs/sha256", + manifestDigest.Encoded())) + if err != nil { + panic(err) + } - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) - err = json.Unmarshal(resp.Body(), &responseStruct) - So(err, ShouldBeNil) - So(responseStruct.Errors, ShouldBeNil) // Even if permissions fail data is coming from the DB + err = os.Remove(path.Join(rootDir, "zot-test/blobs/sha256", configDigest.Encoded())) + if err != nil { + panic(err) + } - err = os.Chmod(rootDir, 0o755) - if err != nil { - panic(err) - } + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) - var manifestDigest godigest.Digest - var configDigest godigest.Digest - manifestDigest, configDigest, _ = GetOciLayoutDigests("../../../../test/data/zot-test") + // Delete manifest blob also and try + err = os.Remove(path.Join(rootDir, "zot-test/blobs/sha256", manifestDigest.Encoded())) + if err != nil { + panic(err) + } - // Delete config blob and try. - err = os.Remove(path.Join(subRootDir, "a/zot-test/blobs/sha256", configDigest.Encoded())) - if err != nil { - panic(err) - } - - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) - - err = os.Remove(path.Join(subRootDir, "a/zot-test/blobs/sha256", - manifestDigest.Encoded())) - if err != nil { - panic(err) - } - - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) - - err = os.Remove(path.Join(rootDir, "zot-test/blobs/sha256", configDigest.Encoded())) - if err != nil { - panic(err) - } - - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) - - // Delete manifest blob also and try - err = os.Remove(path.Join(rootDir, "zot-test/blobs/sha256", manifestDigest.Encoded())) - if err != nil { - panic(err) - } - - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + - "?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}") - So(resp, ShouldNotBeNil) - So(err, ShouldBeNil) - So(resp.StatusCode(), ShouldEqual, 200) + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + }) }) Convey("Test repoListWithNewestImage with vulnerability scan enabled", t, func() { @@ -534,8 +645,21 @@ func TestRepoListWithNewestImage(t *testing.T) { So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, 422) - query := "?query={RepoListWithNewestImage{Name%20NewestImage{Tag%20Vulnerabilities{MaxSeverity%20Count}}}}" - resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + query) + query := `{ + RepoListWithNewestImage{ + Results{ + Name + NewestImage{ + Tag + Vulnerabilities{ + MaxSeverity + Count + } + } + } + } + }` + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) So(resp, ShouldNotBeNil) So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, 200) @@ -543,9 +667,9 @@ func TestRepoListWithNewestImage(t *testing.T) { var responseStruct RepoWithNewestImageResponse err = json.Unmarshal(resp.Body(), &responseStruct) So(err, ShouldBeNil) - So(len(responseStruct.RepoListWithNewestImage.Repos), ShouldEqual, 4) + So(len(responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results), ShouldEqual, 4) - repos := responseStruct.RepoListWithNewestImage.Repos + repos := responseStruct.RepoListWithNewestImage.PaginatedReposResult.Results So(repos[0].NewestImage.Tag, ShouldEqual, "0.0.1") for _, repo := range repos { @@ -3254,7 +3378,7 @@ func TestGlobalSearchPagination(t *testing.T) { defer stopServer(ctlr) WaitTillServerReady(baseURL) - for i := 0; i < 1; i++ { + for i := 0; i < 3; i++ { config, layers, manifest, err := GetImageComponents(10) So(err, ShouldBeNil) @@ -3295,7 +3419,133 @@ func TestGlobalSearchPagination(t *testing.T) { So(responseStruct.GlobalSearchResult.GlobalSearch.Repos, ShouldNotBeEmpty) So(responseStruct.GlobalSearchResult.GlobalSearch.Layers, ShouldBeEmpty) - So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldEqual, 1) + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldEqual, 3) + }) + + Convey("Limit is lower than the repo count", func() { + query := ` + { + GlobalSearch(query:"repo", requestedPage:{limit: 2, offset: 0, sortBy:RELEVANCE}){ + Repos { + Name + } + } + }` + + resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + responseStruct := &GlobalSearchResultResp{} + + err = json.Unmarshal(resp.Body(), responseStruct) + So(err, ShouldBeNil) + + So(responseStruct.GlobalSearchResult.GlobalSearch.Images, ShouldBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Repos, ShouldNotBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Layers, ShouldBeEmpty) + + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldEqual, 2) + }) + + Convey("PageInfo returned proper response", func() { + query := ` + { + GlobalSearch(query:"repo", requestedPage:{limit: 2, offset: 0, sortBy:RELEVANCE}){ + Repos { + Name + } + Page{ + ItemCount + TotalCount + } + } + }` + + resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + responseStruct := &GlobalSearchResultResp{} + + err = json.Unmarshal(resp.Body(), responseStruct) + So(err, ShouldBeNil) + + So(responseStruct.GlobalSearchResult.GlobalSearch.Images, ShouldBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Repos, ShouldNotBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Layers, ShouldBeEmpty) + + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldEqual, 2) + So(responseStruct.GlobalSearchResult.GlobalSearch.Page.TotalCount, ShouldEqual, 3) + So(responseStruct.GlobalSearchResult.GlobalSearch.Page.ItemCount, ShouldEqual, 2) + }) + + Convey("PageInfo when limit is bigger than the repo count", func() { + query := ` + { + GlobalSearch(query:"repo", requestedPage:{limit: 9, offset: 0, sortBy:RELEVANCE}){ + Repos { + Name + } + Page{ + ItemCount + TotalCount + } + } + }` + + resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + responseStruct := &GlobalSearchResultResp{} + + err = json.Unmarshal(resp.Body(), responseStruct) + So(err, ShouldBeNil) + + So(responseStruct.GlobalSearchResult.GlobalSearch.Images, ShouldBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Repos, ShouldNotBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Layers, ShouldBeEmpty) + + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldEqual, 3) + So(responseStruct.GlobalSearchResult.GlobalSearch.Page.TotalCount, ShouldEqual, 3) + So(responseStruct.GlobalSearchResult.GlobalSearch.Page.ItemCount, ShouldEqual, 3) + }) + + Convey("PageInfo when limit and offset have 0 value", func() { + query := ` + { + GlobalSearch(query:"repo", requestedPage:{limit: 0, offset: 0, sortBy:RELEVANCE}){ + Repos { + Name + } + Page{ + ItemCount + TotalCount + } + } + }` + + resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + responseStruct := &GlobalSearchResultResp{} + + err = json.Unmarshal(resp.Body(), responseStruct) + So(err, ShouldBeNil) + + So(responseStruct.GlobalSearchResult.GlobalSearch.Images, ShouldBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Repos, ShouldNotBeEmpty) + So(responseStruct.GlobalSearchResult.GlobalSearch.Layers, ShouldBeEmpty) + + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldEqual, 3) + So(responseStruct.GlobalSearchResult.GlobalSearch.Page.TotalCount, ShouldEqual, 3) + So(responseStruct.GlobalSearchResult.GlobalSearch.Page.ItemCount, ShouldEqual, 3) }) }) } diff --git a/pkg/extensions/search/convert/convert_test.go b/pkg/extensions/search/convert/convert_test.go index 3175e1d0..c9af323c 100644 --- a/pkg/extensions/search/convert/convert_test.go +++ b/pkg/extensions/search/convert/convert_test.go @@ -52,7 +52,8 @@ func TestConvertErrors(t *testing.T) { err = repoDB.SetRepoTag("repo1", "0.1.0", digest11, ispec.MediaTypeImageManifest) So(err, ShouldBeNil) - repoMetas, manifestMetaMap, err := repoDB.SearchRepos(context.Background(), "", repodb.Filter{}, repodb.PageInput{}) + repoMetas, manifestMetaMap, _, err := repoDB.SearchRepos(context.Background(), "", repodb.Filter{}, + repodb.PageInput{}) So(err, ShouldBeNil) ctx := graphql.WithResponseContext(context.Background(), diff --git a/pkg/extensions/search/gql_generated/generated.go b/pkg/extensions/search/gql_generated/generated.go index c0fdde8b..0b394733 100644 --- a/pkg/extensions/search/gql_generated/generated.go +++ b/pkg/extensions/search/gql_generated/generated.go @@ -128,10 +128,18 @@ type ComplexityRoot struct { } PageInfo struct { - NextPage func(childComplexity int) int - ObjectCount func(childComplexity int) int - Pages func(childComplexity int) int - PreviousPage func(childComplexity int) int + ItemCount func(childComplexity int) int + TotalCount func(childComplexity int) int + } + + PaginatedImagesResult struct { + Page func(childComplexity int) int + Results func(childComplexity int) int + } + + PaginatedReposResult struct { + Page func(childComplexity int) int + Results func(childComplexity int) int } Query struct { @@ -182,7 +190,7 @@ type QueryResolver interface { ImageListForCve(ctx context.Context, id string) ([]*ImageSummary, error) ImageListWithCVEFixed(ctx context.Context, id string, image string) ([]*ImageSummary, error) ImageListForDigest(ctx context.Context, id string, requestedPage *PageInput) ([]*ImageSummary, error) - RepoListWithNewestImage(ctx context.Context, requestedPage *PageInput) ([]*RepoSummary, error) + RepoListWithNewestImage(ctx context.Context, requestedPage *PageInput) (*PaginatedReposResult, error) ImageList(ctx context.Context, repo string) ([]*ImageSummary, error) ExpandedRepoInfo(ctx context.Context, repo string) (*RepoInfo, error) GlobalSearch(ctx context.Context, query string, filter *Filter, requestedPage *PageInput) (*GlobalSearchResult, error) @@ -564,33 +572,47 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.PackageInfo.Name(childComplexity), true - case "PageInfo.NextPage": - if e.complexity.PageInfo.NextPage == nil { + case "PageInfo.ItemCount": + if e.complexity.PageInfo.ItemCount == nil { break } - return e.complexity.PageInfo.NextPage(childComplexity), true + return e.complexity.PageInfo.ItemCount(childComplexity), true - case "PageInfo.ObjectCount": - if e.complexity.PageInfo.ObjectCount == nil { + case "PageInfo.TotalCount": + if e.complexity.PageInfo.TotalCount == nil { break } - return e.complexity.PageInfo.ObjectCount(childComplexity), true + return e.complexity.PageInfo.TotalCount(childComplexity), true - case "PageInfo.Pages": - if e.complexity.PageInfo.Pages == nil { + case "PaginatedImagesResult.Page": + if e.complexity.PaginatedImagesResult.Page == nil { break } - return e.complexity.PageInfo.Pages(childComplexity), true + return e.complexity.PaginatedImagesResult.Page(childComplexity), true - case "PageInfo.PreviousPage": - if e.complexity.PageInfo.PreviousPage == nil { + case "PaginatedImagesResult.Results": + if e.complexity.PaginatedImagesResult.Results == nil { break } - return e.complexity.PageInfo.PreviousPage(childComplexity), true + return e.complexity.PaginatedImagesResult.Results(childComplexity), true + + case "PaginatedReposResult.Page": + if e.complexity.PaginatedReposResult.Page == nil { + break + } + + return e.complexity.PaginatedReposResult.Page(childComplexity), true + + case "PaginatedReposResult.Results": + if e.complexity.PaginatedReposResult.Results == nil { + break + } + + return e.complexity.PaginatedReposResult.Results(childComplexity), true case "Query.BaseImageList": if e.complexity.Query.BaseImageList == nil { @@ -1086,10 +1108,8 @@ enum SortCriteria { } type PageInfo { - ObjectCount: Int! - PreviousPage: Int - NextPage: Int - Pages: Int + TotalCount: Int! + ItemCount: Int! } # Pagination parameters @@ -1099,6 +1119,20 @@ input PageInput { sortBy: SortCriteria } +# Paginated list of RepoSummary objects +# If limit is -1, pagination is disabled +type PaginatedReposResult { + Page: PageInfo + Results: [RepoSummary!]! +} + +# Paginated list of ImageSummary objects +# If limit is -1, pagination is disabled +type PaginatedImagesResult { + Page: PageInfo + Results: [ImageSummary!]! +} + input Filter { Os: [String] Arch: [String] @@ -1129,7 +1163,7 @@ type Query { """ Returns a list of repos with the newest tag within """ - RepoListWithNewestImage(requestedPage: PageInput): [RepoSummary!]! # Newest based on created timestamp + RepoListWithNewestImage(requestedPage: PageInput): PaginatedReposResult! # Newest based on created timestamp """ Returns all the images from the specified repo @@ -1887,14 +1921,10 @@ func (ec *executionContext) fieldContext_GlobalSearchResult_Page(ctx context.Con IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "ObjectCount": - return ec.fieldContext_PageInfo_ObjectCount(ctx, field) - case "PreviousPage": - return ec.fieldContext_PageInfo_PreviousPage(ctx, field) - case "NextPage": - return ec.fieldContext_PageInfo_NextPage(ctx, field) - case "Pages": - return ec.fieldContext_PageInfo_Pages(ctx, field) + case "TotalCount": + return ec.fieldContext_PageInfo_TotalCount(ctx, field) + case "ItemCount": + return ec.fieldContext_PageInfo_ItemCount(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) }, @@ -3705,8 +3735,8 @@ func (ec *executionContext) fieldContext_PackageInfo_FixedVersion(ctx context.Co return fc, nil } -func (ec *executionContext) _PageInfo_ObjectCount(ctx context.Context, field graphql.CollectedField, obj *PageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PageInfo_ObjectCount(ctx, field) +func (ec *executionContext) _PageInfo_TotalCount(ctx context.Context, field graphql.CollectedField, obj *PageInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PageInfo_TotalCount(ctx, field) if err != nil { return graphql.Null } @@ -3719,7 +3749,7 @@ func (ec *executionContext) _PageInfo_ObjectCount(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ObjectCount, nil + return obj.TotalCount, nil }) if err != nil { ec.Error(ctx, err) @@ -3736,7 +3766,7 @@ func (ec *executionContext) _PageInfo_ObjectCount(ctx context.Context, field gra return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PageInfo_ObjectCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PageInfo_TotalCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PageInfo", Field: field, @@ -3749,8 +3779,8 @@ func (ec *executionContext) fieldContext_PageInfo_ObjectCount(ctx context.Contex return fc, nil } -func (ec *executionContext) _PageInfo_PreviousPage(ctx context.Context, field graphql.CollectedField, obj *PageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PageInfo_PreviousPage(ctx, field) +func (ec *executionContext) _PageInfo_ItemCount(ctx context.Context, field graphql.CollectedField, obj *PageInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PageInfo_ItemCount(ctx, field) if err != nil { return graphql.Null } @@ -3763,21 +3793,24 @@ func (ec *executionContext) _PageInfo_PreviousPage(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.PreviousPage, nil + return obj.ItemCount, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*int) + res := resTmp.(int) fc.Result = res - return ec.marshalOInt2ᚖint(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PageInfo_PreviousPage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PageInfo_ItemCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "PageInfo", Field: field, @@ -3790,8 +3823,8 @@ func (ec *executionContext) fieldContext_PageInfo_PreviousPage(ctx context.Conte return fc, nil } -func (ec *executionContext) _PageInfo_NextPage(ctx context.Context, field graphql.CollectedField, obj *PageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PageInfo_NextPage(ctx, field) +func (ec *executionContext) _PaginatedImagesResult_Page(ctx context.Context, field graphql.CollectedField, obj *PaginatedImagesResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PaginatedImagesResult_Page(ctx, field) if err != nil { return graphql.Null } @@ -3804,7 +3837,7 @@ func (ec *executionContext) _PageInfo_NextPage(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.NextPage, nil + return obj.Page, nil }) if err != nil { ec.Error(ctx, err) @@ -3813,26 +3846,32 @@ func (ec *executionContext) _PageInfo_NextPage(ctx context.Context, field graphq if resTmp == nil { return graphql.Null } - res := resTmp.(*int) + res := resTmp.(*PageInfo) fc.Result = res - return ec.marshalOInt2ᚖint(ctx, field.Selections, res) + return ec.marshalOPageInfo2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPageInfo(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PageInfo_NextPage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PaginatedImagesResult_Page(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PageInfo", + Object: "PaginatedImagesResult", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + switch field.Name { + case "TotalCount": + return ec.fieldContext_PageInfo_TotalCount(ctx, field) + case "ItemCount": + return ec.fieldContext_PageInfo_ItemCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) }, } return fc, nil } -func (ec *executionContext) _PageInfo_Pages(ctx context.Context, field graphql.CollectedField, obj *PageInfo) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_PageInfo_Pages(ctx, field) +func (ec *executionContext) _PaginatedImagesResult_Results(ctx context.Context, field graphql.CollectedField, obj *PaginatedImagesResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PaginatedImagesResult_Results(ctx, field) if err != nil { return graphql.Null } @@ -3845,7 +3884,95 @@ func (ec *executionContext) _PageInfo_Pages(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Pages, nil + return obj.Results, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*ImageSummary) + fc.Result = res + return ec.marshalNImageSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummaryᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PaginatedImagesResult_Results(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PaginatedImagesResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "RepoName": + return ec.fieldContext_ImageSummary_RepoName(ctx, field) + case "Tag": + return ec.fieldContext_ImageSummary_Tag(ctx, field) + case "Digest": + return ec.fieldContext_ImageSummary_Digest(ctx, field) + case "ConfigDigest": + return ec.fieldContext_ImageSummary_ConfigDigest(ctx, field) + case "LastUpdated": + return ec.fieldContext_ImageSummary_LastUpdated(ctx, field) + case "IsSigned": + return ec.fieldContext_ImageSummary_IsSigned(ctx, field) + case "Size": + return ec.fieldContext_ImageSummary_Size(ctx, field) + case "Platform": + return ec.fieldContext_ImageSummary_Platform(ctx, field) + case "Vendor": + return ec.fieldContext_ImageSummary_Vendor(ctx, field) + case "Score": + return ec.fieldContext_ImageSummary_Score(ctx, field) + case "DownloadCount": + return ec.fieldContext_ImageSummary_DownloadCount(ctx, field) + case "Layers": + return ec.fieldContext_ImageSummary_Layers(ctx, field) + case "Description": + return ec.fieldContext_ImageSummary_Description(ctx, field) + case "Licenses": + return ec.fieldContext_ImageSummary_Licenses(ctx, field) + case "Labels": + return ec.fieldContext_ImageSummary_Labels(ctx, field) + case "Title": + return ec.fieldContext_ImageSummary_Title(ctx, field) + case "Source": + return ec.fieldContext_ImageSummary_Source(ctx, field) + case "Documentation": + return ec.fieldContext_ImageSummary_Documentation(ctx, field) + case "History": + return ec.fieldContext_ImageSummary_History(ctx, field) + case "Vulnerabilities": + return ec.fieldContext_ImageSummary_Vulnerabilities(ctx, field) + case "Authors": + return ec.fieldContext_ImageSummary_Authors(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ImageSummary", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _PaginatedReposResult_Page(ctx context.Context, field graphql.CollectedField, obj *PaginatedReposResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PaginatedReposResult_Page(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Page, nil }) if err != nil { ec.Error(ctx, err) @@ -3854,19 +3981,93 @@ func (ec *executionContext) _PageInfo_Pages(ctx context.Context, field graphql.C if resTmp == nil { return graphql.Null } - res := resTmp.(*int) + res := resTmp.(*PageInfo) fc.Result = res - return ec.marshalOInt2ᚖint(ctx, field.Selections, res) + return ec.marshalOPageInfo2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPageInfo(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_PageInfo_Pages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PaginatedReposResult_Page(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PageInfo", + Object: "PaginatedReposResult", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + switch field.Name { + case "TotalCount": + return ec.fieldContext_PageInfo_TotalCount(ctx, field) + case "ItemCount": + return ec.fieldContext_PageInfo_ItemCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _PaginatedReposResult_Results(ctx context.Context, field graphql.CollectedField, obj *PaginatedReposResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PaginatedReposResult_Results(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Results, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*RepoSummary) + fc.Result = res + return ec.marshalNRepoSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummaryᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PaginatedReposResult_Results(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PaginatedReposResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "Name": + return ec.fieldContext_RepoSummary_Name(ctx, field) + case "LastUpdated": + return ec.fieldContext_RepoSummary_LastUpdated(ctx, field) + case "Size": + return ec.fieldContext_RepoSummary_Size(ctx, field) + case "Platforms": + return ec.fieldContext_RepoSummary_Platforms(ctx, field) + case "Vendors": + return ec.fieldContext_RepoSummary_Vendors(ctx, field) + case "Score": + return ec.fieldContext_RepoSummary_Score(ctx, field) + case "NewestImage": + return ec.fieldContext_RepoSummary_NewestImage(ctx, field) + case "DownloadCount": + return ec.fieldContext_RepoSummary_DownloadCount(ctx, field) + case "StarCount": + return ec.fieldContext_RepoSummary_StarCount(ctx, field) + case "IsBookmarked": + return ec.fieldContext_RepoSummary_IsBookmarked(ctx, field) + case "IsStarred": + return ec.fieldContext_RepoSummary_IsStarred(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type RepoSummary", field.Name) }, } return fc, nil @@ -4247,9 +4448,9 @@ func (ec *executionContext) _Query_RepoListWithNewestImage(ctx context.Context, } return graphql.Null } - res := resTmp.([]*RepoSummary) + res := resTmp.(*PaginatedReposResult) fc.Result = res - return ec.marshalNRepoSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummaryᚄ(ctx, field.Selections, res) + return ec.marshalNPaginatedReposResult2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPaginatedReposResult(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Query_RepoListWithNewestImage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -4260,30 +4461,12 @@ func (ec *executionContext) fieldContext_Query_RepoListWithNewestImage(ctx conte IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "Name": - return ec.fieldContext_RepoSummary_Name(ctx, field) - case "LastUpdated": - return ec.fieldContext_RepoSummary_LastUpdated(ctx, field) - case "Size": - return ec.fieldContext_RepoSummary_Size(ctx, field) - case "Platforms": - return ec.fieldContext_RepoSummary_Platforms(ctx, field) - case "Vendors": - return ec.fieldContext_RepoSummary_Vendors(ctx, field) - case "Score": - return ec.fieldContext_RepoSummary_Score(ctx, field) - case "NewestImage": - return ec.fieldContext_RepoSummary_NewestImage(ctx, field) - case "DownloadCount": - return ec.fieldContext_RepoSummary_DownloadCount(ctx, field) - case "StarCount": - return ec.fieldContext_RepoSummary_StarCount(ctx, field) - case "IsBookmarked": - return ec.fieldContext_RepoSummary_IsBookmarked(ctx, field) - case "IsStarred": - return ec.fieldContext_RepoSummary_IsStarred(ctx, field) + case "Page": + return ec.fieldContext_PaginatedReposResult_Page(ctx, field) + case "Results": + return ec.fieldContext_PaginatedReposResult_Results(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type RepoSummary", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PaginatedReposResult", field.Name) }, } defer func() { @@ -8185,25 +8368,84 @@ func (ec *executionContext) _PageInfo(ctx context.Context, sel ast.SelectionSet, switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("PageInfo") - case "ObjectCount": + case "TotalCount": - out.Values[i] = ec._PageInfo_ObjectCount(ctx, field, obj) + out.Values[i] = ec._PageInfo_TotalCount(ctx, field, obj) if out.Values[i] == graphql.Null { invalids++ } - case "PreviousPage": + case "ItemCount": - out.Values[i] = ec._PageInfo_PreviousPage(ctx, field, obj) + out.Values[i] = ec._PageInfo_ItemCount(ctx, field, obj) - case "NextPage": + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} - out.Values[i] = ec._PageInfo_NextPage(ctx, field, obj) +var paginatedImagesResultImplementors = []string{"PaginatedImagesResult"} - case "Pages": +func (ec *executionContext) _PaginatedImagesResult(ctx context.Context, sel ast.SelectionSet, obj *PaginatedImagesResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, paginatedImagesResultImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PaginatedImagesResult") + case "Page": - out.Values[i] = ec._PageInfo_Pages(ctx, field, obj) + out.Values[i] = ec._PaginatedImagesResult_Page(ctx, field, obj) + case "Results": + + out.Values[i] = ec._PaginatedImagesResult_Results(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var paginatedReposResultImplementors = []string{"PaginatedReposResult"} + +func (ec *executionContext) _PaginatedReposResult(ctx context.Context, sel ast.SelectionSet, obj *PaginatedReposResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, paginatedReposResultImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PaginatedReposResult") + case "Page": + + out.Values[i] = ec._PaginatedReposResult_Page(ctx, field, obj) + + case "Results": + + out.Values[i] = ec._PaginatedReposResult_Results(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -9049,6 +9291,50 @@ func (ec *executionContext) marshalNGlobalSearchResult2ᚖzotregistryᚗioᚋzot return ec._GlobalSearchResult(ctx, sel, v) } +func (ec *executionContext) marshalNImageSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummaryᚄ(ctx context.Context, sel ast.SelectionSet, v []*ImageSummary) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNImageSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummary(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) marshalNImageSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummary(ctx context.Context, sel ast.SelectionSet, v *ImageSummary) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -9074,6 +9360,20 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti return res } +func (ec *executionContext) marshalNPaginatedReposResult2zotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPaginatedReposResult(ctx context.Context, sel ast.SelectionSet, v PaginatedReposResult) graphql.Marshaler { + return ec._PaginatedReposResult(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPaginatedReposResult2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPaginatedReposResult(ctx context.Context, sel ast.SelectionSet, v *PaginatedReposResult) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PaginatedReposResult(ctx, sel, v) +} + func (ec *executionContext) marshalNReferrer2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐReferrer(ctx context.Context, sel ast.SelectionSet, v []*Referrer) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/pkg/extensions/search/gql_generated/models_gen.go b/pkg/extensions/search/gql_generated/models_gen.go index 1c1f2014..176f1457 100644 --- a/pkg/extensions/search/gql_generated/models_gen.go +++ b/pkg/extensions/search/gql_generated/models_gen.go @@ -111,10 +111,8 @@ type PackageInfo struct { } type PageInfo struct { - ObjectCount int `json:"ObjectCount"` - PreviousPage *int `json:"PreviousPage"` - NextPage *int `json:"NextPage"` - Pages *int `json:"Pages"` + TotalCount int `json:"TotalCount"` + ItemCount int `json:"ItemCount"` } type PageInput struct { @@ -123,6 +121,16 @@ type PageInput struct { SortBy *SortCriteria `json:"sortBy"` } +type PaginatedImagesResult struct { + Page *PageInfo `json:"Page"` + Results []*ImageSummary `json:"Results"` +} + +type PaginatedReposResult struct { + Page *PageInfo `json:"Page"` + Results []*RepoSummary `json:"Results"` +} + type Referrer struct { MediaType *string `json:"MediaType"` ArtifactType *string `json:"ArtifactType"` diff --git a/pkg/extensions/search/resolver.go b/pkg/extensions/search/resolver.go index 17836ac2..b767963b 100644 --- a/pkg/extensions/search/resolver.go +++ b/pkg/extensions/search/resolver.go @@ -197,15 +197,16 @@ func repoListWithNewestImage( log log.Logger, //nolint:unparam // may be used by devs for debugging requestedPage *gql_generated.PageInput, repoDB repodb.RepoDB, -) ([]*gql_generated.RepoSummary, error) { +) (*gql_generated.PaginatedReposResult, error) { repos := []*gql_generated.RepoSummary{} + paginatedRepos := &gql_generated.PaginatedReposResult{} if requestedPage == nil { requestedPage = &gql_generated.PageInput{} } skip := convert.SkipQGLField{ - Vulnerabilities: canSkipField(convert.GetPreloads(ctx), "NewestImage.Vulnerabilities"), + Vulnerabilities: canSkipField(convert.GetPreloads(ctx), "Results.NewestImage.Vulnerabilities"), } pageInput := repodb.PageInput{ @@ -216,9 +217,9 @@ func repoListWithNewestImage( ), } - reposMeta, manifestMetaMap, err := repoDB.SearchRepos(ctx, "", repodb.Filter{}, pageInput) + reposMeta, manifestMetaMap, pageInfo, err := repoDB.SearchRepos(ctx, "", repodb.Filter{}, pageInput) if err != nil { - return []*gql_generated.RepoSummary{}, err + return &gql_generated.PaginatedReposResult{}, err } for _, repoMeta := range reposMeta { @@ -226,15 +227,22 @@ func repoListWithNewestImage( repos = append(repos, repoSummary) } - return repos, nil + paginatedRepos.Page = &gql_generated.PageInfo{ + TotalCount: pageInfo.TotalCount, + ItemCount: pageInfo.ItemCount, + } + paginatedRepos.Results = repos + + return paginatedRepos, nil } func globalSearch(ctx context.Context, query string, repoDB repodb.RepoDB, filter *gql_generated.Filter, requestedPage *gql_generated.PageInput, cveInfo cveinfo.CveInfo, log log.Logger, //nolint:unparam -) ([]*gql_generated.RepoSummary, []*gql_generated.ImageSummary, []*gql_generated.LayerSummary, error, +) (*gql_generated.PaginatedReposResult, []*gql_generated.ImageSummary, []*gql_generated.LayerSummary, error, ) { preloads := convert.GetPreloads(ctx) repos := []*gql_generated.RepoSummary{} + paginatedRepos := gql_generated.PaginatedReposResult{} images := []*gql_generated.ImageSummary{} layers := []*gql_generated.LayerSummary{} @@ -264,9 +272,9 @@ func globalSearch(ctx context.Context, query string, repoDB repodb.RepoDB, filte ), } - reposMeta, manifestMetaMap, err := repoDB.SearchRepos(ctx, query, localFilter, pageInput) + reposMeta, manifestMetaMap, pageInfo, err := repoDB.SearchRepos(ctx, query, localFilter, pageInput) if err != nil { - return []*gql_generated.RepoSummary{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err + return &gql_generated.PaginatedReposResult{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err } for _, repoMeta := range reposMeta { @@ -274,6 +282,13 @@ func globalSearch(ctx context.Context, query string, repoDB repodb.RepoDB, filte repos = append(repos, repoSummary) } + + paginatedRepos.Page = &gql_generated.PageInfo{ + TotalCount: pageInfo.TotalCount, + ItemCount: pageInfo.ItemCount, + } + + paginatedRepos.Results = repos } else { // search for images skip := convert.SkipQGLField{ Vulnerabilities: canSkipField(preloads, "Images.Vulnerabilities"), @@ -287,9 +302,9 @@ func globalSearch(ctx context.Context, query string, repoDB repodb.RepoDB, filte ), } - reposMeta, manifestMetaMap, err := repoDB.SearchTags(ctx, query, localFilter, pageInput) + reposMeta, manifestMetaMap, pageInfo, err := repoDB.SearchTags(ctx, query, localFilter, pageInput) if err != nil { - return []*gql_generated.RepoSummary{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err + return &gql_generated.PaginatedReposResult{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err } for _, repoMeta := range reposMeta { @@ -297,9 +312,14 @@ func globalSearch(ctx context.Context, query string, repoDB repodb.RepoDB, filte images = append(images, imageSummaries...) } + + paginatedRepos.Page = &gql_generated.PageInfo{ + TotalCount: pageInfo.TotalCount, + ItemCount: pageInfo.ItemCount, + } } - return repos, images, layers, nil + return &paginatedRepos, images, layers, nil } func canSkipField(preloads map[string]bool, s string) bool { diff --git a/pkg/extensions/search/resolver_test.go b/pkg/extensions/search/resolver_test.go index 1db6fe85..2e4d0bd5 100644 --- a/pkg/extensions/search/resolver_test.go +++ b/pkg/extensions/search/resolver_test.go @@ -31,8 +31,8 @@ func TestGlobalSearch(t *testing.T) { Convey("RepoDB SearchRepos error", func() { mockRepoDB := mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { - return make([]repodb.RepoMetadata, 0), make(map[string]repodb.ManifestMetadata), ErrTestError + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { + return make([]repodb.RepoMetadata, 0), make(map[string]repodb.ManifestMetadata), repodb.PageInfo{}, ErrTestError }, } responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter, @@ -43,13 +43,13 @@ func TestGlobalSearch(t *testing.T) { So(err, ShouldNotBeNil) So(images, ShouldBeEmpty) So(layers, ShouldBeEmpty) - So(repos, ShouldBeEmpty) + So(repos.Results, ShouldBeEmpty) }) Convey("RepoDB SearchRepo is successful", func() { mockRepoDB := mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { repos := []repodb.RepoMetadata{ { Name: "repo1", @@ -101,7 +101,7 @@ func TestGlobalSearch(t *testing.T) { }, } - return repos, manifestMetas, nil + return repos, manifestMetas, repodb.PageInfo{}, nil }, } @@ -123,14 +123,14 @@ func TestGlobalSearch(t *testing.T) { So(err, ShouldBeNil) So(images, ShouldBeEmpty) So(layers, ShouldBeEmpty) - So(repos, ShouldNotBeEmpty) - So(len(repos[0].Vendors), ShouldEqual, 2) + So(repos.Results, ShouldNotBeEmpty) + So(len(repos.Results[0].Vendors), ShouldEqual, 2) }) Convey("RepoDB SearchRepo Bad manifest referenced", func() { mockRepoDB := mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { repos := []repodb.RepoMetadata{ { Name: "repo1", @@ -154,7 +154,7 @@ func TestGlobalSearch(t *testing.T) { }, } - return repos, manifestMetas, nil + return repos, manifestMetas, repodb.PageInfo{}, nil }, } @@ -188,13 +188,13 @@ func TestGlobalSearch(t *testing.T) { So(err, ShouldBeNil) So(images, ShouldBeEmpty) So(layers, ShouldBeEmpty) - So(repos, ShouldBeEmpty) + So(repos.Results, ShouldBeEmpty) }) Convey("RepoDB SearchRepo good manifest referenced and bad config blob", func() { mockRepoDB := mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { repos := []repodb.RepoMetadata{ { Name: "repo1", @@ -218,7 +218,7 @@ func TestGlobalSearch(t *testing.T) { }, } - return repos, manifestMetas, nil + return repos, manifestMetas, repodb.PageInfo{}, nil }, } @@ -241,7 +241,7 @@ func TestGlobalSearch(t *testing.T) { So(err, ShouldBeNil) So(images, ShouldBeEmpty) So(layers, ShouldBeEmpty) - So(repos, ShouldNotBeEmpty) + So(repos.Results, ShouldNotBeEmpty) query = "repo1:1.0.1" responseContext = graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter, @@ -251,14 +251,14 @@ func TestGlobalSearch(t *testing.T) { So(err, ShouldBeNil) So(images, ShouldBeEmpty) So(layers, ShouldBeEmpty) - So(repos, ShouldBeEmpty) + So(repos.Results, ShouldBeEmpty) }) Convey("RepoDB SearchTags gives error", func() { mockRepoDB := mocks.RepoDBMock{ SearchTagsFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { - return make([]repodb.RepoMetadata, 0), make(map[string]repodb.ManifestMetadata), ErrTestError + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { + return make([]repodb.RepoMetadata, 0), make(map[string]repodb.ManifestMetadata), repodb.PageInfo{}, ErrTestError }, } const query = "repo1:1.0.1" @@ -271,13 +271,13 @@ func TestGlobalSearch(t *testing.T) { So(err, ShouldNotBeNil) So(images, ShouldBeEmpty) So(layers, ShouldBeEmpty) - So(repos, ShouldBeEmpty) + So(repos.Results, ShouldBeEmpty) }) Convey("RepoDB SearchTags is successful", func() { mockRepoDB := mocks.RepoDBMock{ SearchTagsFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { repos := []repodb.RepoMetadata{ { Name: "repo1", @@ -323,7 +323,7 @@ func TestGlobalSearch(t *testing.T) { }, } - return repos, manifestMetas, nil + return repos, manifestMetas, repodb.PageInfo{}, nil }, } @@ -346,7 +346,7 @@ func TestGlobalSearch(t *testing.T) { So(err, ShouldBeNil) So(images, ShouldNotBeEmpty) So(layers, ShouldBeEmpty) - So(repos, ShouldBeEmpty) + So(repos.Results, ShouldBeEmpty) }) }) } @@ -356,8 +356,8 @@ func TestRepoListWithNewestImage(t *testing.T) { Convey("RepoDB SearchRepos error", func() { mockRepoDB := mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { - return make([]repodb.RepoMetadata, 0), make(map[string]repodb.ManifestMetadata), ErrTestError + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { + return make([]repodb.RepoMetadata, 0), make(map[string]repodb.ManifestMetadata), repodb.PageInfo{}, ErrTestError }, } responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter, @@ -374,13 +374,13 @@ func TestRepoListWithNewestImage(t *testing.T) { } repos, err := repoListWithNewestImage(responseContext, mockCve, log.NewLogger("debug", ""), &pageInput, mockRepoDB) So(err, ShouldNotBeNil) - So(repos, ShouldBeEmpty) + So(repos.Results, ShouldBeEmpty) }) Convey("RepoDB SearchRepo Bad manifest referenced", func() { mockRepoDB := mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { repos := []repodb.RepoMetadata{ { Name: "repo1", @@ -422,7 +422,7 @@ func TestRepoListWithNewestImage(t *testing.T) { }, } - return repos, manifestMetas, nil + return repos, manifestMetas, repodb.PageInfo{}, nil }, } @@ -440,7 +440,7 @@ func TestRepoListWithNewestImage(t *testing.T) { } repos, err := repoListWithNewestImage(responseContext, mockCve, log.NewLogger("debug", ""), &pageInput, mockRepoDB) So(err, ShouldBeNil) - So(repos, ShouldNotBeEmpty) + So(repos.Results, ShouldNotBeEmpty) }) Convey("Working SearchRepo function", func() { @@ -448,7 +448,7 @@ func TestRepoListWithNewestImage(t *testing.T) { createTime2 := createTime.Add(time.Second) mockRepoDB := mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { pageFinder, err := repodb.NewBaseRepoPageFinder(requestedPage.Limit, requestedPage.Offset, requestedPage.SortBy) So(err, ShouldBeNil) @@ -483,7 +483,7 @@ func TestRepoListWithNewestImage(t *testing.T) { createTime = createTime.Add(time.Second) } - repos = pageFinder.Page() + repos, _ = pageFinder.Page() configBlob1, err := json.Marshal(ispec.Image{ Config: ispec.ImageConfig{ @@ -515,7 +515,7 @@ func TestRepoListWithNewestImage(t *testing.T) { }, } - return repos, manifestMetas, nil + return repos, manifestMetas, repodb.PageInfo{}, nil }, } Convey("RepoDB missing requestedPage", func() { @@ -524,7 +524,7 @@ func TestRepoListWithNewestImage(t *testing.T) { mockCve := mocks.CveInfoMock{} repos, err := repoListWithNewestImage(responseContext, mockCve, log.NewLogger("debug", ""), nil, mockRepoDB) So(err, ShouldBeNil) - So(repos, ShouldNotBeEmpty) + So(repos.Results, ShouldNotBeEmpty) }) Convey("RepoDB SearchRepo is successful", func() { @@ -545,9 +545,9 @@ func TestRepoListWithNewestImage(t *testing.T) { log.NewLogger("debug", ""), &pageInput, mockRepoDB) So(err, ShouldBeNil) So(repos, ShouldNotBeEmpty) - So(len(repos), ShouldEqual, 2) - So(*repos[0].Name, ShouldEqual, "repo2") - So(*repos[0].LastUpdated, ShouldEqual, createTime2) + So(len(repos.Results), ShouldEqual, 2) + So(*repos.Results[0].Name, ShouldEqual, "repo2") + So(*repos.Results[0].LastUpdated, ShouldEqual, createTime2) }) }) }) @@ -960,7 +960,7 @@ func TestImageListForDigest(t *testing.T) { }) } - repos = pageFinder.Page() + repos, _ = pageFinder.Page() return repos, manifestMetaDatas, nil }, @@ -1264,8 +1264,8 @@ func TestQueryResolverErrors(t *testing.T) { mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { - return nil, nil, ErrTestError + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { + return nil, nil, repodb.PageInfo{}, ErrTestError }, }, mocks.CveInfoMock{}, @@ -1347,8 +1347,8 @@ func TestQueryResolverErrors(t *testing.T) { mocks.RepoDBMock{ SearchReposFn: func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, - ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { - return nil, nil, ErrTestError + ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { + return nil, nil, repodb.PageInfo{}, ErrTestError }, }, mocks.CveInfoMock{}, diff --git a/pkg/extensions/search/schema.graphql b/pkg/extensions/search/schema.graphql index 77aa0e6a..e8765413 100644 --- a/pkg/extensions/search/schema.graphql +++ b/pkg/extensions/search/schema.graphql @@ -167,10 +167,8 @@ enum SortCriteria { } type PageInfo { - ObjectCount: Int! - PreviousPage: Int - NextPage: Int - Pages: Int + TotalCount: Int! + ItemCount: Int! } # Pagination parameters @@ -180,6 +178,20 @@ input PageInput { sortBy: SortCriteria } +# Paginated list of RepoSummary objects +# If limit is -1, pagination is disabled +type PaginatedReposResult { + Page: PageInfo + Results: [RepoSummary!]! +} + +# Paginated list of ImageSummary objects +# If limit is -1, pagination is disabled +type PaginatedImagesResult { + Page: PageInfo + Results: [ImageSummary!]! +} + input Filter { Os: [String] Arch: [String] @@ -210,7 +222,7 @@ type Query { """ Returns a list of repos with the newest tag within """ - RepoListWithNewestImage(requestedPage: PageInput): [RepoSummary!]! # Newest based on created timestamp + RepoListWithNewestImage(requestedPage: PageInput): PaginatedReposResult! # Newest based on created timestamp """ Returns all the images from the specified repo diff --git a/pkg/extensions/search/schema.resolvers.go b/pkg/extensions/search/schema.resolvers.go index 5310a2a0..6265ff92 100644 --- a/pkg/extensions/search/schema.resolvers.go +++ b/pkg/extensions/search/schema.resolvers.go @@ -172,17 +172,17 @@ func (r *queryResolver) ImageListForDigest(ctx context.Context, id string, reque } // RepoListWithNewestImage is the resolver for the RepoListWithNewestImage field. -func (r *queryResolver) RepoListWithNewestImage(ctx context.Context, requestedPage *gql_generated.PageInput) ([]*gql_generated.RepoSummary, error) { +func (r *queryResolver) RepoListWithNewestImage(ctx context.Context, requestedPage *gql_generated.PageInput) (*gql_generated.PaginatedReposResult, error) { r.log.Info().Msg("extension api: finding image list") - reposSummary, err := repoListWithNewestImage(ctx, r.cveInfo, r.log, requestedPage, r.repoDB) + paginatedReposResult, err := repoListWithNewestImage(ctx, r.cveInfo, r.log, requestedPage, r.repoDB) if err != nil { r.log.Error().Err(err).Msg("unable to retrieve repo list") - return reposSummary, err + return paginatedReposResult, err } - return reposSummary, nil + return paginatedReposResult, nil } // ImageList is the resolver for the ImageList field. @@ -238,11 +238,12 @@ func (r *queryResolver) GlobalSearch(ctx context.Context, query string, filter * query = cleanQuery(query) filter = cleanFilter(filter) - repos, images, layers, err := globalSearch(ctx, query, r.repoDB, filter, requestedPage, r.cveInfo, r.log) + paginatedReposResult, images, layers, err := globalSearch(ctx, query, r.repoDB, filter, requestedPage, r.cveInfo, r.log) return &gql_generated.GlobalSearchResult{ + Page: paginatedReposResult.Page, Images: images, - Repos: repos, + Repos: paginatedReposResult.Results, Layers: layers, }, err } diff --git a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go index 02a1824f..bcacefcc 100644 --- a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go +++ b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go @@ -457,7 +457,7 @@ func (bdw DBWrapper) GetMultipleRepoMeta(ctx context.Context, filter func(repoMe } } - foundRepos = pageFinder.Page() + foundRepos, _ = pageFinder.Page() return nil }) @@ -622,16 +622,17 @@ func (bdw DBWrapper) DeleteSignature(repo string, signedManifestDigest godigest. func (bdw DBWrapper) SearchRepos(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, -) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { +) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { var ( foundRepos = make([]repodb.RepoMetadata, 0) foundManifestMetadataMap = make(map[string]repodb.ManifestMetadata) pageFinder repodb.PageFinder + pageInfo repodb.PageInfo ) pageFinder, err := repodb.NewBaseRepoPageFinder(requestedPage.Limit, requestedPage.Offset, requestedPage.SortBy) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, repodb.PageInfo{}, err } err = bdw.DB.View(func(tx *bolt.Tx) error { @@ -728,7 +729,7 @@ func (bdw DBWrapper) SearchRepos(ctx context.Context, searchText string, filter } } - foundRepos = pageFinder.Page() + foundRepos, pageInfo = pageFinder.Page() // keep just the manifestMeta we need for _, repoMeta := range foundRepos { @@ -740,7 +741,7 @@ func (bdw DBWrapper) SearchRepos(ctx context.Context, searchText string, filter return nil }) - return foundRepos, foundManifestMetadataMap, err + return foundRepos, foundManifestMetadataMap, pageInfo, err } func (bdw DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, @@ -835,7 +836,7 @@ func (bdw DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, }) } - foundRepos = pageFinder.Page() + foundRepos, _ = pageFinder.Page() // keep just the manifestMeta we need for _, repoMeta := range foundRepos { @@ -852,22 +853,23 @@ func (bdw DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, func (bdw DBWrapper) SearchTags(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, -) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { +) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { var ( foundRepos = make([]repodb.RepoMetadata, 0) foundManifestMetadataMap = make(map[string]repodb.ManifestMetadata) + pageInfo repodb.PageInfo pageFinder repodb.PageFinder ) pageFinder, err := repodb.NewBaseImagePageFinder(requestedPage.Limit, requestedPage.Offset, requestedPage.SortBy) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, repodb.PageInfo{}, err } searchedRepo, searchedTag, err := common.GetRepoTag(searchText) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, repodb.PageInfo{}, errors.Wrap(err, "repodb: error while parsing search text, invalid format") } @@ -957,7 +959,7 @@ func (bdw DBWrapper) SearchTags(ctx context.Context, searchText string, filter r } } - foundRepos = pageFinder.Page() + foundRepos, pageInfo = pageFinder.Page() // keep just the manifestMeta we need for _, repoMeta := range foundRepos { @@ -969,7 +971,7 @@ func (bdw DBWrapper) SearchTags(ctx context.Context, searchText string, filter r return nil }) - return foundRepos, foundManifestMetadataMap, err + return foundRepos, foundManifestMetadataMap, pageInfo, err } func (bdw *DBWrapper) PatchDB() error { diff --git a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go index bb1d55c6..9b4448be 100644 --- a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go +++ b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go @@ -300,7 +300,7 @@ func TestWrapperErrors(t *testing.T) { }) So(err, ShouldBeNil) - _, _, err = boltdbWrapper.SearchRepos(context.Background(), "", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchRepos(context.Background(), "", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) err = boltdbWrapper.DB.Update(func(tx *bbolt.Tx) error { @@ -339,10 +339,10 @@ func TestWrapperErrors(t *testing.T) { }) So(err, ShouldBeNil) - _, _, err = boltdbWrapper.SearchRepos(context.Background(), "repo1", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchRepos(context.Background(), "repo1", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) - _, _, err = boltdbWrapper.SearchRepos(context.Background(), "repo2", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchRepos(context.Background(), "repo2", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) err = boltdbWrapper.DB.Update(func(tx *bbolt.Tx) error { @@ -378,7 +378,7 @@ func TestWrapperErrors(t *testing.T) { }) So(err, ShouldBeNil) - _, _, err = boltdbWrapper.SearchRepos(context.Background(), "repo1", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchRepos(context.Background(), "repo1", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) @@ -392,10 +392,10 @@ func TestWrapperErrors(t *testing.T) { }) So(err, ShouldBeNil) - _, _, err = boltdbWrapper.SearchTags(ctx, "", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchTags(ctx, "", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) - _, _, err = boltdbWrapper.SearchTags(ctx, "repo1:", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchTags(ctx, "repo1:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) err = boltdbWrapper.DB.Update(func(tx *bbolt.Tx) error { @@ -466,13 +466,13 @@ func TestWrapperErrors(t *testing.T) { }) So(err, ShouldBeNil) - _, _, err = boltdbWrapper.SearchTags(ctx, "repo1:", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchTags(ctx, "repo1:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) - _, _, err = boltdbWrapper.SearchTags(ctx, "repo2:", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchTags(ctx, "repo2:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) - _, _, err = boltdbWrapper.SearchTags(ctx, "repo3:", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = boltdbWrapper.SearchTags(ctx, "repo3:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) }) diff --git a/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go b/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go index 6a679ccd..8d8294b5 100644 --- a/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go +++ b/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go @@ -329,7 +329,7 @@ func TestWrapperErrors(t *testing.T) { err = setBadRepoMeta(dynamoWrapper.Client, repoMetaTablename, "repo") //nolint:contextcheck So(err, ShouldBeNil) - _, _, err = dynamoWrapper.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = dynamoWrapper.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) @@ -338,7 +338,7 @@ func TestWrapperErrors(t *testing.T) { err := dynamoWrapper.SetRepoTag("repo", "tag1", "notFoundDigest", "") //nolint:contextcheck So(err, ShouldBeNil) - _, _, err = dynamoWrapper.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = dynamoWrapper.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) @@ -353,7 +353,7 @@ func TestWrapperErrors(t *testing.T) { }) So(err, ShouldBeNil) - _, _, err = dynamoWrapper.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = dynamoWrapper.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) @@ -362,7 +362,7 @@ func TestWrapperErrors(t *testing.T) { err = setBadRepoMeta(dynamoWrapper.Client, repoMetaTablename, "repo") //nolint:contextcheck So(err, ShouldBeNil) - _, _, err = dynamoWrapper.SearchTags(ctx, "repo:", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = dynamoWrapper.SearchTags(ctx, "repo:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) @@ -371,7 +371,7 @@ func TestWrapperErrors(t *testing.T) { err := dynamoWrapper.SetRepoTag("repo", "tag1", "manifestNotFound", "") //nolint:contextcheck So(err, ShouldBeNil) - _, _, err = dynamoWrapper.SearchTags(ctx, "repo:", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = dynamoWrapper.SearchTags(ctx, "repo:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) @@ -389,7 +389,7 @@ func TestWrapperErrors(t *testing.T) { ) So(err, ShouldBeNil) - _, _, err = dynamoWrapper.SearchTags(ctx, "repo:", repodb.Filter{}, repodb.PageInput{}) + _, _, _, err = dynamoWrapper.SearchTags(ctx, "repo:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) }) diff --git a/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go b/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go index a65ba567..4a75034b 100644 --- a/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go +++ b/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go @@ -524,20 +524,21 @@ func (dwr DBWrapper) GetMultipleRepoMeta(ctx context.Context, } } - foundRepos := pageFinder.Page() + foundRepos, _ := pageFinder.Page() return foundRepos, err } func (dwr DBWrapper) SearchRepos(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, -) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { +) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { var ( foundManifestMetadataMap = make(map[string]repodb.ManifestMetadata) manifestMetadataMap = make(map[string]repodb.ManifestMetadata) repoMetaAttributeIterator iterator.AttributesIterator pageFinder repodb.PageFinder + pageInfo repodb.PageInfo ) repoMetaAttributeIterator = iterator.NewBaseDynamoAttributesIterator( @@ -546,7 +547,7 @@ func (dwr DBWrapper) SearchRepos(ctx context.Context, searchText string, filter pageFinder, err := repodb.NewBaseRepoPageFinder(requestedPage.Limit, requestedPage.Offset, requestedPage.SortBy) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, err } repoMetaAttribute, err := repoMetaAttributeIterator.First(ctx) @@ -554,14 +555,14 @@ func (dwr DBWrapper) SearchRepos(ctx context.Context, searchText string, filter for ; repoMetaAttribute != nil; repoMetaAttribute, err = repoMetaAttributeIterator.Next(ctx) { if err != nil { // log - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, err } var repoMeta repodb.RepoMetadata err := attributevalue.Unmarshal(repoMetaAttribute, &repoMeta) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, err } if ok, err := localCtx.RepoIsUserAvailable(ctx, repoMeta.Name); !ok || err != nil { @@ -587,7 +588,7 @@ func (dwr DBWrapper) SearchRepos(ctx context.Context, searchText string, filter if !manifestDownloaded { manifestMeta, err = dwr.GetManifestMeta(repoMeta.Name, godigest.Digest(descriptor.Digest)) //nolint:contextcheck if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, errors.Wrapf(err, "repodb: error while unmarshaling manifest metadata for digest %s", descriptor.Digest) } } @@ -597,7 +598,7 @@ func (dwr DBWrapper) SearchRepos(ctx context.Context, searchText string, filter err = json.Unmarshal(manifestMeta.ConfigBlob, &configContent) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, errors.Wrapf(err, "repodb: error while unmarshaling config content for digest %s", descriptor.Digest) } @@ -638,7 +639,7 @@ func (dwr DBWrapper) SearchRepos(ctx context.Context, searchText string, filter } } - foundRepos := pageFinder.Page() + foundRepos, pageInfo := pageFinder.Page() // keep just the manifestMeta we need for _, repoMeta := range foundRepos { @@ -647,7 +648,7 @@ func (dwr DBWrapper) SearchRepos(ctx context.Context, searchText string, filter } } - return foundRepos, foundManifestMetadataMap, err + return foundRepos, foundManifestMetadataMap, pageInfo, err } func (dwr DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, @@ -733,7 +734,7 @@ func (dwr DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, }) } - foundRepos := pageFinder.Page() + foundRepos, _ := pageFinder.Page() // keep just the manifestMeta we need for _, repoMeta := range foundRepos { @@ -747,7 +748,7 @@ func (dwr DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, func (dwr DBWrapper) SearchTags(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, -) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { +) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { var ( foundManifestMetadataMap = make(map[string]repodb.ManifestMetadata) manifestMetadataMap = make(map[string]repodb.ManifestMetadata) @@ -756,16 +757,17 @@ func (dwr DBWrapper) SearchTags(ctx context.Context, searchText string, filter r ) pageFinder repodb.PageFinder + pageInfo repodb.PageInfo ) pageFinder, err := repodb.NewBaseImagePageFinder(requestedPage.Limit, requestedPage.Offset, requestedPage.SortBy) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, err } searchedRepo, searchedTag, err := common.GetRepoTag(searchText) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, errors.Wrap(err, "repodb: error while parsing search text, invalid format") } @@ -774,14 +776,14 @@ func (dwr DBWrapper) SearchTags(ctx context.Context, searchText string, filter r for ; repoMetaAttribute != nil; repoMetaAttribute, err = repoMetaAttributeIterator.Next(ctx) { if err != nil { // log - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, err } var repoMeta repodb.RepoMetadata err := attributevalue.Unmarshal(repoMetaAttribute, &repoMeta) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, err + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, err } if ok, err := localCtx.RepoIsUserAvailable(ctx, repoMeta.Name); !ok || err != nil { @@ -807,7 +809,7 @@ func (dwr DBWrapper) SearchTags(ctx context.Context, searchText string, filter r manifestMeta, err := dwr.GetManifestMeta(repoMeta.Name, godigest.Digest(descriptor.Digest)) //nolint:contextcheck if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, errors.Wrapf(err, "repodb: error while unmashaling manifest metadata for digest %s", descriptor.Digest) } @@ -815,7 +817,7 @@ func (dwr DBWrapper) SearchTags(ctx context.Context, searchText string, filter r err = json.Unmarshal(manifestMeta.ConfigBlob, &configContent) if err != nil { - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, pageInfo, errors.Wrapf(err, "repodb: error while unmashaling config for manifest with digest %s", descriptor.Digest) } @@ -847,7 +849,7 @@ func (dwr DBWrapper) SearchTags(ctx context.Context, searchText string, filter r } } - foundRepos := pageFinder.Page() + foundRepos, pageInfo := pageFinder.Page() // keep just the manifestMeta we need for _, repoMeta := range foundRepos { @@ -856,7 +858,7 @@ func (dwr DBWrapper) SearchTags(ctx context.Context, searchText string, filter r } } - return foundRepos, foundManifestMetadataMap, err + return foundRepos, foundManifestMetadataMap, pageInfo, err } func (dwr *DBWrapper) PatchDB() error { diff --git a/pkg/meta/repodb/pagination.go b/pkg/meta/repodb/pagination.go index 5f756c54..54ab0ad5 100644 --- a/pkg/meta/repodb/pagination.go +++ b/pkg/meta/repodb/pagination.go @@ -11,9 +11,8 @@ import ( // PageFinder permits keeping a pool of objects using Add // and returning a specific page. type PageFinder interface { - // Add Add(detailedRepoMeta DetailedRepoMeta) - Page() []RepoMetadata + Page() ([]RepoMetadata, PageInfo) Reset() } @@ -59,11 +58,13 @@ func (bpt *RepoPageFinder) Add(namedRepoMeta DetailedRepoMeta) { bpt.pageBuffer = append(bpt.pageBuffer, namedRepoMeta) } -func (bpt *RepoPageFinder) Page() []RepoMetadata { +func (bpt *RepoPageFinder) Page() ([]RepoMetadata, PageInfo) { if len(bpt.pageBuffer) == 0 { - return []RepoMetadata{} + return []RepoMetadata{}, PageInfo{} } + pageInfo := &PageInfo{} + sort.Slice(bpt.pageBuffer, SortFunctions()[bpt.sortBy](bpt.pageBuffer)) // the offset and limit are calculatd in terms of repos counted @@ -82,8 +83,11 @@ func (bpt *RepoPageFinder) Page() []RepoMetadata { detailedReposPage := bpt.pageBuffer[start:end] + pageInfo.ItemCount = len(detailedReposPage) + if start == 0 && end == 0 { detailedReposPage = bpt.pageBuffer + pageInfo.ItemCount = len(detailedReposPage) } repos := make([]RepoMetadata, 0, len(detailedReposPage)) @@ -92,7 +96,9 @@ func (bpt *RepoPageFinder) Page() []RepoMetadata { repos = append(repos, drm.RepoMeta) } - return repos + pageInfo.TotalCount = len(bpt.pageBuffer) + + return repos, *pageInfo } type ImagePageFinder struct { @@ -135,9 +141,16 @@ func (bpt *ImagePageFinder) Add(namedRepoMeta DetailedRepoMeta) { bpt.pageBuffer = append(bpt.pageBuffer, namedRepoMeta) } -func (bpt *ImagePageFinder) Page() []RepoMetadata { +func (bpt *ImagePageFinder) Page() ([]RepoMetadata, PageInfo) { if len(bpt.pageBuffer) == 0 { - return []RepoMetadata{} + return []RepoMetadata{}, PageInfo{} + } + + pageInfo := PageInfo{} + + for _, drm := range bpt.pageBuffer { + repo := drm.RepoMeta + pageInfo.TotalCount += len(repo.Tags) } sort.Slice(bpt.pageBuffer, SortFunctions()[bpt.sortBy](bpt.pageBuffer)) @@ -155,9 +168,11 @@ func (bpt *ImagePageFinder) Page() []RepoMetadata { for _, drm := range bpt.pageBuffer { repo := drm.RepoMeta repos = append(repos, repo) + + pageInfo.ItemCount += len(repo.Tags) } - return repos + return repos, pageInfo } // bring cursor to position in RepoMeta array @@ -174,7 +189,7 @@ func (bpt *ImagePageFinder) Page() []RepoMetadata { // offset is larger than the number of tags if repoStartIndex >= len(bpt.pageBuffer) { - return []RepoMetadata{} + return []RepoMetadata{}, PageInfo{} } // finish counting remaining tags inside the first repo meta @@ -197,12 +212,14 @@ func (bpt *ImagePageFinder) Page() []RepoMetadata { if remainingLimit == 0 { firstRepoMeta.Tags = partialTags repos = append(repos, firstRepoMeta) + pageInfo.ItemCount = len(partialTags) - return repos + return repos, pageInfo } } firstRepoMeta.Tags = partialTags + pageInfo.ItemCount += len(firstRepoMeta.Tags) repos = append(repos, firstRepoMeta) repoStartIndex++ @@ -228,23 +245,26 @@ func (bpt *ImagePageFinder) Page() []RepoMetadata { repoMeta.Tags = partialTags repos = append(repos, repoMeta) + pageInfo.ItemCount += len(partialTags) + break } } - return repos + return repos, pageInfo } // add the whole repo repos = append(repos, repoMeta) + pageInfo.ItemCount += len(repoMeta.Tags) remainingLimit -= len(repoMeta.Tags) if remainingLimit == 0 { - return repos + return repos, pageInfo } } // we arrive here when the limit is bigger than the number of tags - return repos + return repos, pageInfo } diff --git a/pkg/meta/repodb/pagination_test.go b/pkg/meta/repodb/pagination_test.go index be18d935..a3c2f19d 100644 --- a/pkg/meta/repodb/pagination_test.go +++ b/pkg/meta/repodb/pagination_test.go @@ -22,7 +22,8 @@ func TestPagination(t *testing.T) { pageFinder.Reset() - So(pageFinder.Page(), ShouldBeEmpty) + result, _ := pageFinder.Page() + So(result, ShouldBeEmpty) }) }) @@ -52,11 +53,70 @@ func TestPagination(t *testing.T) { pageFinder.Reset() - So(pageFinder.Page(), ShouldBeEmpty) + result, _ := pageFinder.Page() + So(result, ShouldBeEmpty) }) Convey("Page", func() { - Convey("limit < len(tags)", func() { + Convey("no limit or offset", func() { + pageFinder, err := repodb.NewBaseImagePageFinder(0, 0, repodb.AlphabeticAsc) + So(err, ShouldBeNil) + So(pageFinder, ShouldNotBeNil) + + pageFinder.Add(repodb.DetailedRepoMeta{ + RepoMeta: repodb.RepoMetadata{ + Name: "repo1", + Tags: map[string]repodb.Descriptor{ + "tag1": {Digest: "dig1", MediaType: ispec.MediaTypeImageManifest}, + }, + }, + }) + + pageFinder.Add(repodb.DetailedRepoMeta{ + RepoMeta: repodb.RepoMetadata{ + Name: "repo2", + Tags: map[string]repodb.Descriptor{ + "Tag1": {Digest: "dig1", MediaType: ispec.MediaTypeImageManifest}, + "Tag2": {Digest: "dig2", MediaType: ispec.MediaTypeImageManifest}, + "Tag3": {Digest: "dig3", MediaType: ispec.MediaTypeImageManifest}, + "Tag4": {Digest: "dig4", MediaType: ispec.MediaTypeImageManifest}, + }, + }, + }) + _, pageInfo := pageFinder.Page() + So(pageInfo.ItemCount, ShouldEqual, 5) + So(pageInfo.TotalCount, ShouldEqual, 5) + }) + Convey("Test 1 limit < len(tags)", func() { + pageFinder, err := repodb.NewBaseImagePageFinder(5, 2, repodb.AlphabeticAsc) + So(err, ShouldBeNil) + So(pageFinder, ShouldNotBeNil) + + pageFinder.Add(repodb.DetailedRepoMeta{ + RepoMeta: repodb.RepoMetadata{ + Name: "repo1", + Tags: map[string]repodb.Descriptor{ + "tag1": {Digest: "dig1", MediaType: ispec.MediaTypeImageManifest}, + }, + }, + }) + + pageFinder.Add(repodb.DetailedRepoMeta{ + RepoMeta: repodb.RepoMetadata{ + Name: "repo2", + Tags: map[string]repodb.Descriptor{ + "Tag1": {Digest: "dig1", MediaType: ispec.MediaTypeImageManifest}, + "Tag2": {Digest: "dig2", MediaType: ispec.MediaTypeImageManifest}, + "Tag3": {Digest: "dig3", MediaType: ispec.MediaTypeImageManifest}, + "Tag4": {Digest: "dig4", MediaType: ispec.MediaTypeImageManifest}, + }, + }, + }) + _, pageInfo := pageFinder.Page() + So(pageInfo.ItemCount, ShouldEqual, 3) + So(pageInfo.TotalCount, ShouldEqual, 5) + }) + Convey("Test 2 limit < len(tags)", func() { pageFinder, err := repodb.NewBaseImagePageFinder(5, 2, repodb.AlphabeticAsc) So(err, ShouldBeNil) So(pageFinder, ShouldNotBeNil) @@ -120,15 +180,17 @@ func TestPagination(t *testing.T) { }, }) - result := pageFinder.Page() + result, pageInfo := pageFinder.Page() So(result[0].Tags, ShouldContainKey, "Tag2") So(result[0].Tags, ShouldContainKey, "Tag3") So(result[0].Tags, ShouldContainKey, "Tag4") So(result[1].Tags, ShouldContainKey, "Tag11") So(result[1].Tags, ShouldContainKey, "Tag12") + So(pageInfo.ItemCount, ShouldEqual, 5) + So(pageInfo.TotalCount, ShouldEqual, 9) }) - Convey("limit > len(tags)", func() { + Convey("Test 2 limit > len(tags)", func() { pageFinder, err := repodb.NewBaseImagePageFinder(3, 0, repodb.AlphabeticAsc) So(err, ShouldBeNil) So(pageFinder, ShouldNotBeNil) @@ -168,7 +230,7 @@ func TestPagination(t *testing.T) { }, }) - result := pageFinder.Page() + result, _ := pageFinder.Page() So(result[0].Tags, ShouldContainKey, "tag1") So(result[1].Tags, ShouldContainKey, "Tag1") So(result[2].Tags, ShouldContainKey, "Tag11") diff --git a/pkg/meta/repodb/repodb.go b/pkg/meta/repodb/repodb.go index 91cdea97..d3fefdfe 100644 --- a/pkg/meta/repodb/repodb.go +++ b/pkg/meta/repodb/repodb.go @@ -70,11 +70,11 @@ type RepoDB interface { //nolint:interfacebloat // SearchRepos searches for repos given a search string SearchRepos(ctx context.Context, searchText string, filter Filter, requestedPage PageInput) ( - []RepoMetadata, map[string]ManifestMetadata, error) + []RepoMetadata, map[string]ManifestMetadata, PageInfo, error) // SearchTags searches for images(repo:tag) given a search string SearchTags(ctx context.Context, searchText string, filter Filter, requestedPage PageInput) ( - []RepoMetadata, map[string]ManifestMetadata, error) + []RepoMetadata, map[string]ManifestMetadata, PageInfo, error) // FilterTags filters for images given a filter function FilterTags(ctx context.Context, filter FilterFunc, @@ -151,6 +151,11 @@ type PageInput struct { SortBy SortCriteria } +type PageInfo struct { + TotalCount int + ItemCount int +} + type Filter struct { Os []*string Arch []*string diff --git a/pkg/meta/repodb/repodb_test.go b/pkg/meta/repodb/repodb_test.go index 13e77f13..57573195 100644 --- a/pkg/meta/repodb/repodb_test.go +++ b/pkg/meta/repodb/repodb_test.go @@ -604,7 +604,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { err = repoDB.SetManifestMeta(repo1, manifestDigest3, emptyRepoMeta) So(err, ShouldBeNil) - repos, manifesMetaMap, err := repoDB.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 2) So(len(manifesMetaMap), ShouldEqual, 3) @@ -620,7 +620,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { err = repoDB.SetManifestMeta(repo1, manifestDigest1, emptyRepoMeta) So(err, ShouldBeNil) - repos, manifesMetaMap, err := repoDB.SearchRepos(ctx, repo1, repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchRepos(ctx, repo1, repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 1) So(len(manifesMetaMap), ShouldEqual, 1) @@ -634,7 +634,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { err = repoDB.SetRepoTag(repo1, tag2, manifestDigest2, ispec.MediaTypeImageManifest) So(err, ShouldBeNil) - repos, manifesMetaMap, err := repoDB.SearchRepos(ctx, "RepoThatDoesntExist", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchRepos(ctx, "RepoThatDoesntExist", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 0) So(len(manifesMetaMap), ShouldEqual, 0) @@ -655,7 +655,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { err = repoDB.SetManifestMeta("golang", manifestDigest3, emptyRepoMeta) So(err, ShouldBeNil) - repos, manifesMetaMap, err := repoDB.SearchRepos(ctx, "pine", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchRepos(ctx, "pine", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 2) So(manifesMetaMap, ShouldContainKey, manifestDigest1.String()) @@ -678,7 +678,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { err = repoDB.SetManifestMeta(repo3, manifestDigest1, emptyRepoMeta) So(err, ShouldBeNil) - repos, manifesMetaMap, err := repoDB.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchRepos(ctx, "", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 3) So(len(manifesMetaMap), ShouldEqual, 1) @@ -709,7 +709,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { authzCtxKey := localCtx.GetContextKey() ctx := context.WithValue(context.Background(), authzCtxKey, acCtx) - repos, _, err := repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{}) + repos, _, _, err := repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 2) for _, k := range repos { @@ -754,18 +754,18 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { repoNameBuilder.Reset() } - repos, _, err := repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{}) + repos, _, _, err := repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, reposCount) - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 20, SortBy: repodb.AlphabeticAsc, }) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 20) - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 0, SortBy: repodb.AlphabeticAsc, @@ -774,7 +774,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(len(repos), ShouldEqual, 1) So(repos[0].Name, ShouldResemble, "repo0") - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 1, SortBy: repodb.AlphabeticAsc, @@ -783,7 +783,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(len(repos), ShouldEqual, 1) So(repos[0].Name, ShouldResemble, "repo1") - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 49, SortBy: repodb.AlphabeticAsc, @@ -792,7 +792,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(len(repos), ShouldEqual, 1) So(repos[0].Name, ShouldResemble, "repo9") - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 49, SortBy: repodb.AlphabeticDsc, @@ -801,7 +801,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(len(repos), ShouldEqual, 1) So(repos[0].Name, ShouldResemble, "repo0") - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 0, SortBy: repodb.AlphabeticDsc, @@ -811,7 +811,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(repos[0].Name, ShouldResemble, "repo9") // sort by downloads - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 0, SortBy: repodb.Downloads, @@ -821,7 +821,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(repos[0].Name, ShouldResemble, "repo49") // sort by last update - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 0, SortBy: repodb.UpdateTime, @@ -830,7 +830,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(len(repos), ShouldEqual, 1) So(repos[0].Name, ShouldResemble, "repo49") - repos, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 100, SortBy: repodb.UpdateTime, @@ -841,28 +841,28 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { }) Convey("Search with wrong pagination input", func() { - _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + _, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 100, SortBy: repodb.UpdateTime, }) So(err, ShouldBeNil) - _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + _, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: -1, Offset: 100, SortBy: repodb.UpdateTime, }) So(err, ShouldNotBeNil) - _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + _, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: -1, SortBy: repodb.UpdateTime, }) So(err, ShouldNotBeNil) - _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ + _, _, _, err = repoDB.SearchRepos(ctx, "repo", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 1, SortBy: repodb.SortCriteria("InvalidSortingCriteria"), @@ -917,7 +917,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(err, ShouldBeNil) Convey("With exact match", func() { - repos, manifesMetaMap, err := repoDB.SearchTags(ctx, "repo1:0.0.1", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchTags(ctx, "repo1:0.0.1", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 1) So(len(repos[0].Tags), ShouldEqual, 1) @@ -926,14 +926,14 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { }) Convey("With partial repo path", func() { - repos, manifesMetaMap, err := repoDB.SearchTags(ctx, "repo:0.0.1", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchTags(ctx, "repo:0.0.1", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 0) So(len(manifesMetaMap), ShouldEqual, 0) }) Convey("With partial tag", func() { - repos, manifesMetaMap, err := repoDB.SearchTags(ctx, "repo1:0.0", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchTags(ctx, "repo1:0.0", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 1) So(len(repos[0].Tags), ShouldEqual, 2) @@ -942,7 +942,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { So(manifesMetaMap, ShouldContainKey, manifestDigest1.String()) So(manifesMetaMap, ShouldContainKey, manifestDigest3.String()) - repos, manifesMetaMap, err = repoDB.SearchTags(ctx, "repo1:0.", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err = repoDB.SearchTags(ctx, "repo1:0.", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 1) So(len(repos[0].Tags), ShouldEqual, 3) @@ -955,7 +955,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { }) Convey("With bad query", func() { - repos, manifesMetaMap, err := repoDB.SearchTags(ctx, "repo:0.0.1:test", repodb.Filter{}, repodb.PageInput{}) + repos, manifesMetaMap, _, err := repoDB.SearchTags(ctx, "repo:0.0.1:test", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldNotBeNil) So(len(repos), ShouldEqual, 0) So(len(manifesMetaMap), ShouldEqual, 0) @@ -1000,18 +1000,18 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { authzCtxKey := localCtx.GetContextKey() ctx := context.WithValue(context.Background(), authzCtxKey, acCtx) - repos, _, err := repoDB.SearchTags(ctx, "repo1:", repodb.Filter{}, repodb.PageInput{}) + repos, _, _, err := repoDB.SearchTags(ctx, "repo1:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 1) So(repos[0].Name, ShouldResemble, repo1) - repos, _, err = repoDB.SearchTags(ctx, "repo2:", repodb.Filter{}, repodb.PageInput{}) + repos, _, _, err = repoDB.SearchTags(ctx, "repo2:", repodb.Filter{}, repodb.PageInput{}) So(err, ShouldBeNil) So(repos, ShouldBeEmpty) }) Convey("With wrong pagination input", func() { - repos, _, err := repoDB.SearchTags(ctx, "repo2:", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err := repoDB.SearchTags(ctx, "repo2:", repodb.Filter{}, repodb.PageInput{ Limit: -1, }) So(err, ShouldNotBeNil) @@ -1048,7 +1048,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { err = repoDB.SetManifestMeta(repo1, manifestDigest1, repodb.ManifestMetadata{ConfigBlob: configBlob}) So(err, ShouldBeNil) - repos, _, err := repoDB.SearchTags(context.TODO(), "repo1:", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err := repoDB.SearchTags(context.TODO(), "repo1:", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 0, SortBy: repodb.AlphabeticAsc, @@ -1061,7 +1061,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { keys = append(keys, k) } - repos, _, err = repoDB.SearchTags(context.TODO(), "repo1:", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchTags(context.TODO(), "repo1:", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 1, SortBy: repodb.AlphabeticAsc, @@ -1073,7 +1073,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { keys = append(keys, k) } - repos, _, err = repoDB.SearchTags(context.TODO(), "repo1:", repodb.Filter{}, repodb.PageInput{ + repos, _, _, err = repoDB.SearchTags(context.TODO(), "repo1:", repodb.Filter{}, repodb.PageInput{ Limit: 1, Offset: 2, SortBy: repodb.AlphabeticAsc, @@ -1157,7 +1157,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { Os: []*string{&opSys}, } - repos, _, err := repoDB.SearchRepos(context.TODO(), "", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + repos, _, _, err := repoDB.SearchRepos(context.TODO(), "", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 2) So(repos[0].Name, ShouldResemble, "repo1") @@ -1167,7 +1167,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { filter = repodb.Filter{ Os: []*string{&opSys}, } - repos, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + repos, _, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 2) So(repos[0].Name, ShouldResemble, "repo1") @@ -1177,7 +1177,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { filter = repodb.Filter{ Os: []*string{&opSys}, } - repos, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + repos, _, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 0) @@ -1187,7 +1187,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { Os: []*string{&opSys}, Arch: []*string{&arch}, } - repos, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + repos, _, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 2) So(repos[0].Name, ShouldResemble, "repo1") @@ -1199,7 +1199,7 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { Os: []*string{&opSys}, Arch: []*string{&arch}, } - repos, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + repos, _, _, err = repoDB.SearchRepos(context.TODO(), "repo", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 1) }) @@ -1271,7 +1271,8 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { Os: []*string{&opSys}, Arch: []*string{&arch}, } - repos, _, err := repoDB.SearchTags(context.TODO(), "repo1:", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + repos, _, _, err := repoDB.SearchTags(context.TODO(), "repo1:", filter, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 1) So(repos[0].Tags, ShouldContainKey, tag1) @@ -1282,7 +1283,8 @@ func RunRepoDBTests(repoDB repodb.RepoDB, preparationFuncs ...func() error) { Os: []*string{&opSys}, Arch: []*string{&arch}, } - repos, _, err = repoDB.SearchTags(context.TODO(), "repo1:", filter, repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + repos, _, _, err = repoDB.SearchTags(context.TODO(), "repo1:", filter, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) So(err, ShouldBeNil) So(len(repos), ShouldEqual, 0) }) @@ -1516,7 +1518,7 @@ func TestRelevanceSorting(t *testing.T) { err = repoDB.SetManifestMeta(repo4, manifestDigest3, emptyRepoMeta) So(err, ShouldBeNil) - repos, _, err := repoDB.SearchRepos(ctx, "pine", repodb.Filter{}, + repos, _, _, err := repoDB.SearchRepos(ctx, "pine", repodb.Filter{}, repodb.PageInput{SortBy: repodb.Relevance}, ) diff --git a/pkg/test/mocks/repo_db_mock.go b/pkg/test/mocks/repo_db_mock.go index 544161ce..c79fd283 100644 --- a/pkg/test/mocks/repo_db_mock.go +++ b/pkg/test/mocks/repo_db_mock.go @@ -43,10 +43,10 @@ type RepoDBMock struct { DeleteSignatureFn func(repo string, signedManifestDigest godigest.Digest, sm repodb.SignatureMetadata) error SearchReposFn func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput) ( - []repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) + []repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) SearchTagsFn func(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput) ( - []repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) + []repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) FilterTagsFn func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, @@ -203,22 +203,22 @@ func (sdm RepoDBMock) DeleteSignature(repo string, signedManifestDigest godigest func (sdm RepoDBMock) SearchRepos(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, -) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { +) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { if sdm.SearchReposFn != nil { return sdm.SearchReposFn(ctx, searchText, filter, requestedPage) } - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, nil + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, repodb.PageInfo{}, nil } func (sdm RepoDBMock) SearchTags(ctx context.Context, searchText string, filter repodb.Filter, requestedPage repodb.PageInput, -) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, error) { +) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, repodb.PageInfo, error) { if sdm.SearchTagsFn != nil { return sdm.SearchTagsFn(ctx, searchText, filter, requestedPage) } - return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, nil + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, repodb.PageInfo{}, nil } func (sdm RepoDBMock) FilterTags(ctx context.Context, filter repodb.FilterFunc,