AshDispatch. SMSBackend behaviour
(AshDispatch v0.8.5)
View Source
The behaviour an SMS backend implements.
config :ash_dispatch, :sms_backend, MyApp.SMSAshDispatch.SMSBackend.Elks ships with the library and covers 46elks; this
behaviour is for every other provider.
Where it is called from
From AshDispatch.Workers.SendSMS, not from the transport. The transport
enqueues a job and marks the receipt :scheduled; the worker marks
:sending and calls deliver/4. A backend therefore does not have to worry
about holding a database transaction open — but it must not assume it runs
synchronously with the action that triggered the event either.
Requires an Oban queue named :sms.
The job carries only the receipt id, so the context the worker passes is
reconstructed from the receipt: event_id and audience are real, but
data and variables are empty. A backend that needs either should read
receipt.content, which was frozen when the receipt was created and
therefore survives a retry.
The contract
- Read
receipt.recipient(the phone number) and the message body fromreceipt.content. UseAshDispatch.ContentMap.get_content/2: the column is JSONB, so a freshly built struct carries atom keys while one read back from Postgres carries string keys. The dispatcher writes:message. - Send through the provider's API.
- Mark the receipt yourself. The backend is what knows which failures are
permanent:
- delivered:
ReceiptStatus.mark_sent(receipt, %{"id" => provider_id}) - worth retrying:
ReceiptStatus.mark_failed(receipt, reason) - never going to work:
ReceiptStatus.mark_failed_permanent(receipt, reason)
- delivered:
- Return
{:ok, updated_receipt}or{:error, reason}.
An {:error, _} makes the worker mark :failed and lets Oban retry. A
malformed phone number or bad credentials belong in mark_failed_permanent
— five retries will not fix them, and while they sit as :failed they only
delay telling the person who can.
The recipient field
Without an :sms entry in recipient_fields, every recipient raises
"No identifier field configured for sms transport", and the error does not
say where to look:
config :ash_dispatch,
recipient_fields: [
sms: [identifier: :phone, name: [:display_name, :name]]
]
Summary
Callbacks
@callback deliver( receipt :: struct(), context :: AshDispatch.Context.t(), channel :: AshDispatch.Channel.t(), event_config :: map() ) :: {:ok, struct()} | {:error, term()}