AshDispatch.Transports.Preferences (AshDispatch v0.7.0)
View SourceThe opt-out gate, in one place.
Every transport that reaches a person must ask the same question before it
delivers: did this recipient say no? Until 0.7.0 only :email, :in_app
and :webhook asked. :slack, :discord, :sms and :push did not —
which meant a preference honoured on one channel was silently ignored on
another, and the difference was invisible to the person who set it.
Why a shared function rather than a copied block
The check is eight lines, and eight lines copied six times is six chances
to drift. The copies had already begun to: :in_app logged the user id,
:webhook logged the receipt id, and neither said which was intended. One
implementation makes the verdict, the skip reason and the log line the same
everywhere — and "user_opted_out" becomes a value you can count.
The verdict is per RECIPIENT, not per event
A receipt is one recipient. allows_receipt?/4 reads the receipt's own
user_id; reading context.user would apply the event subject's verdict
to all N recipients of a fan-out — the bug this function exists to make
impossible to reintroduce.
Summary
Functions
The reason written on a receipt skipped by an opt-out. Public so it can be counted.
Runs fun when the recipient allows this delivery, otherwise skips.
Functions
@spec reason() :: String.t()
The reason written on a receipt skipped by an opt-out. Public so it can be counted.
Runs fun when the recipient allows this delivery, otherwise skips.
The skipped receipt is returned as {:ok, receipt} — a skip is a
successful outcome, not a failure. The person said no, and we did what
they asked.