lib/ash/resource/transformers/create_join_relationship.ex

defmodule Ash.Resource.Transformers.CreateJoinRelationship do
  @moduledoc """
  Creates an automatically named `has_many` relationship for each many_to_many.

  This will likely not be around for long, as our logic around many to many relationships
  will update soon.
  """
  use Spark.Dsl.Transformer

  alias Spark.Dsl.Transformer

  @extension Ash.Resource.Dsl

  def transform(dsl_state) do
    dsl_state
    |> Transformer.get_entities([:relationships])
    |> Enum.filter(&(&1.type == :many_to_many))
    |> Enum.reject(fn relationship ->
      dsl_state
      |> Transformer.get_entities([:relationships])
      |> Enum.find(&(&1.name == relationship.join_relationship))
    end)
    |> Enum.reduce({:ok, dsl_state}, fn relationship, {:ok, dsl_state} ->
      autogenerated_join_relationship_of = relationship.name

      {:ok, relationship} =
        Transformer.build_entity(
          @extension,
          [:relationships],
          :has_many,
          [
            name: relationship.join_relationship,
            destination: relationship.through,
            destination_attribute: relationship.source_attribute_on_join_resource,
            api: relationship.api,
            source_attribute: relationship.source_attribute,
            private?: true
          ]
          |> add_messages(relationship)
        )

      relationship =
        Map.put(
          relationship,
          :autogenerated_join_relationship_of,
          autogenerated_join_relationship_of
        )

      {:ok, Transformer.add_entity(dsl_state, [:relationships], relationship)}
    end)
  end

  defp add_messages(opts, relationship) do
    new_opts =
      [
        not_found_message: relationship.not_found_message,
        violation_message: relationship.violation_message
      ]
      |> Enum.reject(fn {_, v} ->
        is_nil(v)
      end)

    Keyword.merge(opts, new_opts)
  end

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