defmodule Mv.EmailSync.Loader do @moduledoc """ Helper functions for loading linked records in email synchronization. Centralizes the logic for retrieving related User/Member entities. ## Authorization-independent link checks All functions use the **system actor** for the load. Link existence (linked vs not linked) is therefore determined **independently of the current request actor**. This is required so that validations (e.g. `EmailChangePermission`, `EmailNotUsedByOtherUser`) can correctly decide "member is linked" even when the current user would not have read permission on the related User. Using the request actor would otherwise allow treating a linked member as unlinked and bypass the permission rule. """ alias Mv.Helpers alias Mv.Helpers.SystemActor @doc """ Loads the member linked to a user, returns nil if not linked or on error. Uses system actor for authorization to ensure email sync always works. """ def get_linked_member(user) def get_linked_member(%{member_id: nil}), do: nil def get_linked_member(%{member_id: id}) do system_actor = SystemActor.get_system_actor() opts = Helpers.ash_actor_opts(system_actor) case Ash.get(Mv.Membership.Member, id, opts) do {:ok, member} -> member {:error, _} -> nil end end @doc """ Loads the user linked to a member, returns nil if not linked or on error. Uses system actor for authorization to ensure email sync always works. """ def get_linked_user(member) do system_actor = SystemActor.get_system_actor() opts = Helpers.ash_actor_opts(system_actor) case Ash.load(member, :user, opts) do {:ok, %{user: user}} -> user {:error, _} -> nil end end @doc """ Loads the user linked to a member, returning an error tuple if not linked. Useful when a link is required for the operation. Uses system actor for authorization to ensure email sync always works. """ def load_linked_user!(member) do system_actor = SystemActor.get_system_actor() opts = Helpers.ash_actor_opts(system_actor) case Ash.load(member, :user, opts) do {:ok, %{user: user}} when not is_nil(user) -> {:ok, user} {:ok, _} -> {:error, :no_linked_user} {:error, _} = error -> error end end end