improvement: support omitting generic action return types

This commit is contained in:
Zach Daniel 2024-04-10 13:46:44 -04:00
parent a5074d9d2f
commit bae263ca12
7 changed files with 40 additions and 7 deletions

View file

@ -912,7 +912,7 @@ end
## actions.action
```elixir
action name, returns
action name, returns \\ nil
```

View file

@ -14,6 +14,20 @@ 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
@ -51,7 +65,7 @@ action :priority, :integer do
end
```
> #### Returning resource instances {: .tip}
> #### 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

View file

@ -1261,7 +1261,7 @@ defmodule Ash do
"""
@doc spark_opts: [{1, @run_action_opts}]
@spec run_action(input :: Ash.ActionInput.t(), opts :: Keyword.t()) ::
{:ok, term} | {:error, Ash.Error.t()}
:ok | {:ok, term} | {:error, Ash.Error.t()}
def run_action(input, opts \\ []) do
Ash.Helpers.expect_options!(opts)
domain = Ash.Helpers.domain!(input, opts)

View file

@ -123,8 +123,15 @@ defmodule Ash.Actions.Action do
case authorize(domain, opts[:actor], input) do
:ok ->
case call_run_function(module, input, run_opts, context, false) do
:ok when is_nil(input.action.returns) ->
:ok
{:ok, result} ->
{:ok, result}
if input.action.returns do
{:ok, result}
else
:ok
end
{:ok, result, notifications} ->
remaining = Ash.Notifier.notify(notifications)
@ -133,7 +140,11 @@ defmodule Ash.Actions.Action do
resource_notifications: remaining
})
{:ok, result}
if input.action.returns do
{:ok, result}
else
:ok
end
{:error, error} ->
{:error, error}

View file

@ -24,13 +24,17 @@ defmodule Ash.Resource.Actions.Action do
touches_resources: [Ash.Resource.t()],
constraints: Keyword.t(),
run: {module, Keyword.t()},
returns: Ash.Type.t(),
returns: Ash.Type.t() | nil,
primary?: boolean,
transaction?: boolean
}
import Ash.Resource.Actions.SharedOptions
def transform(%{returns: nil} = action) do
{:ok, action}
end
def transform(%{returns: original_type, constraints: constraints} = thing) do
type = Ash.Type.get_type(original_type)

View file

@ -495,7 +495,7 @@ defmodule Ash.Resource.Dsl do
@action_argument
]
],
args: [:name, :returns]
args: [:name, {:optional, :returns}]
}
@create %Spark.Dsl.Entity{

View file

@ -36,6 +36,10 @@ defmodule Ash.Test.Actions.GenericActionsTest do
{:ok, "Hello #{input.arguments.name}"}
end)
end
action :do_nothing do
run fn _ -> :ok end
end
end
attributes do