Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'internal/gitaly')
-rw-r--r--internal/gitaly/service/ref/find_all_tags.go29
-rw-r--r--internal/gitaly/service/ref/find_all_tags_test.go101
-rw-r--r--internal/gitaly/service/ref/list_refs.go2
-rw-r--r--internal/gitaly/service/ref/refs.go33
-rw-r--r--internal/gitaly/service/ref/remote_branches.go2
5 files changed, 159 insertions, 8 deletions
diff --git a/internal/gitaly/service/ref/find_all_tags.go b/internal/gitaly/service/ref/find_all_tags.go
index e7667d349..43d36eb06 100644
--- a/internal/gitaly/service/ref/find_all_tags.go
+++ b/internal/gitaly/service/ref/find_all_tags.go
@@ -28,16 +28,18 @@ func (s *server) FindAllTags(in *gitalypb.FindAllTagsRequest, stream gitalypb.Re
return helper.ErrInvalidArgument(err)
}
+ opts := buildPaginationOpts(ctx, in.GetPaginationParams())
+
repo := s.localrepo(in.GetRepository())
- if err := s.findAllTags(ctx, repo, sortField, stream); err != nil {
+ if err := s.findAllTags(ctx, repo, sortField, stream, opts); err != nil {
return helper.ErrInternal(err)
}
return nil
}
-func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortField string, stream gitalypb.RefService_FindAllTagsServer) error {
+func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortField string, stream gitalypb.RefService_FindAllTagsServer, opts *paginationOpts) error {
objectReader, err := s.catfileCache.ObjectReader(ctx, repo)
if err != nil {
return fmt.Errorf("error creating object reader: %v", err)
@@ -50,9 +52,21 @@ func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortFiel
chunker := chunk.New(&tagSender{stream: stream})
+ // If `PageToken` is not provided, then `IsPageToken` will always return `true`
+ // and disable pagination logic. If `PageToken` is set, then we will skip all tags
+ // until we reach the tag equal to `PageToken`. After that, tags will be returned
+ // as usual.
+ pastPageToken := opts.IsPageToken([]byte{})
+ limit := opts.Limit
+ i := 0
+
for catfileObjectsIter.Next() {
tag := catfileObjectsIter.Result()
+ if i >= limit {
+ break
+ }
+
var result *gitalypb.Tag
switch tag.ObjectInfo.Type {
case "tag":
@@ -111,9 +125,20 @@ func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortFiel
result.Name = tagName
}
+ if !pastPageToken {
+ pastPageToken = opts.IsPageToken(tag.ObjectName)
+ continue
+ }
+
if err := chunker.Send(result); err != nil {
return fmt.Errorf("sending tag: %w", err)
}
+
+ i++
+ }
+
+ if !pastPageToken {
+ return helper.ErrInvalidArgumentf("could not find page token")
}
if err := catfileObjectsIter.Err(); err != nil {
diff --git a/internal/gitaly/service/ref/find_all_tags_test.go b/internal/gitaly/service/ref/find_all_tags_test.go
index 160b2434d..c53b1ce78 100644
--- a/internal/gitaly/service/ref/find_all_tags_test.go
+++ b/internal/gitaly/service/ref/find_all_tags_test.go
@@ -503,6 +503,107 @@ func TestFindAllTags_invalidRequest(t *testing.T) {
}
}
+func TestFindAllTags_pagination(t *testing.T) {
+ cfg, client := setupRefServiceWithoutRepo(t)
+
+ ctx, cancel := testhelper.Context()
+ defer cancel()
+
+ repoProto, repoPath := gittest.CloneRepo(t, cfg, cfg.Storages[0])
+
+ catfileCache := catfile.NewCache(cfg)
+ defer catfileCache.Stop()
+
+ annotatedTagID := gittest.WriteTag(t, cfg, repoPath, "annotated", "HEAD", gittest.WriteTagConfig{
+ Message: "message",
+ })
+
+ for _, tc := range []struct {
+ desc string
+ paginationParams *gitalypb.PaginationParameter
+ sortBy *gitalypb.FindAllTagsRequest_SortBy
+ exp []string
+ expectedErr error
+ }{
+ {
+ desc: "without pagination",
+ paginationParams: &gitalypb.PaginationParameter{Limit: 100},
+ exp: []string{
+ annotatedTagID.String(),
+ "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
+ "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
+ "8f03acbcd11c53d9c9468078f32a2622005a4841",
+ },
+ },
+ {
+ desc: "with limit restrictions",
+ paginationParams: &gitalypb.PaginationParameter{Limit: 3},
+ exp: []string{
+ annotatedTagID.String(),
+ "f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8",
+ "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
+ },
+ },
+ {
+ desc: "with limit restrictions and page token",
+ paginationParams: &gitalypb.PaginationParameter{Limit: 3, PageToken: "refs/tags/v1.0.0"},
+ exp: []string{
+ "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b",
+ "8f03acbcd11c53d9c9468078f32a2622005a4841",
+ },
+ },
+ {
+ desc: "with reversed sort by name, limit restrictions and page token",
+ paginationParams: &gitalypb.PaginationParameter{Limit: 3, PageToken: "refs/tags/v1.0.0"},
+ sortBy: &gitalypb.FindAllTagsRequest_SortBy{Key: gitalypb.FindAllTagsRequest_SortBy_REFNAME, Direction: gitalypb.SortDirection_DESCENDING},
+ exp: []string{
+ annotatedTagID.String(),
+ },
+ },
+ {
+ desc: "with page token only",
+ paginationParams: &gitalypb.PaginationParameter{PageToken: "refs/tags/v1.1.0"},
+ exp: nil,
+ expectedErr: helper.ErrInvalidArgumentf("could not find page token"),
+ },
+ {
+ desc: "with invalid page token",
+ paginationParams: &gitalypb.PaginationParameter{Limit: 3, PageToken: "refs/tags/invalid"},
+ exp: nil,
+ expectedErr: helper.ErrInvalidArgumentf("could not find page token"),
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ c, err := client.FindAllTags(ctx, &gitalypb.FindAllTagsRequest{
+ Repository: repoProto,
+ PaginationParams: tc.paginationParams,
+ SortBy: tc.sortBy,
+ })
+
+ if tc.expectedErr != nil {
+ _, err = c.Recv()
+ require.NotEqual(t, err, io.EOF)
+ testassert.GrpcEqualErr(t, tc.expectedErr, err)
+ } else {
+ require.NoError(t, err)
+
+ var tags []string
+ for {
+ r, err := c.Recv()
+ if err == io.EOF {
+ break
+ }
+ require.NoError(t, err)
+ for _, tag := range r.GetTags() {
+ tags = append(tags, tag.Id)
+ }
+ }
+ require.Equal(t, tc.exp, tags)
+ }
+ })
+ }
+}
+
func TestFindAllTags_sorted(t *testing.T) {
cfg, client := setupRefServiceWithoutRepo(t)
diff --git a/internal/gitaly/service/ref/list_refs.go b/internal/gitaly/service/ref/list_refs.go
index 3fe351519..f5eb03557 100644
--- a/internal/gitaly/service/ref/list_refs.go
+++ b/internal/gitaly/service/ref/list_refs.go
@@ -34,7 +34,7 @@ func (s *server) ListRefs(in *gitalypb.ListRefsRequest, stream gitalypb.RefServi
patterns = append(patterns, string(pattern))
}
- opts := paginationParamsToOpts(ctx, nil)
+ opts := buildFindRefsOpts(ctx, nil)
opts.cmdArgs = []git.Option{
// %00 inserts the null character into the output (see for-each-ref docs)
git.Flag{Name: "--format=" + strings.Join(localBranchFormatFields, "%00")},
diff --git a/internal/gitaly/service/ref/refs.go b/internal/gitaly/service/ref/refs.go
index d6e2f2930..658d019b8 100644
--- a/internal/gitaly/service/ref/refs.go
+++ b/internal/gitaly/service/ref/refs.go
@@ -22,6 +22,20 @@ const (
tagFormat = "%(objectname) %(objecttype) %(refname:lstrip=2)"
)
+type paginationOpts struct {
+ // Limit allows to set the maximum numbers of elements
+ Limit int
+ // IsPageToken allows control over which results are sent as part of the
+ // response. When IsPageToken evaluates to true for the first time,
+ // results will start to be sent as part of the response. This function
+ // will be called with an empty slice previous to sending the first line
+ // in order to allow sending everything right from the beginning.
+ IsPageToken func([]byte) bool
+ // When PageTokenError is true then the response will return an error when
+ // PageToken is not found.
+ PageTokenError bool
+}
+
type findRefsOpts struct {
cmdArgs []git.Option
delim byte
@@ -144,7 +158,7 @@ func (s *server) findLocalBranches(in *gitalypb.FindLocalBranchesRequest, stream
}
writer := newFindLocalBranchesWriter(stream, objectReader)
- opts := paginationParamsToOpts(ctx, in.GetPaginationParams())
+ opts := buildFindRefsOpts(ctx, in.GetPaginationParams())
opts.cmdArgs = []git.Option{
// %00 inserts the null character into the output (see for-each-ref docs)
git.Flag{Name: "--format=" + strings.Join(localBranchFormatFields, "%00")},
@@ -195,7 +209,7 @@ func (s *server) findAllBranches(in *gitalypb.FindAllBranchesRequest, stream git
return err
}
- opts := paginationParamsToOpts(ctx, nil)
+ opts := buildFindRefsOpts(ctx, nil)
opts.cmdArgs = args
writer := newFindAllBranchesWriter(stream, objectReader)
@@ -306,8 +320,8 @@ func (s *server) validateFindTagRequest(in *gitalypb.FindTagRequest) error {
return nil
}
-func paginationParamsToOpts(ctx context.Context, p *gitalypb.PaginationParameter) *findRefsOpts {
- opts := &findRefsOpts{delim: '\n'}
+func buildPaginationOpts(ctx context.Context, p *gitalypb.PaginationParameter) *paginationOpts {
+ opts := &paginationOpts{}
opts.IsPageToken = func(_ []byte) bool { return true }
opts.Limit = math.MaxInt32
@@ -338,6 +352,17 @@ func paginationParamsToOpts(ctx context.Context, p *gitalypb.PaginationParameter
return opts
}
+func buildFindRefsOpts(ctx context.Context, p *gitalypb.PaginationParameter) *findRefsOpts {
+ opts := buildPaginationOpts(ctx, p)
+
+ refsOpts := &findRefsOpts{delim: '\n'}
+ refsOpts.Limit = opts.Limit
+ refsOpts.IsPageToken = opts.IsPageToken
+ refsOpts.PageTokenError = opts.PageTokenError
+
+ return refsOpts
+}
+
// getTagSortField returns a field that needs to be used to sort the tags.
// If sorting is not provided the default sorting is used: by refname.
func getTagSortField(sortBy *gitalypb.FindAllTagsRequest_SortBy) (string, error) {
diff --git a/internal/gitaly/service/ref/remote_branches.go b/internal/gitaly/service/ref/remote_branches.go
index 6a1c1b672..57ed60999 100644
--- a/internal/gitaly/service/ref/remote_branches.go
+++ b/internal/gitaly/service/ref/remote_branches.go
@@ -36,7 +36,7 @@ func (s *server) findAllRemoteBranches(req *gitalypb.FindAllRemoteBranchesReques
return err
}
- opts := paginationParamsToOpts(ctx, nil)
+ opts := buildFindRefsOpts(ctx, nil)
opts.cmdArgs = args
writer := newFindAllRemoteBranchesWriter(stream, objectReader)