Skip to content

Commit

Permalink
Attachments: Support file data in attachment struct (#292)
Browse files Browse the repository at this point in the history
As discussed in #286 @sbrink and I were working on an improved version of the attachment support:

*** Changes

* supports `|> put_attachment %Attachment{filename: "event.ics", data: "raw content..", content_type: "text/calendar"}`
* sets `Attachment.data` when using `|> put_attachment "/hi.jpg"` or `|> put_attachment %Plug{..}`
* supports attachments in the mailgun and test adapter
* updates the mandrill adapter to use the `attachment.data`
  • Loading branch information
Martin Schurig authored and paulcsmith committed Jul 13, 2017
1 parent d8451d6 commit d09da6a
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 16 deletions.
8 changes: 4 additions & 4 deletions lib/bamboo/adapters/mandrill_adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ defmodule Bamboo.MandrillAdapter do
defp attachments(%{attachments: attachments}) do
attachments
|> Enum.reverse
|> Enum.map(fn(att) ->
|> Enum.map(fn(attachment) ->
%{
name: att.filename,
type: att.content_type,
content: Base.encode64(File.read!(att.path))
name: attachment.filename,
type: attachment.content_type,
content: Base.encode64(attachment.data)
}
end)
end
Expand Down
3 changes: 3 additions & 0 deletions lib/bamboo/adapters/test_adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,7 @@ defmodule Bamboo.TestAdapter do
def clean_assigns(email) do
%{email | assigns: :assigns_removed_for_testing}
end

@doc false
def supports_attachments?, do: true
end
13 changes: 7 additions & 6 deletions lib/bamboo/attachment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ defmodule Bamboo.Attachment do
@moduledoc """
"""

defstruct filename: nil, content_type: nil, path: nil
defstruct filename: nil, content_type: nil, path: nil, data: nil

@doc ~S"""
Creates a new Attachment
Examples:
Attachment.new("/path/to/attachment.png")
Attachment.new("/path/to/attachment.png", filename: "image.png")
Attachment.new("/path/to/attachment.png", filename: "image.png", content_type: "image/png")
Attachment.new(params["file"]) # Where params["file"] is a %Plug.Upload
Bamboo.Attachment.new("/path/to/attachment.png")
Bamboo.Attachment.new("/path/to/attachment.png", filename: "image.png")
Bamboo.Attachment.new("/path/to/attachment.png", filename: "image.png", content_type: "image/png")
Bamboo.Attachment.new(params["file"]) # Where params["file"] is a %Plug.Upload
"""
def new(path, opts \\ [])
if Code.ensure_loaded?(Plug) do
Expand All @@ -21,7 +21,8 @@ defmodule Bamboo.Attachment do
def new(path, opts) do
filename = opts[:filename] || Path.basename(path)
content_type = opts[:content_type] || determine_content_type(path)
%__MODULE__{path: path, filename: filename, content_type: content_type}
data = File.read!(path)
%__MODULE__{path: path, data: data, filename: filename, content_type: content_type}
end

defp determine_content_type(path) do
Expand Down
30 changes: 28 additions & 2 deletions lib/bamboo/email.ex
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ defmodule Bamboo.Email do
assigns: %{},
private: %{}

alias Bamboo.Email
alias Bamboo.{Email, Attachment}

@address_functions ~w(from to cc bcc)a
@attribute_pipe_functions ~w(subject text_body html_body)a
Expand Down Expand Up @@ -203,7 +203,33 @@ defmodule Bamboo.Email do
end

@doc ~S"""
Adds an attachment to the email
Adds an data attachment to the email
## Example
put_attachment(email, %Bamboo.Attachment{})
Requires the fields filename and data of the %Bamboo.Attachment{} struct to be set.
## Example
def create(conn, params) do
#...
email
|> put_attachment(%Bamboo.Attachment{filname: "event.ics", data: "BEGIN:VCALENDAR..."})
#...
end
"""
def put_attachment(%__MODULE__{attachments: _}, %Attachment{filename: nil} = attachment) do
raise "You must provide a filename for the attachment, instead got: #{inspect attachment}"
end
def put_attachment(%__MODULE__{attachments: _}, %Attachment{data: nil} = attachment) do
raise "The attachment must contain data, instead got: #{inspect attachment}"
end
def put_attachment(%__MODULE__{attachments: attachments} = email, %Attachment{} = attachment) do
%{email | attachments: [attachment | attachments]}
end

@doc ~S"""
Adds an file attachment to the email
## Example
put_attachment(email, path, opts \\ [])
Expand Down
6 changes: 3 additions & 3 deletions test/lib/bamboo/attachments_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule Bamboo.AttachmentTest do
attachment = Attachment.new(path)
assert attachment.content_type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
assert attachment.filename == "attachment.docx"
assert attachment.path == path
assert attachment.data
end

test "create an attachment with an unknown content type" do
Expand Down Expand Up @@ -37,7 +37,7 @@ defmodule Bamboo.AttachmentTest do
attachment = Attachment.new(upload)
assert attachment.content_type == "application/msword"
assert attachment.filename == "test.docx"
assert attachment.path == path
assert attachment.data
end

test "create an attachment from a Plug Upload struct with overrides" do
Expand All @@ -48,6 +48,6 @@ defmodule Bamboo.AttachmentTest do
attachment = Attachment.new(upload, filename: "my-attachment.doc", content_type: "application/other")
assert attachment.content_type == "application/other"
assert attachment.filename == "my-attachment.doc"
assert attachment.path == path
assert attachment.data
end
end
29 changes: 28 additions & 1 deletion test/lib/bamboo/email_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,34 @@ defmodule Bamboo.EmailTest do
assert email.private["foo"] == "bar"
end

test "put_attachment/3 atts an attachment to the attachments list" do
describe "put_attachment/2" do
test "adds an attachment to the attachments list" do
attachment = %Bamboo.Attachment{filename: "attachment.docx", data: "content"}
email = new_email() |> put_attachment(attachment)

assert [%Bamboo.Attachment{filename: "attachment.docx"}] = email.attachments
end

test "with no filename throws an error" do
attachment = %Bamboo.Attachment{filename: nil, data: "content"}

msg = "You must provide a filename for the attachment, instead got: %Bamboo.Attachment{content_type: nil, data: \"content\", filename: nil, path: nil}"
assert_raise RuntimeError, msg, fn ->
new_email() |> put_attachment(attachment)
end
end

test "with no data throws an error" do
attachment = %Bamboo.Attachment{filename: "attachment.docx", data: nil}

msg = "The attachment must contain data, instead got: %Bamboo.Attachment{content_type: nil, data: nil, filename: \"attachment.docx\", path: nil}"
assert_raise RuntimeError, msg, fn ->
new_email() |> put_attachment(attachment)
end
end
end

test "put_attachment/3 adds an attachment to the attachments list" do
path = Path.join(__DIR__, "../../support/attachment.docx")
email = new_email() |> put_attachment(path)

Expand Down

0 comments on commit d09da6a

Please sign in to comment.