refactor: email sync changes

This commit is contained in:
Moritz 2025-10-17 14:33:25 +02:00
parent 39afaf3999
commit 7522724945
Signed by: moritz
GPG key ID: 1020A035E5DD0824
5 changed files with 102 additions and 138 deletions

View file

@ -1,45 +1,30 @@
defmodule Mv.Membership.Member.Changes.OverrideEmailFromUserOnLink do
@moduledoc """
Overrides member email with user email when linking a member to a user.
When a member is linked to a user (either during creation or update),
this change ensures that the member's email is updated to match the user's email.
Overrides member email with user email when linking.
User.email is the source of truth when a link is established.
Uses `around_transaction` to guarantee atomicity - both the member
creation/update and email override happen in the SAME database transaction.
"""
use Ash.Resource.Change
alias Mv.EmailSync.Helpers
alias Mv.EmailSync.{Helpers, Loader}
@impl true
def change(changeset, _opts, context) do
# Skip if already syncing to avoid recursion
if Map.get(context, :syncing_email, false) do
changeset
else
# 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, member} <- Helpers.extract_record(result),
{:ok, user} <- load_linked_user(member) do
Helpers.override_with_linked_email(result, user.email)
else
_ -> result
end
end)
override_email(changeset)
end
end
# Load the linked user, returning error tuple if not linked
defp load_linked_user(member) do
case Ash.load(member, :user) do
{:ok, %{user: user}} when not is_nil(user) -> {:ok, user}
{:ok, _} -> {:error, :no_linked_user}
{:error, _} = error -> error
end
defp override_email(changeset) do
Ash.Changeset.around_transaction(changeset, fn cs, callback ->
result = callback.(cs)
with {:ok, member} <- Helpers.extract_record(result),
{:ok, user} <- Loader.load_linked_user!(member) do
Helpers.override_with_linked_email(result, user.email)
else
_ -> result
end
end)
end
end

View file

@ -1,53 +1,32 @@
defmodule Mv.Membership.Member.Changes.SyncEmailToUser do
@moduledoc """
Synchronizes member email changes to the linked user.
When a member's email is updated and the member is linked to a user,
this change automatically updates the user's email to match.
This ensures bidirectional email synchronization.
Uses `around_transaction` to guarantee atomicity - both the member
and user updates happen in the SAME database transaction.
Uses `around_transaction` for atomicity - both updates in the same transaction.
"""
use Ash.Resource.Change
alias Mv.EmailSync.Helpers
alias Mv.EmailSync.{Helpers, Loader}
@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, member} <- Helpers.extract_record(result),
linked_user <- get_linked_user(member) do
Helpers.sync_email_to_linked_record(result, linked_user, new_email)
else
_ -> result
end
end)
Map.get(context, :syncing_email, false) -> changeset
not Ash.Changeset.changing_attribute?(changeset, :email) -> changeset
true -> sync_email(changeset)
end
end
# Load the linked user relationship (returns nil if not linked)
defp get_linked_user(member) do
case Ash.load(member, :user) do
{:ok, %{user: user}} -> user
{:error, _} -> nil
end
defp sync_email(changeset) do
new_email = Ash.Changeset.get_attribute(changeset, :email)
Ash.Changeset.around_transaction(changeset, fn cs, callback ->
result = callback.(cs)
with {:ok, member} <- Helpers.extract_record(result),
linked_user <- Loader.get_linked_user(member) do
Helpers.sync_email_to_linked_record(result, linked_user, new_email)
else
_ -> result
end
end)
end
end