who_need_help/lib/who_need_help/email_delivery.ex

73 lines
2.0 KiB
Elixir

defmodule WhoNeedHelp.EmailDelivery do
@moduledoc """
Delivers email while emitting low-cardinality, purpose-specific telemetry.
Telemetry metadata contains only fixed type and outcome labels. Recipients,
subjects, bodies, references, and other personal data are never attached.
"""
alias WhoNeedHelp.EmailAddress
alias WhoNeedHelp.Mailer
@kinds [
:auth_email_change,
:auth_google_link,
:auth_login,
:auth_registration,
:content_removal_confirmation,
:content_removal_operator,
:content_removal_received,
:content_removal_update,
:support_confirmation,
:support_operator,
:support_update
]
@spec deliver(Swoosh.Email.t(), atom()) :: {:ok, term()} | {:error, term()}
def deliver(%Swoosh.Email{} = email, kind) when kind in @kinds do
started_at = System.monotonic_time()
if valid_envelope?(email) do
try do
result = Mailer.deliver(email)
emit(started_at, kind, result_status(result))
result
rescue
exception ->
emit(started_at, kind, :exception)
reraise exception, __STACKTRACE__
catch
class, reason ->
emit(started_at, kind, :exception)
:erlang.raise(class, reason, __STACKTRACE__)
end
else
emit(started_at, kind, :error)
{:error, :invalid_email_address}
end
end
def kinds, do: @kinds
defp result_status({:ok, _metadata}), do: :ok
defp result_status({:error, _reason}), do: :error
defp valid_envelope?(email) do
[email.from, email.reply_to | email.to ++ email.cc ++ email.bcc]
|> List.flatten()
|> Enum.reject(&is_nil/1)
|> Enum.all?(fn
{_name, address} -> EmailAddress.valid?(address)
_invalid_recipient -> false
end)
end
defp emit(started_at, kind, status) do
:telemetry.execute(
[:who_need_help, :email, :delivery],
%{duration: System.monotonic_time() - started_at},
%{kind: to_string(kind), status: to_string(status)}
)
end
end