Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

GitLab reviews may not have the updated_at field set (#18450) #18461

Merged
merged 1 commit into from
Jan 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions services/migrations/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func init() {
}

// GitlabDownloaderFactory defines a gitlab downloader factory
type GitlabDownloaderFactory struct {
}
type GitlabDownloaderFactory struct{}

// New returns a Downloader related to this factory according MigrateOptions
func (f *GitlabDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
Expand Down Expand Up @@ -184,16 +183,17 @@ func (g *GitlabDownloader) GetTopics() ([]string, error) {

// GetMilestones returns milestones
func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
var perPage = g.maxPerPage
var state = "all"
var milestones = make([]*base.Milestone, 0, perPage)
perPage := g.maxPerPage
state := "all"
milestones := make([]*base.Milestone, 0, perPage)
for i := 1; ; i++ {
ms, _, err := g.client.Milestones.ListMilestones(g.repoID, &gitlab.ListMilestonesOptions{
State: &state,
ListOptions: gitlab.ListOptions{
Page: i,
PerPage: perPage,
}}, nil, gitlab.WithContext(g.ctx))
},
}, nil, gitlab.WithContext(g.ctx))
if err != nil {
return nil, err
}
Expand All @@ -203,7 +203,7 @@ func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
if m.Description != "" {
desc = m.Description
}
var state = "open"
state := "open"
var closedAt *time.Time
if m.State != "" {
state = m.State
Expand Down Expand Up @@ -255,8 +255,8 @@ func (g *GitlabDownloader) normalizeColor(val string) string {

// GetLabels returns labels
func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
var perPage = g.maxPerPage
var labels = make([]*base.Label, 0, perPage)
perPage := g.maxPerPage
labels := make([]*base.Label, 0, perPage)
for i := 1; ; i++ {
ls, _, err := g.client.Labels.ListLabels(g.repoID, &gitlab.ListLabelsOptions{ListOptions: gitlab.ListOptions{
Page: i,
Expand Down Expand Up @@ -327,8 +327,8 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea

// GetReleases returns releases
func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) {
var perPage = g.maxPerPage
var releases = make([]*base.Release, 0, perPage)
perPage := g.maxPerPage
releases := make([]*base.Release, 0, perPage)
for i := 1; ; i++ {
ls, _, err := g.client.Releases.ListReleases(g.repoID, &gitlab.ListReleasesOptions{
Page: i,
Expand Down Expand Up @@ -381,15 +381,15 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
},
}

var allIssues = make([]*base.Issue, 0, perPage)
allIssues := make([]*base.Issue, 0, perPage)

issues, _, err := g.client.Issues.ListProjectIssues(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing issues: %v", err)
}
for _, issue := range issues {

var labels = make([]*base.Label, 0, len(issue.Labels))
labels := make([]*base.Label, 0, len(issue.Labels))
for _, l := range issue.Labels {
labels = append(labels, &base.Label{
Name: l,
Expand All @@ -402,7 +402,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
}

var reactions []*base.Reaction
var awardPage = 1
awardPage := 1
for {
awards, _, err := g.client.AwardEmoji.ListIssueAwardEmoji(g.repoID, issue.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
if err != nil {
Expand Down Expand Up @@ -456,9 +456,9 @@ func (g *GitlabDownloader) GetComments(opts base.GetCommentOptions) ([]*base.Com
return nil, false, fmt.Errorf("unexpected context: %+v", opts.Context)
}

var allComments = make([]*base.Comment, 0, g.maxPerPage)
allComments := make([]*base.Comment, 0, g.maxPerPage)

var page = 1
page := 1

for {
var comments []*gitlab.Discussion
Expand Down Expand Up @@ -503,7 +503,6 @@ func (g *GitlabDownloader) GetComments(opts base.GetCommentOptions) ([]*base.Com
Created: *c.CreatedAt,
})
}

}
if resp.NextPage == 0 {
break
Expand All @@ -526,15 +525,15 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
},
}

var allPRs = make([]*base.PullRequest, 0, perPage)
allPRs := make([]*base.PullRequest, 0, perPage)

prs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
if err != nil {
return nil, false, fmt.Errorf("error while listing merge requests: %v", err)
}
for _, pr := range prs {

var labels = make([]*base.Label, 0, len(pr.Labels))
labels := make([]*base.Label, 0, len(pr.Labels))
for _, l := range pr.Labels {
labels = append(labels, &base.Label{
Name: l,
Expand All @@ -547,12 +546,12 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
pr.State = "closed"
}

var mergeTime = pr.MergedAt
mergeTime := pr.MergedAt
if merged && pr.MergedAt == nil {
mergeTime = pr.UpdatedAt
}

var closeTime = pr.ClosedAt
closeTime := pr.ClosedAt
if merged && pr.ClosedAt == nil {
closeTime = pr.UpdatedAt
}
Expand All @@ -568,7 +567,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
}

var reactions []*base.Reaction
var awardPage = 1
awardPage := 1
for {
awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
if err != nil {
Expand Down Expand Up @@ -641,13 +640,22 @@ func (g *GitlabDownloader) GetReviews(context base.IssueContext) ([]*base.Review
return nil, err
}

var reviews = make([]*base.Review, 0, len(approvals.ApprovedBy))
var createdAt time.Time
if approvals.CreatedAt != nil {
createdAt = *approvals.CreatedAt
} else if approvals.UpdatedAt != nil {
createdAt = *approvals.UpdatedAt
} else {
createdAt = time.Now()
}

reviews := make([]*base.Review, 0, len(approvals.ApprovedBy))
for _, user := range approvals.ApprovedBy {
reviews = append(reviews, &base.Review{
IssueIndex: context.LocalID(),
ReviewerID: int64(user.User.ID),
ReviewerName: user.User.Username,
CreatedAt: *approvals.UpdatedAt,
CreatedAt: createdAt,
// All we get are approvals
State: base.ReviewStateApproved,
})
Expand Down
140 changes: 140 additions & 0 deletions services/migrations/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"strconv"
"testing"
"time"

base "code.gitea.io/gitea/modules/migration"

"github.com/stretchr/testify/assert"
"github.com/xanzy/go-gitlab"
)

func TestGitlabDownloadRepo(t *testing.T) {
Expand Down Expand Up @@ -310,12 +313,14 @@ func TestGitlabDownloadRepo(t *testing.T) {
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
IssueIndex: 1,
ReviewerID: 4102996,
ReviewerName: "zeripath",
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
State: "APPROVED",
},
{
IssueIndex: 1,
ReviewerID: 527793,
ReviewerName: "axifive",
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
Expand All @@ -327,10 +332,145 @@ func TestGitlabDownloadRepo(t *testing.T) {
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{
{
IssueIndex: 2,
ReviewerID: 4575606,
ReviewerName: "real6543",
CreatedAt: time.Date(2020, 4, 19, 19, 24, 21, 108000000, time.UTC),
State: "APPROVED",
},
}, rvs)
}

func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) {
// mux is the HTTP request multiplexer used with the test server.
mux := http.NewServeMux()

// server is a test HTTP server used to provide mock API responses.
server := httptest.NewServer(mux)

// client is the Gitlab client being tested.
client, err := gitlab.NewClient("", gitlab.WithBaseURL(server.URL))
if err != nil {
server.Close()
t.Fatalf("Failed to create client: %v", err)
}

return mux, server, client
}

func gitlabClientMockTeardown(server *httptest.Server) {
server.Close()
}

type reviewTestCase struct {
repoID, prID, reviewerID int
reviewerName string
createdAt, updatedAt *time.Time
expectedCreatedAt time.Time
}

func convertTestCase(t reviewTestCase) (func(w http.ResponseWriter, r *http.Request), base.Review) {
var updatedAtField string
if t.updatedAt == nil {
updatedAtField = ""
} else {
updatedAtField = `"updated_at": "` + t.updatedAt.Format(time.RFC3339) + `",`
}

var createdAtField string
if t.createdAt == nil {
createdAtField = ""
} else {
createdAtField = `"created_at": "` + t.createdAt.Format(time.RFC3339) + `",`
}

handler := func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, `
{
"id": 5,
"iid": `+strconv.Itoa(t.prID)+`,
"project_id": `+strconv.Itoa(t.repoID)+`,
"title": "Approvals API",
"description": "Test",
"state": "opened",
`+createdAtField+`
`+updatedAtField+`
"merge_status": "cannot_be_merged",
"approvals_required": 2,
"approvals_left": 1,
"approved_by": [
{
"user": {
"name": "Administrator",
"username": "`+t.reviewerName+`",
"id": `+strconv.Itoa(t.reviewerID)+`,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
"web_url": "http://localhost:3000/root"
}
}
]
}`)
}
review := base.Review{
IssueIndex: int64(t.prID),
ReviewerID: int64(t.reviewerID),
ReviewerName: t.reviewerName,
CreatedAt: t.expectedCreatedAt,
State: "APPROVED",
}

return handler, review
}

func TestGitlabGetReviews(t *testing.T) {
mux, server, client := gitlabClientMockSetup(t)
defer gitlabClientMockTeardown(server)

repoID := 1324

downloader := &GitlabDownloader{
ctx: context.Background(),
client: client,
repoID: repoID,
}

createdAt := time.Date(2020, 4, 19, 19, 24, 21, 0, time.UTC)

for _, testCase := range []reviewTestCase{
{
repoID: repoID,
prID: 1,
reviewerID: 801,
reviewerName: "someone1",
createdAt: nil,
updatedAt: &createdAt,
expectedCreatedAt: createdAt,
},
{
repoID: repoID,
prID: 2,
reviewerID: 802,
reviewerName: "someone2",
createdAt: &createdAt,
updatedAt: nil,
expectedCreatedAt: createdAt,
},
{
repoID: repoID,
prID: 3,
reviewerID: 803,
reviewerName: "someone3",
createdAt: nil,
updatedAt: nil,
expectedCreatedAt: time.Now(),
},
} {
mock, review := convertTestCase(testCase)
mux.HandleFunc(fmt.Sprintf("/api/v4/projects/%d/merge_requests/%d/approvals", testCase.repoID, testCase.prID), mock)

rvs, err := downloader.GetReviews(base.BasicIssueContext(testCase.prID))
assert.NoError(t, err)
assertReviewsEqual(t, []*base.Review{&review}, rvs)
}
}
18 changes: 9 additions & 9 deletions services/migrations/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,15 @@ func assertRepositoryEqual(t *testing.T, expected, actual *base.Repository) {
}

func assertReviewEqual(t *testing.T, expected, actual *base.Review) {
assert.Equal(t, expected.ID, actual.ID)
assert.Equal(t, expected.IssueIndex, actual.IssueIndex)
assert.Equal(t, expected.ReviewerID, actual.ReviewerID)
assert.Equal(t, expected.ReviewerName, actual.ReviewerName)
assert.Equal(t, expected.Official, actual.Official)
assert.Equal(t, expected.CommitID, actual.CommitID)
assert.Equal(t, expected.Content, actual.Content)
assertTimeEqual(t, expected.CreatedAt, actual.CreatedAt)
assert.Equal(t, expected.State, actual.State)
assert.Equal(t, expected.ID, actual.ID, "ID")
assert.Equal(t, expected.IssueIndex, actual.IssueIndex, "IsssueIndex")
assert.Equal(t, expected.ReviewerID, actual.ReviewerID, "ReviewerID")
assert.Equal(t, expected.ReviewerName, actual.ReviewerName, "ReviewerName")
assert.Equal(t, expected.Official, actual.Official, "Official")
assert.Equal(t, expected.CommitID, actual.CommitID, "CommitID")
assert.Equal(t, expected.Content, actual.Content, "Content")
assert.WithinDuration(t, expected.CreatedAt, actual.CreatedAt, 10*time.Second)
assert.Equal(t, expected.State, actual.State, "State")
assertReviewCommentsEqual(t, expected.Comments, actual.Comments)
}

Expand Down