Repository: PagerDuty/go-pagerduty
Pull request #261 (2021-01-13 11:09:26)

Analytics
Add the analytics endpoint Get aggregated incident data 
https://developer.pagerduty.com/api-reference/reference/REST/openapiv3.json/paths/~1analytics~1metrics~1incidents~1all/post

theckman: @melchiormoulin I've included this in the v1.4.0 milestone, as we're planning to make v1.5.0 a special release and I want to get as many features / bugfixes as I can into the v1.4.x line.

I don't have a specific timeline for when we hope to get that out, but if you don't feel like you'll have time to dedicate to this PR in the near-term I can target this for v1.6.0.
melchiormoulin: Hello @theckman It's been a while i've made this pr, i'm not available right now but I can do this this weekend is that ok for you?
theckman: @melchiormoulin most definitely! Thank you.

Patch:
diff --git a/.gitignore b/.gitignore
index 158ba37..47423b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ bin/*
 *.swp
 pd
 vendor/
-.vscode/
\ No newline at end of file
+.vscode/
+.idea/
diff --git a/analytics.go b/analytics.go
new file mode 100644
index 0000000..95cb70c
--- /dev/null
+++ b/analytics.go
@@ -0,0 +1,65 @@
+package pagerduty
+
+import (
+	"context"
+)
+
+type AnalyticsRequest struct {
+	AnalyticsFilter *AnalyticsFilter `json:"filters,omitempty"`
+	AggregateUnit   string           `json:"aggregate_unit,omitempty"`
+	TimeZone        string           `json:"time_zone,omitempty"`
+}
+type AnalyticsResponse struct {
+	Data            []AnalyticsData  `json:"data,omitempty"`
+	AnalyticsFilter *AnalyticsFilter `json:"filters,omitempty"`
+	AggregateUnit   string           `json:"aggregate_unit,omitempty"`
+	TimeZone        string           `json:"time_zone,omitempty"`
+}
+
+type AnalyticsFilter struct {
+	CreatedAtStart string   `json:"created_at_start,omitempty"`
+	CreatedAtEnd   string   `json:"created_at_end,omitempty"`
+	Urgency        string   `json:"urgency,omitempty"`
+	Major          bool     `json:"major,omitempty"`
+	ServiceIDs     []string `json:"service_ids,omitempty"`
+	TeamIDs        []string `json:"team_ids,omitempty"`
+	PriorityIDs    []string `json:"priority_ids,omitempty"`
+	PriorityName   []string `json:"priority_name,omitempty"`
+}
+
+type AnalyticsData struct {
+	ServiceID                      string `json:"service_id,omitempty"`
+	ServiceName                    string `json:"service_name,omitempty"`
+	TeamID                         string `json:"team_id,omitempty"`
+	TeamName                       string `json:"team_name,omitempty"`
+	MeanSecondsToResolve           int    `json:"mean_seconds_to_resolve,omitempty"`
+	MeanSecondsToFirstAck          int    `json:"mean_seconds_to_first_ack,omitempty"`
+	MeanSecondsToEngage            int    `json:"mean_seconds_to_engage,omitempty"`
+	MeanSecondsToMobilize          int    `json:"mean_seconds_to_mobilize,omitempty"`
+	MeanEngagedSeconds             int    `json:"mean_engaged_seconds,omitempty"`
+	MeanEngagedUserCount           int    `json:"mean_engaged_user_count,omitempty"`
+	TotalEscalationCount           int    `json:"total_escalation_count,omitempty"`
+	MeanAssignmentCount            int    `json:"mean_assignment_count,omitempty"`
+	TotalBusinessHourInterruptions int    `json:"total_business_hour_interruptions,omitempty"`
+	TotalSleepHourInterruptions    int    `json:"total_sleep_hour_interruptions,omitempty"`
+	TotalOffHourInterruptions      int    `json:"total_off_hour_interruptions,omitempty"`
+	TotalSnoozedSeconds            int    `json:"total_snoozed_seconds,omitempty"`
+	TotalEngagedSeconds            int    `json:"total_engaged_seconds,omitempty"`
+	TotalIncidentCount             int    `json:"total_incident_count,omitempty"`
+	UpTimePct                      int    `json:"up_time_pct,omitempty"`
+	UserDefinedEffortSeconds       int    `json:"user_defined_effort_seconds,omitempty"`
+	RangeStart                     string `json:"range_start,omitempty"`
+}
+
+func (c *Client) GetAggregatedIncidentData(ctx context.Context, analytics AnalyticsRequest) (AnalyticsResponse, error) {
+	var analyticsResponse AnalyticsResponse
+	headers := make(map[string]string)
+	headers["X-EARLY-ACCESS"] = "analytics-v2"
+
+	resp, err := c.post(ctx, "/analytics/metrics/incidents/all", analytics, headers)
+	if err != nil {
+		return analyticsResponse, err
+	}
+	err = c.decodeJSON(resp, &analyticsResponse)
+	return analyticsResponse, err
+}
diff --git a/command/analytics_show.go b/command/analytics_show.go
new file mode 100644
index 0000000..6170fea
--- /dev/null
+++ b/command/analytics_show.go
@@ -0,0 +1,105 @@
+package main
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"github.com/PagerDuty/go-pagerduty"
+	"github.com/mitchellh/cli"
+	log "github.com/sirupsen/logrus"
+	"strings"
+	"time"
+)
+
+type AnalyticsShow struct {
+	Meta
+}
+
+func AnalyticsGetAggregatedIncidentDataCommand() (cli.Command, error) {
+	return &AnalyticsShow{}, nil
+}
+
+func (c *AnalyticsShow) Help() string {
+	helpText := `
+	analytics show
+
+	Options:
+
+		 -(service_id|team_id) #MANDATORY provide service or team id to stats on.
+		 -start #Optional RFC3339 format default : 7 days ago
+		 -end #Optional RFC3339 format default : now
+		 -urgency #Optional (high|low)
+	` + c.Meta.Help()
+	return strings.TrimSpace(helpText)
+}
+
+func (c *AnalyticsShow) Synopsis() string {
+	return "Get analytics aggregated incident data"
+}
+
+func (c *AnalyticsShow) Run(args []string) int {
+	flags := c.Meta.FlagSet("analytics show")
+	flags.Usage = func() { fmt.Println(c.Help()) }
+	servID := flags.String("service_id", "", "Service ID")
+	now := time.Now()
+	sevenDaysAgo := now.Add(time.Duration(-24*7) * time.Hour)
+	start := flags.String("start", sevenDaysAgo.Format(time.RFC3339), "start date")
+	end := flags.String("end", now.Format(time.RFC3339), "end date")
+	urgency := flags.String("urgency", "", "high|low")
+	teamID := flags.String("team_id", "", "team ID")
+
+	//log.SetLevel(log.DebugLevel)
+	if err := flags.Parse(args); err != nil {
+		log.Errorln(err)
+		return -1
+	}
+
+	if err := c.Meta.Setup(); err != nil {
+		log.Error(err)
+		return -1
+	}
+
+	client := c.Meta.Client()
+
+	serviceIds := make([]string, 1)
+	if *servID == "" {
+		serviceIds = nil
+	} else {
+		serviceIds[0] = *servID
+	}
+
+	teamIds := make([]string, 1)
+	if *teamID == "" {
+		teamIds = nil
+	} else {
+		teamIds[0] = *teamID
+	}
+
+	analyticsFilter := pagerduty.AnalyticsFilter{
+		CreatedAtStart: *start,
+		CreatedAtEnd:   *end,
+		Urgency:        *urgency,
+		ServiceIDs:     serviceIds,
+		TeamIDs:        teamIds,
+	}
+
+	analytics := pagerduty.AnalyticsRequest{
+		AnalyticsFilter: &analyticsFilter,
+		AggregateUnit:   "day",
+		TimeZone:        "Etc/UTC",
+	}
+
+	aggregatedIncidentData, err := client.GetAggregatedIncidentData(context.Background(), analytics)
+	if err != nil {
+		log.Error(err)
+		return -1
+	}
+
+	aggregatedIncidentDataBytes, err := json.Marshal(aggregatedIncidentData)
+	if err != nil {
+		log.Error(err)
+		return -1
+	}
+	fmt.Println(string(aggregatedIncidentDataBytes))
+	return 0
+}
diff --git a/command/main.go b/command/main.go
index 78965fe..b0d9582 100644
--- a/command/main.go
+++ b/command/main.go
@@ -95,8 +95,9 @@ func loadCommands() map[string]cli.CommandFactory {
 		"user notification-rule show":   UserNotificationRuleShowCommand,
 		"user notification-rule update": UserNotificationRuleUpdateCommand,
 
-		"vendor list": VendorListCommand,
-		"vendor show": VendorShowCommand,
+		"vendor list":                          VendorListCommand,
+		"vendor show":                          VendorShowCommand,
+		"analyticsAggregatedIncidentData show": AnalyticsGetAggregatedIncidentDataCommand,
 	}
 }
 


Test patch:
diff --git a/analytics_test.go b/analytics_test.go
new file mode 100644
index 0000000..1816211
--- /dev/null
+++ b/analytics_test.go
@@ -0,0 +1,53 @@
+package pagerduty
+
+import (
+	"context"
+	"encoding/json"
+	"net/http"
+	"testing"
+)
+
+func TestAnalytics_GetAggregatedIncidentData(t *testing.T) {
+	setup()
+	defer teardown()
+
+	analyticsRequest := AnalyticsRequest{
+		AnalyticsFilter: &AnalyticsFilter{
+			CreatedAtStart: "2021-01-01T15:00:32Z",
+			CreatedAtEnd:   "2021-01-08T15:00:32Z",
+			TeamIDs:        []string{"PCDYDX0"},
+		},
+		AggregateUnit: "day",
+		TimeZone:      "Etc/UTC",
+	}
+	analyticsDataWanted := AnalyticsData{MeanSecondsToResolve: 34550, MeanSecondsToFirstAck: 70, MeanEngagedSeconds: 502, MeanAssignmentCount: 1, TotalBusinessHourInterruptions: 1, TotalEngagedSeconds: 2514, TotalIncidentCount: 5, RangeStart: "2021-01-06T00:00:00.000000"}
+	analyticsFilterWanted := AnalyticsFilter{CreatedAtStart: "2021-01-06T09:21:41Z", CreatedAtEnd: "2021-01-13T09:21:41Z", TeamIDs: []string{"PCDYDX0"}}
+	analyticsResponse := AnalyticsResponse{
+		Data:            []AnalyticsData{analyticsDataWanted},
+		AnalyticsFilter: &analyticsFilterWanted,
+		AggregateUnit:   "day",
+		TimeZone:        "Etc/UTC",
+	}
+	bytesAnalyticsResponse, err := json.Marshal(analyticsResponse)
+	mux.HandleFunc("/analytics/metrics/incidents/all", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		w.Write(bytesAnalyticsResponse)
+	})
+
+	client := &Client{apiEndpoint: server.URL,
+		authToken:  "foo",
+		HTTPClient: defaultHTTPClient,
+	}
+
+	res, err := client.GetAggregatedIncidentData(context.Background(), analyticsRequest)
+	want := AnalyticsResponse{
+		Data:            []AnalyticsData{analyticsDataWanted},
+		AnalyticsFilter: &analyticsFilterWanted,
+		AggregateUnit:   "day",
+		TimeZone:        "Etc/UTC",
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}