ash_oban/lib/transformers/set_defaults.ex
2024-03-29 19:52:22 -04:00

182 lines
5.1 KiB
Elixir

defmodule AshOban.Transformers.SetDefaults do
# Set trigger default values
@moduledoc false
use Spark.Dsl.Transformer
alias Spark.Dsl.Transformer
def after?(AshOban.Transformers.DefineSchedulers), do: false
def after?(_), do: true
def before?(AshOban.Transformers.DefineSchedulers), do: true
def before?(_), do: false
# sobelow_skip ["DOS.BinToAtom"]
def transform(dsl) do
module = Transformer.get_persisted(dsl, :module)
{:ok,
dsl
|> set_domain(module)
|> set_trigger_defaults(module)
|> set_scheduled_action_defaults(module)
|> validate_global_uniqueness(module)}
end
defp set_domain(dsl, module) do
case AshOban.Info.oban_domain(dsl) do
{:ok, domain} when not is_nil(domain) ->
dsl
:error ->
if domain = Ash.Resource.Info.domain(dsl) do
Transformer.set_option(dsl, [:oban], :domain, domain)
else
raise Spark.Error.DslError,
module: module,
message:
"Resources without a statically configured domain require the `domain` option in the `oban` section."
end
end
end
defp validate_global_uniqueness(dsl, module) do
triggers = AshOban.Info.oban_triggers(dsl)
schedulers = AshOban.Info.oban_scheduled_actions(dsl)
triggers
|> Enum.concat(schedulers)
|> Enum.map(& &1.name)
|> Enum.frequencies()
|> Enum.find(fn {_, value} ->
value > 1
end)
|> case do
nil ->
:ok
{name, _} ->
raise Spark.Error.DslError,
path: [:oban, :triggers, name],
module: module,
message: """
The trigger and/or scheduled_action #{inspect(name)} is defined more than once.
Names must be unique across scheduled_actions and triggers.
"""
end
dsl
end
defp set_scheduled_action_defaults(dsl, module) do
dsl
|> Transformer.get_entities([:oban, :scheduled_actions])
|> Enum.reduce(dsl, fn scheduled_action, dsl ->
action_name = scheduled_action.action || scheduled_action.name
case Ash.Resource.Info.action(dsl, action_name) do
nil ->
key_name =
if scheduled_action.action do
:action
else
:name
end
raise Spark.Error.DslError,
path: [:oban, :scheduled_actions, scheduled_action.name, key_name],
module: module,
message: """
No such action #{inspect(action_name)} on #{inspect(module)}.
"""
%{type: bad_type} when bad_type in [:update, :destroy] ->
raise Spark.Error.DslError,
path: [:oban, :scheduled_actions, scheduled_action.name],
module: module,
message: """
Scheduled actions of type #{inspect(bad_type)} are not supported.
"""
_ ->
:ok
end
queue = scheduled_action.queue || default_queue_name(dsl, scheduled_action)
Transformer.replace_entity(dsl, [:oban, :scheduled_actions], %{
scheduled_action
| action: action_name,
queue: queue
})
end)
end
defp set_trigger_defaults(dsl, module) do
dsl
|> Transformer.get_entities([:oban, :triggers])
|> Enum.reduce(dsl, fn trigger, dsl ->
read_action =
case trigger.read_action do
nil ->
Ash.Resource.Info.primary_action(dsl, :read) ||
raise Spark.Error.DslError,
path: [
:oban,
:triggers,
trigger.name,
:read_action
],
module: module,
message: """
No read action was configured for this trigger, and no primary read action exists
"""
read_action ->
Ash.Resource.Info.action(dsl, read_action)
end
action_name = trigger.action || trigger.name
unless Ash.Resource.Info.action(dsl, action_name) do
key_name =
if trigger.action do
:action
else
:name
end
raise Spark.Error.DslError,
path: [:oban, :triggers, trigger.name, key_name],
module: module,
message: """
No such action #{inspect(action_name)} on #{inspect(module)}.
"""
end
unless read_action.pagination && read_action.pagination.keyset? do
raise Spark.Error.DslError,
path: [:oban, :triggers, trigger.name, :read_action],
module: module,
message: """
The read action `:#{read_action.name}` must support keyset pagination in order to be
used by an AshOban trigger.
"""
end
queue = trigger.queue || default_queue_name(dsl, trigger)
Transformer.replace_entity(dsl, [:oban, :triggers], %{
trigger
| read_action: read_action.name,
queue: queue,
scheduler_queue: trigger.scheduler_queue || queue,
action: trigger.action || trigger.name
})
end)
end
# sobelow_skip ["DOS.BinToAtom"]
defp default_queue_name(dsl, trigger) do
:"#{Ash.Resource.Info.short_name(dsl)}_#{trigger.name}"
end
end