defmodule Mv.Membership.Member.Changes.UnrelateUserWhenArgumentNil do @moduledoc """ When :user argument is present and nil/empty on update_member, unrelate the current user. With on_missing: :ignore, manage_relationship does not unrelate when input is nil/[]. This change handles explicit unlink (user: nil or user: %{}) by updating the linked User to set member_id = nil. Only runs when the argument key is present (policy ForbidMemberUserLinkUnlessAdmin ensures only admins can pass :user). """ use Ash.Resource.Change @spec change(Ash.Changeset.t(), keyword(), Ash.Resource.Change.context()) :: Ash.Changeset.t() def change(changeset, _opts, _context) do if unlink_requested?(changeset) do unrelate_current_user(changeset) else changeset end end defp unlink_requested?(changeset) do args = changeset.arguments || %{} if Map.has_key?(args, :user) or Map.has_key?(args, "user") do user_arg = Ash.Changeset.get_argument(changeset, :user) user_arg == nil or (is_map(user_arg) and map_size(user_arg) == 0) else false end end defp unrelate_current_user(changeset) do member = changeset.data actor = Map.get(changeset.context || %{}, :actor) case Ash.load(member, :user, domain: Mv.Membership, authorize?: false) do {:ok, %{user: user}} when not is_nil(user) -> # User's :update action only accepts [:email]; use :update_user so # manage_relationship(:member, ..., on_missing: :unrelate) runs and clears member_id. user |> Ash.Changeset.for_update(:update_user, %{member: nil}, domain: Mv.Accounts) |> Ash.update(domain: Mv.Accounts, actor: actor, authorize?: false) changeset _ -> changeset end end end