Packages

ash

2.13.1
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 chart mermaid.ex
Raw

lib/ash/flow/chart/mermaid.ex

defmodule Ash.Flow.Chart.Mermaid do
@moduledoc "Tools to render an Ash.Flow as a mermaid chart."
@opts [
expand?: [
type: :boolean,
default: true,
doc: """
If the flow should be fully expanded (all `run_flow` steps will be inlined)
"""
],
link_to_flows: [
type: {:fun, 1}
]
]
def chart(flow, opts \\ []) do
opts = Spark.OptionsHelpers.validate!(opts, @opts)
# This is a hack that may not work forever
# Eventually, we may need a separate mode/option for `build`
# that doesn't attempt to substitute arguments into templates
args =
flow
|> Ash.Flow.Info.arguments()
|> Map.new(fn argument ->
{argument.name, {:_arg, argument.name}}
end)
steps =
if opts[:expand?] do
{:ok, %{steps: steps}} = Ash.Flow.Executor.AshEngine.build(flow, args, [])
unwrap(steps)
else
Ash.Flow.Info.steps(flow)
end
arguments = flow |> Ash.Flow.Info.arguments()
init = "flowchart TB"
init
|> add_arguments(arguments, flow)
|> add_steps(steps, steps, opts)
|> add_links(steps, steps, opts)
|> IO.iodata_to_binary()
end
defp add_arguments(message, arguments, flow) do
message =
message
|> add_line("subgraph Flow")
|> add_line("direction TB")
add_line(
message,
"_top_level_flow_(\"#{inspect(flow)}#{flow_description(flow)}\")"
)
Enum.reduce(arguments, message, fn argument, message ->
question_mark =
if argument.allow_nil? do
"?"
else
""
end
add_line(
message,
"_arguments.#{argument.name}(\"#{argument.name}#{question_mark}: #{inspect(argument.type)}\")"
)
end)
|> add_line("end")
end
defp unwrap(steps) do
Enum.map(steps, fn %{step: step} ->
case step do
%{steps: steps} ->
%{step | steps: unwrap(steps)}
step ->
step
end
end)
end
defp add_steps(message, steps, all_steps, opts) do
Enum.reduce(steps, message, fn step, message ->
case step do
%Ash.Flow.Step.Map{steps: steps, over: over} = step ->
id = "#{format_name(step)}.element"
name = format_name(step)
template = format_template(over, all_steps)
message =
message
|> add_line("subgraph #{name} [\"Map Over #{template}\"]")
|> add_line("direction TB")
|> add_line("#{id}(\"Element in #{template}\")")
|> add_steps(steps, all_steps, opts)
|> add_line("end")
message
%Ash.Flow.Step.Branch{steps: steps} = step ->
name = format_name(step)
message
|> add_line("subgraph #{name}.subgraph [\"Branch\"]")
|> add_line("#{format_name(step)}(\"#{short_name(step)} <br/> #{description(step)}\")")
|> add_line("direction TB")
|> add_steps(steps, all_steps, opts)
|> add_line("end")
%Ash.Flow.Step.Transaction{steps: steps} = step ->
name = format_name(step)
message
|> add_line("subgraph #{name}.subgraph [Transaction]")
|> add_line("direction TB")
|> add_steps(steps, all_steps, opts)
|> add_line("end")
%Ash.Flow.Step.RunFlow{flow: flow} = step ->
returns = Ash.Flow.Info.returns(flow)
if returns && opts[:expand?] do
escaped_returns = escape(inspect(Ash.Flow.Info.returns(flow)))
name = format_name(step)
header =
if is_atom(returns) do
"Gather Value"
else
"Gather Values"
end
message
|> add_line("#{name}(\"#{header}: #{escaped_returns}\")")
else
if opts[:link_to_flows] do
link = opts[:link_to_flows].(flow)
message
|> add_line(
~s[#{format_name(step)}("<a href="#{link}">#{inspect(flow)}</a>#{flow_description(flow)}")]
)
else
message
|> add_line("#{format_name(step)}(\"#{inspect(flow)}#{flow_description(flow)}\")")
end
end
%{input: input} = step when not is_nil(input) ->
add_line(
message,
"#{format_name(step)}(\"#{short_name(step)} <br/> #{description(step)}\")"
)
step ->
add_line(message, "#{format_name(step)}(\"#{short_name(step)}\"#{description(step)})")
end
end)
end
defp flow_description(flow) do
case Ash.Flow.Info.description(flow) do
nil ->
""
description ->
"<br/>" <> as_html!(description)
end
end
defp description(%{description: description}) when not is_nil(description) do
"<br/>" <> as_html!(description)
end
defp description(%Ash.Flow.Step.Custom{custom: {mod, opts}}) do
if function_exported?(mod, :describe, 1) do
case mod.describe(opts) do
nil ->
""
description ->
"<br/>" <> as_html!(description)
end
else
""
end
end
defp description(_), do: ""
if Code.ensure_loaded?(Earmark) do
defp as_html!(value) do
value
|> escape()
|> Earmark.as_html!()
|> String.replace("<br>", "<br/>")
end
else
defp as_html!(value),
do:
value
|> String.replace("\"", "\\\"")
|> String.split("\n", trim: true)
|> Enum.join("<br/>")
end
defp short_name(%Ash.Flow.Step.Custom{custom: {Ash.Flow.Step.CustomFunction, _}}) do
"Custom Function"
end
defp short_name(%Ash.Flow.Step.Custom{custom: {mod, opts}}) do
if function_exported?(mod, :short_name, 1) do
mod.short_name(opts)
else
escape(inspect({mod, opts}))
end
end
defp short_name(%Ash.Flow.Step.Branch{steps: steps, output: output, condition: condition}) do
child_step =
if output do
find_step(steps, output)
else
List.last(steps)
end
"Branch condition: #{escape(inspect(condition))} <br> Branch Result: #{short_name(child_step)}"
end
defp short_name(%Ash.Flow.Step.Map{steps: steps, output: output}) do
child_step =
if output do
find_step(steps, output)
else
List.last(steps)
end
"Element of #{short_name(child_step)}"
end
defp short_name(%Ash.Flow.Step.RunFlow{flow: flow}) do
"Run Flow: #{inspect(flow)}"
end
defp short_name(%Ash.Flow.Step.Validate{action: action, resource: resource}) do
"Validate: #{inspect(resource)}.#{action}"
end
defp short_name(%Ash.Flow.Step.Create{action: action, resource: resource}) do
"Create: #{inspect(resource)}.#{action}"
end
defp short_name(%Ash.Flow.Step.Update{action: action, resource: resource}) do
"Update: #{inspect(resource)}.#{action}"
end
defp short_name(%Ash.Flow.Step.Destroy{action: action, resource: resource}) do
"Destroy: #{inspect(resource)}.#{action}"
end
defp short_name(%Ash.Flow.Step.Read{action: action, resource: resource}) do
"Read: #{inspect(resource)}.#{action}"
end
# defp highlight(message, id) do
# add_line(message, "style #{id} fill:#4287f5,stroke:#333,stroke-width:4px")
# end
def add_links(message, steps, all_steps, opts) do
steps
|> do_links(all_steps, opts)
|> Enum.uniq()
|> Enum.reject(fn link ->
[first | rest] = String.split(link, " ")
last = List.last(rest)
first == last
end)
|> Enum.reduce(message, fn link, message ->
add_line(message, link)
end)
end
defp do_links(steps, all_steps, opts) do
Enum.flat_map(steps, fn step ->
case step do
%Ash.Flow.Step.Branch{steps: steps, condition: condition} = step ->
id = "#{format_name(step)}"
steps
|> Enum.map(&link(format_name(step), nil, format_name(&1)))
|> Enum.concat(dependencies(step, all_steps, [condition], id))
|> Enum.concat(do_links(steps, all_steps, opts))
%Ash.Flow.Step.Map{steps: steps, over: over} = step ->
id = "#{format_name(step)}.element"
dependencies(step, all_steps, [over], id) ++ do_links(steps, all_steps, opts)
%Ash.Flow.Step.Transaction{steps: steps} = step ->
dependencies(step, all_steps) ++ do_links(steps, all_steps, opts)
%Ash.Flow.Step.RunFlow{flow: flow} = run_flow_step ->
returns = Ash.Flow.Info.returns(flow)
name = format_name(step)
returns_links =
Enum.flat_map(List.wrap(returns), fn
{key, _} ->
{source, note} =
if opts[:expand?] do
link_source(all_steps, List.wrap(step.name) ++ List.wrap(key))
else
link_source(all_steps, step.name)
end
[link(source, note, name)]
value ->
if opts[:expand?] do
{source, note} =
link_source(all_steps, List.wrap(step.name) ++ List.wrap(value))
[link(source, note, name)]
else
[]
end
end)
if opts[:expand?] do
returns_links
else
returns_links ++ dependencies(run_flow_step, all_steps)
end
step ->
dependencies(step, all_steps)
end
end)
end
defp link(source, nil, name) do
"#{source} --> #{name}"
end
defp link(source, note, name) do
"#{source} --> |#{note}| #{name}"
end
defp format_template(template, all_steps) do
do_format_template(template, all_steps)
end
defp do_format_template(template, all_steps) when is_map(template) do
body =
Enum.map_join(template, ",<br/>", fn {key, value} ->
result = do_format_template(key, all_steps)
if String.starts_with?(result, ":") do
"#{String.trim_leading(result, ":")}: #{do_format_template(value, all_steps)}"
else
"#{inspect(result)} => #{do_format_template(value, all_steps)}"
end
end)
"%{<br/>#{escape(body)}<br/>}"
end
defp do_format_template(template, all_steps) when is_list(template) do
"[#{Enum.map_join(template, ", ", &do_format_template(&1, all_steps))}]"
end
defp do_format_template({:_path, value, path}, all_steps) do
"get_in(#{do_format_template(value, all_steps)}, #{Enum.map_join(path, ", ", &do_format_template(&1, all_steps))})"
end
defp do_format_template({:_result, step_name}, all_steps) do
"result(#{short_name(find_step(all_steps, step_name))})"
end
defp do_format_template({:_element, step_name}, all_steps) do
"element(#{short_name(find_step(all_steps, step_name))})"
end
defp do_format_template(value, all_steps) when is_tuple(value) do
"{#{value |> Tuple.to_list() |> Enum.map_join(", ", &do_format_template(&1, all_steps))}}"
end
defp do_format_template(value, _), do: inspect(value)
defp find_step(steps, name) do
case do_find_step(steps, name) do
nil ->
raise "Could not find step called #{inspect(name)} in #{steps |> step_names() |> Enum.map_join(", ", &inspect/1)}"
step ->
step
end
end
defp do_find_step(steps, name) when is_list(steps),
do: Enum.find_value(steps, &do_find_step(&1, name))
defp do_find_step(%{name: name} = step, name), do: step
defp do_find_step(%{steps: steps}, name), do: do_find_step(steps, name)
defp do_find_step(_, _), do: nil
defp step_names(steps) do
Enum.flat_map(steps, fn step ->
case step do
%{name: name, steps: steps} ->
[name | step_names(steps)]
%{name: name} ->
[name]
end
end)
end
defp escape(string) do
String.replace(string, "\"", "'")
end
defp dependencies(step, all_steps, additional_inputs \\ [], name \\ nil) do
deps =
Enum.flat_map(Ash.Flow.Executor.AshEngine.deps_keys(), fn key ->
case Map.fetch(step, key) do
{:ok, value} ->
[value]
:error ->
[]
end
end)
deps(deps ++ additional_inputs, name || format_name(step), all_steps)
end
defp deps(template, destination, all_steps) do
result_refs = Ash.Flow.Template.result_refs(template) |> Enum.uniq()
arg_refs = Ash.Flow.Template.arg_refs(template) |> Enum.uniq()
element_refs = Ash.Flow.Template.element_refs(template) |> Enum.uniq()
element_ref_deps =
Enum.map(element_refs, fn element ->
"#{do_format_name(element)}.element --> #{destination}"
end)
arg_ref_deps =
Enum.map(arg_refs, fn arg ->
"_arguments.#{arg} -.-> #{destination}"
end)
result_ref_deps =
Enum.map(result_refs, fn dep ->
{source, note} = link_source(all_steps, dep)
link(source, note, destination)
end)
Enum.concat([element_ref_deps, arg_ref_deps, result_ref_deps])
end
defp link_source(all_steps, dep, note \\ nil) do
case find_step(all_steps, dep) do
%Ash.Flow.Step.Map{steps: steps, output: output} = step ->
output_step =
if output do
find_step(steps, output)
else
List.last(steps)
end
case output_step do
nil ->
{format_name(step), note}
output_step ->
link_source(all_steps, output_step.name, "list")
end
step ->
{format_name(step), note}
end
end
defp add_line(message, line) do
[message, "\n", line]
end
defp format_name(step) do
do_format_name(step.name)
end
defp do_format_name(name) do
name
|> List.wrap()
|> List.flatten()
|> Enum.join(".")
end
end