lib/ash/resource/transformers/validate_eager_identities.ex

defmodule Ash.Resource.Transformers.ValidateEagerIdentities do
  @moduledoc """
  Confirms that eager identities are not declared on a resource with no primary read.
  """
  use Ash.Dsl.Transformer

  alias Ash.Dsl.Transformer
  alias Ash.Error.Dsl.DslError

  def after_compile?, do: true

  def transform(resource, dsl_state) do
    primary_read = Ash.Resource.Info.primary_action(resource, :read)

    dsl_state
    |> Transformer.get_entities([:identities])
    |> Enum.filter(&(&1.eager_check_with || &1.pre_check_with))
    |> case do
      [] ->
        {:ok, dsl_state}

      eager ->
        if primary_read do
          non_attributes =
            Enum.filter(eager, fn identity ->
              Enum.any?(identity.keys, &(!Ash.Resource.Info.attribute(resource, &1)))
            end)

          case non_attributes do
            [] ->
              {:ok, dsl_state}

            [identity] ->
              {:error,
               DslError.exception(
                 module: resource,
                 path: [:identities, identity.name],
                 message:
                   "Identity #{identity.name} is declared with `eager_check_with` or `pre_check_with` but not all of the `keys` are attributes."
               )}

            identities ->
              {:error,
               DslError.exception(
                 module: resource,
                 path: [:identities],
                 message:
                   "Identities #{Enum.map_join(identities, ",", & &1.name)} are declared with `eager_check_with` or `pre_check_with` but not all of the `keys` are attributes."
               )}
          end
        else
          names = Enum.map(eager, & &1.name)

          case names do
            [name] ->
              {:error,
               DslError.exception(
                 module: resource,
                 path: [:identities, name],
                 message:
                   "Identity #{name} is declared with `eager_check_with` or `pre_check_with` but the resource has no primary read action."
               )}

            names ->
              {:error,
               DslError.exception(
                 module: resource,
                 path: [:identities],
                 message:
                   "Identities #{Enum.join(names, ",")} are declared with `eager_check_with` or `pre_check_with` but the resource has no primary read action."
               )}
          end
        end
    end
  end
end