diff --git a/lib/acts/begin.ex b/lib/acts/begin.ex index 2c6085a..13532b4 100644 --- a/lib/acts/begin.ex +++ b/lib/acts/begin.ex @@ -14,7 +14,7 @@ defmodule Bonfire.Ecto.Acts.Begin do # import Untangle @doc """ - Runs the given act within a transaction if no errors are detected in the epic. + Runs the given act(s) within a transaction if no errors are detected in the epic. This function takes the modules before the `Commit` module in the `epic.next` list, runs them, and then processes the remaining modules. If there are any errors in the epic, it avoids @@ -59,7 +59,12 @@ defmodule Bonfire.Ecto.Acts.Begin do %{epic | next: rest} true -> - maybe_debug(epic, act, "entering transaction") + if not repo().in_transaction?(), + do: maybe_debug(epic, act, "Begin: entering transaction"), + else: + IO.warn( + "We're already in a transaction, better avoid this and let Epics handle the transaction..." + ) repo().transact_with(fn -> epic = Epic.run(nested) diff --git a/lib/acts/delete.ex b/lib/acts/delete.ex index cf66712..8412c96 100644 --- a/lib/acts/delete.ex +++ b/lib/acts/delete.ex @@ -80,7 +80,7 @@ defmodule Bonfire.Ecto.Acts.Delete do case repo.maybe_preload(object, assoc) |> Map.get(assoc) do loaded when is_map(loaded) or (is_list(loaded) and loaded != []) -> - maybe_debug(epic, act, loaded, "adding association") + maybe_debug(epic, act, loaded, "adding association to delete") loaded |> mark_for_deletion() @@ -109,6 +109,10 @@ defmodule Bonfire.Ecto.Acts.Delete do end end + defp mark_for_deletion(objs) when is_list(objs) do + Enum.map(objs, &mark_for_deletion/1) + end + defp mark_for_deletion(obj) do obj |> Changeset.cast(%{}, []) diff --git a/lib/acts/work.ex b/lib/acts/work.ex index b3e1c30..f51e832 100644 --- a/lib/acts/work.ex +++ b/lib/acts/work.ex @@ -9,6 +9,7 @@ defmodule Bonfire.Ecto.Acts.Work do """ require Logger import Bonfire.Common.Utils + alias Bonfire.Common.Enums # alias Bonfire.Epics.Act alias Bonfire.Epics.Epic @@ -63,7 +64,6 @@ defmodule Bonfire.Ecto.Acts.Work do """ def run(epic, act) do - debug(act) # retrieve the list of keys to check keys = Map.get(epic.assigns, __MODULE__, []) # flatten them all into a keyword list @@ -86,9 +86,13 @@ defmodule Bonfire.Ecto.Acts.Work do epic true -> - maybe_debug(epic, act, "Entering transaction") repo = Bonfire.Common.Config.repo() + #  Note that usually a transaction was already opened in Begin, so as per https://hexdocs.pm/ecto/Ecto.Repo.html#c:transaction/2-nested-transactions this won't start a new one + if not repo.in_transaction?(), + do: maybe_debug(epic, act, "Work: Entering transaction"), + else: debug(act) + case repo.transact_with(fn -> run(epic, act, changesets, repo) end) do {:ok, epic} -> epic {:error, epic} -> epic @@ -116,9 +120,17 @@ defmodule Bonfire.Ecto.Acts.Work do Map.put(changeset, :action, nil) |> repo.upsert() + :delete when is_list(changeset) -> + maybe_debug(epic, act, key, "Deleting multiple changesets on #{repo} at") + + Enum.map(changeset, &repo.delete/1) + |> Enums.all_oks_or_error() + :delete -> maybe_debug(epic, act, key, "Deleting changeset on #{repo} at") - repo.delete(changeset) + + # Doesn't error if delete is stale. Defaults to false. This may happen if the struct has been deleted from the database before this deletion BUT ALSO if there is a rule or a trigger on the database that rejects the delete operation (FIXME: the latter seems undesirable in this case) + repo.delete(changeset, allow_stale: true) other when is_struct(changeset) or is_list(changeset) -> # FIXME: should only trigger this in delete epics!