Ash.ActionInput (ash v3.5.34)
View SourceInput for a custom action
Much like an Ash.Query
and Ash.Changeset
are used to provide inputs into
CRUD actions, this struct provides the inputs required to execute a generic
action.
Summary
Types
Function type for after action hooks.
Function type for after transaction hooks.
Function type for around transaction hooks.
Function type for before action hooks.
Function type for before transaction hooks.
An action input struct for generic (non-CRUD) actions.
Functions
Adds an error to the errors list and marks the action input as invalid.
Adds an after_action hook to the action input.
Adds an after transaction hook to the action input.
Adds an around transaction hook to the action input.
Adds a before_action hook to the action input.
Adds a before transaction hook to the action input.
Deletes one or more arguments from the subject.
Fetches the value of an argument provided to the input.
Creates a new input for a generic action.
Gets the value of an argument provided to the input.
Creates a new action input from a resource.
Sets an argument value on the action input.
Deep merges the provided map into the input context.
Sets a private argument value on the action input.
Sets the tenant to use when calling the action.
Types
@type after_action_fun() :: (t(), term() -> {:ok, term()} | {:ok, term(), [Ash.Notifier.Notification.t()]} | {:error, any()})
Function type for after action hooks.
Receives the action input and the result of the action, and can return the result optionally with notifications, or an error.
@type after_transaction_fun() :: (t(), {:ok, term()} | {:error, any()} -> {:ok, term()} | {:error, any()})
Function type for after transaction hooks.
Receives the action input and the result of the transaction, and returns the result (potentially modified) or an error.
@type around_transaction_fun() :: (t(), (t() -> {:ok, term()} | {:error, any()}) -> {:ok, term()} | {:error, any()})
Function type for around transaction hooks.
Receives an action input and a callback function that executes the transaction, and returns the result of calling the callback or an error.
@type before_action_fun() :: (t() -> t() | {t(), %{notifications: [Ash.Notifier.Notification.t()]}})
Function type for before action hooks.
Receives an action input and returns a modified action input, optionally with notifications.
Function type for before transaction hooks.
Receives an action input and returns a modified action input or an error.
@type t() :: %Ash.ActionInput{ action: Ash.Resource.Actions.Action.t() | nil, after_action: [after_action_fun()], after_transaction: [after_transaction_fun()], arguments: map(), around_transaction: [around_transaction_fun()], before_action: [before_action_fun()], before_transaction: [before_transaction_fun()], context: map(), domain: Ash.Domain.t(), errors: [Ash.Error.t()], invalid_keys: MapSet.t(), params: map(), resource: Ash.Resource.t(), tenant: term(), to_tenant: term(), valid?: boolean() }
An action input struct for generic (non-CRUD) actions.
Contains all the information needed to execute a generic action including
arguments, context, tenant information, validation state, and lifecycle hooks. Built using
for_action/4
and modified with functions like set_argument/3
and set_context/2
.
Functions
@spec add_error( t(), Ash.Error.error_input() | [Ash.Error.error_input()], Ash.Error.path_input() ) :: t()
Adds an error to the errors list and marks the action input as invalid.
This function allows you to add validation errors or other issues to the action input. Once an error is added, the input will be marked as invalid and action execution will be prevented.
Examples
# Add a simple string error
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:send_notification, %{})
...> |> Ash.ActionInput.add_error("Missing required configuration")
iex> input.valid?
false
# Add an error with a specific path
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:process_data, %{})
...> |> Ash.ActionInput.add_error("Invalid format", [:data, :format])
iex> input.errors |> List.first() |> Map.get(:path)
[:data, :format]
# Add multiple errors
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:complex_action, %{})
...> |> Ash.ActionInput.add_error(["Error 1", "Error 2"])
iex> length(input.errors)
2
# Add structured error with keyword list
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:validate_input, %{})
...> |> Ash.ActionInput.add_error(field: :email, message: "is invalid")
See also
Ash.Error.to_ash_error/3
for more on supported error values- Action implementations can use this to add custom validation errors
set_argument/3
automatically adds errors for invalid argument values
@spec after_action( input :: t(), fun :: after_action_fun(), opts :: Keyword.t() ) :: t()
Adds an after_action hook to the action input.
After action hooks are called with the action input and the result returned from the action. They can modify the result, perform side effects, or return errors to halt processing. The hook can return notifications alongside the result.
Examples
# Transform the result after action
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:calculate_stats, %{data: [1, 2, 3]})
...> |> Ash.ActionInput.after_action(fn input, result ->
...> enhanced_result = Map.put(result, :calculated_at, DateTime.utc_now())
...> {:ok, enhanced_result}
...> end)
# Log successful actions
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:important_action, %{})
...> |> Ash.ActionInput.after_action(fn inp, result ->
...> Logger.info("Action completed successfully")
...> {:ok, result}
...> end)
# Return notifications
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:notify_users, %{message: "Hello"})
...> |> Ash.ActionInput.after_action(fn input, result ->
...> notification = %Ash.Notifier.Notification{
...> resource: input.resource,
...> action: input.action,
...> data: result
...> }
...> {:ok, result, [notification]}
...> end)
# Handle errors
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:risky_action, %{})
...> |> Ash.ActionInput.after_action(fn input, result ->
...> if is_error_result?(result) do
...> {:error, "Action failed with custom error"}
...> else
...> {:ok, result}
...> end
...> end)
See also
before_action/3
for hooks that run before the action executesfor_action/4
for creating action inputsAsh.run_action/2
for executing the action with the input
@spec after_transaction( input :: t(), fun :: after_transaction_fun(), opts :: Keyword.t() ) :: t()
Adds an after transaction hook to the action input.
After transaction hooks are executed after the transaction completes, regardless of success or failure. They receive both the input and the transaction result, and can modify the result.
Examples
# Add cleanup after transaction
iex> input
...> |> Ash.ActionInput.after_transaction(fn input, result ->
...> cleanup_resources()
...> result
...> end)
See also
before_transaction/2
for hooks that run before the transactionaround_transaction/2
for hooks that wrap the entire transactionafter_action/2
for hooks that run after the action (inside transaction)
@spec around_transaction( input :: t(), fun :: around_transaction_fun(), opts :: Keyword.t() ) :: t()
Adds an around transaction hook to the action input.
Around transaction hooks wrap the entire transaction execution. They receive a callback function that they must call to execute the transaction, allowing them to add logic both before and after the transaction.
Examples
# Add retry logic around transaction
iex> input
...> |> Ash.ActionInput.around_transaction(fn input, callback ->
...> case callback.(input) do
...> {:ok, result} -> {:ok, result}
...> {:error, %{retryable?: true}} -> callback.(input) # Retry once
...> error -> error
...> end
...> end)
See also
before_transaction/2
for hooks that run before the transactionafter_transaction/2
for hooks that run after the transactionbefore_action/3
andafter_action/2
for hooks that run inside the transaction
@spec before_action( input :: t(), fun :: before_action_fun(), opts :: Keyword.t() ) :: t()
Adds a before_action hook to the action input.
Before action hooks are called with the action input and can modify it before the action executes. They can also add errors to halt processing or return notifications to be processed later.
Examples
# Validate arguments before action
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:send_notification, %{message: "Hello"})
...> |> Ash.ActionInput.before_action(fn input ->
...> if String.length(input.arguments.message) > 100 do
...> Ash.ActionInput.add_error(input, "Message too long")
...> else
...> input
...> end
...> end)
# Set computed arguments
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:process_data, %{data: "test"})
...> |> Ash.ActionInput.before_action(fn input ->
...> Ash.ActionInput.set_argument(input, :processed_at, DateTime.utc_now())
...> end)
# Return notifications
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:audit_action, %{})
...> |> Ash.ActionInput.before_action(fn input ->
...> notification = %Ash.Notifier.Notification{
...> resource: input.resource,
...> action: input.action,
...> data: %{audit: "before_action"}
...> }
...> {input, %{notifications: [notification]}}
...> end)
Options
prepend?
- Iftrue
, adds the hook to the beginning of the list instead of the end
See also
after_action/2
for hooks that run after the action completesfor_action/4
for creating action inputsadd_error/2
for adding validation errors in hooks
@spec before_transaction( input :: t(), fun :: before_transaction_fun(), opts :: Keyword.t() ) :: t()
Adds a before transaction hook to the action input.
Before transaction hooks are executed before the transaction begins (if the action is transactional). They can modify the action input or halt execution by returning an error.
Examples
# Add logging before transaction
iex> input
...> |> Ash.ActionInput.before_transaction(fn input ->
...> IO.puts("Starting transaction for action")
...> input
...> end)
See also
after_transaction/2
for hooks that run after the transactionaround_transaction/2
for hooks that wrap the entire transactionbefore_action/3
for hooks that run before the action (inside transaction)
@spec delete_argument( input :: t(), argument_or_arguments :: atom() | String.t() | [atom() | String.t()] ) :: t()
Deletes one or more arguments from the subject.
Parameters
subject
- The subject to delete arguments fromarguments
- Single argument name or list of argument names to delete
Fetches the value of an argument provided to the input.
Returns {:ok, value}
if the argument exists, or :error
if not found.
This is the safer alternative to get_argument/2
when you need to distinguish
between a nil
value and a missing argument.
Examples
# Fetch an argument that exists
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:send_notification, %{priority: :high})
...> |> Ash.ActionInput.fetch_argument(:priority)
{:ok, :high}
# Fetch an argument that doesn't exist
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:send_notification, %{})
...> |> Ash.ActionInput.fetch_argument(:missing_arg)
:error
# Distinguish between nil and missing arguments
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:example, %{optional_field: nil})
...> |> Ash.ActionInput.fetch_argument(:optional_field)
{:ok, nil}
# Use in conditional logic
iex> input = MyApp.Post |> Ash.ActionInput.for_action(:process, %{})
iex> case Ash.ActionInput.fetch_argument(input, :mode) do
...> {:ok, mode} -> "Processing in #{mode} mode"
...> :error -> "Using default processing mode"
...> end
"Using default processing mode"
See also
get_argument/2
for simpler argument accessset_argument/3
for setting argument valuesfor_action/4
for providing initial arguments
@spec for_action( resource_or_input :: Ash.Resource.t() | t(), action :: atom(), params :: map(), opts :: Keyword.t() ) :: t()
Creates a new input for a generic action.
This is the primary way to create action inputs for generic actions. It validates the action exists, sets up the input with proper defaults, and validates any provided arguments according to the action's argument definitions.
Examples
# Create input for a simple action
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:send_notification, %{message: "Hello"})
%Ash.ActionInput{action: %{name: :send_notification}, arguments: %{message: "Hello"}, ...}
# Create input with options
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:complex_action, %{data: "test"},
...> actor: current_user, authorize?: true)
%Ash.ActionInput{arguments: %{data: "test"}, ...}
# Create input and then modify it
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:example, %{})
...> |> Ash.ActionInput.set_context(%{source: "api"})
iex> input.action.name
:example
Options
:domain
(Ash.Domain
) - The domain to use for the action. The resource's domain is used by default.:context
(map/0
) - Context to set on the action input. The default value is%{}
.:authorize?
- Whether or not to run authorization on the action. Default behavior of this option is controlled by the domain.:tenant
(term/0
) - The tenant to use for the action.:scope
(term/0
) - A value that implements theAsh.Scope.ToOpts
protocol, for passing around actor/tenant/context in a single value. SeeAsh.Scope.ToOpts
for more.:actor
(term/0
) - The actor performing the action:skip_unknown_inputs
- A list of unknown inputs to skip. Use:*
to skip all unknown inputs.:tracer
(term/0
) - A tracer or list of tracers to trace action execution.:private_arguments
(map/0
) - A list of private arguments to be set before the action is invoked. The default value is%{}
.
See also
new/2
for creating basic inputsset_argument/3
for adding arguments after creationAsh.run_action/2
for executing the action with the inputAsh.Resource.Dsl.actions.action
for defining generic actions- Generic Actions Guide for understanding generic actions
- Actions Guide for general action concepts
Gets the value of an argument provided to the input.
Returns the argument value if found, or nil
if not found. Arguments are
validated and cast according to the action's argument definitions when set.
Examples
# Get an argument that exists
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:send_email, %{recipient: "user@example.com"})
...> |> Ash.ActionInput.get_argument(:recipient)
"user@example.com"
# Get an argument that doesn't exist returns nil
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:send_email, %{})
...> |> Ash.ActionInput.get_argument(:missing_arg)
nil
# Arguments can be accessed by string or atom key
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:example, %{"message" => "hello"})
...> |> Ash.ActionInput.get_argument(:message)
"hello"
See also
fetch_argument/2
for safer argument access with explicit error handlingset_argument/3
for setting argument valuesfor_action/4
for providing initial arguments
@spec new(Ash.Resource.t(), Ash.Domain.t()) :: t()
Creates a new action input from a resource.
This creates a basic action input struct that can be used as a starting point
for building inputs for generic actions. Use for_action/4
to create an input
bound to a specific action.
Examples
# Create a new action input for a resource
iex> Ash.ActionInput.new(MyApp.Post)
%Ash.ActionInput{resource: MyApp.Post, domain: nil, ...}
# Usually you'll want to use for_action/4 instead
iex> MyApp.Post |> Ash.ActionInput.for_action(:send_notification, %{message: "Hello"})
%Ash.ActionInput{action: %{name: :send_notification}, arguments: %{message: "Hello"}, ...}
See also
for_action/4
for creating action-specific inputsset_argument/3
for adding argumentsset_context/2
for adding context
Sets an argument value on the action input.
The argument value is validated and cast according to the action's argument definition. If validation fails, errors will be added to the input and it will be marked as invalid.
Examples
# Set a simple argument
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:send_notification, %{})
...> |> Ash.ActionInput.set_argument(:message, "Hello World")
...> |> Ash.ActionInput.get_argument(:message)
"Hello World"
# Set multiple arguments by chaining
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:complex_action, %{})
...> |> Ash.ActionInput.set_argument(:priority, :high)
...> |> Ash.ActionInput.set_argument(:batch_size, 100)
iex> Ash.ActionInput.get_argument(input, :priority)
:high
# Arguments are validated according to type
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:schedule_job, %{})
...> |> Ash.ActionInput.set_argument(:run_at, ~U[2024-01-01 10:00:00Z])
iex> input.valid?
true
See also
get_argument/2
for retrieving argument valuesset_private_argument/3
for setting private argumentsfor_action/4
for providing initial arguments
Deep merges the provided map into the input context.
Context is used to pass additional information through the action pipeline that can be accessed by action implementations, changes, and validations. The context is merged deeply, so nested maps will be combined rather than replaced.
Do not use the private
key in your custom context, as that is reserved for
internal use.
Examples
# Set simple context values
iex> MyApp.Post
...> |> Ash.ActionInput.new()
...> |> Ash.ActionInput.set_context(%{source: "api", user_id: 123})
...> |> then(& &1.context.source)
"api"
# Context is merged deeply
iex> input = MyApp.Post
...> |> Ash.ActionInput.new()
...> |> Ash.ActionInput.set_context(%{metadata: %{version: 1}})
...> |> Ash.ActionInput.set_context(%{metadata: %{trace_id: "abc123"}})
iex> input.context.metadata
%{version: 1, trace_id: "abc123"}
# Use context in action implementations
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:process_data, %{data: "test"})
...> |> Ash.ActionInput.set_context(%{
...> request_id: "req_456",
...> feature_flags: %{new_algorithm: true}
...> })
See also
for_action/4
for setting context when creating inputs- Action implementations can access context for custom logic
set_tenant/2
for tenant-specific context
Sets a private argument value on the action input.
Private arguments are not exposed in the public API and can only be set
internally. This function will only work for arguments marked as public?: false
in the action definition.
Examples
# Set a private argument (assuming :internal_flag is private)
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:example, %{})
...> |> Ash.ActionInput.set_private_argument(:internal_flag, true)
...> |> Ash.ActionInput.get_argument(:internal_flag)
true
# Attempting to set a public argument as private will error
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:example, %{})
...> |> Ash.ActionInput.set_private_argument(:public_arg, "value")
iex> input.valid?
false
# Use in action implementations for internal state
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:complex_workflow, %{data: "user_data"})
...> |> Ash.ActionInput.set_private_argument(:workflow_step, 1)
See also
set_argument/3
for setting public argumentsget_argument/2
for retrieving argument values- Action argument definitions with
public?: false
@spec set_tenant(t(), Ash.ToTenant.t()) :: t()
Sets the tenant to use when calling the action.
In multitenant applications, this configures which tenant's data the action should operate on. The tenant value is used for data isolation and access control.
Examples
# Set tenant using a string identifier
iex> MyApp.Post
...> |> Ash.ActionInput.new()
...> |> Ash.ActionInput.set_tenant("org_123")
...> |> then(& &1.tenant)
"org_123"
# Set tenant using a struct that implements Ash.ToTenant
iex> org = %MyApp.Organization{id: 456}
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:send_notification, %{})
...> |> Ash.ActionInput.set_tenant(org)
iex> input.tenant == org
true
# Use with action execution
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:cleanup, %{})
...> |> Ash.ActionInput.set_tenant("tenant_456")
...> |> Ash.run_action()
See also
for_action/4
for setting tenant when creating inputsAsh.ToTenant
protocol for custom tenant conversionset_context/2
for adding tenant to action context