Packages
Event-driven notification system for Ash Framework with multiple transport types
Current section
Files
Jump to
Current section
Files
lib/recipient_resolver/dsl.ex
defmodule AshDispatch.RecipientResolver.Dsl do
@moduledoc """
DSL macros for defining audiences in a RecipientResolver.
## Example
audiences do
audience :user, from_context: :user
audience :admins, query: [role: :admin]
audience :owner, resolve: :resolve_owner
end
"""
@doc """
Define audiences block.
All audience definitions must be inside this block.
"""
defmacro audiences(do: block) do
quote do
unquote(block)
end
end
@doc """
Define an audience with its resolution strategy.
## Options
* `:from_context` - Extract from context.data. Can be:
* An atom: `from_context: :user` extracts `context.data.user`
* A list of atoms: `from_context: [:user, :assignee]` tries each key until non-nil
* `:extract` - When using `from_context` with a collection, extract this field from each item.
Example: `from_context: [:meeting, :participants], extract: :user`
* `:filter` - Filter extracted recipients by field values. Only recipients matching
ALL conditions are included. Works with `from_context`, `query`, and `path`.
Example: `from_context: :user, filter: [user_type: :customer, is_active: true]`
* `:from_resource` - Extract email/name fields directly from the event's resource.
Creates a raw recipient map without needing a custom resolver function.
Example: `from_resource: [email: :contact_email, name: :contact_name]`
Required keys: `:email`. Optional: `:name`, `:id`.
If `:id` is not specified, uses the resource's `:id` field.
* `:query` - Ash filter to query user_resource.
Example: `query: [role: :admin, is_active: true]`
* `:path` - Relationship path to follow from the resource.
Example: `path: [:customer, :customer_users, :user]`
* `:combine` - Union of other audiences (deduped by id).
Example: `combine: [:owner, :team]`
* `:resolve` - Custom resolver function. Can be:
* An atom: function name in this module, called as `function_name(resource, context)`
* A tuple: `{Module, :function}` or `{Module, :function, extra_args}`
* `:raw` - When `true`, skip `to_recipient/1` conversion. Use for audiences
that return pre-formatted recipient maps instead of user structs.
Default: `false`
## Examples
# Extract from context
audience :user, from_context: :user
# Try multiple context keys
audience :assignee, from_context: [:user, :assignee]
# Extract field from collection
audience :participants, from_context: [:meeting, :participants], extract: :user
# Filter extracted recipients by field values
audience :customer, from_context: [:customer_user, :user], filter: [user_type: :customer]
# Extract email/name from resource fields (no custom resolver needed!)
audience :lead_contact, from_resource: [email: :contact_email, name: :contact_name]
# Query users
audience :admins, query: [role: :admin, is_active: true]
# Follow relationship path
audience :customer_users, path: [:customer, :users]
# Combine audiences
audience :stakeholders, combine: [:owner, :team]
# Custom resolver
audience :owner, resolve: :resolve_owner
# Custom resolver returning raw maps (use from_resource instead when possible)
audience :lead_contact, resolve: :resolve_lead_contact, raw: true
"""
defmacro audience(name, opts) do
quote do
@audiences %{
name: unquote(name),
from_context: unquote(opts[:from_context]),
extract: unquote(opts[:extract]),
filter: unquote(opts[:filter]),
from_resource: unquote(opts[:from_resource]),
query: unquote(opts[:query]),
path: unquote(opts[:path]),
combine: unquote(opts[:combine]),
resolve: unquote(opts[:resolve]),
raw: unquote(opts[:raw]) || false
}
end
end
end