AshDispatch.Event.RecipientExtractor (AshDispatch v0.5.0)

View Source

Extracts recipient identifiers and names from recipient structs using cascading configuration.

For usage examples and complete guide, see the Recipient Field Extraction topic guide.

Configuration Resolution Order

The extractor uses a cascading resolution strategy (most specific to least specific):

  1. Event DSL override - Configured directly in the event definition
  2. Audience + Transport config - Audience-specific transport configuration
  3. Transport config - Transport-specific configuration
  4. Generic default - Single default for all transports
  5. Error - No configuration found

Configuration Structure

# config/config.exs
config :ash_dispatch,
  recipient_fields: [
    # Per-transport defaults (transport-first structure)
    email: [
      identifier: :email,
      name: [:display_name, :name, :contact_person, :full_name]  # fallback chain
    ],
    discord: [
      identifier: :discord_id,
      name: [:app_username, :display_name, :name]
    ],
    in_app: [
      identifier: :id,
      name: [:display_name, :name]
    ],
    sms: [
      identifier: :mobile_phone,
      name: [:first_name, :name]
    ],

    # Per-audience overrides (optional)
    audiences: [
      admin: [
        email: [name: :full_name]  # Admins show full name in emails
      ],
      customer: [
        email: [
          identifier: :contact_email,
          name: [:company_name, :contact_person]
        ]
      ]
    ]
  ]

Field Extraction Formats

The extractor supports multiple field specification formats:

  • :field_name - Direct atom field access
  • {:field, :field_name} - Explicit field tuple
  • {:field, [:nested, :path]} - Nested field access via get_in/2
  • {:string_field, "key"} - String key access for JSON data
  • &Module.function/1 - Custom extraction function
  • nil - No field (e.g., webhooks don't need names)

Examples

# Extract with generic defaults
RecipientExtractor.extract_identifier(user, :email, :user)
# → Uses config[:recipient_fields][:identifier] → :email

# Extract with transport override
RecipientExtractor.extract_identifier(user, :sms, :user)
# → Uses config[:recipient_fields][:sms][:identifier] → :mobile_phone

# Extract with audience override
RecipientExtractor.extract_name(admin, :email, :admin)
# → Uses config[:recipient_fields][:audiences][:admin][:name] → :full_name

# Extract with nested field
RecipientExtractor.extract_identifier(%{contact: %{email: "..."}}, :email, :user)
# With config: identifier: {:field, [:contact, :email]}
# → "..."

# Extract with custom function
RecipientExtractor.extract_identifier(lead, :email, :lead)
# With config: identifier: &MyApp.extract_lead_email/1
# → Calls MyApp.extract_lead_email(lead)

Summary

Functions

Extracts the recipient identifier (email, phone, webhook URL, etc.) based on transport and audience.

Extracts the recipient display name based on transport and audience.

Functions

extract_identifier(recipient, transport, audience, event_config \\ nil)

Extracts the recipient identifier (email, phone, webhook URL, etc.) based on transport and audience.

Parameters

  • recipient - The recipient struct/map to extract from
  • transport - The transport type (:email, :sms, :in_app, etc.)
  • audience - The audience type (:user, :admin, :lead, etc.)
  • event_config - Optional event-specific configuration (from DSL)

Returns

The extracted identifier string, or raises an error if extraction fails.

Examples

iex> user = %User{email: "user@example.com", phone: "555-1234"}
iex> RecipientExtractor.extract_identifier(user, :email, :user)
"user@example.com"

iex> RecipientExtractor.extract_identifier(user, :sms, :user)
"555-1234"

extract_name(recipient, transport, audience, event_config \\ nil)

Extracts the recipient display name based on transport and audience.

Parameters

  • recipient - The recipient struct/map to extract from
  • transport - The transport type (:email, :sms, :in_app, etc.)
  • audience - The audience type (:user, :admin, :lead, etc.)
  • event_config - Optional event-specific configuration (from DSL)

Returns

The extracted name string, or nil if no name is configured/available.

Examples

iex> user = %User{email: "user@example.com", contact_person: "John Doe"}
iex> RecipientExtractor.extract_name(user, :email, :user)
"John Doe"