ash/documentation/topics/actions/generic-actions.md

67 lines
2.3 KiB
Markdown
Raw Normal View History

# Generic Actions
Generic actions are so named because there are no special rules about how they work. A generic action takes arguments and returns a value. The struct used for building input for a generic action is `Ash.ActionInput`.
```elixir
action :say_hello, :string do
argument :name, :string, allow_nil?: false
run fn input, _ ->
{:ok, "Hello: #{input.arguments.name}"}
end
end
```
A generic action declares its arguments, return type, and implementation, as illustrated above.
## Why use generic actions?
The example above could be written as a normal function in elixir, i.e
```elixir
def say_hello(name), do: "Hello: #{name}"
```
The benefit of using generic actions instead of defining normal functions:
- They can be used with api extensions like `ash_json_api` and `ash_graphql`
- Their inputs are type checked and casted
- They support Ash authorization patterns (i.e policies)
- They can be included in the code interface of a resource
- They can be made transactional with a single option (`transaction? true`)
If you don't need any of the above, then there is no problem with writing regular Elixir functions!
## Return types and constraints
Generic actions do not cast their return types. It is expected that the action return a valid value for the type that they declare. However, declaring additional constraints can inform API usage, and make the action more clear. For example:
```elixir
action :priority, :integer do
constraints [min: 1, max: 3]
argument :status, :atom, constraints: [one_of: [:high, :medium, :low]]
run fn input, _ ->
case input.arguments.status do
:high -> {:ok, 3}
:medium -> {:ok, 2}
:low -> {:ok, 1}
end
end
end
```
> #### Returning resource instances {: .tip}
>
> It sometimes happens that you want to make a generic action which returns an
> instance or instances of the resource. It's natural to assume that you can
2024-04-08 00:29:23 +12:00
> set your action's return type to the name of your resource. This won't work
> as resources do not define a type, unless they are embedded. In embedded resources, this won't work because the module is still being compiled, so referencing yourself as a type causes a compile error. Instead, use the `:struct` type and the `instance_of` constraint, like so:
> ```elixir
> action :get, :struct do
> constraints instance_of: __MODULE__
>
> run # ...
> end
> ```