93 lines
3 KiB
Elixir
93 lines
3 KiB
Elixir
defmodule Mv.EmailSync.Helpers do
|
|
@moduledoc """
|
|
Shared helper functions for email synchronization between User and Member.
|
|
|
|
Handles the complexity of `around_transaction` callback results and
|
|
provides clean abstractions for email updates within transactions.
|
|
"""
|
|
|
|
require Logger
|
|
import Ecto.Changeset
|
|
|
|
@doc """
|
|
Extracts the record from an Ash action result.
|
|
|
|
Handles both 2-tuple `{:ok, record}` and 4-tuple
|
|
`{:ok, record, changeset, notifications}` patterns.
|
|
"""
|
|
def extract_record({:ok, record, _changeset, _notifications}), do: {:ok, record}
|
|
def extract_record({:ok, record}), do: {:ok, record}
|
|
def extract_record({:error, _} = error), do: error
|
|
|
|
@doc """
|
|
Updates the result with a new record while preserving the original structure.
|
|
|
|
If the original result was a 4-tuple, returns a 4-tuple with the updated record.
|
|
If it was a 2-tuple, returns a 2-tuple with the updated record.
|
|
"""
|
|
def update_result_record({:ok, _old_record, changeset, notifications}, new_record) do
|
|
{:ok, new_record, changeset, notifications}
|
|
end
|
|
|
|
def update_result_record({:ok, _old_record}, new_record) do
|
|
{:ok, new_record}
|
|
end
|
|
|
|
@doc """
|
|
Updates an email field directly via Ecto within the current transaction.
|
|
|
|
This bypasses Ash's action system to ensure the update happens in the
|
|
same database transaction as the parent action.
|
|
"""
|
|
def update_email_via_ecto(record, new_email) do
|
|
record
|
|
|> cast(%{email: to_string(new_email)}, [:email])
|
|
|> Mv.Repo.update()
|
|
end
|
|
|
|
@doc """
|
|
Synchronizes email to a linked record if it exists.
|
|
|
|
Returns the original result unchanged, or an error if sync fails.
|
|
"""
|
|
def sync_email_to_linked_record(result, linked_record, new_email) do
|
|
with {:ok, _source} <- extract_record(result),
|
|
record when not is_nil(record) <- linked_record,
|
|
{:ok, _updated} <- update_email_via_ecto(record, new_email) do
|
|
# Successfully synced - return original result unchanged
|
|
result
|
|
else
|
|
nil ->
|
|
# No linked record - return original result
|
|
result
|
|
|
|
{:error, error} ->
|
|
# Sync failed - log and propagate error to rollback transaction
|
|
Logger.error("Email sync failed: #{inspect(error)}")
|
|
{:error, error}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Overrides the record's email with the linked email if emails differ.
|
|
|
|
Returns updated result with new record, or original result if no update needed.
|
|
"""
|
|
def override_with_linked_email(result, linked_email) do
|
|
with {:ok, record} <- extract_record(result),
|
|
true <- record.email != to_string(linked_email),
|
|
{:ok, updated_record} <- update_email_via_ecto(record, linked_email) do
|
|
# Email was different - return result with updated record
|
|
update_result_record(result, updated_record)
|
|
else
|
|
false ->
|
|
# Emails already match - no update needed
|
|
result
|
|
|
|
{:error, error} ->
|
|
# Override failed - log and propagate error
|
|
Logger.error("Email override failed: #{inspect(error)}")
|
|
{:error, error}
|
|
end
|
|
end
|
|
end
|