lib/ash/resource/change/get_and_lock.ex

defmodule Ash.Resource.Change.GetAndLock do
  @moduledoc """
  Refetches the record being updated or destroyed, and locks it with the given type.
  """
  use Ash.Resource.Change

  @impl true
  def change(changeset, opts, context) do
    Ash.Changeset.before_action(changeset, fn changeset ->
      primary_key = Ash.Resource.Info.primary_key(changeset.resource)
      pkey_values = changeset.data |> Map.take(primary_key) |> Map.to_list()

      case Ash.get(
             changeset.resource,
             pkey_values,
             domain: changeset.domain,
             tracer: context.tracer,
             tenant: context.tenant,
             authorize?: false,
             lock: opts[:lock]
           ) do
        {:ok, record} ->
          %{changeset | data: record}

        {:error, error} ->
          Ash.Changeset.add_error(changeset, error)
      end
    end)
  end

  @impl true
  def atomic(_changeset, opts, _) do
    if opts[:skip_atomic?] do
      :ok
    else
      {:not_atomic,
       """
       Cannot lock during an atomic update. If no lock is necessary in an atomic situation,
       add `skip_atomic_?: true`, i.e `change get_and_lock(#{opts[:lock]}, skip_atomic?: true)`
       """}
    end
  end
end