Packages
swoosh
1.18.4
1.26.3
1.26.2
1.26.1
1.26.0
1.25.3
1.25.2
1.25.1
1.25.0
1.24.0
1.23.1
1.23.0
1.22.1
1.22.0
1.21.0
1.20.1
1.20.0
1.19.9
1.19.8
1.19.7
1.19.6
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.17.10
1.17.9
1.17.8
retired
1.17.7
retired
1.17.6
1.17.5
1.17.4
retired
1.17.3
1.17.2
1.17.1
1.17.0
1.16.12
1.16.11
1.16.10
1.16.9
1.16.8
1.16.7
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
retired
1.16.1
retired
1.16.0
1.15.3
1.15.2
1.15.1
1.15.0
1.14.4
1.14.3
1.14.2
1.14.1
1.14.0
1.13.0
1.12.0
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
retired
1.11.0
1.10.3
1.10.2
1.10.1
1.10.0
1.9.1
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.2
1.5.1
1.5.0
1.4.0
1.3.11
1.3.10
1.3.9
1.3.8
retired
1.3.7
1.3.6
retired
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.9
1.0.8
retired
1.0.7
retired
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.25.6
0.25.5
0.25.4
0.25.3
retired
0.25.2
0.25.1
0.25.0
0.24.4
0.24.3
0.24.2
0.24.1
0.24.0
0.23.5
0.23.4
0.23.3
0.23.2
0.23.1
0.23.0
0.22.2
0.22.1
0.22.0
0.21.1
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Compose, deliver and test your emails easily in Elixir. Supports SMTP, Sendgrid, Mandrill, Postmark, Mailgun and many more out of the box. Preview your emails in the browser. Test your email sending code.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/swoosh/adapters/postup.ex
defmodule Swoosh.Adapters.PostUp do
@moduledoc ~S"""
An adapter that sends email using the PostUp API, specifically triggered mailing. This
corresponds to transactional emails.
API reference: [PostUp API docs](https://apidocs.postup.com/docs/send-a-triggered-mailing)
**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.
## Example
# config/config.exs
config :sample, Sample.Mailer,
adapter: Swoosh.Adapters.PostUp,
username: "BMO",
password: "hellofootball"
# lib/sample/mailer.ex
defmodule Sample.Mailer do
use Swoosh.Mailer, otp_app: :sample
end
## Using with provider options
Specify custom tags as a string delimited by semicolons:
import Swoosh.Email
new()
|> from({"BMO", "bmo@example.com"})
|> to([
{["FirstName=Finn;LastName=Mertins;custom_tag=Something Else"], "finnthehuman@example.com"},
{["FirstName=Jake"], "jakethedog@example.com"},
])
|> subject("BMO says hi!")
|> reply_to("Football@example.com")
|> html_body("<h1>Hello :)</h1>")
|> text_body("Hi!")
|> put_provider_option(:send_template_id, 42)
## Usage with just template and no other options
Use an invalid email address to ignore the `from` option, which is required by Swoosh but causes
email templates to be overwritten as part of the "context" field in the request body.
import Swoosh.Email
new()
|> from("IGNORED")
|> to([
{"FirstName=Finn;LastName=Mertins", "finnthehuman@example.com"},
{"FirstName=Jake", "jakethedog@example.com"},
])
|> put_provider_option(:send_template_id, 42)
## Provider Options
Note that most of these options are nested under the optional "content" field in the JSON request
body alongside "fromEmail", "fromName", "htmlBody", etc.
* `send_template_id` (integer): unique number assigned to each send template.
**Required field.**
* `unsub_content_id` (integer): ID for replacement unsubscribe content in specified template.
* `reply_content_id` (integer): Same as above for "reply" content.
* `header_content_id` (integer): Same as above for header content.
* `footer_content_id` (integer): Same as above for footer content.
* `forward_to_friend_content_id` (integer): Same as above for "forward to friend" content.
"""
use Swoosh.Adapter, required_config: [:username, :password]
require Logger
alias Swoosh.Email
@base_url "https://api.postup.com/api"
@api_endpoint "/templatemailing"
@provider_options [
:send_template_id,
:unsub_content_id,
:reply_content_id,
:header_content_id,
:footer_content_id,
:forward_to_friend_content_id
]
defp base_url(config), do: config[:base_url] || @base_url
@impl Swoosh.Adapter
def deliver(%Email{} = email, config \\ []) do
credentials = Base.encode64("#{config[:username]}:#{config[:password]}")
headers = [
{"Accept", "application/json"},
{"Content-Type", "application/json"},
{"User-Agent", "swoosh/#{Swoosh.version()}"},
{"authorization", "Basic #{credentials}"}
]
request_body = email |> prepare_payload() |> Swoosh.json_library().encode!
url = [base_url(config), @api_endpoint]
case Swoosh.ApiClient.post(url, headers, request_body, email) do
{:ok, code, _headers, response_body} when code >= 200 and code <= 399 ->
Swoosh.json_library().decode(response_body)
{: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_payload(email) do
%{}
|> prepare_from(email)
|> prepare_reply_to(email)
|> prepare_to(email)
|> prepare_subject(email)
|> prepare_text_content(email)
|> prepare_html_content(email)
|> prepare_provider_option(email)
end
# fromEmail and fromName are not required fields when using a PostUp template.
# Swoosh requires a from field; specify some invalid email address to omit it from request body
defp prepare_from(payload, %Email{from: {name, email}}) when is_binary(email) do
cond do
(is_nil(name) or name == "") and not String.match?(email, ~r/^.+@.+\..+$/) ->
payload
true ->
payload
|> prepare_content("fromEmail", email)
|> prepare_content("fromName", name)
end
end
defp prepare_reply_to(payload, %Email{reply_to: nil}),
do: payload
defp prepare_reply_to(payload, %Email{reply_to: {name, reply_to}}) when name in [nil, ""],
do: prepare_content(payload, "replyToEmail", reply_to)
defp prepare_reply_to(payload, %Email{reply_to: {reply_to_name, reply_to_email}}) do
payload
|> prepare_content("replyToEmail", reply_to_email)
|> prepare_content("replyToName", reply_to_name)
end
defp prepare_to(payload, %Email{to: to}) do
recipients = Enum.map(to, &prepare_recipient/1)
Map.put(payload, "recipients", recipients)
end
defp prepare_subject(payload, %Email{subject: ""}), do: payload
defp prepare_subject(payload, %Email{subject: subject}),
do: prepare_content(payload, "subject", subject)
defp prepare_subject(payload, _), do: payload
defp prepare_text_content(payload, %Email{text_body: nil}), do: payload
defp prepare_text_content(payload, %Email{text_body: text_body}),
do: prepare_content(payload, "textBody", text_body)
defp prepare_html_content(payload, %Email{html_body: nil}), do: payload
defp prepare_html_content(payload, %Email{html_body: html_content}),
do: prepare_content(payload, "htmlBody", html_content)
defp prepare_provider_option(payload, %Email{provider_options: options}) do
_required = Map.fetch!(options, :send_template_id)
options
|> Map.take(@provider_options)
|> Enum.reduce(payload, fn
{:send_template_id, send_template_id}, acc ->
Map.put(acc, "SendTemplateId", send_template_id)
{key, value}, acc ->
content_key =
case key do
:unsub_content_id -> "unsubContentId"
:reply_content_id -> "replyContentId"
:header_content_id -> "headerContentId"
:footer_content_id -> "footerContentId"
:forward_to_friend_content_id -> "forwardToFriendContentId"
end
prepare_content(acc, content_key, value)
end)
end
defp prepare_recipient({nil, email}),
do: %{address: email, tags: []}
defp prepare_recipient({tags, email}) when is_binary(tags),
do: %{address: email, tags: String.split(tags, ";", trim: true)}
defp prepare_content(payload, key, value) when is_binary(key) do
put_in(payload, [Access.key("content", %{}), key], value)
end
end