Repository: xanzy/go-gitlab
Pull request #1505 (2022-07-14 00:18:56)

feat(issues): iteration field, search by iteration
Add missing Iteration field to Issue struct and support searching by
iteration ID.

Add tests for new features.

---

I've been using these changes for a work project for a few days now without issue.


Patch:
diff --git a/boards.go b/boards.go
index a75c9bd..fa90b92 100644
--- a/boards.go
+++ b/boards.go
@@ -33,21 +33,11 @@ type IssueBoardsService struct {
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
 type IssueBoard struct {
-	ID        int        `json:"id"`
-	Name      string     `json:"name"`
-	Project   *Project   `json:"project"`
-	Milestone *Milestone `json:"milestone"`
-	Assignee  *struct {
-		ID        int    `json:"id"`
-		Username  string `json:"username"`
-		Name      string `json:"name"`
-		State     string `json:"state"`
-		AvatarURL string `json:"avatar_url"`
-		WebURL    string `json:"web_url"`
-	} `json:"assignee"`
-	Lists  []*BoardList    `json:"lists"`
-	Weight int             `json:"weight"`
-	Labels []*LabelDetails `json:"labels"`
+	ID        int          `json:"id"`
+	Name      string       `json:"name"`
+	Project   *Project     `json:"project"`
+	Milestone *Milestone   `json:"milestone"`
+	Lists     []*BoardList `json:"lists"`
 }
 
 func (b IssueBoard) String() string {
@@ -58,18 +48,9 @@ func (b IssueBoard) String() string {
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html
 type BoardList struct {
-	ID       int `json:"id"`
-	Assignee *struct {
-		ID       int    `json:"id"`
-		Name     string `json:"name"`
-		Username string `json:"username"`
-	} `json:"assignee"`
-	Iteration      *ProjectIteration `json:"iteration"`
-	Label          *Label            `json:"label"`
-	MaxIssueCount  int               `json:"max_issue_count"`
-	MaxIssueWeight int               `json:"max_issue_weight"`
-	Milestone      *Milestone        `json:"milestone"`
-	Position       int               `json:"position"`
+	ID       int    `json:"id"`
+	Label    *Label `json:"label"`
+	Position int    `json:"position"`
 }
 
 func (b BoardList) String() string {
@@ -276,10 +257,7 @@ func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int,
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list
 type CreateIssueBoardListOptions struct {
-	LabelID     *int `url:"label_id,omitempty" json:"label_id,omitempty"`
-	AssigneeID  *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
-	MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
-	IterationID *int `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
+	LabelID *int `url:"label_id" json:"label_id"`
 }
 
 // CreateIssueBoardList creates a new issue board list.
diff --git a/issues.go b/issues.go
index 4d9e0b4..7280efb 100644
--- a/issues.go
+++ b/issues.go
@@ -120,6 +120,7 @@ type Issue struct {
 	MergeRequestCount    int                    `json:"merge_requests_count"`
 	EpicIssueID          int                    `json:"epic_issue_id"`
 	Epic                 *Epic                  `json:"epic"`
+	Iteration            *GroupIteration        `json:"iteration"`
 	TaskCompletionStatus *TasksCompletionStatus `json:"task_completion_status"`
 }
 
@@ -232,6 +233,7 @@ type ListIssuesOptions struct {
 	UpdatedBefore      *time.Time       `url:"updated_before,omitempty" json:"updated_before,omitempty"`
 	Confidential       *bool            `url:"confidential,omitempty" json:"confidential,omitempty"`
 	IssueType          *string          `url:"issue_type,omitempty" json:"issue_type,omitempty"`
+	IterationID        *int             `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
 }
 
 // ListIssues gets all issues created by authenticated user. This function
@@ -284,6 +286,7 @@ type ListGroupIssuesOptions struct {
 	UpdatedAfter       *time.Time       `url:"updated_after,omitempty" json:"updated_after,omitempty"`
 	UpdatedBefore      *time.Time       `url:"updated_before,omitempty" json:"updated_before,omitempty"`
 	IssueType          *string          `url:"issue_type,omitempty" json:"issue_type,omitempty"`
+	IterationID        *int             `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
 }
 
 // ListGroupIssues gets a list of group issues. This function accepts
@@ -343,6 +346,7 @@ type ListProjectIssuesOptions struct {
 	UpdatedBefore      *time.Time       `url:"updated_before,omitempty" json:"updated_before,omitempty"`
 	Confidential       *bool            `url:"confidential,omitempty" json:"confidential,omitempty"`
 	IssueType          *string          `url:"issue_type,omitempty" json:"issue_type,omitempty"`
+	IterationID        *int             `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
 }
 
 // ListProjectIssues gets a list of project issues. This function accepts


Test patch:
diff --git a/boards_test.go b/boards_test.go
index 892e1de..46dfb8a 100644
--- a/boards_test.go
+++ b/boards_test.go
@@ -49,8 +49,7 @@ func TestIssueBoardsService_CreateIssueBoard(t *testing.T) {
 			Path:              "diaspora-project-site",
 			PathWithNamespace: "diaspora/diaspora-project-site",
 		},
-		Lists:  []*BoardList{},
-		Labels: []*LabelDetails{},
+		Lists: []*BoardList{},
 	}
 
 	ib, resp, err := client.Boards.CreateIssueBoard(5, nil, nil)
@@ -115,8 +114,7 @@ func TestIssueBoardsService_UpdateIssueBoard(t *testing.T) {
 			Path:              "diaspora-project-site",
 			PathWithNamespace: "diaspora/diaspora-project-site",
 		},
-		Lists:  []*BoardList{},
-		Labels: []*LabelDetails{},
+		Lists: []*BoardList{},
 	}
 
 	ib, resp, err := client.Boards.UpdateIssueBoard(5, 1, nil, nil)
diff --git a/group_boards_test.go b/group_boards_test.go
index 63e29f0..ba60c36 100644
--- a/group_boards_test.go
+++ b/group_boards_test.go
@@ -583,14 +583,6 @@ func TestGroupIssueBoardsService_CreateGroupIssueBoardList(t *testing.T) {
 		ID:       9,
 		Label:    nil,
 		Position: 0,
-		Milestone: &Milestone{
-			ID:          7,
-			IID:         3,
-			Title:       "Milestone with due date",
-			Description: "",
-			State:       "active",
-			WebURL:      "https://gitlab.example.com/groups/issue-reproduce/-/milestones/3",
-		},
 	}
 
 	bl, resp, err := client.GroupIssueBoards.CreateGroupIssueBoardList(5, 1, nil, nil)
diff --git a/issues_test.go b/issues_test.go
index 4e47c95..1b29f42 100644
--- a/issues_test.go
+++ b/issues_test.go
@@ -324,6 +324,62 @@ func TestListIssuesSearchInDescription(t *testing.T) {
 		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
 	}
 }
+
+func TestListIssuesSearchByIterationID(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/issues", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		testURL(t, r, "/api/v4/issues?iteration_id=90")
+		fmt.Fprint(w, `
+			[
+				{
+					"id": 1,
+					"title": "A Test Issue Title",
+					"description": "This is the description for the issue",
+					"iteration": {
+						"id":90,
+						"iid":4,
+						"sequence":2,
+						"group_id":162,
+						"state":2,
+						"web_url":"https://gitlab.com/groups/my-group/-/iterations/90"
+					}
+				}
+			]`,
+		)
+	})
+
+	listProjectIssue := &ListIssuesOptions{
+		IterationID: Int(90),
+	}
+
+	issues, _, err := client.Issues.ListIssues(listProjectIssue)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	want := []*Issue{{
+		ID:          1,
+		Title:       "A Test Issue Title",
+		Description: "This is the description for the issue",
+		Iteration: &GroupIteration{
+			ID:       90,
+			IID:      4,
+			Sequence: 2,
+			GroupID:  162,
+			State:    2,
+			WebURL:   "https://gitlab.com/groups/my-group/-/iterations/90",
+		},
+	}}
+
+	if !reflect.DeepEqual(want, issues) {
+		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
+	}
+ }
+
 func TestListProjectIssues(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
@@ -355,6 +411,61 @@ func TestListProjectIssues(t *testing.T) {
 	}
 }
 
+func TestListProjectIssuesSearchByIterationID(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/projects/1/issues", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		testURL(t, r, "/api/v4/projects/1/issues?iteration_id=90")
+		fmt.Fprint(w, `
+			[
+				{
+					"id": 1,
+					"title": "A Test Issue Title",
+					"description": "This is the description for the issue",
+					"iteration": {
+						"id":90,
+						"iid":4,
+						"sequence":2,
+						"group_id":162,
+						"state":2,
+						"web_url":"https://gitlab.com/groups/my-group/-/iterations/90"
+					}
+				}
+			]`,
+		)
+	})
+
+	listProjectIssue := &ListProjectIssuesOptions{
+		IterationID: Int(90),
+	}
+
+	issues, _, err := client.Issues.ListProjectIssues(1 ,listProjectIssue)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	want := []*Issue{{
+		ID:          1,
+		Title:       "A Test Issue Title",
+		Description: "This is the description for the issue",
+		Iteration: &GroupIteration{
+			ID:       90,
+			IID:      4,
+			Sequence: 2,
+			GroupID:  162,
+			State:    2,
+			WebURL:   "https://gitlab.com/groups/my-group/-/iterations/90",
+		},
+	}}
+
+	if !reflect.DeepEqual(want, issues) {
+		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
+	}
+ }
+
 func TestListGroupIssues(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
@@ -388,6 +499,61 @@ func TestListGroupIssues(t *testing.T) {
 	}
 }
 
+func TestListGroupIssuesSearchByIterationID(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/issues", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		testURL(t, r, "/api/v4/groups/1/issues?iteration_id=90")
+		fmt.Fprint(w, `
+			[
+				{
+					"id": 1,
+					"title": "A Test Issue Title",
+					"description": "This is the description for the issue",
+					"iteration": {
+						"id":90,
+						"iid":4,
+						"sequence":2,
+						"group_id":162,
+						"state":2,
+						"web_url":"https://gitlab.com/groups/my-group/-/iterations/90"
+					}
+				}
+			]`,
+		)
+	})
+
+	listProjectIssue := &ListGroupIssuesOptions{
+		IterationID: Int(90),
+	}
+
+	issues, _, err := client.Issues.ListGroupIssues(1, listProjectIssue)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	want := []*Issue{{
+		ID:          1,
+		Title:       "A Test Issue Title",
+		Description: "This is the description for the issue",
+		Iteration: &GroupIteration{
+			ID:       90,
+			IID:      4,
+			Sequence: 2,
+			GroupID:  162,
+			State:    2,
+			WebURL:   "https://gitlab.com/groups/my-group/-/iterations/90",
+		},
+	}}
+
+	if !reflect.DeepEqual(want, issues) {
+		t.Errorf("Issues.ListIssues returned %+v, want %+v", issues, want)
+	}
+ }
+
 func TestCreateIssue(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)