Adding a notification
This guide adds a kind of notification: something SourceOrigin tells an account’s owner about their account, or administrators about the service, in their inbox and by email. Notifications describes what the owner sees; domain events how notifications are stored.
A notification comes from an event: when an event about an account is
recorded, DomainEvent::notification decides whether it tells the account’s
owner, and the event’s transaction writes the notification and queues its
emails. So adding one changes no page, route or job.
What you’ll have at the end
- A
Notificationkind, written to the inbox in the transaction of the event it comes from. - Its subject and body, which the inbox, the notification’s page and its email all show.
- Its email, sent to the addresses you chose.
Before you start
- Find the event the notification comes from. If the change records none yet, add one first: see recording its event.
- Decide who is told. Every notification is emailed, and none can be turned off, so notify only what the owner needs to know about their account. A change the owner made themselves usually tells them nothing new.
1. Add the kind
Add a variant to Notification in crates/common/src/notifications/mod.rs:
#![allow(unused)]
fn main() {
/// Support removed the account's profile picture.
PictureRemoved,
}
A notification is stored as it serializes, under its snake_case kind, such as
picture_removed, so never rename one once released. It holds only enums, flags
and timestamps, never an address, a secret or a Personal value: it must read
the same once its event’s personal data is erased. An older release skips a kind
it doesn’t know, so adding one is safe during a deploy.
2. Map the event to it
In DomainEvent::notification, in the same file, move the event out of the
return None list into an arm of its own. The method names every event, so a new
event cannot be added without deciding:
#![allow(unused)]
fn main() {
Self::PictureRemoved {} if by_support => Notification::PictureRemoved,
}
by_support is true when an administrator or an operator at the CLI acted. Guard
on it when only support’s change is news to the owner, and keep the owner’s own
case in the return None list, with a comment saying why.
3. Choose who receives it
Add its arm to Notification::audience, which says whose inbox it goes to:
Audience | Whose inbox |
|---|---|
Subject | the account the event is about |
Administrators | every active administrator, for an event about the service rather than one account, as a raised alert is |
Then add its arm to Notification::addressees, which says which of each
account’s addresses receive its email:
Addressees | Who receives the email |
|---|---|
Primary | the primary verified address. An account without one gets no email |
Affected | the address the event replaced or removed, so whoever reads that mailbox hears of it |
Verified | every verified address, so one compromised mailbox cannot hide it |
Most notifications go to Primary. Affected reaches an address only for
the events whose address the email channel reads, in the affected match of
crates/database/src/repositories/deliveries/email.rs: name yours there too.
Use Verified for what an attacker holding one mailbox would want to hide, as a
recovery’s notifications do.
A notification about the account itself is always emailed, and no preference
can turn it off. Only a kind that isn’t about the account, as an announcement
isn’t, may belong to an EmailCategory (crates/common/src/notifications),
whose email the owner turns off in their inbox’s Email preferences. Only
announcements are wired today. A new category needs more than its variant, with
its label and description, which settings and the dashboard show:
- a migration widening the
CHECKonnotification_preferences.category(0039_notification_preferences.sql), or saving the preference fails; - a check in the channel that sends its email, when the email is queued and
when it is sent, as the
announcements_emailedSQL function does for theannouncementchannel: theemailchannel checks no preference; - a decision about its unsubscribe link, which
AccountService::unsubscribe(apps/auth-service/src/notifications/preferences.rs) only handles for announcements.
See email preferences.
4. Write its texts
Add its arms to Notification::subject, one line, which is the inbox’s line and
the email’s subject, and Notification::body, what it says below:
#![allow(unused)]
fn main() {
Self::PictureRemoved => "SourceOrigin support removed your profile picture",
}
Write them for the owner: what happened to their account, what changes for them,
and what to do if they didn’t expect it. The body may use the notification’s own
fields, as DeletionScheduled formats its date. The email templates in
apps/auth-service/templates/email/ render the subject and body as they are, so
they need no change.
Test it
- In
crates/common/src/notifications/tests.rs, test which events notify and for whom, such asonly_support_renames_notify, and the kind’s stored form when it has fields, asnotifications_serialize_by_kinddoes. - If the email goes to
AffectedorVerifiedaddresses, add the kind tonotifications_are_emailed_to_the_addresses_their_kind_names, inapps/auth-service/src/deliveries/email/tests.rs, which checks that each address receives it once.
just test -p sourceorigin-common notifications
just db test -E 'test(notifications_are_emailed)'
Document it
- Add it to the list in notifications, which says what the inbox holds and who receives each email.
- If the user guide describes the change that causes it, say there that the owner is told.
The inbox, the dashboard’s notifications page, auth-service account <user> notifications, the data export and the email all show every kind, so no other
surface needs a change.
Limits
- Links. A notification’s email links its page in the inbox. Only two
kinds link to something else: a recovery’s notifications link the recovery’s
undo page, through the
recovery_idtheemailchannel finds for them and the notification page’sabout_recovery, and an alert’s links the dashboard’s Alerts tab, throughNotification::links_alerts. A new kind of link needs its email and its page both. - During a deploy. The previous release’s email workers may claim the email
of a kind they don’t know. They can’t decode it, so they mark the delivery
failed. An operator retries it with
auth-service deliveries retry <id>once the new release serves; see the delivery queue.