Skip to content
quarry Artifact [82290282c7]
Login

Artifact [82290282c7]

Artifact 82290282c7346d740cece9ab5ed4aaf20dcdea1a82f82a168af8d1b7289b49a2:


package notify

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

// ReplyEndpoint is notify's record of where it delivers the forge's
// reply events (RFC 0014 Phase 1).
type ReplyEndpoint struct {
	ID  string `json:"id"`
	URL string `json:"url"`
}

// EnsureReplyEndpoint registers url as a webhook endpoint in notify's
// delivery stream, once. An endpoint already carrying that URL is left
// as it is; otherwise one is created, active, signing with secret.
//
// The secret travels in the create request because quarry mints it:
// the forge verifies every delivery against its own copy, and an
// endpoint notify created with a secret of its own choosing would
// leave quarry with nothing to verify. A notify that does not yet
// honour the field still creates the endpoint, and its unsigned
// deliveries are refused at the forge — closed, not silent.
func (c *Client) EnsureReplyEndpoint(ctx context.Context, url, secret string) (ep ReplyEndpoint, made bool, err error) {
	if c == nil || c.base == "" || url == "" {
		return ReplyEndpoint{}, false, nil
	}
	status, body, err := c.do(ctx, "GET", "/v1/webhooks/endpoints", nil)
	if err != nil {
		return ReplyEndpoint{}, false, fmt.Errorf("notify: list endpoints: %w", err)
	}
	if status >= 300 {
		return ReplyEndpoint{}, false, fmt.Errorf("notify: list endpoints: status %d", status)
	}
	var have struct {
		Data []ReplyEndpoint `json:"data"`
	}
	if err := json.Unmarshal(body, &have); err != nil {
		return ReplyEndpoint{}, false, fmt.Errorf("notify: list endpoints: %w", err)
	}
	for _, e := range have.Data {
		if e.URL == url {
			return e, false, nil
		}
	}
	status, body, err = c.post(ctx, "/v1/webhooks/endpoints",
		map[string]any{"url": url, "active": true, "secret": secret})
	if err != nil {
		return ReplyEndpoint{}, false, fmt.Errorf("notify: create endpoint: %w", err)
	}
	if status >= 300 {
		return ReplyEndpoint{}, false, fmt.Errorf("notify: create endpoint: status %d", status)
	}
	if err := json.Unmarshal(body, &ep); err != nil {
		return ReplyEndpoint{}, false, fmt.Errorf("notify: create endpoint: %w", err)
	}
	ep.URL = url
	return ep, true, nil
}