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/render/raw.ex b/lib/bamboo/adapters/render/raw.ex
index 1f3d840..b72c9ac 100644
--- a/lib/bamboo/adapters/render/raw.ex
+++ b/lib/bamboo/adapters/render/raw.ex
@@ -3,83 +3,195 @@ defmodule BambooSes.Render.Raw do
Functions for rendering email messages into strings.
"""
+ # TODO render according to logic table: text, html, has_attachments, has_content_id_attachments
@doc """
Returns a tuple with all data needed for the underlying adapter to send.
"""
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 = String.length(email.text_body) > 0
+ has_html = 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
+ )
+ # |> IO.inspect()
|> :mimemail.encode()
+
+ # has_attachments =
+ # email
+ # # Returns a list of tuples
+ # |> compile_parts()
+ # # Nests the tuples and attaches necessary metadata
+ # |> nest_parts(email, extra_headers)
+ # |> :mimemail.encode()
+ end
+
+ defp build_parts(false, false, _, _, email, headers) do
+ {
+ "multipart",
+ "mixed",
+ headers,
+ %{},
+ prepare_attachments(email.attachments)
+ }
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, 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())
@@ -171,21 +283,4 @@ defmodule BambooSes.Render.Raw do
end
defp mailbox({name, address}), do: "#{name} <#{address}>"
-
- 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/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/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