Repository: PagerDuty/go-pagerduty
Pull request #404 (2021-11-23 10:24:00)

Add support for response_plays
Adding support for the response_plays API and enable adding response_plays to services

petetanton: @theckman thanks for your feedback!
I'll commit some changes and address your comments one-by-one

Patch:
diff --git a/response_play.go b/response_play.go
new file mode 100644
index 0000000..b46bacc
--- /dev/null
+++ b/response_play.go
@@ -0,0 +1,135 @@
+package pagerduty
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+
+	"github.com/google/go-querystring/query"
+)
+
+type ResponsePlay struct {
+	ID                 string          `json:"id,omitempty"`
+	Type               string          `json:"type,omitempty"`
+	Summary            string          `json:"summary,omitempty"`
+	Self               string          `json:"self,omitempty"`
+	HTMLURL            string          `json:"html_url,omitempty"`
+	Name               string          `json:"name,omitempty"`
+	Description        string          `json:"description"`
+	Team               *APIReference   `json:"team,omitempty"`
+	Subscribers        []*APIReference `json:"subscribers,omitempty"`
+	SubscribersMessage string          `json:"subscribers_message"`
+	Responders         []*APIReference `json:"responders,omitempty"`
+	RespondersMessage  string          `json:"responders_message"`
+	Runnability        *string         `json:"runnability"`
+	ConferenceNumber   *string         `json:"conference_number"`
+	ConferenceUrl      *string         `json:"conference_url"`
+	ConferenceType     *string         `json:"conference_type"`
+}
+
+type ListResponsePlaysResponse struct {
+	ResponsePlays []ResponsePlay `json:"response_plays"`
+}
+
+type ListResponsePlaysOptions struct {
+	FilterForManualRun bool   `url:"filter_for_manual_run,omitempty"`
+	Query              string `url:"query,omitempty"`
+}
+
+// ListResponsePlays lists existing response plays.
+func (c *Client) ListResponsePlays(ctx context.Context, o ListResponsePlaysOptions) (ListResponsePlaysResponse, error) {
+	var result ListResponsePlaysResponse
+
+	v, err := query.Values(o)
+	if err != nil {
+		return result, err
+	}
+
+	resp, err := c.get(ctx, "/response_plays?"+v.Encode())
+	if err != nil {
+		return result, err
+	}
+
+	if err = c.decodeJSON(resp, &result); err != nil {
+		return result, err
+	}
+
+	return result, nil
+}
+
+// CreateResponsePlay creates a new response play.
+func (c *Client) CreateResponsePlay(ctx context.Context, rp ResponsePlay) (ResponsePlay, error) {
+	d := map[string]ResponsePlay{
+		"response_play": rp,
+	}
+
+	resp, err := c.post(ctx, "/response_plays", d, nil)
+	return getResponsePlayFromResponse(c, resp, err)
+}
+
+// GetResponsePlay gets details about an existing response play.
+func (c *Client) GetResponsePlay(ctx context.Context, id string) (ResponsePlay, error) {
+	resp, err := c.get(ctx, "/response_plays/"+id)
+	return getResponsePlayFromResponse(c, resp, err)
+}
+
+// UpdateResponsePlay updates an existing response play.
+func (c *Client) UpdateResponsePlay(ctx context.Context, rp ResponsePlay) (ResponsePlay, error) {
+	d := map[string]ResponsePlay{
+		"response_play": rp,
+	}
+
+	resp, err := c.put(ctx, "/response_plays/"+rp.ID, d, nil)
+	return getResponsePlayFromResponse(c, resp, err)
+}
+
+// DeleteResponsePlay deletes an existing response play.
+func (c *Client) DeleteResponsePlay(ctx context.Context, id string) error {
+	_, err := c.delete(ctx, "/response_plays/"+id)
+	return err
+}
+
+// RunResponsePlay runs a response play on a given incident.
+func (c *Client) RunResponsePlay(ctx context.Context, from string, responsePlayID string, incidentID string) error {
+	d := map[string]APIReference{
+		"incident": {
+			ID:   incidentID,
+			Type: "incident_reference",
+		},
+	}
+
+	h := map[string]string{
+		"From": from,
+	}
+
+	resp, err := c.post(ctx, "/response_plays/"+responsePlayID+"/run", d, h)
+	if err != nil {
+		return err
+	}
+
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("failed to run response play %s on incident %s (status code: %d)", responsePlayID, incidentID, resp.StatusCode)
+	}
+
+	return nil
+}
+
+func getResponsePlayFromResponse(c *Client, resp *http.Response, err error) (ResponsePlay, error) {
+	if err != nil {
+		return ResponsePlay{}, err
+	}
+
+	var target map[string]ResponsePlay
+	if dErr := c.decodeJSON(resp, &target); dErr != nil {
+		return ResponsePlay{}, fmt.Errorf("Could not decode JSON response: %v", dErr)
+	}
+
+	const rootNode = "response_play"
+
+	t, nodeOK := target[rootNode]
+	if !nodeOK {
+		return ResponsePlay{}, fmt.Errorf("JSON response does not have %s field", rootNode)
+	}
+
+	return t, nil
+}
diff --git a/service.go b/service.go
index 1547fac..9dbf59e 100644
--- a/service.go
+++ b/service.go
@@ -95,6 +95,7 @@ type Service struct {
 	AlertGrouping           string                   `json:"alert_grouping,omitempty"`
 	AlertGroupingTimeout    *uint                    `json:"alert_grouping_timeout,omitempty"`
 	AlertGroupingParameters *AlertGroupingParameters `json:"alert_grouping_parameters,omitempty"`
+	ResponsePlay            *APIObject               `json:"response_play,omitempty"`
 }
 
 // AlertGroupingParameters defines how alerts on the servicewill be automatically grouped into incidents


Test patch:
diff --git a/response_play_test.go b/response_play_test.go
new file mode 100644
index 0000000..1fc6e2c
--- /dev/null
+++ b/response_play_test.go
@@ -0,0 +1,151 @@
+package pagerduty
+
+import (
+	"context"
+	"net/http"
+	"testing"
+)
+
+// ListResponsePlays
+func TestResponsePlay_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/response_plays", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		_, _ = w.Write([]byte(`{"response_plays": [{"id": "1"}]}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	res, err := client.ListResponsePlays(context.TODO(), ListResponsePlaysOptions{})
+
+	want := ListResponsePlaysResponse{
+		ResponsePlays: []ResponsePlay{
+			{
+				ID: "1",
+			},
+		},
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// Get ResponsePlay
+func TestResponsePlay_Get(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/response_plays/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		_, _ = w.Write([]byte(`{"response_play": {"id": "1","name":"foo"}}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+
+	id := "1"
+	res, err := client.GetResponsePlay(context.TODO(), id)
+
+	want := ResponsePlay{
+		ID:   "1",
+		Name: "foo",
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// Create ResponsePlay
+func TestResponsePlay_Create(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/response_plays", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		_, _ = w.Write([]byte(`{"response_play": {"id": "1","name":"foo"}}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	input := ResponsePlay{
+		Name: "foo",
+	}
+	res, err := client.CreateResponsePlay(context.TODO(), input)
+
+	want := ResponsePlay{
+		ID:   "1",
+		Name: "foo",
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// Update ResponsePlay
+func TestResponsePlay_Update(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/response_plays/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+		_, _ = w.Write([]byte(`{"response_play": {"id": "1","name":"foo"}}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+
+	input := ResponsePlay{
+		ID:   "1",
+		Name: "foo",
+	}
+	res, err := client.UpdateResponsePlay(context.TODO(), input)
+
+	want := ResponsePlay{
+		ID:   "1",
+		Name: "foo",
+	}
+
+	if err != nil {
+		t.Fatal(err)
+	}
+	testEqual(t, want, res)
+}
+
+// Delete ResponsePlay
+func TestResponsePlay_Delete(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/response_plays/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	id := "1"
+	err := client.DeleteResponsePlay(context.TODO(), id)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+// Run ResponsePlay
+func TestResponsePlay_Run(t *testing.T) {
+	setup()
+	defer teardown()
+
+	mux.HandleFunc("/response_plays/1/run", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		_, _ = w.Write([]byte(`{"incident": {"id": "5","type":"incident_reference"}}`))
+	})
+
+	client := defaultTestClient(server.URL, "foo")
+	err := client.RunResponsePlay(context.TODO(), "foo@example.com", "1", "5")
+
+	if err != nil {
+		t.Fatal(err)
+	}
+}