Documentation
¶
Overview ¶
Package notifyhttp is the HTTP shape of an inbox.
It is here rather than in generated code for the reason the auth routes and the file routes are: the tables are rig's own and are the same in every project, so there is nothing for a generator to vary. Five routes regenerated identically into every application would be five routes to keep in step.
Nothing here decides who may do anything. Every handler narrows to the account on the claims and cannot be asked to do otherwise: none of these routes takes a `scope` parameter, because "read everybody's notifications" is not a thing an application means.
A project that wants more than this — the filter grammar, the sort keys, a generated client over the same rows — sets `notifications.expose` and gets a projected resource as well. Both stay, and the difference between them is the point: this is what a project gets without thinking about it.
Index ¶
Constants ¶
const DefaultBasePath = "/notifications"
DefaultBasePath is where the routes are mounted unless a project says otherwise.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler serves the inbox.
func (*Handler) Mount ¶
Mount registers the five routes on a router.
Every one of them goes through httpx.Caller.Wrap, which is what makes "narrows to the caller" structural rather than something five handlers each remember to do.
GET /notifications the caller's inbox, newest first
GET /notifications/_unread-count the badge, one number
POST /notifications/{id}/_read mark one read
POST /notifications/_read-all mark the page's worth read
DELETE /notifications/{id} remove one from the inbox
type Line ¶
type Line struct {
ID uuid.UUID `json:"id"`
NotificationID uuid.UUID `json:"notificationId"`
Kind string `json:"kind"`
// EventCount is how many events this line stands for: ten comments on one
// post are one line saying ten.
EventCount int `json:"eventCount"`
ReadAt *time.Time `json:"readAt"`
CreatedAt time.Time `json:"createdAt"`
}
Line is one inbox row on the wire.
It carries the subject's identifier and not the subject's row, and that is a decision rather than an omission. A client doing live sync already has the post and wants the identifier; embedding would be a second query per page and two joins deep — through the notification — on the hottest read in the system. A client that wants the rows turns `notifications.expose` on and gets a resource with `embed` on its relations.
type Options ¶
type Options struct {
// BasePath is the prefix the routes sit under. Empty means
// [DefaultBasePath].
BasePath string
// Claims identifies the caller.
//
// Required, and taken rather than assumed for the reason the generated
// server takes it: a project authenticates its own way, and an inbox route
// that established the caller differently from every other route would be a
// second answer to the one question a tenant boundary rests on. The
// generated mount passes the server's own.
Claims func(*http.Request) (tenancy.Claims, error)
// Fail writes an error response. Nil means [httpx.Fail].
//
// It is taken rather than assumed so that an inbox route's 404 carries the
// request id and lands in the same log line as every other route's — that is
// what the generated server's writer adds. The shape is the same either way
// now: both write [httpx.Error].
Fail func(http.ResponseWriter, *http.Request, error)
}
Options configure the handler.