add action changes for email sync

This commit is contained in:
Moritz 2025-10-16 17:51:31 +02:00
parent dbe1df73fd
commit 6a397c962d
Signed by: moritz
GPG key ID: 1020A035E5DD0824
7 changed files with 331 additions and 0 deletions

View file

@ -0,0 +1,55 @@
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