diff --git a/merge_requests.go b/merge_requests.go index f5b68fdfa..458255f33 100644 --- a/merge_requests.go +++ b/merge_requests.go @@ -202,6 +202,7 @@ type ListMergeRequestsOptions struct { Scope *string `url:"scope,omitempty" json:"scope,omitempty"` AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"` AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"` + NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"` AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` ApproverIDs *ApproverIDsValue `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` ApprovedByIDs *ApproverIDsValue `url:"approved_by_ids,omitempty" json:"approved_by_ids,omitempty"` @@ -262,6 +263,7 @@ type ListProjectMergeRequestsOptions struct { Scope *string `url:"scope,omitempty" json:"scope,omitempty"` AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"` AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"` + NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"` AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` ApproverIDs *ApproverIDsValue `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` ApprovedByIDs *ApproverIDsValue `url:"approved_by_ids,omitempty" json:"approved_by_ids,omitempty"` @@ -323,6 +325,7 @@ type ListGroupMergeRequestsOptions struct { Scope *string `url:"scope,omitempty" json:"scope,omitempty"` AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"` AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"` + NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"` AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` ApproverIDs *ApproverIDsValue `url:"approver_ids,omitempty" json:"approver_ids,omitempty"` ApprovedByIDs *ApproverIDsValue `url:"approved_by_ids,omitempty" json:"approved_by_ids,omitempty"` diff --git a/merge_requests_test.go b/merge_requests_test.go index b7301ca16..cfee8161f 100644 --- a/merge_requests_test.go +++ b/merge_requests_test.go @@ -202,6 +202,120 @@ func TestListProjectMergeRequests(t *testing.T) { } } +func TestListProjectMergeRequestsAuthorUsername(t *testing.T) { + mux, client := setup(t) + + path := "/api/v4/projects/278964/merge_requests" + + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + testParams(t, r, "assignee_id=Any&author_username=hfyngvason&with_labels_details=true&with_merge_status_recheck=true") + mustWriteHTTPResponse(t, w, "testdata/get_merge_requests_author_username.json") + }) + + opts := ListProjectMergeRequestsOptions{ + AssigneeID: AssigneeID(UserIDAny), + AuthorUsername: String("hfyngvason"), + WithLabelsDetails: Bool(true), + WithMergeStatusRecheck: Bool(true), + } + + mergeRequests, _, err := client.MergeRequests.ListProjectMergeRequests(278964, &opts) + + require.NoError(t, err) + require.Equal(t, 1, len(mergeRequests)) + + validStates := []string{"opened", "closed", "locked", "merged"} + detailedMergeStatuses := []string{ + "blocked_status", + "broken_status", + "checking", + "ci_must_pass", + "ci_still_running", + "discussions_not_resolved", + "draft_status", + "external_status_checks", + "mergeable", + "not_approved", + "not_open", + "policies_denied", + "unchecked", + } + allCreatedBefore := time.Date(2019, 8, 21, 0, 0, 0, 0, time.UTC) + allCreatedAfter := time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC) + + for _, mr := range mergeRequests { + require.Equal(t, 278964, mr.ProjectID) + require.Contains(t, validStates, mr.State) + assert.Less(t, mr.CreatedAt.Unix(), allCreatedBefore.Unix()) + assert.Greater(t, mr.CreatedAt.Unix(), allCreatedAfter.Unix()) + assert.LessOrEqual(t, mr.CreatedAt.Unix(), mr.UpdatedAt.Unix()) + assert.LessOrEqual(t, mr.TaskCompletionStatus.CompletedCount, mr.TaskCompletionStatus.Count) + require.Contains(t, detailedMergeStatuses, mr.DetailedMergeStatus) + // list requests do not provide these fields: + assert.Nil(t, mr.Pipeline) + assert.Nil(t, mr.HeadPipeline) + assert.Equal(t, "", mr.DiffRefs.HeadSha) + } +} + +func TestListProjectMergeRequestsNotAuthorUsername(t *testing.T) { + mux, client := setup(t) + + path := "/api/v4/projects/278964/merge_requests" + + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + testParams(t, r, "assignee_id=Any¬%5Bauthor_username%5D=hfyngvason&with_labels_details=true&with_merge_status_recheck=true") + mustWriteHTTPResponse(t, w, "testdata/get_merge_requests_not_author_username.json") + }) + + opts := ListProjectMergeRequestsOptions{ + AssigneeID: AssigneeID(UserIDAny), + NotAuthorUsername: String("hfyngvason"), + WithLabelsDetails: Bool(true), + WithMergeStatusRecheck: Bool(true), + } + + mergeRequests, _, err := client.MergeRequests.ListProjectMergeRequests(278964, &opts) + + require.NoError(t, err) + require.Equal(t, 2, len(mergeRequests)) + + validStates := []string{"opened", "closed", "locked", "merged"} + detailedMergeStatuses := []string{ + "blocked_status", + "broken_status", + "checking", + "ci_must_pass", + "ci_still_running", + "discussions_not_resolved", + "draft_status", + "external_status_checks", + "mergeable", + "not_approved", + "not_open", + "policies_denied", + "unchecked", + } + allCreatedBefore := time.Date(2019, 8, 21, 0, 0, 0, 0, time.UTC) + allCreatedAfter := time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC) + + for _, mr := range mergeRequests { + require.Equal(t, 278964, mr.ProjectID) + require.Contains(t, validStates, mr.State) + assert.Less(t, mr.CreatedAt.Unix(), allCreatedBefore.Unix()) + assert.Greater(t, mr.CreatedAt.Unix(), allCreatedAfter.Unix()) + assert.LessOrEqual(t, mr.CreatedAt.Unix(), mr.UpdatedAt.Unix()) + assert.LessOrEqual(t, mr.TaskCompletionStatus.CompletedCount, mr.TaskCompletionStatus.Count) + require.Contains(t, detailedMergeStatuses, mr.DetailedMergeStatus) + // list requests do not provide these fields: + assert.Nil(t, mr.Pipeline) + assert.Nil(t, mr.HeadPipeline) + assert.Equal(t, "", mr.DiffRefs.HeadSha) + } +} + func TestCreateMergeRequestPipeline(t *testing.T) { mux, client := setup(t) diff --git a/testdata/get_merge_requests_author_username.json b/testdata/get_merge_requests_author_username.json new file mode 100644 index 000000000..898528a63 --- /dev/null +++ b/testdata/get_merge_requests_author_username.json @@ -0,0 +1,89 @@ +[ + { + "id": 35385049, + "iid": 15442, + "project_id": 278964, + "title": "Draft: Use structured logging for DB load balancer", + "description": "## What does this MR do?", + "state": "opened", + "created_at": "2019-08-20T10:58:54.413Z", + "updated_at": "2019-08-20T12:01:49.849Z", + "merged_by": null, + "merged_at": null, + "closed_by": null, + "closed_at": null, + "target_branch": "master", + "source_branch": "use-structured-logging-for-db-load-balancer", + "user_notes_count": 1, + "upvotes": 0, + "downvotes": 0, + "assignee": { + "id": 4088036, + "name": "Hordur Freyr Yngvason", + "username": "hfyngvason", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", + "web_url": "https://gitlab.com/hfyngvason" + }, + "author": { + "id": 4088036, + "name": "Hordur Freyr Yngvason", + "username": "hfyngvason", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", + "web_url": "https://gitlab.com/hfyngvason" + }, + "assignees": [ + { + "id": 4088036, + "name": "Hordur Freyr Yngvason", + "username": "hfyngvason", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png", + "web_url": "https://gitlab.com/hfyngvason" + } + ], + "reviewers": [ + { + "id": 2535118, + "name": "Thong Kuah", + "username": "tkuah", + "state": "active", + "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", + "web_url": "https://gitlab.com/tkuah" + } + ], + "source_project_id": 278964, + "target_project_id": 278964, + "labels": [ + "backend", + "backstage", + "database", + "database::review pending", + "group::autodevops and kubernetes" + ], + "work_in_progress": true, + "milestone": null, + "merge_when_pipeline_succeeds": false, + "detailed_merge_status": "mergeable", + "sha": "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7", + "merge_commit_sha": null, + "discussion_locked": null, + "should_remove_source_branch": null, + "force_remove_source_branch": true, + "reference": "!15442", + "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15442", + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "squash": true, + "task_completion_status": { + "count": 12, + "completed_count": 0 + }, + "approvals_before_merge": 1 + } +] diff --git a/testdata/get_merge_requests_not_author_username.json b/testdata/get_merge_requests_not_author_username.json new file mode 100644 index 000000000..97c83d98f --- /dev/null +++ b/testdata/get_merge_requests_not_author_username.json @@ -0,0 +1,201 @@ +[ + { + "id": 35384461, + "iid": 15441, + "project_id": 278964, + "title": "Draft: Implement public MR-level approval rules API", + "description": "## What does this MR do?", + "state": "opened", + "created_at": "2019-08-20T10:51:56.806Z", + "updated_at": "2019-08-20T11:00:25.244Z", + "merged_by": null, + "merged_at": null, + "closed_by": null, + "closed_at": null, + "target_branch": "master", + "source_branch": "12055-public-mr-approval-rules-api", + "user_notes_count": 1, + "upvotes": 0, + "downvotes": 0, + "assignee": { + "id": 1884221, + "name": "Patrick Bajao", + "username": "patrickbajao", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", + "web_url": "https://gitlab.com/patrickbajao" + }, + "author": { + "id": 1884221, + "name": "Patrick Bajao", + "username": "patrickbajao", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", + "web_url": "https://gitlab.com/patrickbajao" + }, + "assignees": [ + { + "id": 1884221, + "name": "Patrick Bajao", + "username": "patrickbajao", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/1884221/avatar.png", + "web_url": "https://gitlab.com/patrickbajao" + } + ], + "source_project_id": 278964, + "target_project_id": 278964, + "labels": [ + "Create [DEPRECATED]", + "Deliverable", + "GitLab Enterprise Edition", + "GitLab Starter", + "api", + "approvals", + "backend", + "devops::create", + "feature", + "group::source code", + "workflow::In dev" + ], + "work_in_progress": true, + "milestone": { + "id": 731038, + "iid": 37, + "group_id": 9970, + "title": "12.3", + "description": "", + "state": "active", + "created_at": "2018-12-07T12:40:55.400Z", + "updated_at": "2019-01-16T19:50:20.313Z", + "due_date": "2019-09-22", + "start_date": "2019-08-08", + "web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/37" + }, + "merge_when_pipeline_succeeds": false, + "detailed_merge_status": "mergeable", + "sha": "dbb2b82236b86328f44a1754c9188c0991e707e5", + "merge_commit_sha": null, + "discussion_locked": null, + "should_remove_source_branch": null, + "force_remove_source_branch": false, + "reference": "!15441", + "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15441", + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "squash": false, + "task_completion_status": { + "count": 8, + "completed_count": 1 + }, + "approvals_before_merge": 1 + }, + { + "id": 35368820, + "iid": 15440, + "project_id": 278964, + "title": "Log in Prometheus current number of host and index", + "description": "## What does this MR do?", + "state": "opened", + "created_at": "2019-08-20T10:20:51.687Z", + "updated_at": "2019-08-20T11:06:40.659Z", + "merged_by": null, + "merged_at": null, + "closed_by": null, + "closed_at": null, + "target_branch": "master", + "source_branch": "load-balancing-prometheus", + "user_notes_count": 2, + "upvotes": 0, + "downvotes": 0, + "assignee": { + "id": 4059128, + "name": "Avielle Wolfe", + "username": "avielle", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4059128/avatar.png", + "web_url": "https://gitlab.com/avielle" + }, + "author": { + "id": 2535118, + "name": "Thong Kuah", + "username": "tkuah", + "state": "active", + "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", + "web_url": "https://gitlab.com/tkuah" + }, + "assignees": [ + { + "id": 4059128, + "name": "Avielle Wolfe", + "username": "avielle", + "state": "active", + "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4059128/avatar.png", + "web_url": "https://gitlab.com/avielle" + }, + { + "id": 2535118, + "name": "Thong Kuah", + "username": "tkuah", + "state": "active", + "avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon", + "web_url": "https://gitlab.com/tkuah" + } + ], + "source_project_id": 278964, + "target_project_id": 278964, + "labels": [ + "Configure [DEPRECATED]", + "Observability", + "P1", + "backend", + "backstage", + "database", + "database::review pending", + "devops::configure", + "gitlab.com", + "group::autodevops and kubernetes", + "infradev", + "workflow::In dev" + ], + "work_in_progress": false, + "milestone": { + "id": 731038, + "iid": 37, + "group_id": 9970, + "title": "12.3", + "description": "", + "state": "active", + "created_at": "2018-12-07T12:40:55.400Z", + "updated_at": "2019-01-16T19:50:20.313Z", + "due_date": "2019-09-22", + "start_date": "2019-08-08", + "web_url": "https://gitlab.com/groups/gitlab-org/-/milestones/37" + }, + "merge_when_pipeline_succeeds": false, + "detailed_merge_status": "mergeable", + "sha": "eae31acf34d2df2aba2ea469e432cab1c6a05b53", + "merge_commit_sha": null, + "discussion_locked": null, + "should_remove_source_branch": null, + "force_remove_source_branch": false, + "reference": "!15440", + "web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15440", + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "squash": false, + "task_completion_status": { + "count": 12, + "completed_count": 1 + }, + "approvals_before_merge": 1 + } +]