Repository: PagerDuty/go-pagerduty
Pull request #557 (2025-06-20 07:40:26)

feat: add incident analytics by escalation policy
This MR is to add capabilities defined in this page
https://developer.pagerduty.com/api-reference/8b369ae2eff5e-get-aggregated-escalation-policy-data

1. Add missing filter (escalation_policy_ids, min ack and min manual escalations)
2. Add missing response (escalation policy ID and escalation policy Name)
3. Add missing method GetAggregatedEscalationPolicyData


One thing that I'm not sure, please confirm:
in the docs, it's using min_ackowledgements, but this is a typo, it should be min_acknowledgements. If this block merging than I'll remove this changes from my MR.


Patch:
diff --git a/analytics.go b/analytics.go
index 17789c1..13b2524 100644
--- a/analytics.go
+++ b/analytics.go
@@ -52,14 +52,19 @@ type AnalyticsRawIncidentsResponse struct {
 // AnalyticsFilter represents the set of filters as part of the request to PagerDuty when
 // requesting analytics.
 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"`
-	PriorityNames  []string `json:"priority_names,omitempty"`
+	CreatedAtStart        string   `json:"created_at_start,omitempty"`
+	CreatedAtEnd          string   `json:"created_at_end,omitempty"`
+	Urgency               string   `json:"urgency,omitempty"`
+	Major                 bool     `json:"major,omitempty"`
+	MinAcknowledgements   int      `json:"min_acknowledgements,omitempty"`
+	MinTimeoutEscalations int      `json:"min_timeout_escalations,omitempty"`
+	MinManualEscalations  int      `json:"min_manual_escalations,omitempty"`
+	TeamIDs               []string `json:"team_ids,omitempty"`
+	ServiceIDs            []string `json:"service_ids,omitempty"`
+	EscalationPolicyIDs   []string `json:"escalation_policy_ids,omitempty"`
+	PriorityIDs           []string `json:"priority_ids,omitempty"`
+	PriorityNames         []string `json:"priority_names,omitempty"`
+	PDAdvanceUsed         bool     `json:"pd_advance_used,omitempty"`
 }
 
 // AnalyticsData represents the structure of the aggregated analytics we have available.
@@ -68,6 +73,8 @@ type AnalyticsData struct {
 	ServiceName                    string  `json:"service_name,omitempty"`
 	TeamID                         string  `json:"team_id,omitempty"`
 	TeamName                       string  `json:"team_name,omitempty"`
+	EscalationPolicyID             string  `json:"escalation_policy_id,omitempty"`
+	EscalationPolicyName           string  `json:"escalation_policy_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"`
@@ -155,6 +162,11 @@ func (c *Client) GetAggregatedTeamData(ctx context.Context, analytics AnalyticsR
 	return c.getAggregatedData(ctx, analytics, "teams")
 }
 
+// GetAggregatedEscalationPolicyData gets the aggregated escalation policy analytics for the requested data.
+func (c *Client) GetAggregatedEscalationPolicyData(ctx context.Context, analytics AnalyticsRequest) (AnalyticsResponse, error) {
+	return c.getAggregatedData(ctx, analytics, "escalation_policies")
+}
+
 func (c *Client) getAggregatedData(ctx context.Context, analytics AnalyticsRequest, endpoint string) (AnalyticsResponse, error) {
 	h := map[string]string{
 		"X-EARLY-ACCESS": "analytics-v2",


Test patch:
diff --git a/analytics_test.go b/analytics_test.go
index 78fe36a..ed75dd2 100644
--- a/analytics_test.go
+++ b/analytics_test.go
@@ -141,6 +141,50 @@ func TestAnalytics_GetAggregatedTeamData(t *testing.T) {
 	testEqual(t, want, res)
 }
 
+func TestAnalytics_GetAggregatedEscalationPolicyData(t *testing.T) {
+	setup()
+	defer teardown()
+
+	analyticsRequest := AnalyticsRequest{
+		Filters: &AnalyticsFilter{
+			CreatedAtStart:      "2021-01-01T15:00:32Z",
+			CreatedAtEnd:        "2021-01-08T15:00:32Z",
+			EscalationPolicyIDs: []string{"PCDYDX0"},
+		},
+		AggregateUnit: "day",
+		TimeZone:      "Etc/UTC",
+	}
+	analyticsDataWanted := AnalyticsData{MeanAssignmentCount: 1, MeanEngagedSeconds: 502, MeanEngagedUserCount: 0, MeanSecondsToResolve: 34550, MeanSecondsToFirstAck: 70, TotalBusinessHourInterruptions: 1, TotalEngagedSeconds: 2514, TotalIncidentCount: 5, RangeStart: "2021-01-06T00:00:00.000000", EscalationPolicyID: "PCDYDX0", EscalationPolicyName: "FooEscalationPolicy", UpTimePct: 89.86111111111111}
+	analyticsFilterWanted := AnalyticsFilter{CreatedAtStart: "2021-01-06T09:21:41Z", CreatedAtEnd: "2021-01-13T09:21:41Z", EscalationPolicyIDs: []string{"PCDYDX0"}}
+	analyticsResponse := AnalyticsResponse{
+		Data:          []AnalyticsData{analyticsDataWanted},
+		Filters:       &analyticsFilterWanted,
+		AggregateUnit: "day",
+		TimeZone:      "Etc/UTC",
+	}
+	bytesAnalyticsResponse, err := json.Marshal(analyticsResponse)
+	testErrCheck(t, "json.Marshal()", "", err)
+
+	mux.HandleFunc("/analytics/metrics/incidents/escalation_policies", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		_, _ = w.Write(bytesAnalyticsResponse)
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+
+	res, err := client.GetAggregatedEscalationPolicyData(context.Background(), analyticsRequest)
+	want := AnalyticsResponse{
+		Data:          []AnalyticsData{analyticsDataWanted},
+		Filters:       &analyticsFilterWanted,
+		AggregateUnit: "day",
+		TimeZone:      "Etc/UTC",
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
 func TestAnalytics_GetAnalyticsIncidentsById(t *testing.T) {
 	setup()
 	defer teardown()