Current section

Files

Jump to
swoosh lib swoosh adapters mailjet.ex
Raw

lib/swoosh/adapters/mailjet.ex

defmodule Swoosh.Adapters.Mailjet do
@moduledoc ~S"""
An adapter that sends email using the Mailjet API.
For reference: [Mailjet API docs](https://dev.mailjet.com/guides/#send-api-v3-1)
**This adapter requires an API Client.** Swoosh comes with Hackney, Finch and Req out of the box.
See the [installation section](https://hexdocs.pm/swoosh/Swoosh.html#module-installation)
for details.
> ### Dependency {: .info}
>
> Mailjet adapter requires `Plug` to work properly.
## Example
# config/config.exs
config :sample, Sample.Mailer,
adapter: Swoosh.Adapters.Mailjet,
api_key: "my-api-key",
secret: "my-secret-key"
# lib/sample/mailer.ex
defmodule Sample.Mailer do
use Swoosh.Mailer, otp_app: :sample
end
## Using with provider options
import Swoosh.Email
new()
|> from({"Billi Wang", "billi_wang@example.com"})
|> to({"Nai Nai", "nainai@example.com"})
|> reply_to("a24@example.com")
|> cc({"Haiyan Wang", "haiyan_wang@example.com"})
|> cc("lujian@example.com")
|> bcc({"Hao Hao", "haohao@example.com"})
|> bcc("aiko@example.com")
|> subject("Hello, Nai Nai!")
|> html_body("<h1>Hello</h1>")
|> text_body("Hello")
|> put_provider_option(:template_id, 123)
|> put_provider_option(:template_error_deliver, true)
|> put_provider_option(:template_error_reporting, "developer@example.com")
|> put_provider_option(:variables, %{firstname: "lulu", lastname: "wang"})
|> put_provider_option(:custom_id, "custom_id")
|> put_provider_option(:event_payload, "event_payload")
|> put_provider_option(:track_opens, false)
|> put_provider_option(:track_clicks, false)
|> put_provider_option(:url_tags, "utm_source=transactional&utm_medium=email")
## Provider options
* `:template_id` (integer) - `TemplateID`, unique template id of the
template to be used as email content
* `:template_error_deliver` (boolean) - `TemplateErrorDeliver`,
send even if error in template if `true`, otherwise stop email delivery
immediately upon error
* `:template_error_reporting` (string | tuple | map) - `TemplateErrorReporting`,
email address or a tuple of name and email address of a recipient to send a
carbon copy upon error
* `:variables` (map) - `Variables`, custom key-value variable for the email
content
* `:custom_id` (string) - `CustomID`, custom id for the email
* `:event_payload` (string | map) - `EventPayload`, custom payload that will
be attached on the mailjet webhook events
* `:track_opens` (boolean) - `TrackOpens`, enable or disable open tracking
* `:track_clicks` (boolean) - `TrackClicks`, enable or disable click tracking
* `:url_tags` (string) - `URLTags`, URL query parameters to append to all
URLs in the message (e.g. `"utm_source=transactional&utm_medium=email"`)
"""
use Swoosh.Adapter,
required_config: [:api_key, :secret],
required_deps: [plug: Plug.Conn.Query]
alias Swoosh.{Email, Attachment}
@base_url "https://api.mailjet.com/v3.1"
@api_endpoint "send"
def deliver(%Email{} = email, config \\ []) do
send_request(:single, prepare_body(email), email, config)
end
def deliver_many(emails, config \\ [])
def deliver_many([], _config) do
{:ok, []}
end
def deliver_many(emails, config) when is_list(emails) do
send_request(:many, prepare_body(emails), emails, config)
end
defp send_request(type, body, email_or_emails, config) do
email = email_or_emails |> List.wrap() |> List.first()
headers = prepare_headers(config)
url = [base_url(config), "/", @api_endpoint]
case Swoosh.ApiClient.post(url, headers, body, email) do
{:ok, 200, _headers, body} ->
{:ok, parse_results(type, body)}
{:ok, error_code, _headers, body} when error_code >= 400 ->
{:error, {error_code, parse_results(type, body)}}
{:error, reason} ->
{:error, reason}
end
end
defp parse_results(type, %{"Messages" => results}) do
results =
Enum.map(results, fn
%{"Status" => "success"} = result -> get_message_id(result)
per_message_error -> per_message_error
end)
case {type, results} do
{:single, [single]} -> single
{:many, list} -> list
end
end
defp parse_results(type, body) when is_binary(body) do
parse_results(type, Swoosh.json_library().decode!(body))
end
defp parse_results(_type, global_error) do
global_error
end
defp get_message_id(%{"To" => [%{"MessageID" => message_id}]}) do
%{id: message_id}
end
defp get_message_id(%{"To" => multiple_receivers}) do
%{
id:
Enum.map(
multiple_receivers,
fn %{"MessageID" => message_id} ->
message_id
end
)
}
end
defp base_url(config), do: config[:base_url] || @base_url
defp prepare_headers(config) do
[
{"User-Agent", "swoosh/#{Swoosh.version()}"},
{"Authorization", "Basic #{auth(config)}"},
{"Content-Type", "application/json"}
]
end
defp auth(config), do: Base.encode64("#{config[:api_key]}:#{config[:secret]}")
defp prepare_body(emails) do
emails
|> List.wrap()
|> Enum.map(&prepare_message/1)
|> wrap_messages()
|> Swoosh.json_library().encode!()
end
defp prepare_message(email) do
%{}
|> prepare_from(email)
|> prepare_to(email)
|> prepare_subject(email)
|> prepare_html(email)
|> prepare_text(email)
|> prepare_cc(email)
|> prepare_bcc(email)
|> prepare_reply_to(email)
|> prepare_attachments(email)
|> prepare_variables(email)
|> prepare_template(email)
|> prepare_custom_headers(email)
|> prepare_custom_id(email)
|> prepare_event_payload(email)
|> prepare_track_opens(email)
|> prepare_track_clicks(email)
|> prepare_url_tags(email)
end
defp wrap_messages(body) when is_list(body), do: %{Messages: body}
defp prepare_event_payload(body, %{provider_options: %{event_payload: event_payload}})
when is_binary(event_payload),
do: Map.put(body, "EventPayload", event_payload)
defp prepare_event_payload(body, %{provider_options: %{event_payload: event_payload}})
when is_map(event_payload),
do: Map.put(body, "EventPayload", Swoosh.json_library().encode!(event_payload))
defp prepare_event_payload(body, _options), do: body
defp prepare_custom_id(body, %{provider_options: %{custom_id: custom_id}}),
do: Map.put(body, "CustomID", custom_id)
defp prepare_custom_id(body, _options), do: body
defp prepare_custom_headers(body, %{headers: headers}),
do: Map.put(body, "Headers", headers)
defp prepare_attachments(body, %{attachments: []}), do: body
defp prepare_attachments(body, %{attachments: attachments}) do
{normal_attachments, inline_attachments} =
Enum.split_with(attachments, fn %{type: type} -> type == :attachment end)
body
|> Map.put(
"Attachments",
Enum.map(normal_attachments, &prepare_attachment/1)
)
|> Map.put(
"InlinedAttachments",
Enum.map(inline_attachments, &prepare_attachment/1)
)
end
defp prepare_attachment(attachment) do
%{
"ContentType" => attachment.content_type,
"Filename" => attachment.filename,
"Base64Content" => Attachment.get_content(attachment, :base64),
"ContentId" => attachment.cid || attachment.filename
}
end
defp prepare_recipients(recipients),
do: Enum.map(recipients, &prepare_recipient(&1))
defp prepare_recipient({name, address}),
do: %{"Name" => name, "Email" => address}
defp prepare_from(body, %{from: from}),
do: Map.put(body, "From", prepare_recipient(from))
defp prepare_to(body, %{to: to}),
do: Map.put(body, "To", prepare_recipients(to))
defp prepare_reply_to(body, %{reply_to: nil}), do: body
defp prepare_reply_to(body, %{reply_to: reply_to}),
do: Map.put(body, "ReplyTo", prepare_recipient(reply_to))
defp prepare_cc(body, %{cc: []}), do: body
defp prepare_cc(body, %{cc: cc}),
do: Map.put(body, "Cc", prepare_recipients(cc))
defp prepare_bcc(body, %{bcc: []}), do: body
defp prepare_bcc(body, %{bcc: bcc}),
do: Map.put(body, "Bcc", prepare_recipients(bcc))
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_body}),
do: Map.put(body, "TextPart", text_body)
defp prepare_html(body, %{html_body: nil}), do: body
defp prepare_html(body, %{html_body: html_body}),
do: Map.put(body, "HTMLPart", html_body)
defp prepare_variables(body, %{provider_options: %{variables: variables}}) do
Map.put(body, "Variables", variables)
end
defp prepare_variables(body, _email), do: body
defp prepare_template(body, %{
provider_options: %{template_id: template_id} = provider_options
}) do
body =
body
|> Map.put("TemplateID", template_id)
|> Map.put("TemplateLanguage", true)
|> Map.put(
"TemplateErrorDeliver",
!!provider_options[:template_error_deliver]
)
case provider_options[:template_error_reporting] do
nil ->
body
{name, email} when is_binary(name) and is_binary(email) ->
Map.put(body, "TemplateErrorReporting", %{
"Email" => email,
"Name" => name
})
email when is_binary(email) ->
Map.put(body, "TemplateErrorReporting", %{
"Email" => email,
"Name" => ""
})
map when is_map(map) ->
Map.put(body, "TemplateErrorReporting", map)
end
end
defp prepare_template(body, _email), do: body
defp prepare_track_opens(body, %{provider_options: %{track_opens: true}}),
do: Map.put(body, "TrackOpens", "enabled")
defp prepare_track_opens(body, %{provider_options: %{track_opens: false}}),
do: Map.put(body, "TrackOpens", "disabled")
defp prepare_track_opens(body, _email), do: body
defp prepare_track_clicks(body, %{provider_options: %{track_clicks: true}}),
do: Map.put(body, "TrackClicks", "enabled")
defp prepare_track_clicks(body, %{provider_options: %{track_clicks: false}}),
do: Map.put(body, "TrackClicks", "disabled")
defp prepare_track_clicks(body, _email), do: body
defp prepare_url_tags(body, %{provider_options: %{url_tags: url_tags}}),
do: Map.put(body, "URLTags", url_tags)
defp prepare_url_tags(body, _email), do: body
end