defmodule Ash.Resource.Transformers.CreateJoinRelationship do @moduledoc """ Creates an automatically named `has_many` relationship for each many_to_many. """ use Spark.Dsl.Transformer alias Spark.{Dsl.Transformer, Error.DslError} @extension Ash.Resource.Dsl def transform(dsl_state) do dsl_state |> Transformer.get_entities([:relationships]) |> Enum.filter(&(&1.type == :many_to_many)) |> Enum.reduce_while({:ok, dsl_state}, fn relationship, {:ok, dsl_state} -> dsl_state |> Transformer.get_entities([:relationships]) |> Enum.find(&(&1.name == relationship.join_relationship)) |> case do nil when relationship.through == nil -> error = DslError.exception( path: [:relationships, relationship.name], message: "Either `through` or `join_relationship` with an existing relationship is required." ) {:halt, {:error, error}} nil -> {:ok, join_relationship} = Transformer.build_entity( @extension, [:relationships], :has_many, [ name: relationship.join_relationship, destination: relationship.through, destination_attribute: relationship.source_attribute_on_join_resource, domain: relationship.domain, source_attribute: relationship.source_attribute, public?: false ] |> add_messages(relationship) ) join_relationship = Map.put(join_relationship, :autogenerated_join_relationship_of, relationship.name) dsl_state = Transformer.add_entity(dsl_state, [:relationships], join_relationship) {:cont, {:ok, dsl_state}} join_relationship -> relationship = %{ relationship | through: join_relationship.destination, source_attribute: join_relationship.source_attribute, source_attribute_on_join_resource: join_relationship.destination_attribute } dsl_state = Transformer.replace_entity( dsl_state, [:relationships], relationship, &(&1.name == relationship.name) ) {:cont, {:ok, dsl_state}} end 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