Packages

Event-driven notification system for Ash Framework with multiple transport types

Retired package: Release invalid - Use vulcora organization package

Current section

Files

Jump to
ash_dispatch lib dsl transformers validate_channels.ex
Raw

lib/dsl/transformers/validate_channels.ex

defmodule AshDispatch.Dsl.Transformers.ValidateChannels do
@moduledoc """
Transformer that validates channel configurations.
Checks:
- At least one channel is defined
- Transport types are valid
- In-app channels have required content fields
- Channel time specifications are valid
"""
use Spark.Dsl.Transformer
alias Spark.Dsl.Transformer
alias Spark.Error.DslError
@valid_transports [:email, :in_app, :discord, :sms, :slack, :webhook]
@valid_policies [:always, :skip_if_read]
@impl true
def transform(dsl_state) do
channels = Transformer.get_entities(dsl_state, [:dispatch, :channels])
with :ok <- validate_channels_exist(channels, dsl_state),
:ok <- validate_transport_types(channels, dsl_state),
:ok <- validate_policies(channels, dsl_state),
:ok <- validate_in_app_content(channels, dsl_state) do
{:ok, dsl_state}
end
end
# Validate at least one channel exists
defp validate_channels_exist([], dsl_state) do
{:error,
DslError.exception(
module: Transformer.get_persisted(dsl_state, :module),
path: [:dispatch, :channels],
message: "At least one channel must be defined"
)}
end
defp validate_channels_exist(_channels, _dsl_state), do: :ok
# Validate transport types
defp validate_transport_types(channels, dsl_state) do
invalid_channels =
Enum.filter(channels, fn channel ->
channel.transport not in @valid_transports
end)
case invalid_channels do
[] ->
:ok
[channel | _] ->
{:error,
DslError.exception(
module: Transformer.get_persisted(dsl_state, :module),
path: [:dispatch, :channels],
message: """
Invalid transport type: #{inspect(channel.transport)}
Valid transport types: #{inspect(@valid_transports)}
"""
)}
end
end
# Validate policies
defp validate_policies(channels, dsl_state) do
invalid_channels =
Enum.filter(channels, fn channel ->
channel.policy not in @valid_policies
end)
case invalid_channels do
[] ->
:ok
[channel | _] ->
{:error,
DslError.exception(
module: Transformer.get_persisted(dsl_state, :module),
path: [:dispatch, :channels],
message: """
Invalid policy: #{inspect(channel.policy)}
Valid policies: #{inspect(@valid_policies)}
"""
)}
end
end
# Validate in-app channels have required content
defp validate_in_app_content(channels, dsl_state) do
in_app_channels = Enum.filter(channels, fn ch -> ch.transport == :in_app end)
if length(in_app_channels) > 0 do
# Check if notification_title is set (either in DSL or will use callback)
title = Transformer.get_option(dsl_state, [:dispatch, :content], :notification_title)
if is_nil(title) do
# It's ok - event can override via callback
# Just warn in docs that in-app requires notification_title/2
:ok
else
:ok
end
else
:ok
end
end
end