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,
api: relationship.api,
source_attribute: relationship.source_attribute,
private?: true
]
|> 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