defmodule AshPaperTrail.ChangeBuilders.FullDiff.UnionChange do
@moduledoc """
A non-embedded union attribute change will be represented as a map:
%{ to: nil }
%{ to: %{value: value, type: type } }
%{ from: %{value: value, type: type }, to: %{value: value, type: type } }
%{ unchanged: %{value: value, type: type } }
If the from & to are embedded resources with the same primary key
then, we'll have consider it changed and represent it as:
%{ changed: %{type: type, updated: %{ ...attributes... } } }
If the union value is an embedded resource the `value` key will be replaced with
created, unchanged, updated, destroyed.
%{ from: nil, created: %{type: type, value: %{ ...attributes... } } }
%{ unchanged: %{type: type, value: %{ ...attributes... } } }
%{ updated: %{type: type, value: %{ ...attributes... } } }
%{ from: %{type: type, value: value}, created: %{type: type, value: %{ ...attributes... } }
%{ destroyed: %{type: type, value: %{ ...attributes... } }, to: nil }
%{ destroyed: %{type: type, value: %{ ...attributes... } }, created: %{type: type, value: %{ ...attributes... } } }
%{ destroyed: %{type: type, destroyed: %{ ...attributes... } }, to: %{type: type, value: value } }
"""
import AshPaperTrail.ChangeBuilders.FullDiff.Helpers
def build(attribute, changeset) do
dump_union_data_value(changeset, attribute)
|> union_change_map()
end
# Returns two tuples for the data and value. Each tuple contains:
# { present_or_embeddedness, type, value }
defp dump_union_data_value(changeset, attribute) do
data_tuple =
if changeset.action_type == :create do
:not_present
else
data = Ash.Changeset.get_data(changeset, attribute.name)
dump_union_type_value(data, attribute)
end
value_tuple =
case Ash.Changeset.fetch_change(changeset, attribute.name) do
{:ok, value} ->
dump_union_type_value(value, attribute)
:error ->
:not_present
end
{data_tuple, value_tuple}
end
# Returns a tuple {embedded, type, value}
def dump_union_type_value(nil, _attribute), do: {:non_embedded, nil, nil}
def dump_union_type_value(value, attribute) do
%{"type" => type, "value" => dumped_value} = dump_value(value, attribute)
if embedded_union?(attribute.type, type) do
uid = unique_id(value, dumped_value)
{:embedded, type, uid, dumped_value}
else
{:non_embedded, type, dumped_value}
end
end
end