AshDispatch.RecipientResolver behaviour (AshDispatch v0.5.1)
View SourceBehaviour and DSL for declarative recipient resolution.
This module provides a declarative way to define how notification recipients are resolved for each audience type, following Ash Framework patterns.
Usage
Create a resolver module in your application:
defmodule MyApp.RecipientResolver do
use AshDispatch.RecipientResolver,
user_resource: MyApp.Accounts.User
audiences do
# Context-based - extract from event context
audience :user, from_context: :user
audience :assignee, from_context: [:user, :assignee]
# Query-based - query user resource with filter
audience :admins, query: [role: :admin, is_active: true]
# Relationship path - follow relationships on resource
audience :team, path: [:team_members, :user]
# Composite - union of other audiences
audience :stakeholders, combine: [:owner, :team]
# Custom resolver - for complex business logic
audience :owner, resolve: :resolve_owner
# Non-user recipient - extract email/name from resource fields
audience :lead_contact, from_resource: [email: :contact_email, name: :contact_name]
end
@impl true
def to_recipient(%MyApp.Accounts.User{} = user) do
%{
id: user.id,
email: to_string(user.email),
display_name: user.full_name || to_string(user.email)
}
end
def resolve_owner(resource, context) do
# Your custom logic
end
endThen configure ash_dispatch to use it:
config :ash_dispatch,
recipient_resolver: MyApp.RecipientResolverAudience Resolution Strategies
| Strategy | DSL | Description |
|---|---|---|
from_context | audience :user, from_context: :user | Extract from context.data |
from_context (fallback) | audience :assignee, from_context: [:user, :assignee] | Try each key until non-nil |
from_context + extract | audience :participants, from_context: [:meeting, :participants], extract: :user | Get collection, extract field |
from_resource | audience :lead_contact, from_resource: [email: :contact_email, name: :contact_name] | Extract email/name from resource fields |
query | audience :admins, query: [role: :admin] | Query user_resource with Ash filter |
path | audience :team, path: [:team_members, :user] | Follow relationship path on resource |
combine | audience :stakeholders, combine: [:owner, :team] | Union of other audiences (deduped) |
resolve | audience :owner, resolve: :resolve_owner | Custom function |
resolve + raw | audience :lead_contact, resolve: :fn, raw: true | Custom function, skip to_recipient |
Summary
Callbacks
Resolve recipients for an audience.
This callback is optional - if not implemented, the default resolution
logic will be used based on the audiences DSL configuration.
@callback to_recipient(user :: struct()) :: %{ :id => term(), :email => String.t(), optional(:display_name) => String.t(), optional(:first_name) => String.t() }
Convert a user struct to a recipient map.
The returned map must have at minimum:
:id- unique identifier for deduplication:email- email address for email transport
Optional fields:
:display_name- name shown in templates:first_name- first name for personalization