diff --git a/gitlab.go b/gitlab.go index aaf434b35..697e21df9 100644 --- a/gitlab.go +++ b/gitlab.go @@ -132,6 +132,7 @@ type Client struct { GroupCluster *GroupClustersService GroupImportExport *GroupImportExportService GroupIssueBoards *GroupIssueBoardsService + GroupIterations *GroupIterationsService GroupLabels *GroupLabelsService GroupMembers *GroupMembersService GroupMilestones *GroupMilestonesService @@ -168,6 +169,7 @@ type Client struct { ProjectAccessTokens *ProjectAccessTokensService ProjectCluster *ProjectClustersService ProjectImportExport *ProjectImportExportService + ProjectIterations *ProjectIterationsService ProjectMembers *ProjectMembersService ProjectMirrors *ProjectMirrorService ProjectSnippets *ProjectSnippetsService @@ -327,6 +329,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.GroupCluster = &GroupClustersService{client: c} c.GroupImportExport = &GroupImportExportService{client: c} c.GroupIssueBoards = &GroupIssueBoardsService{client: c} + c.GroupIterations = &GroupIterationsService{client: c} c.GroupLabels = &GroupLabelsService{client: c} c.GroupMembers = &GroupMembersService{client: c} c.GroupMilestones = &GroupMilestonesService{client: c} @@ -363,6 +366,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) { c.ProjectAccessTokens = &ProjectAccessTokensService{client: c} c.ProjectCluster = &ProjectClustersService{client: c} c.ProjectImportExport = &ProjectImportExportService{client: c} + c.ProjectIterations = &ProjectIterationsService{client: c} c.ProjectMembers = &ProjectMembersService{client: c} c.ProjectMirrors = &ProjectMirrorService{client: c} c.ProjectSnippets = &ProjectSnippetsService{client: c} diff --git a/group_iterations.go b/group_iterations.go new file mode 100644 index 000000000..a642091c2 --- /dev/null +++ b/group_iterations.go @@ -0,0 +1,90 @@ +// +// Copyright 2022, Daniel Steinke +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "fmt" + "net/http" + "time" +) + +// IterationsAPI handles communication with the iterations related methods +// of the GitLab API +// +// GitLab API docs: https://docs.gitlab.com/ee/api/group_iterations.html +type GroupIterationsService struct { + client *Client +} + +// GroupInteration represents a GitLab iteration. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/group_iterations.html +type GroupIteration struct { + ID int `json:"id"` + IID int `json:"iid"` + Sequence int `json:"sequence"` + GroupID int `json:"group_id"` + Title string `json:"title"` + Description string `json:"description"` + State int `json:"state"` + CreatedAt *time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` + DueDate *ISOTime `json:"due_date"` + StartDate *ISOTime `json:"start_date"` + WebURL string `json:"web_url"` +} + +func (i GroupIteration) String() string { + return Stringify(i) +} + +// ListGroupIterationsOptions contains the available ListGroupIterations() +// options +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_iterations.html#list-group-iterations +type ListGroupIterationsOptions struct { + ListOptions + State *string `url:"state,omitempty" json:"state,omitempty"` + Search *string `url:"search,omitempty" json:"search,omitempty"` + IncludeAncestors *bool `url:"include_ancestors,omitempty" json:"include_ancestors,omitempty"` +} + +// ListGroupIterations returns a list of group iterations. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_iterations.html#list-group-iterations +func (s *GroupIterationsService) ListGroupIterations(gid interface{}, opt *ListGroupIterationsOptions, options ...RequestOptionFunc) ([]*GroupIteration, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/iterations", PathEscape(group)) + + req, err := s.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var gis []*GroupIteration + resp, err := s.client.Do(req, &gis) + if err != nil { + return nil, nil, err + } + + return gis, resp, err +} diff --git a/group_iterations_test.go b/group_iterations_test.go new file mode 100644 index 000000000..4d2030faf --- /dev/null +++ b/group_iterations_test.go @@ -0,0 +1,49 @@ +package gitlab + +import ( + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestListGroupIterations(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/groups/5/iterations", + func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, `[ + { + "id": 53, + "iid": 13, + "sequence": 1, + "group_id": 5, + "title": "Iteration II", + "description": "Ipsum Lorem ipsum", + "state": 2, + "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13" + } + ]`) + }) + + iterations, _, err := client.GroupIterations.ListGroupIterations(5, &ListGroupIterationsOptions{}) + if err != nil { + t.Errorf("GroupIterations.ListGroupIterations returned error: %v", err) + } + + want := []*GroupIteration{{ + ID: 53, + IID: 13, + Sequence: 1, + GroupID: 5, + Title: "Iteration II", + Description: "Ipsum Lorem ipsum", + State: 2, + WebURL: "http://gitlab.example.com/groups/my-group/-/iterations/13", + }} + if !reflect.DeepEqual(want, iterations) { + t.Errorf("GroupIterations.ListGroupIterations returned %+v, want %+v", iterations, want) + } +} diff --git a/project_iterations.go b/project_iterations.go new file mode 100644 index 000000000..78583efab --- /dev/null +++ b/project_iterations.go @@ -0,0 +1,90 @@ +// +// Copyright 2022, Daniel Steinke +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package gitlab + +import ( + "fmt" + "net/http" + "time" +) + +// IterationsAPI handles communication with the project iterations related +// methods of the GitLab API +// +// GitLab API docs: https://docs.gitlab.com/ee/api/iterations.html +type ProjectIterationsService struct { + client *Client +} + +// ProjectIteration represents a GitLab project iteration. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/iterations.html +type ProjectIteration struct { + ID int `json:"id"` + IID int `json:"iid"` + Sequence int `json:"sequence"` + GroupID int `json:"group_id"` + Title string `json:"title"` + Description string `json:"description"` + State int `json:"state"` + CreatedAt *time.Time `json:"created_at"` + UpdatedAt *time.Time `json:"updated_at"` + DueDate *ISOTime `json:"due_date"` + StartDate *ISOTime `json:"start_date"` + WebURL string `json:"web_url"` +} + +func (i ProjectIteration) String() string { + return Stringify(i) +} + +// ListProjectIterationsOptions contains the available ListProjectIterations() +// options +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_iterations.html#list-project-iterations +type ListProjectIterationsOptions struct { + ListOptions + State *string `url:"state,omitempty" json:"state,omitempty"` + Search *string `url:"search,omitempty" json:"search,omitempty"` + IncludeAncestors *bool `url:"include_ancestors,omitempty" json:"include_ancestors,omitempty"` +} + +// ListProjectIterations returns a list of projects iterations. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/group_iterations.html#list-project-iterations +func (i *ProjectIterationsService) ListProjectIterations(pid interface{}, opt *ListProjectIterationsOptions, options ...RequestOptionFunc) ([]*ProjectIteration, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("projects/%s/iterations", PathEscape(project)) + + req, err := i.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var pis []*ProjectIteration + resp, err := i.client.Do(req, &pis) + if err != nil { + return nil, resp, err + } + + return pis, resp, err +} diff --git a/project_iterations_test.go b/project_iterations_test.go new file mode 100644 index 000000000..ca304c66c --- /dev/null +++ b/project_iterations_test.go @@ -0,0 +1,49 @@ +package gitlab + +import ( + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestListProjectIterations(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/projects/42/iterations", + func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprintf(w, `[ + { + "id": 53, + "iid": 13, + "sequence": 1, + "group_id": 5, + "title": "Iteration II", + "description": "Ipsum Lorem ipsum", + "state": 2, + "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13" + } + ]`) + }) + + iterations, _, err := client.ProjectIterations.ListProjectIterations(42, &ListProjectIterationsOptions{}) + if err != nil { + t.Errorf("GroupIterations.ListGroupIterations returned error: %v", err) + } + + want := []*ProjectIteration{{ + ID: 53, + IID: 13, + Sequence: 1, + GroupID: 5, + Title: "Iteration II", + Description: "Ipsum Lorem ipsum", + State: 2, + WebURL: "http://gitlab.example.com/groups/my-group/-/iterations/13", + }} + if !reflect.DeepEqual(want, iterations) { + t.Errorf("ProjectIterations.ListProjectIterations returned %+v, want %+v", iterations, want) + } +} diff --git a/testdata/list_group_iterations.json b/testdata/list_group_iterations.json new file mode 100644 index 000000000..9d032c083 --- /dev/null +++ b/testdata/list_group_iterations.json @@ -0,0 +1,16 @@ +[ + { + "id": 53, + "iid": 13, + "sequence": 1, + "group_id": 5, + "title": "Iteration II", + "description": "Ipsum Lorem ipsum", + "state": 2, + "created_at": "2020-01-27T05:07:12.573Z", + "updated_at": "2020-01-27T05:07:12.573Z", + "due_date": "2020-02-01", + "start_date": "2020-02-14", + "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13" + } +]