defmodule Swoosh.Adapters.Mailtrap do
@provider_options_body_fields [
:custom_variables,
:category
]
@moduledoc ~s"""
An adapter that sends email using the Mailtrap API.
For reference: [Mailtrap API docs](https://api-docs.mailtrap.io/docs/mailtrap-api-docs/67f1d70aeb62c-send-email)
**This adapter requires an API Client.** Swoosh comes with Hackney and Finch out of the box.
See the [installation section](https://hexdocs.pm/swoosh/Swoosh.html#module-installation)
for details.
## Example
# config/config.exs
config :sample, Sample.Mailer,
adapter: Swoosh.Adapters.Mailtrap,
api_key: "my-api-key"
# lib/sample/mailer.ex
defmodule Sample.Mailer do
use Swoosh.Mailer, otp_app: :sample
end
## Sandbox mode
For [sandbox mode](https://api-docs.mailtrap.io/docs/mailtrap-api-docs/bcf61cdc1547e-send-email-early-access), use the following config:
# config/config.exs
config :sample, Sample.Mailer,
adapter: Swoosh.Adapters.Mailtrap,
api_key: "my-api-key",
sandbox_inbox_id: "111111"
## Using with provider options
import Swoosh.Email
new()
|> from({"Xu Shang-Chi", "xu.shangchi@example.com"})
|> to({"Katy", "katy@example.com"})
|> reply_to("xu.xialing@example.com")
|> cc("yingli@example.com")
|> cc({"Xu Wenwu", "xu.wenwu@example.com"})
|> bcc("yingnan@example.com")
|> bcc({"Jon Jon", "jonjon@example.com"})
|> subject("Hello, Ten Rings!")
|> html_body("<h1>Hello</h1>")
|> text_body("Hello")
|> put_provider_option(:custom_variables, %{
my_var: %{my_message_id: 123},
my_other_var: %{my_other_id: 1, stuff: 2}
})
|> put_provider_option(:category, "welcome")
## Provider Options
Supported provider options are the following:
#### Inserted into request body
* `:category` (string) - an email category
* `:custom_variables` (map) - a map contains fields
"""
use Swoosh.Adapter, required_config: [:api_key]
alias Swoosh.Email
@base_url "https://send.api.mailtrap.io"
@sandbox_base_url "https://sandbox.api.mailtrap.io"
@api_endpoint "/api/send"
@impl true
def deliver(%Email{} = email, config \\ []) do
headers = [
{"Content-Type", "application/json"},
{"User-Agent", "swoosh/#{Swoosh.version()}"},
{"Authorization", "Bearer #{config[:api_key]}"}
]
body = email |> prepare_body() |> Swoosh.json_library().encode!
url = prepare_url(config)
case Swoosh.ApiClient.post(url, headers, body, email) do
{:ok, code, _headers, body} when code >= 200 and code <= 399 ->
{:ok, %{ids: Swoosh.json_library().decode!(body)["message_ids"]}}
{:ok, code, _headers, body} when code >= 400 ->
case Swoosh.json_library().decode(body) do
{:ok, error} -> {:error, {code, error}}
{:error, _} -> {:error, {code, body}}
end
{:error, reason} ->
{:error, reason}
end
end
defp prepare_url(config) do
if config[:sandbox_inbox_id] do
base_url = config[:base_url] || @sandbox_base_url
[base_url, Path.join([@api_endpoint, config[:sandbox_inbox_id]])]
else
[base_url(config), @api_endpoint]
end
end
defp base_url(config),
do: config[:base_url] || @base_url
defp prepare_body(email) do
%{}
|> prepare_from(email)
|> prepare_to(email)
|> prepare_cc(email)
|> prepare_bcc(email)
|> prepare_subject(email)
|> prepare_text(email)
|> prepare_html(email)
|> prepare_attachments(email)
|> prepare_reply_to(email)
|> prepare_custom_headers(email)
|> prepare_provider_options_body_fields(email)
end
defp email_item({"", email}), do: %{email: email}
defp email_item({name, email}), do: %{email: email, name: name}
defp email_item(email), do: %{email: email}
defp reply_to_item({_, email}), do: email
defp reply_to_item(email), do: email
defp prepare_from(body, %{from: from}),
do: Map.put(body, :from, from |> email_item)
defp prepare_to(body, %{to: to}),
do: Map.put(body, :to, to |> Enum.map(&email_item(&1)))
defp prepare_cc(body, %{cc: []}), do: body
defp prepare_cc(body, %{cc: cc}),
do: Map.put(body, :cc, cc |> Enum.map(&email_item(&1)))
defp prepare_bcc(body, %{bcc: []}), do: body
defp prepare_bcc(body, %{bcc: bcc}),
do: Map.put(body, :bcc, bcc |> Enum.map(&email_item(&1)))
defp prepare_subject(body, %{subject: subject}),
do: Map.put(body, :subject, subject)
defp prepare_text(body, %{text_body: nil}), do: body
defp prepare_text(body, %{text_body: text}),
do: Map.put(body, :text, text)
defp prepare_html(body, %{html_body: nil}), do: body
defp prepare_html(body, %{html_body: html}),
do: Map.put(body, :html, html)
defp prepare_attachments(body, %{attachments: []}), do: body
defp prepare_attachments(body, %{attachments: attachments}) do
attachments =
Enum.map(attachments, fn attachment ->
attachment_info = %{
filename: attachment.filename,
type: attachment.content_type,
content: Swoosh.Attachment.get_content(attachment, :base64)
}
extra =
case attachment.type do
:inline -> %{disposition: "inline", content_id: attachment.filename}
:attachment -> %{disposition: "attachment"}
end
Map.merge(attachment_info, extra)
end)
Map.put(body, :attachments, attachments)
end
defp prepare_reply_to(body, %{reply_to: nil}), do: body
defp prepare_reply_to(body, %{reply_to: reply_to}) do
Map.put(body, :headers, %{"Reply-To" => reply_to_item(reply_to)})
end
defp prepare_custom_headers(body, %{headers: headers})
when map_size(headers) == 0,
do: body
defp prepare_custom_headers(%{headers: body_headers} = body, %{headers: headers}) do
Map.put(body, :headers, Map.merge(headers, body_headers))
end
defp prepare_custom_headers(body, %{headers: headers}),
do: Map.put(body, :headers, headers)
defp prepare_provider_options_body_fields(body, %{provider_options: provider_options}) do
Map.merge(body, Map.take(provider_options, @provider_options_body_fields))
end
end