Skip to content
quarry Artifact [06e7d5f09f]
Login

Artifact [06e7d5f09f]

Artifact 06e7d5f09f86ed236f38bdb63354a173cc6ed6fcbe346e08de2ac8e58c61988e:


package notify

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
)

// PushResult is how one triggered push went, as notify recorded it on
// the push message: Status is notify's word (sent, error, skipped) and
// Error its text, which names each device that refused and why. Found
// is false until the push step has run.
type PushResult struct {
	Found  bool
	Status string
	Error  string
	// Devices is each device's outcome (extras.devices, notify 0.1.9);
	// empty when notify recorded none.
	Devices []PushDevice
}

// PushDevice is one device's outcome on a push: the push service's host,
// delivered, failed, retired or gone, and the status and reason when it
// refused. notify never echoes the endpoint, and neither does this.
type PushDevice struct {
	Host    string `json:"host"`
	Outcome string `json:"outcome"`
	Status  int    `json:"status,omitempty"`
	Reason  string `json:"reason,omitempty"`
}

// PushOutcome reads the push message notify wrote for one trigger,
// found by its transaction id: the notification first, then its
// messages. notify's domain types carry no json tags, so the keys are
// the Go field names (ID, Channel, Status, ErrorText).
func (c *Client) PushOutcome(ctx context.Context, subscriber, txn string) (PushResult, error) {
	if c == nil || c.base == "" || txn == "" {
		return PushResult{}, nil
	}
	q := url.Values{"transactionId": {txn}, "limit": {"1"}}
	if subscriber != "" {
		q.Set("subscriberId", subscriber)
	}
	status, body, err := c.do(ctx, "GET", "/v1/notifications?"+q.Encode(), nil)
	if err != nil {
		return PushResult{}, fmt.Errorf("notify: push outcome: %w", err)
	}
	if status >= 300 {
		return PushResult{}, fmt.Errorf("notify: push outcome: status %d", status)
	}
	var list struct {
		Items []struct {
			ID string `json:"ID"`
		} `json:"items"`
	}
	if err := json.Unmarshal(body, &list); err != nil {
		return PushResult{}, fmt.Errorf("notify: push outcome: %w", err)
	}
	if len(list.Items) == 0 || list.Items[0].ID == "" {
		return PushResult{}, nil
	}
	status, body, err = c.do(ctx, "GET", "/v1/notifications/"+url.PathEscape(list.Items[0].ID)+"/messages", nil)
	if err != nil {
		return PushResult{}, fmt.Errorf("notify: push outcome: %w", err)
	}
	if status >= 300 {
		return PushResult{}, fmt.Errorf("notify: push outcome: status %d", status)
	}
	var msgs []struct {
		Channel   string `json:"Channel"`
		Status    string `json:"Status"`
		ErrorText string `json:"ErrorText"`
		Extras    struct {
			Devices []PushDevice `json:"devices"`
		} `json:"extras"`
	}
	if err := json.Unmarshal(body, &msgs); err != nil {
		return PushResult{}, fmt.Errorf("notify: push outcome: %w", err)
	}
	for _, m := range msgs {
		if m.Channel == "push" {
			return PushResult{Found: true, Status: m.Status, Error: m.ErrorText, Devices: m.Extras.Devices}, nil
		}
	}
	return PushResult{}, nil
}