diff options
author | Zeger-Jan van de Weg <zegerjan@gitlab.com> | 2017-10-30 13:15:52 +0300 |
---|---|---|
committer | Zeger-Jan van de Weg <zegerjan@gitlab.com> | 2017-10-30 13:15:52 +0300 |
commit | a35fdb3719289e4a3abf665b68f4ab91a5990b06 (patch) | |
tree | 5b548992f71050be7e424590eb51bd6065677eb2 | |
parent | 53610026e03f02d4e10149cdae42c4d62c9f3552 (diff) | |
parent | 808241e24d2e8287384df066eeba1790ef2952fb (diff) |
Merge branch 'feature/implement-wiki-find-page-rpc' into 'master'
Implement WikiFindPage RPC
Closes #678
See merge request gitlab-org/gitaly!419
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | internal/service/wiki/find_page.go | 43 | ||||
-rw-r--r-- | internal/service/wiki/find_page_test.go | 219 | ||||
-rw-r--r-- | internal/service/wiki/server.go | 4 | ||||
-rw-r--r-- | internal/service/wiki/testhelper_test.go | 28 | ||||
-rw-r--r-- | internal/service/wiki/write_page_test.go | 33 | ||||
-rw-r--r-- | ruby/lib/gitaly_server/wiki_service.rb | 44 |
7 files changed, 349 insertions, 24 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d51e4b60..c3e633dfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ UNRELEASED +- Implement WikiFindPage RPC + https://gitlab.com/gitlab-org/gitaly/merge_requests/419 - Update gitlab_git to b3ba3996e0bd329eaa574ff53c69673efaca6eef and set `GL_USERNAME` env variable for hook excecution https://gitlab.com/gitlab-org/gitaly/merge_requests/423 diff --git a/internal/service/wiki/find_page.go b/internal/service/wiki/find_page.go new file mode 100644 index 000000000..79eea6900 --- /dev/null +++ b/internal/service/wiki/find_page.go @@ -0,0 +1,43 @@ +package wiki + +import ( + "gitlab.com/gitlab-org/gitaly/internal/rubyserver" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +func (s *server) WikiFindPage(request *pb.WikiFindPageRequest, stream pb.WikiService_WikiFindPageServer) error { + ctx := stream.Context() + + if len(request.GetTitle()) == 0 { + return grpc.Errorf(codes.InvalidArgument, "WikiFindPage: Empty Title") + } + + client, err := s.WikiServiceClient(ctx) + if err != nil { + return err + } + + clientCtx, err := rubyserver.SetHeaders(ctx, request.GetRepository()) + if err != nil { + return err + } + + rubyStream, err := client.WikiFindPage(clientCtx, request) + if err != nil { + return err + } + + return rubyserver.Proxy(func() error { + resp, err := rubyStream.Recv() + if err != nil { + md := rubyStream.Trailer() + stream.SetTrailer(md) + return err + } + return stream.Send(resp) + }) +} diff --git a/internal/service/wiki/find_page_test.go b/internal/service/wiki/find_page_test.go new file mode 100644 index 000000000..67bde39e3 --- /dev/null +++ b/internal/service/wiki/find_page_test.go @@ -0,0 +1,219 @@ +package wiki + +import ( + "bytes" + "io" + "testing" + + gitlog "gitlab.com/gitlab-org/gitaly/internal/git/log" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestSuccessfulWikiFindPageRequest(t *testing.T) { + ctx, cancel := testhelper.Context() + defer cancel() + + server, serverSocketPath := runWikiServiceServer(t) + defer server.Stop() + + client, conn := newWikiClient(t, serverSocketPath) + defer conn.Close() + + content := bytes.Repeat([]byte("Mock wiki page content"), 10000) + + page1Name := "Home Page" + page2Name := "Installing/Step 133-b" + page3Name := "Installing/Step 133-c" + + writeWikiPage(t, client, page1Name, content) + head1ID := testhelper.MustRunCommand(t, nil, "git", "-C", wikiRepoPath, "show", "--format=format:%H", "--no-patch", "HEAD") + page1Commit, err := gitlog.GetCommit(ctx, wikiRepo, string(head1ID), "") + require.NoError(t, err, "look up git commit after writing a wiki page") + + writeWikiPage(t, client, page2Name, content) + + writeWikiPage(t, client, page3Name, content) + head3ID := testhelper.MustRunCommand(t, nil, "git", "-C", wikiRepoPath, "show", "--format=format:%H", "--no-patch", "HEAD") + page3Commit, err := gitlog.GetCommit(ctx, wikiRepo, string(head3ID), "") + require.NoError(t, err, "look up git commit after writing a wiki page") + + testCases := []struct { + desc string + request *pb.WikiFindPageRequest + expectedPage *pb.WikiPage + }{ + { + desc: "title only", + request: &pb.WikiFindPageRequest{ + Repository: wikiRepo, + Title: []byte(page1Name), + }, + expectedPage: &pb.WikiPage{ + Version: &pb.WikiPageVersion{ + Commit: page3Commit, + Format: "markdown", + }, + Title: []byte(page1Name), + Format: "markdown", + UrlPath: "Home-Page", + Path: []byte("Home-Page.md"), + Name: []byte(page1Name), + Historical: false, + }, + }, + { + desc: "title + revision that includes the page", + request: &pb.WikiFindPageRequest{ + Repository: wikiRepo, + Title: []byte(page1Name), + Revision: []byte(page1Commit.Id), + }, + expectedPage: &pb.WikiPage{ + Version: &pb.WikiPageVersion{ + Commit: page1Commit, + Format: "markdown", + }, + Title: []byte(page1Name), + Format: "markdown", + UrlPath: "Home-Page", + Path: []byte("Home-Page.md"), + Name: []byte(page1Name), + Historical: true, + }, + }, + { + desc: "title + revision that does not include the page", + request: &pb.WikiFindPageRequest{ + Repository: wikiRepo, + Title: []byte(page2Name), + Revision: []byte(page1Commit.Id), + }, + expectedPage: nil, + }, + { + desc: "title + directory that includes the page", + request: &pb.WikiFindPageRequest{ + Repository: wikiRepo, + Title: []byte("Step 133-b"), + Directory: []byte("Installing"), + }, + expectedPage: &pb.WikiPage{ + Version: &pb.WikiPageVersion{ + Commit: page3Commit, + Format: "markdown", + }, + Title: []byte("Step 133 b"), + Format: "markdown", + UrlPath: "Installing/Step-133-b", + Path: []byte("Installing/Step-133-b.md"), + Name: []byte("Step 133 b"), + Historical: false, + }, + }, + { + desc: "title + directory that does not include the page", + request: &pb.WikiFindPageRequest{ + Repository: wikiRepo, + Title: []byte("Step 133-b"), + Directory: []byte("Installation"), + }, + expectedPage: nil, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.desc, func(t *testing.T) { + ctx, cancel := testhelper.Context() + defer cancel() + + c, err := client.WikiFindPage(ctx, testCase.request) + require.NoError(t, err) + + expectedPage := testCase.expectedPage + receivedPage := readFullWikiPageFromWikiFindPageClient(t, c) + + // require.Equal doesn't display a proper diff when either expected/actual has a field + // with large data (RawData in our case), so we compare page attributes and content separately. + receivedContent := receivedPage.GetRawData() + if receivedPage != nil { + receivedPage.RawData = nil + } + + require.Equal(t, expectedPage, receivedPage, "mismatched page attributes") + if expectedPage != nil { + require.Equal(t, content, receivedContent, "mismatched page content") + } + }) + } +} + +func TestFailedWikiFindPageDueToValidation(t *testing.T) { + server, serverSocketPath := runWikiServiceServer(t) + defer server.Stop() + + client, conn := newWikiClient(t, serverSocketPath) + defer conn.Close() + + testCases := []struct { + desc string + title string + code codes.Code + }{ + { + desc: "empty page path", + title: "", + code: codes.InvalidArgument, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.desc, func(t *testing.T) { + ctx, cancel := testhelper.Context() + defer cancel() + + request := &pb.WikiFindPageRequest{ + Repository: wikiRepo, + Title: []byte(testCase.title), + } + + c, err := client.WikiFindPage(ctx, request) + require.NoError(t, err) + + err = drainWikiFindPageResponse(c) + testhelper.AssertGrpcError(t, err, testCase.code, "") + }) + } +} + +func drainWikiFindPageResponse(c pb.WikiService_WikiFindPageClient) error { + for { + _, err := c.Recv() + if err != nil { + return err + } + } +} + +func readFullWikiPageFromWikiFindPageClient(t *testing.T, c pb.WikiService_WikiFindPageClient) (wikiPage *pb.WikiPage) { + for { + resp, err := c.Recv() + if err == io.EOF { + break + } else if err != nil { + t.Fatal(err) + } + + if wikiPage == nil { + wikiPage = resp.GetPage() + } else { + wikiPage.RawData = append(wikiPage.RawData, resp.GetPage().GetRawData()...) + } + } + + return wikiPage +} diff --git a/internal/service/wiki/server.go b/internal/service/wiki/server.go index 5091f0fe8..b2c589e10 100644 --- a/internal/service/wiki/server.go +++ b/internal/service/wiki/server.go @@ -19,7 +19,3 @@ func NewServer(rs *rubyserver.Server) pb.WikiServiceServer { func (s *server) WikiGetPageVersions(_ *pb.WikiGetPageVersionsRequest, _ pb.WikiService_WikiGetPageVersionsServer) error { return helper.Unimplemented } - -func (s *server) WikiFindPage(_ *pb.WikiFindPageRequest, _ pb.WikiService_WikiFindPageServer) error { - return helper.Unimplemented -} diff --git a/internal/service/wiki/testhelper_test.go b/internal/service/wiki/testhelper_test.go index 1ee5769bd..0de56997e 100644 --- a/internal/service/wiki/testhelper_test.go +++ b/internal/service/wiki/testhelper_test.go @@ -13,6 +13,7 @@ import ( pb "gitlab.com/gitlab-org/gitaly-proto/go" log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) @@ -85,3 +86,30 @@ func newWikiClient(t *testing.T, serverSocketPath string) (pb.WikiServiceClient, return pb.NewWikiServiceClient(conn), conn } + +func writeWikiPage(t *testing.T, client pb.WikiServiceClient, name string, content []byte) { + commitDetails := &pb.WikiCommitDetails{ + Name: []byte("Ahmad Sherif"), + Email: []byte("ahmad@gitlab.com"), + Message: []byte("Add " + name), + } + + request := &pb.WikiWritePageRequest{ + Repository: wikiRepo, + Name: []byte(name), + Format: "markdown", + CommitDetails: commitDetails, + Content: content, + } + + ctx, cancel := testhelper.Context() + defer cancel() + + stream, err := client.WikiWritePage(ctx) + require.NoError(t, err) + + require.NoError(t, stream.Send(request)) + + _, err = stream.CloseAndRecv() + require.NoError(t, err) +} diff --git a/internal/service/wiki/write_page_test.go b/internal/service/wiki/write_page_test.go index edfb6e547..b61724321 100644 --- a/internal/service/wiki/write_page_test.go +++ b/internal/service/wiki/write_page_test.go @@ -64,7 +64,7 @@ func TestSuccessfulWikiWritePageRequest(t *testing.T) { headID := testhelper.MustRunCommand(t, nil, "git", "-C", wikiRepoPath, "show", "--format=format:%H", "--no-patch", "HEAD") commit, err := gitlog.GetCommit(ctx, wikiRepo, string(headID), "") - require.NoError(t, err, "look up git commit before merge is applied") + require.NoError(t, err, "look up git commit after writing a wiki page") require.Equal(t, authorName, commit.Author.Name, "author name mismatched") require.Equal(t, authorEmail, commit.Author.Email, "author email mismatched") @@ -81,40 +81,33 @@ func TestFailedWikiWritePageDueToDuplicatePage(t *testing.T) { client, conn := newWikiClient(t, serverSocketPath) defer conn.Close() + pageName := "Installing Gitaly" + content := []byte("Mock wiki page content") commitDetails := &pb.WikiCommitDetails{ Name: []byte("Ahmad Sherif"), Email: []byte("ahmad@gitlab.com"), - Message: []byte("Add installation instructions"), + Message: []byte("Add " + pageName), } + writeWikiPage(t, client, pageName, content) + request := &pb.WikiWritePageRequest{ Repository: wikiRepo, - Name: []byte("Installing Gitaly"), + Name: []byte(pageName), Format: "markdown", CommitDetails: commitDetails, - Content: []byte("Mock wiki page content"), + Content: content, } - ctx1, cancel1 := testhelper.Context() - defer cancel1() - - stream1, err := client.WikiWritePage(ctx1) - require.NoError(t, err) - - require.NoError(t, stream1.Send(request)) - - _, err = stream1.CloseAndRecv() - require.NoError(t, err) - - ctx2, cancel2 := testhelper.Context() - defer cancel2() + ctx, cancel := testhelper.Context() + defer cancel() - stream2, err := client.WikiWritePage(ctx2) + stream, err := client.WikiWritePage(ctx) require.NoError(t, err) - require.NoError(t, stream2.Send(request)) + require.NoError(t, stream.Send(request)) - response, err := stream2.CloseAndRecv() + response, err := stream.CloseAndRecv() require.NoError(t, err) expectedResponse := &pb.WikiWritePageResponse{DuplicateError: []byte("Cannot write //Installing-Gitaly.md, found //Installing-Gitaly.md.")} diff --git a/ruby/lib/gitaly_server/wiki_service.rb b/ruby/lib/gitaly_server/wiki_service.rb index 1077d4d1b..2fd56ae6b 100644 --- a/ruby/lib/gitaly_server/wiki_service.rb +++ b/ruby/lib/gitaly_server/wiki_service.rb @@ -34,5 +34,49 @@ module GitalyServer end end end + + def wiki_find_page(request, call) + bridge_exceptions do + repo = Gitlab::Git::Repository.from_call(call) + wiki = Gitlab::Git::Wiki.new(repo) + + page = wiki.page( + title: request.title, + version: request.revision.presence, + dir: request.directory.presence + ) + + unless page + return Enumerator.new do |y| + y.yield Gitaly::WikiFindPageResponse.new + end + end + + version = Gitaly::WikiPageVersion.new( + commit: gitaly_commit_from_rugged(page.version.commit.raw_commit), + format: page.version.format.to_s + ) + gitaly_wiki_page = Gitaly::WikiPage.new( + version: version, + format: page.format.to_s, + title: page.title.b, + url_path: page.url_path.to_s, + path: page.path.b, + name: page.name.b, + historical: page.historical? + ) + + Enumerator.new do |y| + io = StringIO.new(page.text_data) + while chunk = io.read(Gitlab.config.git.write_buffer_size) + gitaly_wiki_page.raw_data = chunk + + y.yield Gitaly::WikiFindPageResponse.new(page: gitaly_wiki_page) + + gitaly_wiki_page = Gitaly::WikiPage.new + end + end + end + end end end |