-
Notifications
You must be signed in to change notification settings - Fork 333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Attachments: Support file data in attachment struct + mailgun attachment support #292
Conversation
How close is this PR to being ready to merge? I'm working on a project that needs to generate and attach vCards to e-mails, so being able to create an attachment from data rather than a file would be super helpful. We use the SendGrid adapter, so I'm starting to work on a PR to add attachment support for that adapter today. I'd love it if I could base my work off of the code in this PR. Thanks for all your work on this library! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for you work on this!
The description says:
# Supports...
|> put_attachment %Attachment{filename: "event.ics", data: "raw content..", content_type: "text/calendar"}
But I don't see documentation for that or a test, but maybe I missed it. If docs or tests are missing, could you add those? I think once that's in I can merge this and release a new RC.
Thank you so much!
@@ -112,7 +112,7 @@ defmodule Bamboo.MandrillAdapter do | |||
%{ | |||
name: att.filename, | |||
type: att.content_type, | |||
content: Base.encode64(File.read!(att.path)) | |||
content: Base.encode64(att.data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we're in this PR, could you rename att
to attachment
?
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
lib/bamboo/attachment.ex
Outdated
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 | ||
Attachment.new_from_file("/path/to/attachment.png") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you change these to Bamboo.Attachment
so it's clear this is coming from Bamboo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I think new
is better here since there isn't a second function. Alternatively you could add new_from_data
so there were two different functions. I'd probably prefer just new
though
lib/bamboo/email.ex
Outdated
@@ -220,7 +220,10 @@ defmodule Bamboo.Email do | |||
#... | |||
end | |||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding docs for sending data would be awesome here
lib/bamboo/attachment.ex
Outdated
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 | ||
Attachment.new_from_file("/path/to/attachment.png") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I think new
is better here since there isn't a second function. Alternatively you could add new_from_data
so there were two different functions. I'd probably prefer just new
though
I tried this branch on something I was working on, and I noticed there were a number of test failures that will need to be resolved. It doesn't look like CircleCI is running builds right now, so there's no indication of a problem here. From the test failures, I couldn't tell if the code was doing the right thing and the tests needed to be changed, or if the tests were right and the code was wrong. I suspect the latter, but I'm not confident enough in that to be able to suggest fixes. |
Yeah I'm not sure what's up with Circle. I'll work on that. Busy with client work now, but hopefully in the next few days. In the meantime I can run it locally before merging to make sure it all works ok. @schurig are you seeing failures locally? |
I'll do it asap! I remember that I fixed all tests. But I can't confirm at the moment. |
I haven't found a way to test the attachments for the mailgun adapter. Can anyone help me with it? It uses multipart as content type and I can't get the attachment by calling |
@schurig I just came to check in and didn't realize you left that comment. Sorry about that. I would remove the changes from the MailgunAdapter and it's tests and instead do a separate PR for those. That way we can merge in the attachments changes and work on the adapters separately. Does that sound good? |
@paulcsmith that sounds good!! |
@paulcsmith ready for a 2nd review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one small comment. Looks awesome. Thank you!
lib/bamboo/email.ex
Outdated
#... | ||
end | ||
""" | ||
def put_attachment(%__MODULE__{attachments: attachments} = email, %Attachment{filename: _filename, data: _data} = attachment) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can shorten the pattern match of the attach meant to %Attachment{}
since you wouldn't be able to initialize that struct without those fields
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You actually can. Then the values are set to nil
because we don't specify anything as enforced keys:
iex(1)> %Bamboo.Attachment{}
%Bamboo.Attachment{content_type: nil, data: nil, filename: nil, path: nil}
But you're right - since we don't check the actual value it doesn't make sense for that check. But it would only make sense to put pass an Attachment struct with both keys (filename
and data
) specified in there. Do you think we should throw an error if one of it is empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we have 2 functions. One for the happy path and another for attachments that are missing data and filename
def put_attachment(_email, %Attachment{filename: nil, data: nil} = attachment) do
raise "You must pass a valid attachment, instead got: #{inspect attachment}"
end
def put_attachment(%__MODULE__{attachments: attachments} = email, %Attachment{} = attachment) do
# happy path!
end
I think this would cover the common error case. We can add different error cases later if people run into things often. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@paulcsmith I've made the error messages more specific since it would still jump into the happy path if one of the params is nil
. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one small comment. I like the two error messages 👍
lib/bamboo/email.ex
Outdated
raise "You must provide a filename for the attachment." | ||
end | ||
def put_attachment(%__MODULE__{attachments: attachments} = email, %Attachment{data: nil}) do | ||
raise "The attachment must contain data." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you include the attachment in both error message so that it's easier to see what they passed in?
raise "The attachment must contain data, instead got: #{inspect attachment}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! Done. Also cleaned up a bit. There were some warnings about not used variables.
Wonderful work! Thanks for your first contribution to Bamboo! |
As discussed in #286 @sbrink and I were working on an improved version of the attachment support:
Changes
|> put_attachment %Attachment{filename: "event.ics", data: "raw content..", content_type: "text/calendar"}
Attachment.data
when using|> put_attachment "/hi.jpg"
or|> put_attachment %Plug{..}
attachment.data
Todo