Repository: PagerDuty/go-pagerduty
Pull request #541 (2024-10-04 20:58:11)

fix: Support the From header when snoozing an incident
The API docs list From as a required header.

https://developer.pagerduty.com/api-reference/fc871396fb23f-snooze-an-incident#request-headers

adamdecaf: This is a breaking change but the snooze calls don't work currently, so they're already broken. 
adamdecaf: cc @ChuckCrawford  @imjaroiswebdev 
adamdecaf: @ChezCrawford thanks - should be g2g now 
ChezCrawford: Ahhh. As I am coming out of my holiday-vacation-fog I am realizing that this is actually a breaking change for some users. The `From` header is only required when using an Account API Key or an App OAuth Token. Clients authenticating with User API Keys are currently able to use this function without issue.

If we are going to make a breaking change here I am tempted to ask that we do this the _right way_ by adding a `SnoozeIncidentOptions` struct with `From` and `Duration` fields. Since the other "write incident" functions currently work the way you have this implemented we can leave this as-is.
adamdecaf: I can make that change to use a struct. Yes, it's breaking but for me Snooze wasn't even working without this. 
ChezCrawford: > Yes, it's breaking but for me Snooze wasn't even working without this.

You are likely using one of the two types of tokens I mentioned above. I did verify that it works correctly with User API Keys right now.

>  I can make that change to use a struct.

I think that is the best move here. If we are going to make a breaking change let's change it to the "correct" thing that will work properly for everyone.


Patch:
diff --git a/incident.go b/incident.go
index b6f8d85..55dfe34 100644
--- a/incident.go
+++ b/incident.go
@@ -225,6 +225,7 @@ type ManageIncidentsOptions struct {
 	Status           string            `json:"status,omitempty"`
 	Title            string            `json:"title,omitempty"`
 	Priority         *APIReference     `json:"priority,omitempty"`
+	Urgency          string            `json:"urgency,omitempty"`
 	Assignments      []Assignee        `json:"assignments,omitempty"`
 	EscalationLevel  uint              `json:"escalation_level,omitempty"`
 	EscalationPolicy *APIReference     `json:"escalation_policy,omitempty"`
@@ -540,21 +541,29 @@ func (c *Client) CreateIncidentNote(id string, note IncidentNote) error {
 	return err
 }
 
+type SnoozeIncidentOptions struct {
+	From     string
+	Duration uint
+}
+
 // SnoozeIncidentWithResponse sets an incident to not alert for a specified
 // period of time.
 //
 // Deprecated: Use SnoozeIncidentWithContext instead.
-func (c *Client) SnoozeIncidentWithResponse(id string, duration uint) (*Incident, error) {
-	return c.SnoozeIncidentWithContext(context.Background(), id, duration)
+func (c *Client) SnoozeIncidentWithResponse(id string, o SnoozeIncidentOptions) (*Incident, error) {
+	return c.SnoozeIncidentWithContext(context.Background(), id, o)
 }
 
 // SnoozeIncidentWithContext sets an incident to not alert for a specified period of time.
-func (c *Client) SnoozeIncidentWithContext(ctx context.Context, id string, duration uint) (*Incident, error) {
-	d := map[string]uint{
-		"duration": duration,
+func (c *Client) SnoozeIncidentWithContext(ctx context.Context, id string, o SnoozeIncidentOptions) (*Incident, error) {
+	headers := map[string]string{
+		"From": o.From,
+	}
+	body := map[string]uint{
+		"duration": o.Duration,
 	}
 
-	resp, err := c.post(ctx, "/incidents/"+id+"/snooze", d, nil)
+	resp, err := c.post(ctx, "/incidents/"+id+"/snooze", body, headers)
 	if err != nil {
 		return nil, err
 	}
@@ -570,10 +579,13 @@ func (c *Client) SnoozeIncidentWithContext(ctx context.Context, id string, durat
 // SnoozeIncident sets an incident to not alert for a specified period of time.
 //
 // Deprecated: Use SnoozeIncidentWithContext instead.
-func (c *Client) SnoozeIncident(id string, duration uint) error {
+func (c *Client) SnoozeIncident(id string, o SnoozeIncidentOptions) error {
+	headers := make(map[string]string)
+	headers["From"] = o.From
 	data := make(map[string]uint)
-	data["duration"] = duration
-	_, err := c.post(context.Background(), "/incidents/"+id+"/snooze", data, nil)
+	data["duration"] = o.Duration
+
+	_, err := c.post(context.Background(), "/incidents/"+id+"/snooze", data, headers)
 	return err
 }
 


Test patch:
diff --git a/incident_test.go b/incident_test.go
index 5108b34..c2809b3 100644
--- a/incident_test.go
+++ b/incident_test.go
@@ -664,13 +664,18 @@ func TestIncident_SnoozeIncident(t *testing.T) {
 
 	mux.HandleFunc("/incidents/1/snooze", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "POST")
+		testEqual(t, r.Header.Get("From"), "from")
+
 		_, _ = w.Write([]byte(`{"incident": {"id": "1", "pending_actions": [{"type": "unacknowledge", "at":"2019-12-31T16:58:35Z"}]}}`))
 	})
 	client := defaultTestClient(server.URL, "foo")
 	var duration uint = 3600
 	id := "1"
 
-	err := client.SnoozeIncident(id, duration)
+	err := client.SnoozeIncident(id, SnoozeIncidentOptions{
+		From:     "from",
+		Duration: duration,
+	})
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -683,13 +688,18 @@ func TestIncident_SnoozeIncidentWithResponse(t *testing.T) {
 
 	mux.HandleFunc("/incidents/1/snooze", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "POST")
+		testEqual(t, r.Header.Get("From"), "from")
+
 		_, _ = w.Write([]byte(`{"incident": {"id": "1", "pending_actions": [{"type": "unacknowledge", "at":"2019-12-31T16:58:35Z"}]}}`))
 	})
 	client := defaultTestClient(server.URL, "foo")
 	var duration uint = 3600
 	id := "1"
 
-	res, err := client.SnoozeIncidentWithResponse(id, duration)
+	res, err := client.SnoozeIncidentWithResponse(id, SnoozeIncidentOptions{
+		From:     "from",
+		Duration: duration,
+	})
 
 	want := &Incident{
 		APIObject: APIObject{