diff --git a/config/config.exs b/config/config.exs
index 408948a..ed1c192 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,3 +1,7 @@
import Config
+if Mix.env() == :test do
+ config :logger, level: :info
+end
+
# import_config "#{Mix.env}.exs"
diff --git a/lib/bamboo/adapters/message/content.ex b/lib/bamboo/adapters/message/content.ex
index d76f142..f8dce24 100644
--- a/lib/bamboo/adapters/message/content.ex
+++ b/lib/bamboo/adapters/message/content.ex
@@ -4,6 +4,8 @@ defmodule BambooSes.Message.Content do
Depending on email it can generate simple, raw or template content.
"""
+ alias BambooSes.Encoding
+
@type t :: %__MODULE__{
Template:
%{
@@ -78,8 +80,8 @@ defmodule BambooSes.Message.Content do
when is_map(template_params),
do: %__MODULE__{Template: template_params}
- defp build_content(_email, _template_params, subject, text, html, [], []),
- do: build_simple_content(subject, text, html)
+ defp build_content(_email, _template_params, subject, text, html, headers, []),
+ do: build_simple_content(subject, text, html, headers)
defp build_content(email, _template_params, _subject, _text, _html, _headers, _attachments) do
raw_data =
@@ -94,18 +96,26 @@ defmodule BambooSes.Message.Content do
}
end
- defp build_simple_content(subject, text, html) do
+ defp build_simple_content(subject, text, html, headers) do
%__MODULE__{
Simple: %{
Subject: %{
Charset: "UTF-8",
Data: subject
},
- Body: build_simple_body(text, html)
+ Body: build_simple_body(text, html),
+ Headers: build_headers(headers)
}
}
end
+ defp build_headers(headers) do
+ Enum.map(
+ headers,
+ fn {name, value} -> %{"Name" => name, "Value" => Encoding.maybe_rfc1342_encode(value)} end
+ )
+ end
+
defp build_simple_body(text, html) do
%{}
|> put_text(text)
diff --git a/lib/bamboo/adapters/render/LICENSE.txt b/lib/bamboo/adapters/render/LICENSE.txt
deleted file mode 100644
index 9438d7d..0000000
--- a/lib/bamboo/adapters/render/LICENSE.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 2012 Kamil Ciemniewski
-Copyright (c) 2023 Kalys Osmonov
-
-MIT License
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/bamboo/adapters/render/raw.ex b/lib/bamboo/adapters/render/raw.ex
index 79baa6d..f785ffe 100644
--- a/lib/bamboo/adapters/render/raw.ex
+++ b/lib/bamboo/adapters/render/raw.ex
@@ -10,79 +10,181 @@ defmodule BambooSes.Render.Raw do
alias BambooSes.Encoding
def render(email, extra_headers \\ []) do
- email
- # Returns a list of tuples
- |> compile_parts()
- # Nests the tuples and attaches necessary metadata
- |> nest_parts(email, extra_headers)
+ has_text = !is_nil(email.text_body) && String.length(email.text_body) > 0
+ has_html = !is_nil(email.html_body) && String.length(email.html_body) > 0
+ has_attachments = length(filter_regular_attachments(email)) > 0
+ has_inline_attachments = length(filter_inline_attachments(email)) > 0
+
+ headers = headers_for(email) ++ extra_headers
+
+ build_parts(
+ has_text,
+ has_html,
+ has_attachments,
+ has_inline_attachments,
+ email,
+ headers
+ )
|> :mimemail.encode()
end
- defp nest_parts(parts, email, extra_headers) do
- {top_mime_type, top_mime_sub_type, _, _, top_content_part} = nested_content_part_tuples(parts)
+ defp build_parts(false, false, _, _, email, headers) do
+ {
+ "multipart",
+ "mixed",
+ headers,
+ %{},
+ prepare_attachments(email.attachments)
+ }
+ end
+
+ defp build_parts(false, true, false, false, email, headers) do
+ {
+ "text",
+ "html",
+ headers,
+ parameters_for(nil),
+ email.html_body
+ }
+ end
+ defp build_parts(false, true, false, true, email, headers) do
{
- top_mime_type,
- top_mime_sub_type,
- headers_for(email) ++ extra_headers,
+ "multipart",
+ "related",
+ headers,
%{},
- top_content_part
+ [
+ # generates html
+ build_parts(false, true, false, false, email, [])
+ ] ++ prepare_attachments(filter_inline_attachments(email))
}
end
- defp nested_content_part_tuples(parts) do
- plain_part_tuple = body_part_tuple(parts, :plain)
- html_part_tuple = body_part_tuple(parts, :html)
- # attachment_part_tuples(parts)
- inline_attachment_part_tuples = []
- attached_attachment_part_tuples = attachment_part_tuples(parts)
-
- related_or_html_part_tuple =
- if Enum.empty?(inline_attachment_part_tuples) do
- html_part_tuple
- else
- if is_nil(html_part_tuple),
- do: nil,
- else:
- {"multipart", "related", [], %{}, [html_part_tuple | inline_attachment_part_tuples]}
- end
-
- alternative_or_plain_tuple =
- if is_nil(related_or_html_part_tuple) do
- plain_part_tuple
- else
- {"multipart", "alternative", [], %{}, [plain_part_tuple, related_or_html_part_tuple]}
- end
-
- mixed_or_alternative_tuple =
- if Enum.empty?(attached_attachment_part_tuples) do
- alternative_or_plain_tuple
- else
- if is_nil(alternative_or_plain_tuple),
- do: nil,
- else:
- {"multipart", "mixed", [], %{},
- [alternative_or_plain_tuple | attached_attachment_part_tuples]}
- end
-
- mixed_or_alternative_tuple
- end
-
- @spec body_part_tuple([tuple()], atom()) :: nil | tuple()
- defp body_part_tuple(parts, type) do
- part = Enum.find(parts, &(elem(&1, 0) == type))
-
- if is_nil(part) do
- nil
- else
- {
- mime_type_for(part),
- mime_subtype_for(part),
- headers_for(part),
- parameters_for(part),
- elem(part, 1)
- }
- end
+ defp build_parts(false, true, true, false, email, headers) do
+ {
+ "multipart",
+ "mixed",
+ headers,
+ %{},
+ [
+ # generates html
+ build_parts(false, true, false, false, email, [])
+ ] ++ prepare_attachments(email.attachments)
+ }
+ end
+
+ defp build_parts(false, true, true, true, email, headers) do
+ {
+ "multipart",
+ "mixed",
+ headers,
+ %{},
+ [
+ # generates html
+ build_parts(false, true, false, true, email, [])
+ ] ++ prepare_attachments(filter_regular_attachments(email))
+ }
+ end
+
+ defp build_parts(true, false, false, false, email, headers) do
+ {
+ "text",
+ "plain",
+ headers,
+ parameters_for(nil),
+ email.text_body
+ }
+ end
+
+ defp build_parts(true, false, _, _, email, headers) do
+ {
+ "multipart",
+ "mixed",
+ headers,
+ %{},
+ [
+ # generates text
+ build_parts(true, false, false, false, email, [])
+ ] ++ prepare_attachments(email.attachments)
+ }
+ end
+
+ defp build_parts(true, true, false, false, email, headers) do
+ {
+ "multipart",
+ "alternative",
+ headers,
+ %{},
+ [
+ # generates text
+ build_parts(true, false, false, false, email, []),
+ # generates html
+ build_parts(false, true, false, false, email, [])
+ ]
+ }
+ end
+
+ defp build_parts(true, true, false, true, email, headers) do
+ {
+ "multipart",
+ "related",
+ headers,
+ %{},
+ [
+ # generates alternative
+ build_parts(true, true, false, false, email, [])
+ ] ++ prepare_attachments(filter_inline_attachments(email))
+ }
+ end
+
+ defp build_parts(true, true, true, false, email, headers) do
+ {
+ "multipart",
+ "mixed",
+ headers,
+ %{},
+ [
+ # generates alternative
+ build_parts(true, true, false, false, email, [])
+ ] ++ prepare_attachments(email.attachments)
+ }
+ end
+
+ defp build_parts(true, true, true, true, email, headers) do
+ {
+ "multipart",
+ "mixed",
+ headers,
+ %{},
+ [
+ # generates related with alternative
+ build_parts(true, true, false, true, email, [])
+ ] ++ prepare_attachments(filter_regular_attachments(email))
+ }
+ end
+
+ defp prepare_attachments(attachments) do
+ attachments
+ |> Enum.map(fn attachment -> {:attachment, attachment.data, attachment} end)
+ |> attachment_part_tuples()
+ end
+
+ def filter_inline_attachments(email) do
+ Enum.filter(email.attachments, fn
+ attachment ->
+ !is_nil(attachment) &&
+ !is_nil(attachment.content_id) &&
+ String.length(attachment.content_id) > 0
+ end)
+ end
+
+ def filter_regular_attachments(email) do
+ Enum.filter(email.attachments, fn
+ attachment ->
+ !is_nil(attachment) &&
+ (is_nil(attachment.content_id) || String.length(attachment.content_id) == 0)
+ end)
end
@spec attachment_part_tuples([tuple()]) :: list(tuple())
@@ -178,21 +280,4 @@ defmodule BambooSes.Render.Raw do
end
defp preprocess_header({key, value}), do: {key, value}
-
- defp compile_parts(email) do
- [
- {:plain, email.text_body},
- {:html, email.html_body},
- Enum.map(email.attachments, fn attachment ->
- {:attachment, attachment.data, attachment}
- end)
- ]
- |> List.flatten()
- |> Enum.filter(¬_empty_tuple_value(&1))
- end
-
- defp not_empty_tuple_value(tuple) when is_tuple(tuple) do
- value = elem(tuple, 1)
- value != nil && value != [] && value != ""
- end
end
diff --git a/mix.lock b/mix.lock
index 3f417ec..468d7b0 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,19 +1,19 @@
%{
"bamboo": {:hex, :bamboo, "2.2.0", "f10a406d2b7f5123eb1f02edfa043c259db04b47ab956041f279eaac776ef5ce", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "8c3b14ba7d2f40cb4be04128ed1e2aff06d91d9413d38bafb4afccffa3ade4fc"},
- "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
+ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
- "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
+ "credo": {:hex, :credo, "1.7.8", "9722ba1681e973025908d542ec3d95db5f9c549251ba5b028e251ad8c24ab8c5", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cb9e87cc64f152f3ed1c6e325e7b894dea8f5ef2e41123bd864e3cd5ceb44968"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
"eiconv": {:hex, :eiconv, "1.0.0", "ee1e47ee37799a05beff7a68d61f63cccc93101833c4fb94b454c23b12a21629", [:rebar3], [], "hexpm", "8c80851decf72fc4571a70278d7932e9a87437770322077ecf797533fbb792cd"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_aws": {:hex, :ex_aws, "2.5.3", "9c2d05ba0c057395b12c7b5ca6267d14cdaec1d8e65bdf6481fe1fd245accfb4", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "67115f1d399d7ec4d191812ee565c6106cb4b1bbf19a9d4db06f265fd87da97e"},
"ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
- "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
+ "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
"gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"},
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
- "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
+ "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
diff --git a/test/lib/bamboo/adapters/content_raw_parts_text.exs b/test/lib/bamboo/adapters/content_raw_parts_text.exs
new file mode 100644
index 0000000..a440ef4
--- /dev/null
+++ b/test/lib/bamboo/adapters/content_raw_parts_text.exs
@@ -0,0 +1,354 @@
+defmodule BambooSes.ContentRawPartsTest do
+ use ExUnit.Case
+ alias BambooSes.Message.Content
+ alias BambooSes.{EmailParser, TestHelpers}
+ alias Bamboo.Email
+
+ @moduledoc """
+
+ TEXT | HTML | ATTACHMENTS | INLINE ATTACHMENTS | RESULT
+ f | f | f | f | NOT VALID
+ f | f | f | t | mixed(attachments[])
+ f | f | t | f | mixed(attachments[])
+ f | f | t | t | mixed(attachments[])
+ f | t | f | f | text/html
+ f | t | f | t | related(html,inline_attachments[])
+ f | t | t | f | mixed(html,attachments[])
+ f | t | t | t | mixed(related(html,inline_attachments[]),attachments[])
+ t | f | f | f | text/plain
+ t | f | f | t | mixed(text,attachments[])
+ t | f | t | f | mixed(text,attachments[])
+ t | f | t | t | mixed(text,attachments[])
+ t | t | f | f | alternative(text,html)
+ t | t | f | t | related(alternative(text,html),inline_attachments[])
+ t | t | t | f | mixed(alternative(text,html),attachments[])
+ t | t | t | t | mixed(related(alternative(text,html),inline_attachments[]),attachments[])
+
+ """
+
+ @doc "f f f t"
+ test "generates multipart/mixed when only inline attachments are provided; no text, no html" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("")
+ |> Email.html_body("")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [png]} = EmailParser.parse(raw_data)
+ assert {"image", "png", _, _, _} = png
+ end
+
+ @doc "f f t f"
+ test "generates multipart/mixed when only regular attachments are provided; no text, no html" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("")
+ |> Email.html_body("")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [pdf]} = EmailParser.parse(raw_data)
+ assert {"application", "pdf", _, _, _} = pdf
+ end
+
+ @doc "f f t t"
+ test "generates multipart/mixed when inline and regular attachments are provided; no text, no html" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("")
+ |> Email.html_body("")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [pdf, png]} = EmailParser.parse(raw_data)
+ assert {"application", "pdf", _, _, _} = pdf
+ assert {"image", "png", _, _, _} = png
+ end
+
+ @doc "f t f f"
+ test "generates text/html when only html is provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("")
+ |> Email.html_body("Email body")
+ |> Email.put_header("X-Custom-Header", "custom-value")
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"text", "html", _, _, "Email body"} = EmailParser.parse(raw_data)
+ end
+
+ @doc "f t f t"
+ test "generates multipart/related when html and attachments with content_id are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("")
+ |> Email.html_body("Email body
")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "related", _, _, [html, png]} = EmailParser.parse(raw_data)
+ assert {"text", "html", _, _, "Email body
"} = html
+ assert {"image", "png", _, _, _} = png
+ end
+
+ @doc "f t t f"
+ test "generates multipart/mixed when html and attachments are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("")
+ |> Email.html_body("Email body")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [html, pdf]} = EmailParser.parse(raw_data)
+ assert {"text", "html", _, _, "Email body"} = html
+ assert {"application", "pdf", _, _, _} = pdf
+ end
+
+ @doc "f t t t"
+ test "generates multipart/mixed with multipart/related" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("")
+ |> Email.html_body("Email body
")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [related, pdf]} = EmailParser.parse(raw_data)
+ assert {"multipart", "related", _, _, [html, png]} = related
+ assert {"application", "pdf", _, _, _} = pdf
+ assert {"text", "html", _, _, "Email body
"} = html
+ assert {"image", "png", _, _, _} = png
+ end
+
+ @doc "t f f f"
+ test "generates text/plain when only text is provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("")
+ |> Email.put_header("X-Custom-Header", "custom-value")
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"text", "plain", _, _, "Email text body"} = EmailParser.parse(raw_data)
+ end
+
+ @doc "t f f t"
+ test "generates multipart/mixed when text and inline attathments are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [text, png]} = EmailParser.parse(raw_data)
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"image", "png", _, _, _} = png
+ end
+
+ @doc "t f t f"
+ test "generates multipart/mixed when text and attachments are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [text, pdf]} = EmailParser.parse(raw_data)
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"application", "pdf", _, _, _} = pdf
+ end
+
+ @doc "t f t t"
+ test "generates multipart/mixed when text and both inline and regular attachments are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [text, pdf, png]} = EmailParser.parse(raw_data)
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"image", "png", _, _, _} = png
+ assert {"application", "pdf", _, _, _} = pdf
+ end
+
+ @doc "t t f f"
+ test "generates multipart/alternative when text and html are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("Email html body")
+ |> Email.put_header("X-Custom-Header", "custom-value")
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "alternative", _, _, [text, html]} = EmailParser.parse(raw_data)
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"text", "html", _, _, "Email html body"} = html
+ end
+
+ @doc "t t f t"
+ test "generates multipart/related when text, html and inline attachments are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("Email html body")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "related", _, _, [alternative, png]} = EmailParser.parse(raw_data)
+ assert {"multipart", "alternative", _, _, [text, html]} = alternative
+ assert {"image", "png", _, _, _} = png
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"text", "html", _, _, "Email html body"} = html
+ end
+
+ @doc "t t t f"
+ test "generates multipart/mixed when text, html and regular attachements are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("Email html body")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [alternative, pdf]} = EmailParser.parse(raw_data)
+ assert {"multipart", "alternative", _, _, [text, html]} = alternative
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"text", "html", _, _, "Email html body"} = html
+ assert {"application", "pdf", _, _, _} = pdf
+ end
+
+ @doc "t t t t"
+ test "generates multipart/mixed when text, html and both inline and regular attachements are provided" do
+ content =
+ TestHelpers.new_email()
+ |> Email.text_body("Email text body")
+ |> Email.html_body("Email html body")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/pole.png"),
+ content_id: "img-1"
+ )
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
+ |> Content.build_from_bamboo_email()
+
+ %Content{
+ Raw: %{
+ Data: raw_data
+ }
+ } = content
+
+ assert {"multipart", "mixed", _, _, [related, pdf]} = EmailParser.parse(raw_data)
+ assert {"multipart", "related", _, _, [alternative, png]} = related
+ assert {"multipart", "alternative", _, _, [text, html]} = alternative
+ assert {"image", "png", _, _, _} = png
+ assert {"text", "plain", _, _, "Email text body"} = text
+ assert {"application", "pdf", _, _, _} = pdf
+ assert {"text", "html", _, _, "Email html body"} = html
+ end
+end
diff --git a/test/lib/bamboo/adapters/content_raw_test.exs b/test/lib/bamboo/adapters/content_raw_test.exs
index ed2cae9..bc1aaae 100644
--- a/test/lib/bamboo/adapters/content_raw_test.exs
+++ b/test/lib/bamboo/adapters/content_raw_test.exs
@@ -7,6 +7,7 @@ defmodule BambooSes.ContentRawTest do
test "generates raw content when there is a header" do
content =
TestHelpers.new_email()
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
|> Email.put_header("X-Custom-Header", "custom-header-value")
|> Content.build_from_bamboo_email()
@@ -18,12 +19,7 @@ defmodule BambooSes.ContentRawTest do
parsed_content = EmailParser.parse(raw_data)
- raw_data
- |> EmailParser.parse()
-
assert EmailParser.subject(parsed_content) == "Welcome to the app."
- assert EmailParser.text(parsed_content) == "Thanks for joining!"
- assert EmailParser.html(parsed_content) == "Thanks for joining!"
assert header = EmailParser.header(parsed_content, "X-Custom-Header")
assert header == "custom-header-value"
end
@@ -100,6 +96,7 @@ defmodule BambooSes.ContentRawTest do
content =
TestHelpers.new_email()
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
|> Email.put_header("X-Custom-Header", custom_header)
|> Content.build_from_bamboo_email()
diff --git a/test/lib/bamboo/adapters/content_simple_test.exs b/test/lib/bamboo/adapters/content_simple_test.exs
index f7abe85..61810ff 100644
--- a/test/lib/bamboo/adapters/content_simple_test.exs
+++ b/test/lib/bamboo/adapters/content_simple_test.exs
@@ -15,7 +15,36 @@ defmodule BambooSes.ContentSimpleTest do
Html: %{Charset: "UTF-8", Data: "Thanks for joining!"},
Text: %{Charset: "UTF-8", Data: "Thanks for joining!"}
},
- Subject: %{Charset: "UTF-8", Data: "Welcome to the app."}
+ Subject: %{Charset: "UTF-8", Data: "Welcome to the app."},
+ Headers: []
+ }
+ }
+ end
+
+ test "generates simple content with headers" do
+ content =
+ new_email()
+ |> Email.put_header("X-Custom-Header", "custom-value")
+ |> Email.put_header("X-Custom-Non-Ascii-Header", "𐰴𐰀𐰽𐱄𐰆𐰢")
+ |> Content.build_from_bamboo_email()
+
+ assert content == %Content{
+ Simple: %{
+ Body: %{
+ Html: %{Charset: "UTF-8", Data: "Thanks for joining!"},
+ Text: %{Charset: "UTF-8", Data: "Thanks for joining!"}
+ },
+ Subject: %{Charset: "UTF-8", Data: "Welcome to the app."},
+ Headers: [
+ %{
+ "Name" => "X-Custom-Header",
+ "Value" => "custom-value"
+ },
+ %{
+ "Name" => "X-Custom-Non-Ascii-Header",
+ "Value" => "=?utf-8?B?8JCwtPCQsIDwkLC98JCxhPCQsIbwkLCi?="
+ }
+ ]
}
}
end
@@ -32,7 +61,8 @@ defmodule BambooSes.ContentSimpleTest do
Html: %{Charset: "UTF-8", Data: "Thanks for joining!"},
Text: %{Charset: "UTF-8", Data: "Thanks for joining!"}
},
- Subject: %{Charset: "UTF-8", Data: "Welcome to the app."}
+ Subject: %{Charset: "UTF-8", Data: "Welcome to the app."},
+ Headers: []
}
}
end
diff --git a/test/lib/bamboo/adapters/ses_adapter_test.exs b/test/lib/bamboo/adapters/ses_adapter_test.exs
index 1d84719..ffe8962 100644
--- a/test/lib/bamboo/adapters/ses_adapter_test.exs
+++ b/test/lib/bamboo/adapters/ses_adapter_test.exs
@@ -323,6 +323,7 @@ defmodule Bamboo.SesAdapterTest do
TestHelpers.new_email()
|> Email.put_header("X-Custom-Header", "header-value; another-value")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
|> SesAdapter.deliver(%{})
end
@@ -351,6 +352,7 @@ defmodule Bamboo.SesAdapterTest do
TestHelpers.new_email()
|> Email.put_header("X-Custom-Header", "header-value")
|> Email.from({"John [Schmidt]", "from@example.com"})
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
|> SesAdapter.deliver(%{})
end
@@ -377,7 +379,7 @@ defmodule Bamboo.SesAdapterTest do
expect(HttpMock, :request, expected_request_fn)
TestHelpers.new_email()
- |> Email.put_header("X-Custom-Header", "header-value")
+ |> Email.put_attachment(Path.join(__DIR__, "../../../support/invoice.pdf"))
|> Email.put_header("Reply-To", {"John Schmidt", "reply-to@example.com"})
|> SesAdapter.deliver(%{})
end
diff --git a/test/support/pole.png b/test/support/pole.png
new file mode 100644
index 0000000..3fda0cc
Binary files /dev/null and b/test/support/pole.png differ