fix: properly rollback transactions on returned errors in generic actions

This commit is contained in:
Zach Daniel 2023-10-06 13:39:05 -04:00
parent b8be1123ba
commit 49042b737d
2 changed files with 23 additions and 11 deletions

View file

@ -57,8 +57,9 @@ defmodule Ash.Actions.Action do
end end
try do try do
resources = Enum.reject(resources, &Ash.DataLayer.in_transaction?/1)
resources resources
|> Enum.reject(&Ash.DataLayer.in_transaction?/1)
|> Ash.DataLayer.transaction( |> Ash.DataLayer.transaction(
fn -> fn ->
case authorize(api, opts[:actor], input) do case authorize(api, opts[:actor], input) do
@ -67,12 +68,15 @@ defmodule Ash.Actions.Action do
{:ok, result} -> {:ok, result} ->
{:ok, result, []} {:ok, result, []}
{:error, error} ->
Ash.DataLayer.rollback(resources, error)
other -> other ->
other raise_invalid_manual_action_return!(input, other)
end end
{:error, error} -> {:error, error} ->
{:error, error} Ash.DataLayer.rollback(resources, error)
end end
end, end,
nil, nil,
@ -135,13 +139,7 @@ defmodule Ash.Actions.Action do
{:error, error} {:error, error}
other -> other ->
raise """ raise_invalid_manual_action_return!(input, other)
Invalid return from generic action #{input.resource}.#{input.action.name}.
Expected {:ok, result} or {:error, error}, got:
#{inspect(other)}
"""
end end
{:error, error} -> {:error, error} ->
@ -152,6 +150,16 @@ defmodule Ash.Actions.Action do
end end
end end
defp raise_invalid_manual_action_return!(input, other) do
raise """
Invalid return from generic action #{input.resource}.#{input.action.name}.
Expected {:ok, result} or {:error, error}, got:
#{inspect(other)}
"""
end
defp authorize(api, actor, input) do defp authorize(api, actor, input) do
input.resource input.resource
|> Ash.Resource.Info.authorizers() |> Ash.Resource.Info.authorizers()

View file

@ -350,7 +350,11 @@ defmodule Ash.DataLayer do
end end
@doc "Rolls back the current transaction" @doc "Rolls back the current transaction"
@spec rollback(Ash.Resource.t(), term) :: no_return @spec rollback(Ash.Resource.t() | list(Ash.Resource.t()), term) :: no_return
def rollback([resource | _], term) do
rollback(resource, term)
end
def rollback(resource, term) do def rollback(resource, term) do
data_layer(resource).rollback(resource, term) data_layer(resource).rollback(resource, term)
end end