lib/ash/notifier/notifier.ex

defmodule Ash.Notifier do
  @moduledoc """
  A notifier is an extension that receives various events
  """
  @callback notify(Ash.Notifier.Notification.t()) :: :ok

  defmacro __using__(_) do
    quote do
      @behaviour Ash.Notifier
    end
  end

  @doc """
  Sends any notifications that can be sent, and returns the rest.

  A notification can only be sent if you are not currently in a transaction
  for the resource in question.
  """
  @spec notify(list(Ash.Notifier.Notification.t()) | Ash.Notifier.Notification.t()) ::
          list(Ash.Notifier.Notification.t())
  def notify(resource_notifications) do
    {unsent, to_send} =
      resource_notifications
      |> List.wrap()
      |> Enum.group_by(& &1.resource)
      |> Enum.split_with(fn {resource, _} ->
        resource && Ash.DataLayer.in_transaction?(resource)
      end)

    for {resource, notifications} <- to_send do
      for notification <- notifications do
        case notification.for do
          nil ->
            for notifier <- Ash.Resource.Info.notifiers(resource) do
              notifier.notify(notification)
            end

          allowed_notifiers ->
            for notifier <- List.wrap(allowed_notifiers) do
              notifier.notify(notification)
            end
        end
      end
    end

    unsent
    |> Enum.map(&elem(&1, 1))
    |> List.flatten()
  end
end