Notifications
A notification is one thing one person was told, in one tenant: a title, the
rest of the message, and a path within the application it points at, sent by
mail as well when the caller asks. The module promises that telling somebody is
one call — the row and the request for mail commit with whatever caused them —
that nobody’s list is anybody else’s, and that no address, body or link ever
travels in an event. The module is
modules/notification; this page states
nothing that tree does not.
The map
features/notification in the recordWhat it promises
Entity |
|
Events |
|
Permissions |
none. Both routes are |
Service |
|
Routes and screens
| Method | Path | Does | Authorization | Publishes |
|---|---|---|---|---|
|
|
List my notifications |
signed in |
— |
|
|
Mark one of my notifications read |
signed in |
|
-
GET /api/v1/notification/notifications— the caller’s own notifications, newest first; there is no route that lists anybody else’s. -
POST /api/v1/notification/notifications/{id}/read— marks one of the caller’s own read and publishesnotification.read; somebody else’s is not found rather than refused, and marking it read again changes nothing and publishes nothing. -
No
rest.Spec: a Spec’s list route is the whole tenant and these rows are addressed to a person, so the two routes are written by hand ininternal/handler.goand scoped by the principal rather than by a permission. -
Screens: none — with no
rest.Specthere is no resource in the register and nothing generated, and no navigation entry, because a nav entry names the permission that decides who sees the link and there is none (module.go). Hand-written pages: none.
Authorization
| Authorization | Who passes | Routes |
|---|---|---|
none: any signed-in member |
a session of this tenant |
|
Events, jobs and subscriptions
| Event | Published by | Handled by |
|---|---|---|
|
a job, hook or command in the module |
|
|
a job, hook or command in the module |
|
|
|
|
-
Publishes: the three events above; the read route declares
notification.readso the boot gate can check it. -
Jobs: none, written out as a decision: a notification is caused by something happening, which is an event and not the clock (ADR 0004).
-
Subscriptions:
notification.email_requested→internal.SendMailin the worker (internal/mail.go): it reads the row back inside the event’s own tenant transaction, resolves the address throughRecipientsand the tenant’s host throughHosts, rendersnotice.tmplas plain text with the link made absolute on that host, and hands the message to theMailer. A notice deleted in between is a skip and a log line; a mailer that fails fails the subscription, so the outbox retries it and dead-letters it.
What it needs
|
Interface |
Supplied by |
|
|
declared by this module; the |
|
|
declared by this module; the |
|
|
required, or |
|
|
|
Configuration: none read by the module itself; main builds notification.Mail from mail.host, mail.port, mail.username, mail.password (PLATFORMKIT_MAIL_PASSWORD) and mail.from, and run warns at boot when mail.host is empty.
Who uses it
-
auth —
contracts.Notifier, its own name forNotify, to tell somebody in the application that a set-password link was sent, pointing at/auth/resetand never carrying the token; and the sameMailerandHostLookup, to hand the link itself straight to the mail server so the secret is in the mail and in no row, outbox payload or audit event (auth/contracts/auth.go,auth/internal/password.go). An invitation reaches this module that way, through auth’suser.invitedsubscription, not from the tenant module. -
A private client module —
contracts.Service, which its registry entry requires, to tell a customer about a milestone from one of its subscriptions, with mail; its test takesnotificationtest.NewFake(). -
The admin and the native shell do not reach it: it registers no
httpx.Resource, so there is nothing for the screens or the catalogue to draw.
Verification
-
go test ./modules/notification/…— the fake and the real service pass one conformance suite (conformance.go); the internal tests prove one tenant’s notifications are not another’s, that the worker reads the row back and mails the tenant’s own absolute link, that the outbox carries no address, title, body or link (internal/service_test.go), that a hung mail server is a timeout and not a wait, and that a mailer that fails fails the subscription (internal/smtp_test.go). -
No browser journey;
apps/platformkit/app_test.goraises a notice through the service main holds, lists it and marks it read through the two routes, findsnotification.readin the audit trail and the message in the mailbox. -
Not proven: delivery to a real mail server, including the STARTTLS and authentication branches of
SMTP.Send; the subscription against a live NATS fleet.