Ash.ActionInput (ash v3.5.24)
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
Functions
Adds an error to the errors list and marks the action input as invalid.
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 t() :: %Ash.ActionInput{ action: Ash.Resource.Actions.Action.t() | nil, arguments: map(), context: map(), domain: Ash.Domain.t(), errors: term(), 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, and validation state. 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
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