-
Notifications
You must be signed in to change notification settings - Fork 780
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
Attachment helper for base64 encoding #505
Conversation
//sample file
const fs = require('fs');
const sgMail = require('@sendgrid/mail');
const {
classes: {
Attachment
}
} = require('@sendgrid/helpers');
let attachment = new Attachment({
filename: 'indexes.txt',
type: 'plain/text',
disposition: 'attachment',
contentId: 'mytext',
filePath: '/home/hugocarmo/indexes.js',
//content: fs.readFileSync('/home/hugocarmo/indexes.js'), // will work
//content: 'w6cK', // will work
});
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'name@example.com',
from: 'name@example.com',
subject: 'Hello attachment',
html: '<p>Here’s an attachment for you!</p>',
attachments: [
attachment
],
};
console.log(msg); |
@@ -39,17 +40,29 @@ class Attachment { | |||
data = toCamelCase(data); | |||
|
|||
//Extract properties from data | |||
const {content, filename, type, disposition, contentId} = data; | |||
const {content, filename, type, disposition, contentId, file} = 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.
I'm worried that file
vs. filename
will be a bit confusing. Maybe instead of passing a file object, we can have two separate new settings filePath
and fileEncoding
.
(The filename
setting should probably be deprecated and introduce a new one fileName
to be consistent)
const {content, filename, type, disposition, contentId} = data; | ||
const {content, filename, type, disposition, contentId, file} = data; | ||
|
||
let fileContent = !file ? content : this.readFile(file); |
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.
File content is immutable so can be const
. I'm not sure about the logic either though. If content is passed, should that take priority over a file? Maybe a warning if both are passed?
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 that the file should take priority over content, and warn the user if both is passed is a good idea.
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.
Is there any example of how to give the user a warning here?
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.
Actually we don't show warnings anywhere really, just throw errors. Maybe an error is suitable, because you are not expected to pass both values. Then, we also don't need to worry about what takes priority.
this.setFilename(filename); | ||
this.setType(type); | ||
this.setDisposition(disposition); | ||
this.setContentId(contentId); | ||
} | ||
|
||
/** | ||
* Read a file and return its content as base64 | ||
*/ | ||
readFile(file) { |
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.
If we use a separate setting for the encoding you can pass it in here, with a default value of utf8
, e.g.
readFile(path, encoding = 'utf8') {
...
}
readFile(file) { | ||
let encoding = !file.encoding ? 'utf8' : file.encoding; | ||
let data = fs.readFileSync(file.path); | ||
let buff = Buffer.from(data, encoding); |
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.
All let
's are const
ant and I'm wondering if we should return the buffer, instead of converting to string at this point? That way you can use the helper function to read files and get buffers and manipulate them if needed before passing to the attachment.
Also, we can update the Attachment class to detect if a Buffer is passed and automatically convert to string.
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.
Thanks for the tip.
Should I convert the method readFile to a helper function? Do you think that another helper function to convert a string to Buffer is necessary?
According to fs.readFileSync documentation if the encoding
is passed, then a string is returned, otherwise it'll return a buffer.
Perhaps in this case the buffer conversion using an encoding is completely unnecessary, just if the user want to get a string from the buffer, then the encoding would be useful.
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.
Might not need to expose it, as it's basically just using readFileSync
. Let's pass the encoding straight to readFileSync
, so we won't need to convert to buffer.
|
||
if ((typeof content !== 'undefined') && (typeof filePath !== 'undefined')) { | ||
throw new Error( | ||
'The props \'content\' and \'filePath\' cannot be used together.' |
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.
Could you use template strings to make it a bit cleaner without the backslashes? E.g.
`The props 'content' and 'filePath' cannot be used together.`
return; | ||
} | ||
|
||
this.setContent(content); |
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.
Could use a one liner for all of this:
this.setContent(filePath ? this.readFile(filePath) : content);
return; | ||
} | ||
if (typeof content !== 'string') { | ||
} else if (contentType === 'object' |
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.
Could you please install ESLint for your code editor and run it on your code to ensure the code style is the same to the rest of the code base?
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.
Sure, I was using the StandardJS
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.
Have you used ESLint on your changes? The code style is still different, e.g. else if
's and else
's should start on a new line.
} else if (content instanceof Buffer) { | ||
this.content = content.toString('base64'); | ||
return; | ||
} else if (contentType !== 'string') { |
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.
Could you write matching unit tests for all these new content type cases?
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 will...
@adamreisnz the changes were made, I'm sorry for the long response time, last week I was so busy. |
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.
Thanks for the changes, it's almost done just some code style issues to keep things consistent. ESLint can auto-fix these for you if you run it from the command line or in your editor 👍
@thinkingserious @mbernier I'm happy with this, assuming it works as intended and has been tested! |
cool! thanks for your review @adamreisnz :) |
We have not been able to merge your Pull Request, but because you are awesome - we wanted to make sure you could still get a SendGrid Hacktoberfest shirt. Please go fill out our swag form before Nov 5th and we will send the shirt! (We know that you might have tried this before and it didn’t work, sorry about that!) You have till Nov 5th to fill out this form in order to get the Hacktoberfest shirt!Thank you for contributing during Hacktoberfest! We hope to see you in the repos soon! Just so you know, we always give away a SendGrid shirt for your first ever non-Hacktoberfest PR that gets merged. |
@spelcaster I have tried your above code and tried to upload a 10mb pdf file. But It does not work. Again it does not work with a simple text file. I have set the type to 'application/pdf' then too it does not work. Any help will be appriciated.
|
@mgadirocks Have you tried with absolute path? See https://github.com/sendgrid/sendgrid-nodejs/pull/505/files#diff-de4f2f8e9851d3bd62c6424afddcf90fR58 |
LGTM |
Hi @spelcaster, I'm ready to merge this now, thanks for your patience! Could you please take a moment to fix the merge conflicts? Thank you! With Best Regards, Elmer |
- If the content is a Buffer and the disposition is 'attachment', then the Buffer will be encoded to a base64 string; - If a filePath is given, then file will be read and its content will be used, the rule to convert the file content to base64 is the same described above; - It's not allowed to use content and filePath together; - Added unit test for the new cases;
@thinkingserious I've squashed the commits into only 1 and resolved the conflict. Could you verify if the rule to encode the buffer into a base64 string is correct? Thanks... |
Hello @spelcaster, |
base64;