Repository: PagerDuty/go-pagerduty
Pull request #539 (2024-09-27 14:45:16)

Add support for endpoints at /alert_grouping_settings
In response to the deprecation of alert_grouping fields of services
<img width="1091" alt="Screenshot 2024-09-27 at 11 47 20" src="https://github.com/user-attachments/assets/fcbc78ed-349c-47e2-b103-41435899ada2">

we added support to the new endpoints for alert grouping settings
<img width="455" alt="Screenshot 2024-09-27 at 11 47 05" src="https://github.com/user-attachments/assets/217fa39b-734f-4517-80b4-5e50261552ba">



Patch:
diff --git a/alert_grouping_setting.go b/alert_grouping_setting.go
new file mode 100644
index 0000000..c7e0cb3
--- /dev/null
+++ b/alert_grouping_setting.go
@@ -0,0 +1,215 @@
+package pagerduty
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/google/go-querystring/query"
+)
+
+// AlertGroupingSettingConfigTime is the configuration content for a
+// AlertGroupingSetting of type "time"
+type AlertGroupingSettingConfigTime struct {
+	Timeout uint `json:"timeout"`
+}
+
+// AlertGroupingSettingConfigIntelligent is the configuration content for a
+// AlertGroupingSetting of type "intelligent"
+type AlertGroupingSettingConfigIntelligent struct {
+	TimeWindow            uint  `json:"time_window"`
+	RecommendedTimeWindow *uint `json:"recommended_time_window,omitempty"`
+}
+
+// AlertGroupingSettingConfigContentBased is the configuration content for a
+// AlertGroupingSetting of type "content_based" or "content_based_intelligent"
+type AlertGroupingSettingConfigContentBased struct {
+	TimeWindow            uint     `json:"time_window"`
+	RecommendedTimeWindow *uint    `json:"recommended_time_window,omitempty"`
+	Aggregate             string   `json:"aggregate"`
+	Fields                []string `json:"fields"`
+}
+
+type AlertGroupingSettingType string
+
+const (
+	AlertGroupingSettingContentBasedType            AlertGroupingSettingType = "content_based"
+	AlertGroupingSettingContentBasedIntelligentType AlertGroupingSettingType = "content_based_intelligent"
+	AlertGroupingSettingIntelligentType             AlertGroupingSettingType = "intelligent"
+	AlertGroupingSettingTimeType                    AlertGroupingSettingType = "time"
+)
+
+// AlertGroupingSetting is a configuration used during the grouping of the
+// alerts easier to reuse and share between many services
+type AlertGroupingSetting struct {
+	ID          string                        `json:"id,omitempty"`
+	Name        string                        `json:"name,omitempty"`
+	Description string                        `json:"description,omitempty"`
+	Type        AlertGroupingSettingType      `json:"type,omitempty"`
+	Config      interface{}                   `json:"config,omitempty"`
+	Services    []AlertGroupingSettingService `json:"services"`
+}
+
+// AlertGroupingSettingService is a reference to services associated with an
+// alert grouping setting.
+type AlertGroupingSettingService struct {
+	ID   string `json:"id,omitempty"`
+	Name string `json:"name,omitempty"`
+}
+
+// CreateAlertGroupingSetting creates an instance of AlertGroupingSettings for
+// either one service or many services that are in the alert group setting
+func (c *Client) CreateAlertGroupingSetting(ctx context.Context, a AlertGroupingSetting) (*AlertGroupingSetting, error) {
+	d := map[string]AlertGroupingSetting{"alert_grouping_setting": a}
+
+	resp, err := c.post(ctx, "/alert_grouping_settings", d, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var resultRaw struct {
+		AlertGroupingSetting alertGroupingSettingRaw `json:"alert_grouping_setting"`
+	}
+	if err := c.decodeJSON(resp, &resultRaw); err != nil {
+		return nil, fmt.Errorf("Could not decode JSON response: %v", err)
+	}
+
+	value := resultRaw.AlertGroupingSetting
+	return getAlertGroupingSettingFromRaw(value)
+}
+
+// ListAlertGroupingSettingsOptions is the data structure used when calling the
+// ListAlertGroupingSettings API endpoint.
+type ListAlertGroupingSettingsOptions struct {
+	After      string   `url:"after,omitempty"`
+	Before     string   `url:"before,omitempty"`
+	Limit      uint     `url:"limit,omitempty"`
+	Total      bool     `url:"total,omitempty"`
+	ServiceIDs []string `url:"service_ids,omitempty,brackets"`
+}
+
+// ListAlertGroupingSettingsResponse is the data structure returned from calling
+// the ListAlertGroupingSettingsResponse API endpoint.
+type ListAlertGroupingSettingsResponse struct {
+	After                 string                 `json:"after,omitempty"`
+	Before                string                 `json:"before,omitempty"`
+	Limit                 uint                   `json:"limit,omitempty"`
+	Total                 bool                   `json:"total,omitempty"`
+	AlertGroupingSettings []AlertGroupingSetting `json:"alert_grouping_settings"`
+}
+
+// ListAlertGroupingSettings lists all of your alert grouping settings including
+// both single service settings and global content based settings.
+func (c *Client) ListAlertGroupingSettings(ctx context.Context, o ListAlertGroupingSettingsOptions) (*ListAlertGroupingSettingsResponse, error) {
+	v, err := query.Values(o)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := c.get(ctx, "/alert_grouping_settings?"+v.Encode(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var resultRaw struct {
+		ListAlertGroupingSettingsResponse
+		AlertGroupingSettings []alertGroupingSettingRaw `json:"alert_grouping_settings"`
+	}
+
+	if err = c.decodeJSON(resp, &resultRaw); err != nil {
+		return nil, err
+	}
+
+	settings := make([]AlertGroupingSetting, 0, len(resultRaw.AlertGroupingSettings))
+	for _, rawItem := range resultRaw.AlertGroupingSettings {
+		v, _ := getAlertGroupingSettingFromRaw(rawItem)
+		if v != nil {
+			settings = append(settings, *v)
+		}
+	}
+
+	result := &resultRaw.ListAlertGroupingSettingsResponse
+	result.AlertGroupingSettings = settings
+	return result, nil
+}
+
+// GetAlertGroupingSetting get an existing Alert Grouping Setting.
+func (c *Client) GetAlertGroupingSetting(ctx context.Context, id string) (*AlertGroupingSetting, error) {
+	resp, err := c.get(ctx, "/alert_grouping_settings/"+id, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var resultRaw struct {
+		AlertGroupingSetting alertGroupingSettingRaw `json:"alert_grouping_setting"`
+	}
+	if err := c.decodeJSON(resp, &resultRaw); err != nil {
+		return nil, fmt.Errorf("Could not decode JSON response: %v", err)
+	}
+
+	value := resultRaw.AlertGroupingSetting
+	return getAlertGroupingSettingFromRaw(value)
+}
+
+// DeleteAlertGroupingSetting deletes an existing Alert Grouping Setting.
+func (c *Client) DeleteAlertGroupingSetting(ctx context.Context, id string) error {
+	_, err := c.delete(ctx, "/alert_grouping_settings/"+id)
+	return err
+}
+
+// UpdateAlertGroupingSetting updates an Alert Grouping Setting.
+func (c *Client) UpdateAlertGroupingSetting(ctx context.Context, a AlertGroupingSetting) (*AlertGroupingSetting, error) {
+	d := map[string]AlertGroupingSetting{"alert_grouping_setting": a}
+
+	resp, err := c.put(ctx, "/alert_grouping_settings/"+a.ID, d, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var resultRaw struct {
+		AlertGroupingSetting alertGroupingSettingRaw `json:"alert_grouping_setting"`
+	}
+	if err := c.decodeJSON(resp, &resultRaw); err != nil {
+		return nil, fmt.Errorf("Could not decode JSON response: %v", err)
+	}
+
+	value := resultRaw.AlertGroupingSetting
+	return getAlertGroupingSettingFromRaw(value)
+}
+
+// alertGroupingSettingRaw is an AlertGroupingSetting that overrides its Config with a json raw
+// message in order to parse it later.
+type alertGroupingSettingRaw struct {
+	AlertGroupingSetting
+	Config json.RawMessage `json:"config,omitempty"`
+}
+
+// getAlertGroupingSettingFromRaw transform the content of a Alert Grouping
+// Setting "config" field from a json raw message into the data structure
+// corresponding to its "type".
+func getAlertGroupingSettingFromRaw(raw alertGroupingSettingRaw) (*AlertGroupingSetting, error) {
+	result := &raw.AlertGroupingSetting
+
+	switch raw.Type {
+	case AlertGroupingSettingContentBasedType, AlertGroupingSettingContentBasedIntelligentType:
+		var cfg AlertGroupingSettingConfigContentBased
+		if err := json.Unmarshal(raw.Config, &cfg); err != nil {
+			return nil, err
+		}
+		result.Config = cfg
+	case AlertGroupingSettingIntelligentType:
+		var cfg AlertGroupingSettingConfigIntelligent
+		if err := json.Unmarshal(raw.Config, &cfg); err != nil {
+			return nil, err
+		}
+		result.Config = cfg
+	case AlertGroupingSettingTimeType:
+		var cfg AlertGroupingSettingConfigTime
+		if err := json.Unmarshal(raw.Config, &cfg); err != nil {
+			return nil, err
+		}
+		result.Config = cfg
+	}
+
+	return result, nil
+}


Test patch:
diff --git a/alert_grouping_setting_test.go b/alert_grouping_setting_test.go
new file mode 100644
index 0000000..059312e
--- /dev/null
+++ b/alert_grouping_setting_test.go
@@ -0,0 +1,184 @@
+package pagerduty
+
+import (
+	"context"
+	"net/http"
+	"testing"
+)
+
+// Create AlertGroupingSetting
+func TestAlertGroupingSetting_Create(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/alert_grouping_settings", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		_, _ = w.Write([]byte(`{
+			"alert_grouping_setting": {
+				"id": "PZC4OM1",
+				"name": "Example of Alert Grouping Setting",
+				"description": "This Alert Grouping Setting is an example",
+				"type": "content_based",
+				"config": {
+					"time_window": 900,
+					"aggregate": "all",
+					"fields": [
+						"summary",
+						"component",
+						"custom_details.host",
+						"custom_details.field1.field2"
+					]
+				},
+				"services": [
+					{"id": "P0KJZ0A"},
+					{"id": "PA15YRT"}
+				]
+			}
+		}`))
+	})
+
+	ctx := context.Background()
+	client := defaultTestClient(server.URL, "foo")
+	input := AlertGroupingSetting{
+		Name: "foo",
+	}
+	res, err := client.CreateAlertGroupingSetting(ctx, input)
+
+	want := &AlertGroupingSetting{
+		ID:          "PZC4OM1",
+		Name:        "Example of Alert Grouping Setting",
+		Description: "This Alert Grouping Setting is an example",
+		Type:        "content_based",
+		Config: AlertGroupingSettingConfigContentBased{
+			TimeWindow: uint(900),
+			Aggregate:  "all",
+			Fields: []string{
+				"summary",
+				"component",
+				"custom_details.host",
+				"custom_details.field1.field2",
+			},
+		},
+		Services: []AlertGroupingSettingService{
+			{ID: "P0KJZ0A"},
+			{ID: "PA15YRT"},
+		},
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// ListAlertGroupingSettings
+func TestAlertGroupingSettings_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/alert_grouping_settings", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		_, _ = w.Write([]byte(`{
+			"alert_grouping_settings": [{
+				"id": "PZC4OM1",
+				"name": "Example of Alert Grouping Setting",
+				"description": "This Alert Grouping Setting is an example",
+				"type": "time",
+				"config": {"timeout": 60},
+				"services": [
+					{"id": "P0KJZ0A"},
+					{"id": "PA15YRT"}
+				]
+			}]
+		}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	o := ListAlertGroupingSettingsOptions{}
+	res, err := client.ListAlertGroupingSettings(context.Background(), o)
+
+	want := &ListAlertGroupingSettingsResponse{
+		AlertGroupingSettings: []AlertGroupingSetting{
+			{
+				ID:          "PZC4OM1",
+				Name:        "Example of Alert Grouping Setting",
+				Description: "This Alert Grouping Setting is an example",
+				Type:        "time",
+				Config: AlertGroupingSettingConfigTime{
+					Timeout: uint(60),
+				},
+				Services: []AlertGroupingSettingService{
+					{ID: "P0KJZ0A"},
+					{ID: "PA15YRT"},
+				},
+			},
+		},
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// GetAlertGroupingSettings
+func TestAlertGroupingSettings_Get(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/alert_grouping_settings/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		_, _ = w.Write([]byte(`{"alert_grouping_setting": {"id": "1"}}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	res, err := client.GetAlertGroupingSetting(context.Background(), "1")
+
+	want := &AlertGroupingSetting{
+		ID: "1",
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// UpdateAlertGroupingSettings
+func TestAlertGroupingSettings_Update(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/alert_grouping_settings/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+		_, _ = w.Write([]byte(`{"alert_grouping_setting": {"id": "1"}}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+
+	want := AlertGroupingSetting{ID: "1"}
+	res, err := client.UpdateAlertGroupingSetting(context.Background(), want)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, &want, res)
+}
+
+// DeleteAlertGroupingSettings
+func TestAlertGroupingSettings_Delete(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/alert_grouping_settings/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+		w.WriteHeader(204)
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	err := client.DeleteAlertGroupingSetting(context.Background(), "1")
+
+	if err != nil {
+		t.Fatal(err)
+	}
+}