mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
70 lines
2.3 KiB
Markdown
70 lines
2.3 KiB
Markdown
|
# 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
|
||
|
> set your action's return type to the name of your resource. Unfortunately
|
||
|
> this will result in a compile error as the resource struct is not yet defined
|
||
|
> at the time of DSL transformation. The work around is to define an action
|
||
|
> that returns `:struct` and is constrained to only be of a specific type, eg:
|
||
|
>
|
||
|
> ```elixir
|
||
|
> action :get, :struct do
|
||
|
> constraints instance_of: __MODULE__
|
||
|
>
|
||
|
> run # ...
|
||
|
> end
|
||
|
> ```
|