mirror of
https://github.com/ash-project/ash.git
synced 2024-09-22 06:23:04 +12:00
80 lines
2.6 KiB
Markdown
80 lines
2.6 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.
|
|
|
|
> ### No return? No problem! {: .tip}
|
|
>
|
|
> Generic actions can omit a return type, in which case running them returns `:ok` if successful.
|
|
>
|
|
> ```elixir
|
|
> action :schedule_job do
|
|
> argument :job_name, :string, allow_nil?: false
|
|
> run fn input ->
|
|
> # Schedule the job
|
|
> :ok
|
|
> end
|
|
> end
|
|
> ```
|
|
|
|
## 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 {: .info}
|
|
>
|
|
> 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. 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
|
|
> ```
|