Packages
ash
2.4.13
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/flow/flow.ex
defmodule Ash.Flow do
@moduledoc """
A flow is a static definition of a set of steps to be .
See the {{link:ash:guide:Flows}} guide for more.
"""
@type t :: module
use Spark.Dsl,
default_extensions: [
extensions: [Ash.Flow.Dsl]
]
require Ash.Tracer
@spec run!(any, any, Keyword.t()) :: Ash.Flow.Result.t() | no_return()
def run!(flow, input, opts \\ []) do
case run(flow, input, opts) do
%Ash.Flow.Result{valid?: true} = result ->
result
%Ash.Flow.Result{errors: errors} ->
raise Ash.Error.to_error_class(errors)
end
end
@spec run(any, any, Keyword.t()) :: Ash.Flow.Result.t()
def run(flow, input, opts \\ []) do
params = input
executor = opts[:executor] || Ash.Flow.Executor.AshEngine
opts =
opts
|> add_actor()
|> add_tenant()
|> add_tracer()
Ash.Tracer.span :flow, Ash.Flow.Info.trace_name(flow), opts[:tracer] do
Ash.Tracer.telemetry_span [:ash, :flow], %{
flow_short_name: Ash.Flow.Info.short_name(flow)
} do
with {:ok, input} <- cast_input(flow, input),
{:ok, built} <- executor.build(flow, input, opts) do
case executor.execute(built, input, opts) do
{:ok, result, metadata} ->
%Ash.Flow.Result{
flow: flow,
params: params,
input: input,
result: result,
notifications: metadata[:notifications] || [],
valid?: true,
complete?: true
}
{:ok, result} ->
%Ash.Flow.Result{
flow: flow,
params: params,
input: input,
result: result,
valid?: true,
complete?: true
}
{:error, metadata, error} ->
complete? = complete?(error)
%Ash.Flow.Result{
flow: flow,
params: params,
input: input,
notifications: metadata[:notifications] || [],
runner_metadata: if(not complete?, do: metadata[:runner_metadata]),
valid?: false,
complete?: complete?,
errors: List.wrap(error)
}
{:error, error} ->
%Ash.Flow.Result{
flow: flow,
params: params,
input: input,
valid?: false,
complete?: complete?(error),
errors: List.wrap(error)
}
end
else
{:error, new_input, error} ->
%Ash.Flow.Result{
flow: flow,
params: input,
input: new_input,
valid?: false,
complete?: complete?(error),
errors: List.wrap(error)
}
{:error, error} ->
%Ash.Flow.Result{
flow: flow,
params: input,
input: input,
valid?: false,
complete?: complete?(error),
errors: List.wrap(error)
}
end
end
end
end
defp complete?(%Ash.Error.Flow.Halted{}) do
false
end
defp complete?(_) do
true
end
defp add_actor(opts) do
if Keyword.has_key?(opts, :actor) do
opts
else
case Process.get(:ash_actor) do
{:actor, value} ->
Keyword.put(opts, :actor, value)
_ ->
opts
end
end
end
defp add_tenant(opts) do
if Keyword.has_key?(opts, :actor) do
opts
else
case Process.get(:ash_tenant) do
{:tenant, value} ->
Keyword.put(opts, :tenant, value)
_ ->
opts
end
end
end
defp add_tracer(opts) do
if Keyword.has_key?(opts, :tracer) do
opts
else
case Process.get(:ash_tracer) do
{:tracer, value} ->
Keyword.put(opts, :tracer, value || Application.get_env(:ash, :tracer))
_ ->
Keyword.put(opts, :tracer, Application.get_env(:ash, :tracer))
end
end
end
defp cast_input(flow, params) do
arguments = Ash.Flow.Info.arguments(flow)
Enum.reduce_while(params, {:ok, %{}}, fn {name, value}, {:ok, acc} ->
case Enum.find(arguments, &(&1.name == name || to_string(&1.name) == name)) do
nil ->
{:cont, {:ok, acc}}
arg ->
with {:ok, value} <- Ash.Changeset.cast_input(arg.type, value, arg.constraints, flow),
{:constrained, {:ok, casted}}
when not is_nil(value) <-
{:constrained, Ash.Type.apply_constraints(arg.type, value, arg.constraints)} do
{:cont, {:ok, Map.put(acc, arg.name, casted)}}
else
{:constrained, {:ok, nil}} ->
{:cont, {:ok, Map.put(acc, arg.name, nil)}}
{:constrained, {:error, error}} ->
{:halt, {:error, acc, error}}
{:error, error} ->
{:halt, {:error, acc, error}}
end
end
end)
end
@doc false
# sobelow_skip ["DOS.StringToAtom"]
def handle_before_compile(_opts) do
quote bind_quoted: [] do
{opt_args, args} =
__MODULE__
|> Ash.Flow.Info.arguments()
|> Enum.split_with(&(&1.allow_nil? || !is_nil(&1.default)))
args = Enum.map(args, & &1.name)
opt_args = Enum.map(opt_args, & &1.name)
arg_vars = Enum.map(args, &{&1, [], Elixir})
@doc Ash.Flow.Info.description(__MODULE__)
def run!(unquote_splicing(arg_vars), input \\ %{}, opts \\ []) do
{input, opts} =
if opts == [] && Keyword.keyword?(input) do
{%{}, input}
else
{input, opts}
end
opt_input =
Enum.reduce(unquote(opt_args), input, fn opt_arg, input ->
case Map.fetch(input, opt_arg) do
{:ok, val} ->
Map.put(input, opt_arg, val)
:error ->
case Map.fetch(input, to_string(opt_arg)) do
{:ok, val} ->
Map.put(input, opt_arg, val)
:error ->
input
end
end
end)
required_input =
unquote(args)
|> Enum.zip([unquote_splicing(arg_vars)])
|> Map.new()
all_input = Map.merge(required_input, opt_input)
Ash.Flow.run!(__MODULE__, all_input, opts)
end
def run(unquote_splicing(arg_vars), input \\ %{}, opts \\ []) do
{input, opts} =
if opts == [] && Keyword.keyword?(input) do
{%{}, input}
else
{input, opts}
end
opt_input =
Enum.reduce(unquote(opt_args), input, fn opt_arg, input ->
case Map.fetch(input, opt_arg) do
{:ok, val} ->
Map.put(input, opt_arg, val)
:error ->
case Map.fetch(input, to_string(opt_arg)) do
{:ok, val} ->
Map.put(input, opt_arg, val)
:error ->
input
end
end
end)
required_input =
unquote(args)
|> Enum.zip([unquote_splicing(arg_vars)])
|> Map.new()
all_input = Map.merge(required_input, opt_input)
Ash.Flow.run(__MODULE__, all_input, opts)
end
@default_short_name __MODULE__
|> Module.split()
|> Enum.reverse()
|> Enum.take(2)
|> Enum.reverse()
|> Enum.map_join("_", &Macro.underscore/1)
|> String.to_atom()
def default_short_name do
@default_short_name
end
end
end
def handle_modifiers(action_input) do
do_handle_modifiers(action_input)
end
defp do_handle_modifiers(action_input)
when is_map(action_input) and not is_struct(action_input) do
Map.new(action_input, fn {key, value} ->
new_key = do_handle_modifiers(key)
new_val = do_handle_modifiers(value)
{new_key, new_val}
end)
end
defp do_handle_modifiers(action_input) when is_list(action_input) do
Enum.map(action_input, &do_handle_modifiers(&1))
end
defp do_handle_modifiers({:_path, value, path}) do
do_get_in(do_handle_modifiers(value), path)
end
defp do_handle_modifiers({:_expr, expr}) do
case Ash.Filter.hydrate_refs(expr, %{
resource: nil,
aggregates: %{},
calculations: %{},
public?: false
}) do
{:ok, hydrated} ->
case Ash.Filter.Runtime.do_match(nil, hydrated) do
{:ok, result} ->
result
:unknown ->
raise """
Expression #{inspect(expr)} could not be evaluated in the context of the flow
"""
{:error, error} ->
raise Ash.Error.to_error_class(error)
end
{:error, error} ->
raise Ash.Error.to_error_class(error)
end
end
defp do_handle_modifiers(action_input) when is_tuple(action_input) do
List.to_tuple(do_handle_modifiers(Tuple.to_list(action_input)))
end
defp do_handle_modifiers(other), do: other
@doc false
def do_get_in(value, []), do: value
def do_get_in(value, [key | rest]) when is_atom(key) and is_struct(value) do
do_get_in(Map.get(value, key), rest)
end
def do_get_in(value, [key | rest]) do
do_get_in(get_in(value, [key]), rest)
end
def do_fetch_in(value, []), do: {:ok, value}
def do_fetch_in(value, [key | rest]) when is_atom(key) and is_struct(value) do
case Map.fetch(value, key) do
{:ok, value} ->
do_fetch_in(value, rest)
:error ->
:error
end
end
def do_fetch_in(value, [key | rest]) when is_map(value) do
case Map.fetch(value, key) do
{:ok, result} ->
do_fetch_in(result, rest)
:error ->
:error
end
end
def do_fetch_in(_, _), do: :error
def remap_result_references(action_input, prefix) do
do_remap_result_references(action_input, prefix)
end
defp do_remap_result_references(action_input, prefix)
when is_map(action_input) and not is_struct(action_input) do
Map.new(action_input, fn {key, value} ->
new_key = do_remap_result_references(key, prefix)
new_val = do_remap_result_references(value, prefix)
{new_key, new_val}
end)
end
defp do_remap_result_references(action_input, prefix) when is_list(action_input) do
Enum.map(action_input, &do_remap_result_references(&1, prefix))
end
defp do_remap_result_references({:_path, value, path}, prefix) do
{:_path, do_remap_result_references(value, prefix), do_remap_result_references(path, prefix)}
end
defp do_remap_result_references({:_merge, items}, prefix) do
{:_merge, do_remap_result_references(items, prefix)}
end
defp do_remap_result_references({:_expr, expr}, prefix) do
{:_expr, Ash.Filter.walk_filter_template(expr, &do_remap_result_references(&1, prefix))}
end
defp do_remap_result_references({:_result, step}, prefix) when is_function(prefix) do
{:_result, prefix.(step)}
end
defp do_remap_result_references({:_result, step}, prefix) do
{:_result, [prefix | List.wrap(step)]}
end
defp do_remap_result_references({:_element, step}, prefix) when is_function(prefix) do
{:_element, prefix.(step)}
end
defp do_remap_result_references({:_element, step}, prefix) do
{:_element, [prefix | List.wrap(step)]}
end
defp do_remap_result_references(action_input, input) when is_tuple(action_input) do
List.to_tuple(do_remap_result_references(Tuple.to_list(action_input), input))
end
defp do_remap_result_references(other, _), do: other
def set_dependent_values(action_input, input) do
do_set_dependent_values(action_input, input)
end
defp do_set_dependent_values(action_input, input)
when is_map(action_input) and not is_struct(action_input) do
Map.new(action_input, fn {key, value} ->
new_key = do_set_dependent_values(key, input)
new_val = do_set_dependent_values(value, input)
{new_key, new_val}
end)
end
defp do_set_dependent_values(action_input, input) when is_list(action_input) do
Enum.map(action_input, &do_set_dependent_values(&1, input))
end
defp do_set_dependent_values({:_path, value, path}, input) do
{:_path, do_set_dependent_values(value, input), do_set_dependent_values(path, input)}
end
defp do_set_dependent_values({:_expr, expr}, input) do
{:_expr, Ash.Filter.walk_filter_template(expr, &do_set_dependent_values(&1, input))}
end
defp do_set_dependent_values({:_merge, items}, input) do
items
|> Enum.map(&do_set_dependent_values(&1, input))
|> Enum.reject(&is_nil/1)
|> Enum.reduce(%{}, &Map.merge(&2, &1))
end
defp do_set_dependent_values({:_result, step}, input) do
get_in(input, [:results, step])
end
defp do_set_dependent_values({:_element, step}, input) do
get_in(input, [:elements, step])
end
defp do_set_dependent_values({:_range, start, finish}, input) do
do_set_dependent_values(start, input)..do_set_dependent_values(finish, input)
end
defp do_set_dependent_values(action_input, input) when is_tuple(action_input) do
List.to_tuple(do_set_dependent_values(Tuple.to_list(action_input), input))
end
defp do_set_dependent_values(other, _), do: other
def arg_refs(input) when is_map(input) do
Enum.flat_map(input, &arg_refs/1)
end
def arg_refs(input) when is_list(input) do
Enum.flat_map(input, &arg_refs/1)
end
def arg_refs({:_expr, expr}) do
list_expr_refs(
expr,
fn
{:_arg, _} ->
true
_ ->
false
end
)
|> Enum.map(&elem(&1, 1))
end
def arg_refs({:_arg, name}) do
[name]
end
def arg_refs(input) when is_tuple(input) do
input
|> Tuple.to_list()
|> Enum.flat_map(&arg_refs/1)
end
def arg_refs(_), do: []
def element_refs(input) when is_map(input) do
Enum.flat_map(input, &element_refs/1)
end
def element_refs(input) when is_list(input) do
Enum.flat_map(input, &element_refs/1)
end
def element_refs({:_expr, expr}) do
list_expr_refs(
expr,
fn
{:_element, _} ->
true
_ ->
false
end
)
|> Enum.map(&elem(&1, 1))
end
def element_refs({:_element, name}) do
[name]
end
def element_refs(input) when is_tuple(input) do
input
|> Tuple.to_list()
|> Enum.flat_map(&element_refs/1)
end
def element_refs(_), do: []
def result_refs(input) when is_map(input) do
Enum.flat_map(input, &result_refs/1)
end
def result_refs(input) when is_list(input) do
Enum.flat_map(input, &result_refs/1)
end
def result_refs({:_expr, expr}) do
list_expr_refs(
expr,
fn
{:_result, _} ->
true
_ ->
false
end
)
|> Enum.map(&elem(&1, 1))
end
def result_refs({:_result, name}) do
[name]
end
def result_refs(input) when is_tuple(input) do
input
|> Tuple.to_list()
|> Enum.flat_map(&result_refs/1)
end
def result_refs(_), do: []
defp list_expr_refs(expression, matcher) do
expression
|> do_list_expr_refs(matcher)
|> Enum.uniq()
end
defp do_list_expr_refs(list, matcher) when is_list(list) do
Enum.flat_map(list, &do_list_expr_refs(&1, matcher))
end
defp do_list_expr_refs({key, value}, matcher) when is_atom(key) do
if matcher.({key, value}) do
[{key, value}]
else
do_list_expr_refs(value, matcher)
end
end
defp do_list_expr_refs(expression, matcher) do
case expression do
%Ash.Query.BooleanExpression{left: left, right: right} ->
do_list_expr_refs(left, matcher) ++ do_list_expr_refs(right, matcher)
%Ash.Query.Not{expression: not_expr} ->
do_list_expr_refs(not_expr, matcher)
%{__predicate__?: _, left: left, right: right} ->
do_list_expr_refs(left, matcher) ++
do_list_expr_refs(right, matcher)
%{__predicate__?: _, arguments: args} ->
Enum.flat_map(args, &do_list_expr_refs(&1, matcher))
%Ash.Query.Call{args: args} ->
args
|> Enum.flat_map(&do_list_expr_refs(&1, matcher))
v ->
if matcher.(v) do
[v]
else
[]
end
end
end
def handle_input_template(action_input, input) do
{val, deps} = do_handle_input_template(action_input, input)
{val, Enum.uniq(deps)}
end
defp do_handle_input_template({:_expr, expr}, input) do
{new_expr, deps} = do_handle_input_template(expr, input)
{{:_expr, new_expr}, deps}
end
defp do_handle_input_template(
%Ash.Query.BooleanExpression{left: left, right: right} = expr,
input
) do
{new_left, left_deps} = do_handle_input_template(left, input)
{new_right, right_deps} = do_handle_input_template(right, input)
{%{expr | left: new_left, right: new_right}, left_deps ++ right_deps}
end
defp do_handle_input_template(%Ash.Query.Not{expression: expression} = not_expr, input) do
{new_expr, deps} = do_handle_input_template(expression, input)
{%{not_expr | expression: new_expr}, deps}
end
defp do_handle_input_template(%{__predicate__?: _, left: left, right: right} = pred, input) do
{new_left, left_deps} = do_handle_input_template(left, input)
{new_right, right_deps} = do_handle_input_template(right, input)
{%{pred | left: new_left, right: new_right}, left_deps ++ right_deps}
end
defp do_handle_input_template(%{__predicate__?: _, arguments: arguments} = func, input) do
{args, deps} =
Enum.reduce(arguments, {[], []}, fn arg, {args, deps} ->
{new_arg, new_deps} = do_handle_input_template(arg, input)
{[new_arg | args], deps ++ new_deps}
end)
{%{func | arguments: Enum.reverse(args)}, deps}
end
defp do_handle_input_template(%Ash.Query.Call{args: arguments} = call, input) do
{args, deps} =
Enum.reduce(arguments, {[], []}, fn arg, {args, deps} ->
{new_arg, new_deps} = do_handle_input_template(arg, input)
{[new_arg | args], deps ++ new_deps}
end)
{%{call | args: Enum.reverse(args)}, deps}
end
defp do_handle_input_template(action_input, input)
when is_map(action_input) and not is_struct(action_input) do
Enum.reduce(action_input, {%{}, []}, fn {key, value}, {acc, deps} ->
{new_key, key_deps} = do_handle_input_template(key, input)
{new_val, val_deps} = do_handle_input_template(value, input)
{Map.put(acc, new_key, new_val), deps ++ key_deps ++ val_deps}
end)
end
defp do_handle_input_template(action_input, input) when is_list(action_input) do
{new_items, deps} =
Enum.reduce(action_input, {[], []}, fn item, {items, deps} ->
{new_item, new_deps} = do_handle_input_template(item, input)
{[new_item | items], new_deps ++ deps}
end)
{Enum.reverse(new_items), deps}
end
defp do_handle_input_template({:_path, value, path}, input) do
{new_value, value_deps} = do_handle_input_template(value, input)
{new_path, path_deps} = do_handle_input_template(path, input)
{{:_path, new_value, new_path}, value_deps ++ path_deps}
end
defp do_handle_input_template({:_merge, items}, input) do
{new_items, deps} =
Enum.reduce(items, {[], []}, fn item, {items, deps} ->
{new_item, new_deps} = do_handle_input_template(item, input)
{[new_item | items], new_deps ++ deps}
end)
{{:_merge, new_items}, deps}
end
defp do_handle_input_template({:_arg, name}, input) do
{Map.get(input, name) || Map.get(input, to_string(name)), []}
end
defp do_handle_input_template({:_result, step}, _input) do
{{:_result, step}, [{:_result, step}]}
end
defp do_handle_input_template(action_input, input) when is_tuple(action_input) do
{list, deps} = do_handle_input_template(Tuple.to_list(action_input), input)
{List.to_tuple(list), deps}
end
defp do_handle_input_template(other, _), do: {other, []}
end