/
brevo.ex
215 lines (158 loc) · 6.25 KB
/
brevo.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
defmodule Swoosh.Adapters.Brevo do
@moduledoc ~S"""
An adapter that sends email using the Brevo API (Transactional emails only).
For reference: [Brevo API docs](https://developers.brevo.com/reference/sendtransacemail)
**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.Brevo,
api_key: "my-api-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("nora@example.com")
|> to("shushu@example.com")
|> subject("Hello, Wally!")
|> text_body("Hello")
|> put_provider_option(:id, 42)
|> put_provider_option(:template_id, 42)
|> put_provider_option(:params, %{param1: "a", param2: 123})
|> put_provider_option(:tags, ["tag_1", "tag_2"])
|> put_provider_option(:schedule_at, ~U[2022-11-15 11:00:00Z])
## Provider Options
* `sender_id` (integer) - `sender`, the sender `id` where this library will
add email obtained from the `from/1`
* `template_id` (integer) - `templateId`, the Id of the `active`
transactional email template
* `params` (map) - `params`, a map of key/value attributes to customize the
template
* `tags` (list[string]) - `tags`, a list of tags for each email for easy
filtering
* `schedule_at` (UTC DateTime) - `schedule_at`, a UTC date-time on which the email has to schedule
"""
use Swoosh.Adapter, required_config: [:api_key]
alias Swoosh.Email
@base_url "https://api.brevo.com/v3"
@api_endpoint "/smtp/email"
defp base_url(config), do: config[:base_url] || @base_url
def deliver(%Email{} = email, config \\ []) do
headers = [
{"Accept", "application/json"},
{"Content-Type", "application/json"},
{"User-Agent", "swoosh/#{Swoosh.version()}"},
{"Api-Key", config[:api_key]}
]
body = email |> prepare_payload() |> Swoosh.json_library().encode!
url = [base_url(config), @api_endpoint]
case Swoosh.ApiClient.post(url, headers, body, email) do
{:ok, code, _headers, body} when code >= 200 and code <= 399 ->
{:ok, %{id: extract_message_id(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 extract_message_id(body) do
body
|> Swoosh.json_library().decode!()
|> Map.get("messageId")
end
defp prepare_payload(email) do
%{}
|> prepare_from(email)
|> prepare_reply_to(email)
|> prepare_to(email)
|> prepare_cc(email)
|> prepare_bcc(email)
|> prepare_subject(email)
|> prepare_text_content(email)
|> prepare_html_content(email)
|> prepare_template_id(email)
|> prepare_headers(email)
|> prepare_params(email)
|> prepare_tags(email)
|> prepare_attachments(email)
|> prepare_schedule_at(email)
end
defp prepare_from(payload, %{from: {_name, email}, provider_options: %{sender_id: sender_id}}),
do: Map.put(payload, "sender", %{id: sender_id, email: email})
defp prepare_from(payload, %{from: {_, "TEMPLATE"}}), do: payload
defp prepare_from(payload, %{from: from}),
do: Map.put(payload, "sender", prepare_recipient(from))
defp prepare_reply_to(payload, %{reply_to: nil}),
do: payload
defp prepare_reply_to(payload, %{reply_to: reply_to}),
do: Map.put(payload, "replyTo", prepare_recipient(reply_to))
defp prepare_to(payload, %{to: to}) do
Map.put(payload, "to", Enum.map(to, &prepare_recipient/1))
end
defp prepare_cc(payload, %{cc: []}), do: payload
defp prepare_cc(payload, %{cc: cc}) do
Map.put(payload, "cc", Enum.map(cc, &prepare_recipient/1))
end
defp prepare_bcc(payload, %{bcc: []}), do: payload
defp prepare_bcc(payload, %{bcc: bcc}) do
Map.put(payload, "bcc", Enum.map(bcc, &prepare_recipient/1))
end
defp prepare_subject(payload, %{subject: ""}), do: payload
defp prepare_subject(payload, %{subject: subject}),
do: Map.put(payload, "subject", subject)
defp prepare_subject(payload, _), do: payload
defp prepare_text_content(payload, %{text_body: nil}), do: payload
defp prepare_text_content(payload, %{text_body: text_content}) do
Map.put(payload, "textContent", text_content)
end
defp prepare_html_content(payload, %{html_body: nil}), do: payload
defp prepare_html_content(payload, %{html_body: html_content}) do
Map.put(payload, "htmlContent", html_content)
end
defp prepare_template_id(payload, %{provider_options: %{template_id: template_id}}) do
Map.put(payload, "templateId", template_id)
end
defp prepare_template_id(payload, _), do: payload
defp prepare_headers(payload, %{headers: map}) when map_size(map) == 0, do: payload
defp prepare_headers(payload, %{headers: headers}) do
Map.put(payload, "headers", headers)
end
defp prepare_params(payload, %{provider_options: %{params: params}}) when is_map(params) do
Map.put(payload, "params", params)
end
defp prepare_params(payload, _), do: payload
defp prepare_tags(payload, %{provider_options: %{tags: tags}}) when is_list(tags) do
Map.put(payload, "tags", tags)
end
defp prepare_tags(payload, _), do: payload
defp prepare_attachments(payload, %{attachments: []}), do: payload
defp prepare_attachments(payload, %{attachments: attachments}) do
Map.put(
payload,
"attachment",
Enum.map(
attachments,
&%{
name: &1.filename,
content: Swoosh.Attachment.get_content(&1, :base64)
}
)
)
end
defp prepare_schedule_at(payload, %{provider_options: %{schedule_at: schedule_at}}) do
Map.put(payload, "scheduledAt", schedule_at)
end
defp prepare_schedule_at(payload, _), do: payload
defp prepare_recipient({name, email}) when name in [nil, ""],
do: %{email: email}
defp prepare_recipient({name, email}),
do: %{name: name, email: email}
end