55 lines
1.7 KiB
Elixir
55 lines
1.7 KiB
Elixir
defmodule Mv.Accounts.User.Changes.SyncEmailToMember do
|
|
@moduledoc """
|
|
Synchronizes user email changes to the linked member.
|
|
|
|
When a user's email is updated and the user is linked to a member,
|
|
this change automatically updates the member's email to match.
|
|
|
|
This ensures bidirectional email synchronization with User.email
|
|
as the source of truth.
|
|
|
|
Uses `around_transaction` to guarantee atomicity - both the user
|
|
and member updates happen in the SAME database transaction.
|
|
"""
|
|
use Ash.Resource.Change
|
|
alias Mv.EmailSync.Helpers
|
|
|
|
@impl true
|
|
def change(changeset, _opts, context) do
|
|
cond do
|
|
# Skip if already syncing to avoid recursion
|
|
Map.get(context, :syncing_email, false) ->
|
|
changeset
|
|
|
|
# Only proceed if email is actually changing
|
|
not Ash.Changeset.changing_attribute?(changeset, :email) ->
|
|
changeset
|
|
|
|
# Apply the sync logic
|
|
true ->
|
|
new_email = Ash.Changeset.get_attribute(changeset, :email)
|
|
|
|
# around_transaction receives the changeset (cs) from Ash
|
|
# and a callback that executes the actual database operation
|
|
Ash.Changeset.around_transaction(changeset, fn cs, callback ->
|
|
result = callback.(cs)
|
|
|
|
with {:ok, user} <- Helpers.extract_record(result),
|
|
linked_member <- get_linked_member(user) do
|
|
Helpers.sync_email_to_linked_record(result, linked_member, new_email)
|
|
else
|
|
_ -> result
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
defp get_linked_member(%{member_id: nil}), do: nil
|
|
|
|
defp get_linked_member(%{member_id: member_id}) do
|
|
case Ash.get(Mv.Membership.Member, member_id) do
|
|
{:ok, member} -> member
|
|
{:error, _} -> nil
|
|
end
|
|
end
|
|
end
|