Packages

ash

2.15.16
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
ash lib ash flow flow.ex
Raw

lib/ash/flow/flow.ex

defmodule Ash.Flow do
@moduledoc """
A flow is a static definition of a set of steps to be run.
See the [guide](/documentation/topics/flows.md) for more.
"""
@type t :: module
use Spark.Dsl,
default_extensions: [
extensions: [Ash.Flow.Dsl]
]
require Ash.Tracer
defdelegate element_refs(input), to: Ash.Flow.Template
@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, :tenant) 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
opts =
case Process.get(:ash_tracer) do
{:tracer, value} ->
do_add_tracer(opts, value)
_ ->
opts
end
case Application.get_env(:ash, :tracer) do
nil ->
opts
tracer ->
do_add_tracer(opts, tracer)
end
end
defp do_add_tracer(opts, tracer) do
tracer = List.wrap(tracer)
Keyword.update(opts, :tracer, tracer, fn existing_tracer ->
if is_list(existing_tracer) do
Enum.uniq(tracer ++ existing_tracer)
else
if is_nil(existing_tracer) do
tracer
else
Enum.uniq(tracer ++ existing_tracer)
end
end
end)
end
defp cast_input(flow, params) do
arguments = Ash.Flow.Info.arguments(flow)
Enum.reduce_while(arguments, {:ok, %{}}, fn arg, {:ok, acc} ->
value =
case Map.fetch(params, arg.name) do
:error ->
Map.fetch(params, to_string(arg.name))
other ->
other
end
case value do
:error ->
if is_nil(arg.default) do
{:cont, {:ok, acc}}
else
value =
case arg.default do
{m, f, a} ->
apply(m, f, a)
fun when is_function(fun, 0) ->
fun.()
value ->
value
end
{:cont, {:ok, Map.put(acc, arg.name, value)}}
end
{:ok, value} ->
with {:ok, value} <-
Ash.Type.Helpers.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"]
@impl Spark.Dsl
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.Expr.eval_hydrated(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
@impl Spark.Dsl
def explain(dsl_state, _) do
Ash.Flow.Info.description(dsl_state)
end
end