notifypost

package
v1.138.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package notifypost is the transport half of notification channels: one Sender per chat or webhook kind, turning a document into a post on its upstream.

It is separate from notifychannel, which persists the operator's channel records, because the two share nothing: a record is read and written by the admin surface, a post is made by the send worker, and neither needs the other's machinery. What they share is the Channel value, which lives in the domain both depend on.

No sender holds a credential. The three kinds deliver through an Upstream the api gateway materializes for the connection the channel names, so a bot token lives exactly where every other upstream credential lives.

Index

Constants

View Source
const MattermostMaxTextBytes = 16000

MattermostMaxTextBytes is the most a mattermost channel sends in one message. A Mattermost post is capped at 16,383 characters by the server; the platform cuts below that and keeps the link, so the server never refuses a post for its length.

View Source
const WebhookMaxTextBytes = 12000

WebhookMaxTextBytes is the most a webhook channel sends in one message. An incoming webhook is consumed by whichever server the URL belongs to, most often Slack or Mattermost, so the cap is the lower of the two.

Variables

View Source
var ErrTerminal = errors.New("notifychannel: upstream refused the message")

ErrTerminal marks a send failure that retrying cannot fix: the upstream refused the message itself rather than failing to receive it. A channel posting to a chat channel that was deleted, or through a bot that was uninstalled, fails this way on every attempt, so burning five of them delays nothing and fills the history with the same line five times.

The worker matches it with errors.Is and fails the row at once. Everything else -- a timeout, a refused connection, a 5xx, a rate limit -- retries on the existing backoff.

Functions

This section is empty.

Types

type DeliveryIDCarrier added in v1.138.3

type DeliveryIDCarrier interface {
	DeliveryIDHeader() string
}

DeliveryIDCarrier is an Upstream that names a header a delivery's id is sent in: an hmac connection's hmac_id_header, which its signature covers. *upstreamcall.Upstream implements it.

type Envelope added in v1.138.3

type Envelope struct {
	// ID is the queue row's id, the same on every retry of that row, so a
	// receiver deduplicates on it.
	ID string `json:"id"`
	// Type is what produced the notification, one of the
	// notification.Document* types; a receiver routes on it.
	Type       string    `json:"type"`
	OccurredAt time.Time `json:"occurred_at"`
	Deployment string    `json:"deployment,omitempty"`
	Channel    string    `json:"channel"`
	Title      string    `json:"title"`
	// Body is the markdown body as sent, whole: a system reads fields, and a
	// chat client's display cap does not apply to it.
	Body   string                       `json:"body,omitempty"`
	Link   string                       `json:"link,omitempty"`
	Source *notification.DocumentSource `json:"source,omitempty"`
	// Data is the sender's structured payload, verbatim.
	Data json.RawMessage `json:"data,omitempty"`
}

Envelope is the body a JSON webhook channel posts for one notification.

type MattermostSender

type MattermostSender struct {
	// contains filtered or unexported fields
}

MattermostSender posts to a Mattermost channel through the v4 REST API.

The bot token is the connection's credential, applied by the connection's authenticator as a bearer token, so this sender never sees it.

func NewMattermostSender

func NewMattermostSender(upstream UpstreamFunc) *MattermostSender

NewMattermostSender builds the mattermost transport over upstream.

func (*MattermostSender) Send

Send posts the document to the channel's target.

Mattermost answers an application-level refusal with the matching HTTP status rather than with a 200 and a flag, so the shared status classification is the whole verdict and there is no envelope to read.

type RetryAfterError added in v1.138.3

type RetryAfterError struct {
	After time.Duration
	Err   error
}

RetryAfterError is a send the upstream asked to have retried after a delay: a 429 or a 503 carrying Retry-After. The worker waits that long, within its own longest backoff, rather than its own schedule.

func (*RetryAfterError) Error added in v1.138.3

func (r *RetryAfterError) Error() string

Error is the upstream's answer.

func (*RetryAfterError) Unwrap added in v1.138.3

func (r *RetryAfterError) Unwrap() error

Unwrap returns the upstream's answer.

type Sender

type Sender interface {
	// Send posts d to ch, returning ErrTerminal for a refusal that
	// retrying cannot fix.
	Send(ctx context.Context, ch notification.Channel, d notification.Delivery) error
}

Sender delivers one document to one channel of its kind.

type Senders

type Senders struct {
	// contains filtered or unexported fields
}

Senders dispatches a document to the sender for its channel's kind.

The email kind is absent: an email channel fans out at enqueue to one ordinary queue row per recipient, which the existing renderer and SMTP sender deliver. There is nothing for a channel transport to do with it, and a second mail path would be a second place for the deployment's mail settings to be read.

func NewSenders

func NewSenders(upstream UpstreamFunc, deployment string) *Senders

NewSenders builds the dispatcher for the HTTP kinds over upstream.

deployment names this deployment in a JSON webhook envelope, so a receiver fed by several can tell them apart.

func (*Senders) For

func (s *Senders) For(kind string) (Sender, bool)

For returns the sender for a kind, and whether this dispatcher delivers it.

func (*Senders) Send

Send delivers d through the sender for ch's kind.

type Upstream

type Upstream interface {
	// BaseURL is the connection's upstream root.
	BaseURL() string
	// Do applies the connection's authentication and static headers and
	// sends the request.
	Do(req *http.Request) (*http.Response, error)
}

Upstream is the authorized transport a channel delivers through: the connection's base URL and its ability to make an authorized call.

It is an interface here, satisfied by *apigateway.Upstream, so this package depends on the idea of an authorized upstream rather than on the api gateway. That keeps the senders testable against an httptest server and keeps the credential on the gateway's side of the seam.

type UpstreamFunc

type UpstreamFunc func(connection string) (Upstream, error)

UpstreamFunc resolves a connection name to its authorized transport. The platform wires it to the api gateway; a test wires it to an httptest server.

type WebhookSender

type WebhookSender struct {
	// contains filtered or unexported fields
}

WebhookSender posts to a webhook in the channel's format: one {"text": ...} body for a chat client's incoming webhook (the default), or one JSON envelope for a system receiving events (#1997).

The whole address is the connection's base_url, including the secret path segment an incoming webhook URL carries, so the kind sends to the base itself and names no path. That secret is the connection's to hold: a webhook URL is a bearer credential written as a URL, which is why the kind names a connection like the two token kinds rather than storing a URL of its own. A connection can hold the segment as path_secret, encrypted, rather than in its base_url.

A JSON delivery is signed when the connection's auth_mode is hmac: the channel holds no secret and no signing settings of its own.

func NewWebhookSender

func NewWebhookSender(upstream UpstreamFunc, deployment string) *WebhookSender

NewWebhookSender builds the webhook transport over upstream.

func (*WebhookSender) Send

Send posts the delivery to the webhook.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL