lib/balance/transformers/add_structure.ex

defmodule AshDoubleEntry.Balance.Transformers.AddStructure do
  @moduledoc """
  Adds all the structure required for the resource. See the getting started guide for more.
  """
  use Spark.Dsl.Transformer
  import Spark.Dsl.Builder

  def before?(Ash.Resource.Transformers.CachePrimaryKey), do: true
  def before?(_), do: false

  def transform(dsl) do
    dsl
    |> Ash.Resource.Builder.add_attribute(:id, :uuid,
      primary_key?: true,
      writable?: false,
      generated?: true,
      allow_nil?: false,
      default: &Ash.UUID.generate/0
    )
    |> Ash.Resource.Builder.add_attribute(:balance, :decimal, allow_nil?: false)
    |> Ash.Resource.Builder.add_relationship(
      :belongs_to,
      :transfer,
      AshDoubleEntry.Balance.Info.balance_transfer_resource!(dsl),
      attribute_type: AshDoubleEntry.ULID,
      allow_nil?: false,
      attribute_writable?: true
    )
    |> Ash.Resource.Builder.add_relationship(
      :belongs_to,
      :account,
      AshDoubleEntry.Balance.Info.balance_account_resource!(dsl),
      allow_nil?: false,
      attribute_writable?: true
    )
    |> add_primary_read_action()
    |> Ash.Resource.Builder.add_action(:create, :upsert_balance,
      accept: [:balance, :account_id, :transfer_id],
      upsert?: true,
      upsert_identity: :unique_references
    )
    |> Ash.Resource.Builder.add_identity(:unique_references, [:account_id, :transfer_id],
      pre_check_with: pre_check_with(dsl)
    )
  end

  defbuilder add_primary_read_action(dsl) do
    if Ash.Resource.Info.primary_action(dsl, :read) do
      {:ok, dsl}
    else
      Ash.Resource.Builder.add_action(dsl, :read, :_autogenerated_primary_read,
        primary?: true,
        pagination: Ash.Resource.Builder.build_pagination(keyset?: true)
      )
    end
  end

  defp pre_check_with(dsl) do
    case AshDoubleEntry.Balance.Info.balance_pre_check_identities_with(dsl) do
      :error ->
        nil

      {:ok, value} ->
        value
    end
  end
end