Repository: xanzy/go-gitlab
Pull request #901 (2020-08-05 11:39:56)

Add support Issues Statistics API
Add support Issues Statistics API https://docs.gitlab.com/ee/api/issues_statistics.html. Related Issue https://github.com/xanzy/go-gitlab/issues/730.


Patch:
diff --git a/gitlab.go b/gitlab.go
index d686f81..ae41af0 100644
--- a/gitlab.go
+++ b/gitlab.go
@@ -128,6 +128,7 @@ type Client struct {
 	Groups                *GroupsService
 	IssueLinks            *IssueLinksService
 	Issues                *IssuesService
+	IssuesStatistics      *IssuesStatisticsService
 	Jobs                  *JobsService
 	Keys                  *KeysService
 	Labels                *LabelsService
@@ -283,6 +284,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
 	c.Groups = &GroupsService{client: c}
 	c.IssueLinks = &IssueLinksService{client: c}
 	c.Issues = &IssuesService{client: c, timeStats: timeStats}
+	c.IssuesStatistics = &IssuesStatisticsService{client: c}
 	c.Jobs = &JobsService{client: c}
 	c.Keys = &KeysService{client: c}
 	c.Labels = &LabelsService{client: c}
diff --git a/issues_statistics.go b/issues_statistics.go
new file mode 100644
index 0000000..0f61c3f
--- /dev/null
+++ b/issues_statistics.go
@@ -0,0 +1,186 @@
+//
+// Copyright 2017, Sander van Harmelen
+//
+// 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"
+	"time"
+)
+
+// IssuesStatisticsService handles communication with the issues statistics
+// related methods of the GitLab API.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/issues_statistics.html
+type IssuesStatisticsService struct {
+	client *Client
+}
+
+// IssuesStatistics represents a GitLab issues statistic.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/issues_statistics.html
+type IssuesStatistics struct {
+	Statistics struct {
+		Counts struct {
+			All    int `json:"all"`
+			Closed int `json:"closed"`
+			Opened int `json:"opened"`
+		} `json:"counts"`
+	} `json:"statistics"`
+}
+
+func (n IssuesStatistics) String() string {
+	return Stringify(n)
+}
+
+// GetIssuesStatisticsOptions represents the available GetIssuesStatistics() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issues_statistics.html#get-issues-statistics
+type GetIssuesStatisticsOptions struct {
+	Labels           *Labels    `url:"labels,omitempty" json:"labels,omitempty"`
+	Milestone        *Milestone `url:"milestone,omitempty" json:"milestone,omitempty"`
+	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"`
+	AssigneeID       *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	AssigneeUsername []string   `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
+	MyReactionEmoji  *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+	IIDs             []int      `url:"iids,omitempty" json:"iids,omitempty"`
+	Search           *string    `url:"search,omitempty" json:"search,omitempty"`
+	In               *string    `url:"in,omitempty" json:"in,omitempty"`
+	CreatedAfter     *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore    *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	UpdatedAfter     *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
+	UpdatedBefore    *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
+	Confidential     *bool      `url:"confidential,omitempty" json:"confidential,omitempty"`
+}
+
+// GetIssuesStatistics gets issues statistics on all issues the authenticated
+// user has access to.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issues_statistics.html#get-issues-statistics
+func (s *IssuesStatisticsService) GetIssuesStatistics(opt *GetIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
+	req, err := s.client.NewRequest("GET", "issues_statistics", opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	is := new(IssuesStatistics)
+	resp, err := s.client.Do(req, is)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return is, resp, err
+}
+
+// GetGroupIssuesStatisticsOptions represents the available GetGroupIssuesStatistics()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issues_statistics.html#get-group-issues-statistics
+type GetGroupIssuesStatisticsOptions struct {
+	Labels           *Labels    `url:"labels,omitempty" json:"labels,omitempty"`
+	IIDs             []int      `url:"iids,omitempty" json:"iids,omitempty"`
+	Milestone        *Milestone `url:"milestone,omitempty" json:"milestone,omitempty"`
+	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"`
+	AssigneeID       *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	AssigneeUsername []string   `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
+	MyReactionEmoji  *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+	Search           *string    `url:"search,omitempty" json:"search,omitempty"`
+	CreatedAfter     *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore    *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	UpdatedAfter     *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
+	UpdatedBefore    *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
+	Confidential     *bool      `url:"confidential,omitempty" json:"confidential,omitempty"`
+}
+
+// GetGroupIssuesStatistics gets issues count statistics for given group.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issues_statistics.html#get-group-issues-statistics
+func (s *IssuesStatisticsService) GetGroupIssuesStatistics(gid interface{}, opt *GetGroupIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/issues_statistics", pathEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	is := new(IssuesStatistics)
+	resp, err := s.client.Do(req, is)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return is, resp, err
+}
+
+// GetProjectIssuesStatisticsOptions represents the available
+// GetProjectIssuesStatistics() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issues_statistics.html#get-project-issues-statistics
+type GetProjectIssuesStatisticsOptions struct {
+	IIDs             []int      `url:"iids,omitempty" json:"iids,omitempty"`
+	Labels           *Labels    `url:"labels,omitempty" json:"labels,omitempty"`
+	Milestone        *Milestone `url:"milestone,omitempty" json:"milestone,omitempty"`
+	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"`
+	AssigneeID       *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
+	AssigneeUsername []string   `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
+	MyReactionEmoji  *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
+	Search           *string    `url:"search,omitempty" json:"search,omitempty"`
+	CreatedAfter     *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
+	CreatedBefore    *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	UpdatedAfter     *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
+	UpdatedBefore    *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
+	Confidential     *bool      `url:"confidential,omitempty" json:"confidential,omitempty"`
+}
+
+// GetProjectIssuesStatistics gets issues count statistics for given project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/issues_statistics.html#get-project-issues-statistics
+func (s *IssuesStatisticsService) GetProjectIssuesStatistics(pid interface{}, opt *GetProjectIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/issues_statistics", pathEscape(project))
+
+	req, err := s.client.NewRequest("GET", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	is := new(IssuesStatistics)
+	resp, err := s.client.Do(req, is)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return is, resp, err
+}


Test patch:
diff --git a/issues_statistics_test.go b/issues_statistics_test.go
new file mode 100644
index 0000000..d44061e
--- /dev/null
+++ b/issues_statistics_test.go
@@ -0,0 +1,138 @@
+package gitlab
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+func TestGetIssuesStatistics(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/issues_statistics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testURL(t, r, "/api/v4/issues_statistics?assignee_id=1&author_id=1")
+		fmt.Fprint(w, `{"statistics": {"counts": {"all": 20,"closed": 5,"opened": 15}}}`)
+	})
+
+	opt := &GetIssuesStatisticsOptions{
+		AssigneeID: Int(1),
+		AuthorID:   Int(1),
+	}
+
+	issue, _, err := client.IssuesStatistics.GetIssuesStatistics(opt)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	want := &IssuesStatistics{
+		Statistics: struct {
+			Counts struct {
+				All    int `json:"all"`
+				Closed int `json:"closed"`
+				Opened int `json:"opened"`
+			} `json:"counts"`
+		}{
+			Counts: struct {
+				All    int `json:"all"`
+				Closed int `json:"closed"`
+				Opened int `json:"opened"`
+			}{
+				20, 5, 15,
+			},
+		},
+	}
+
+	if !reflect.DeepEqual(want, issue) {
+		t.Errorf("IssuesStatistics.GetIssuesStatistics returned %+v, want %+v", issue, want)
+	}
+}
+
+func TestGetGroupIssuesStatistics(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/issues_statistics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testURL(t, r, "/api/v4/groups/1/issues_statistics?assignee_id=1&author_id=1")
+		fmt.Fprint(w, `{"statistics": {"counts": {"all": 20,"closed": 5,"opened": 15}}}`)
+	})
+
+	opt := &GetGroupIssuesStatisticsOptions{
+		AssigneeID: Int(1),
+		AuthorID:   Int(1),
+	}
+
+	issue, _, err := client.IssuesStatistics.GetGroupIssuesStatistics(1, opt)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	want := &IssuesStatistics{
+		Statistics: struct {
+			Counts struct {
+				All    int `json:"all"`
+				Closed int `json:"closed"`
+				Opened int `json:"opened"`
+			} `json:"counts"`
+		}{
+			Counts: struct {
+				All    int `json:"all"`
+				Closed int `json:"closed"`
+				Opened int `json:"opened"`
+			}{
+				20, 5, 15,
+			},
+		},
+	}
+
+	if !reflect.DeepEqual(want, issue) {
+		t.Errorf("IssuesStatistics.GetGroupIssuesStatistics returned %+v, want %+v", issue, want)
+	}
+}
+
+func TestGetProjectIssuesStatistics(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/projects/1/issues_statistics", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testURL(t, r, "/api/v4/projects/1/issues_statistics?assignee_id=1&author_id=1")
+		fmt.Fprint(w, `{"statistics": {"counts": {"all": 20,"closed": 5,"opened": 15}}}`)
+	})
+
+	opt := &GetProjectIssuesStatisticsOptions{
+		AssigneeID: Int(1),
+		AuthorID:   Int(1),
+	}
+
+	issue, _, err := client.IssuesStatistics.GetProjectIssuesStatistics(1, opt)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	want := &IssuesStatistics{
+		Statistics: struct {
+			Counts struct {
+				All    int `json:"all"`
+				Closed int `json:"closed"`
+				Opened int `json:"opened"`
+			} `json:"counts"`
+		}{
+			Counts: struct {
+				All    int `json:"all"`
+				Closed int `json:"closed"`
+				Opened int `json:"opened"`
+			}{
+				20, 5, 15,
+			},
+		},
+	}
+
+	if !reflect.DeepEqual(want, issue) {
+		t.Errorf("IssuesStatistics.GetProjectIssuesStatistics returned %+v, want %+v", issue, want)
+	}
+}