defmodule Ash do @moduledoc """ General purpose tools for working with Ash and Ash resources. """ for {function, arity} <- Ash.Api.Functions.functions() do if function == :load do def load({:ok, result}, load) do load(result, load) end def load({:error, error}, _), do: {:error, error} def load([], _), do: {:ok, []} def load(nil, _), do: {:ok, nil} def load(%page_struct{results: []} = page, _) when page_struct in [Ash.Page.Keyset, Ash.Page.Offset] do {:ok, page} end end if function == :load! do def load!({:ok, result}, load) do {:ok, load!(result, load)} end def load!({:error, error}, _), do: raise(Ash.Error.to_error_class(error)) def load!([], _), do: [] def load!(nil, _), do: nil def load!(%page_struct{results: []} = page, _) when page_struct in [Ash.Page.Keyset, Ash.Page.Offset] do page end end args = Macro.generate_arguments(arity, __MODULE__) docs_arity = if function in Ash.Api.Functions.no_opts_functions() do arity else arity + 1 end @doc "Calls `c:Ash.Api.#{function}/#{docs_arity}` on the resource's configured api. See those callback docs for more." def unquote(function)(unquote_splicing(args)) do resource = Ash.Api.GlobalInterface.resource_from_args!(unquote(function), unquote(arity), [ unquote_splicing(args) ]) api = Ash.Resource.Info.api(resource) if !api do Ash.Api.GlobalInterface.raise_no_api_error!(resource, unquote(function), unquote(arity)) end apply(api, unquote(function), [unquote_splicing(args)]) end unless function in Ash.Api.Functions.no_opts_functions() do args = Macro.generate_arguments(arity + 1, __MODULE__) if function == :load! do def load!({:ok, result}, load, opts) do {:ok, load(result, load, opts)} end def load!({:error, error}, _, _), do: raise(Ash.Error.to_error_class(error)) def load!(nil, _, _), do: nil def load!([], _, _), do: [] def load!(%page_struct{results: []} = page, _, _) when page_struct in [Ash.Page.Keyset, Ash.Page.Offset] do page end end if function == :load do def load({:ok, result}, load, opts) do load(result, load, opts) end def load({:error, error}, _, _), do: {:error, error} def load([], _, _), do: {:ok, []} def load(nil, _, _), do: {:ok, nil} def load(%page_struct{results: []} = page, _, _) when page_struct in [Ash.Page.Keyset, Ash.Page.Offset] do {:ok, page} end end @doc "Calls `c:Ash.Api.#{function}/#{arity + 1}` on the resource's configured api. See those callback docs for more." def unquote(function)(unquote_splicing(args)) do resource = Ash.Api.GlobalInterface.resource_from_args!(unquote(function), unquote(arity), [ unquote_splicing(args) ]) api = Ash.Resource.Info.api(resource) if !api do Ash.Api.GlobalInterface.raise_no_api_error!(resource, unquote(function), unquote(arity)) end apply(api, unquote(function), [unquote_splicing(args)]) end end end @doc """ Converts a context map to opts to be passed into an action. """ def context_to_opts(map, add_to \\ []) when is_map(map) do add_to |> add_if_present(map, :actor) |> add_if_present(map, :authorize?) |> add_if_present(map, :tracer) end defp add_if_present(opts, map, key) do case Map.fetch(map, key) do {:ok, value} -> Keyword.put(opts, key, value) :error -> opts end end @doc deprecated: "See `Ash.ProcessHelpers`. This alias will be removed in 3.0" defdelegate get_context_for_transfer(opts \\ []), to: Ash.ProcessHelpers @doc deprecated: "See `Ash.ProcessHelpers`. This alias will be removed in 3.0" defdelegate transfer_context(term, opts \\ []), to: Ash.ProcessHelpers @doc deprecated: """ Sets context into the process dictionary that is used for all changesets and queries. """ @spec set_context(map) :: :ok def set_context(map) do Process.put(:ash_context, map) :ok end @doc deprecated: """ Deep merges context into the process dictionary that is used for all changesets and queries. """ @spec merge_context(map) :: :ok def merge_context(map) do update_context(&Ash.Helpers.deep_merge_maps(&1, map)) :ok end @doc deprecated: """ Updates the context into the process dictionary that is used for all changesets and queries. """ @spec update_context((map -> map)) :: :ok def update_context(fun) do context = Process.get(:ash_context, %{}) set_context(fun.(context)) :ok end @doc deprecated: """ Sets actor into the process dictionary that is used for all changesets and queries. """ @spec set_actor(map) :: :ok def set_actor(map) do Process.put(:ash_actor, {:actor, map}) :ok end @doc deprecated: """ Sets authorize? into the process dictionary that is used for all changesets and queries. """ @spec set_authorize?(map) :: :ok def set_authorize?(map) do Process.put(:ash_authorize?, {:authorize?, map}) :ok end @doc deprecated: """ Sets the tracer into the process dictionary that will be used to trace requests """ @spec set_tracer(module | list(module)) :: :ok def set_tracer(module) do case Process.get(:ash_tracer, module) do nil -> Process.put(:ash_tracer, module) tracer -> Process.put(:ash_tracer, Enum.uniq(List.wrap(tracer) ++ List.wrap(module))) end :ok end @doc deprecated: """ Removes a tracer from the process dictionary. """ @spec remove_tracer(module | list(module)) :: :ok def remove_tracer(module) do case Process.get(:ash_tracer, module) do nil -> :ok tracer -> Process.put(:ash_tracer, List.wrap(tracer) -- List.wrap(module)) end :ok end @doc deprecated: """ Gets the current actor from the process dictionary """ @spec get_actor() :: term() def get_actor do case Process.get(:ash_actor) do {:actor, value} -> value _ -> nil end end @doc deprecated: """ Gets the current tracer """ @spec get_tracer() :: term() def get_tracer do case Process.get(:ash_tracer) do {:tracer, value} -> value _ -> Application.get_env(:ash, :tracer) end end @doc deprecated: """ Gets the current authorize? from the process dictionary """ @spec get_authorize?() :: term() def get_authorize? do case Process.get(:ash_authorize?) do {:authorize?, value} -> value _ -> nil end end @doc deprecated: """ Sets tenant into the process dictionary that is used for all changesets and queries. """ @spec set_tenant(term()) :: :ok def set_tenant(tenant) do Process.put(:ash_tenant, {:tenant, tenant}) :ok end @doc deprecated: """ Gets the current tenant from the process dictionary """ @spec get_tenant() :: term() def get_tenant do case Process.get(:ash_tenant) do {:tenant, value} -> value _ -> nil end end @doc deprecated: """ Gets the current context from the process dictionary """ @spec get_context() :: term() def get_context do Process.get(:ash_context, %{}) || %{} end end