I'm developing a Firebase Function, which is triggered when a new order is added to the Realtime Database. The first thing it does is to creat a pdf and pipe it to a google cloud storage bucket.
On the .on("finish") event of the bucket stream, the next function gets started, which should send the piped pdf via email to the customer.
Everything seems to work, at least a bit.
First I had the problem, that the attached pdf always was empty. (Not just blank. I also opened it in notepad++ and it really was all empty). When I checked the doc and bucketFileSream vars inside the bucketFileStream.on("finished") function both had a length of 0. A check of the doc var directly after doc.end showed a length of somewhat 612.
I then changed the flow, that in the sendOrderEmail function I also open a new Read Stream from the newly created File in the bucket.
Now I get at least some stuff of the PDF in the attachement, but never the whole content.
When I check the PDF uploaded to the bucket, it looks like it should.
I googled alot and found some answers that were also targeting this topic, but as also seen in comments on these questions, they were not completly helpful.
- PDF Attachment NodeMailer
- Where to generate a PDF of Firebase Database data - mobile app, or Firebase Hosting web app
- How to attach file to an email with nodemailer
I also checked with the nodemailer documentation how to pass the attachement correctly and implemented it as documented. But no success.
I think that the mail gets sent before the Read Stream has finished.
Here the Package Versions I use:
- "@google-cloud/storage": "1.5.2"
- "@types/pdfkit": "^0.7.35",
- "firebase-admin": "5.8.0",
- "firebase-functions": "^0.7.3"
- "nodemailer": "4.4.1",
Can anyone tell me what I'm doing wrong or provide a working example, which uses current package versions, for this usecase?
Here is the code which drives me crazy...
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const nodemailer = require("nodemailer");
const pdfkit = require("pdfkit");
const storage = require("@google-cloud/storage")({projectId: `${PROJECT_ID}`})
const mailTransport = nodemailer.createTransport({
host: "smtp.office365.com",
port: 587,
secureConnection: false,
auth: {
user: "userName",
pass: "userPassword"
},
tls: {
ciphers: "SSLv3",
}
});
exports.added = function(event) {
const order = event.data.val();
const userId = event.params.userId;
// Load User Data by userId
return admin
.database()
.ref("/users/" +userId)
.once("value")
.then(function (snapshot) {
return generateOrderPDF(snapshot.val(), userId);
});
};
function generateOrderPDF(user, userId) {
const doc = new pdfkit();
const bucket = storage.bucket(functions.config().bucket);
const filename = `/${userId}/test-` + Date.now() + ".pdf";
const file = bucket.file(filename);
const bucketFileStream = file.createWriteStream();
// Pipe its output to the bucket
doc.pipe(bucketFileStream);
// Do creation Stuff....
doc.end();
bucketFileStream.on("finish", function () {
return sendOrderEmail(user, filename);
});
bucketFileStream.on("error", function(err) {
console.error(err);
});
}
function sendOrderEmail(user, filename) {
const email = user.email;
const firstname = user.firstName;
const mailOptions = {
from: "[email protected]",
to: email,
subject: "Order"
};
const bucket = storage.bucket(functions.config().bucket);
const file = bucket.file(filename);
mailOptions.html = mailTemplate;
mailOptions.attachments = [{
filename: "test.pdf",
content: file.createReadStream()
}];
return mailTransport.sendMail(mailOptions).then(() => {
console.log("New order email sent to:", email);
}).catch(error => {
console.error(error);
});
}