How do you query data using Mongoose within a service function? - node.js

I have a service in my node application where I am trying to use nodemailer to send an email with a link to clients. The issue I am having is that when I try to query the recipient email address using Mongoose in the service I get this response message in the console log Promise { <pending> } and obviously my email does not send because I am not returning an email address. I am using the code below as the service that is called from my controller. It looks like everything is working fine in the controller because when I use a test email address as the recipient email the message is sent and function returns no errors. Am I writing the function to query the email wrong or is there a better way I should be setting this up?
var nodemailer = require('nodemailer');
const Obrf = require("../models/Obrf");
const sendLink = (x) => {
var id = x;
const offerid = id.toString();
const url = 'http://localhost:3000/offerletter/' + offerid
async function returnEmail (y) {
const email = await Obrf.findById(y, 'email_address').exec();
return email;
};
const sendToEmail = returnEmail(id);
console.log(sendToEmail);
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'sender#gmail.com',
pass: 'password'
}
});
var mailOptions = {
from: 'sender#gmail.com',
to: sendToEmail,
subject: 'Congratulations on your offer!',
text: 'Go to: ' + url + ' to sign your offer letter!'
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
};
module.exports = {
sendLink,
};

async before a function makes the function return a promise:
are you sure if Obrf.findById(y, 'email_address').exec() returning promise ?
if not remove the aysnc await
function returnEmail (y) {
const email = Obrf.findById(y, 'email_address').exec();
return email;
};
const sendToEmail = returnEmail(id);
if its returning the promise then do something like this
const sendToEmail = returnEmail(id).then(id=>id);

Related

How should I create the node js sendinblue email api?

I have attached here my sendinblue node js typescript api, I am
getting error only not getting any mail in my gmail. I am using this
package for mail send sib-api-v3-sdk. below is my api code, what is
the issue. what i have to add in my api.
const Sib = require('sib-api-v3-sdk');
const loginemail = async (req: Request) => {
const connection = await createConnection()
try{
const client=Sib.ApiClient.instance;
let apiKey = client.authentications['apiKey'];
apiKey.apiKey = process.env.API_KEY;
let transEmailApi = new Sib.TransactionalEmailsApi();
let sender ={
email: 'jagadeeshwaran907#gmail.com'}
let reciever = [{
email: 'jagadeeshwaran907#gmail.com'
}]
let v= transEmailApi.sendTransacEmail( {
sender,
to: reciever,
subject:"transactional mail has been sent successfully",
textContent: `successfully`
}).then(console.log("error"))
return v;
} catch(error){
await connection.end()
throw error
}
}

Variable not picking up returned value from function

I'm trying to collect the of returned values of two functions, send-mail and sendsms, into a variable(var ordermessage). It picks up the returned value from send-sms just fine.
I'm using the mailgun api for send-mail, and ordermessage just picks up 'undefined'. But send-mail keeps running.
I've tried `await mailgun.messages().send(...)`
I've tried `const myvar = await mailgun.messages().send(...)`
and also `let myvar = await mailgun.messages().send...`
Nada.
I tried using a function that had the api call as a callback. Still got undefined. The email and sms both get sent, but I need the reply from the email server. I'm using Mailgun trial so I need to return a response.
send-mail.js
var mailgun = require('mailgun-js')({apiKey: process.env.MAILGUN_API_KEY, domain: process.env.MAILGUN_DOMAIN});
var processresponse = "\n";
var data = {
from: 'Zigy Demo Store <admin_demo#zigy.com>',
to: email,
subject: 'You have placed an order.',
text: body
};
console.log("\n----------START EMAIL-------------\n");
mailgun.messages()
.send(data, function (error, body) {
console.log("\nFinally running MAILGUN. Return body is\n", body);
if (body == undefined || body == false) {
console.log("Got nothing from server.");
} else {
processresponse += body.message;
console.log("***********************Gotten reply from Mailgun server.*******************************\n", processresponse);
}
});
OrderController function
module.exports = {
neworder: async function(req, res) {
var sendemail = require('./send-mail');
var sendtext = require('./send-sms');
var orderdetails = 'New order created at '+ new Date() + '\nItem ---> Price'; //Message that will be sent to users.
var item;
var printcart = await Shoppingcart.findOne({
where: {
id: req.body.cart,
owner: req.body.user
}
}).populate('product');
var ordermessage = '';
for (items in printcart.product) {
item = printcart.product[items];
orderdetails += '\n' + item.name + ' ---> ' + item.price;
}
console.log(orderdetails);
//to get email addr and phone number
phone = req.body.phone;
var email = req.body.email;
var user = await User.findOne({id:printcart.owner});
ordermessage += await sendemail(email, orderdetails);
console.log("\nAfter email, the message is ", ordermessage);
ordermessage += await sendtext(phone, orderdetails);
console.log("\nAfter text, Printing order message to be returned to browser ", ordermessage);
console.log("Final message ", ordermessage);
res.send(ordermessage);
}
};
Terminal
----------START EMAIL-------------
Calling test function
After email, the message is
Finally running MAILGUN. Return body is
{ id:
'<20190222062410.1.FD7A4868FA0ADF5E#sandbox612cf746219c46ad93d5dc588f9341ff.mailgun.org>',
message: 'Queued. Thank you.' }
***********************Gotten reply from Mailgun server.*******************************
Queued. Thank you.
Checking list of verified numbers...
Found the phonenumber!
You will receive an SMS message with your order details soon.
After text, Printing order message to be returned to browser
You will receive an SMS message with your order details soon.
Final message
You will receive an SMS message with your order details soon.
SM9bc04208f9354834a153fb1ffd7dc8bb
Any help will be greatly appreciated.
Edit: I called res.write from inside send-mail.js and send-sms.js instead and got rid of the variable ordermessage.
This is a guess: I think the mailgun.messages().send(...) method doesn't return anything as it uses a classic callback. So you will always get undefined from it.
But you get the result from the body parameter in the callback. You could use Promisify to transform the callback into a promise style method.
const util = require('util');
const fs = require('fs');
const messages = mailgun.messages();
const sendPromisified = util.promisify(messages.send());
sendPromisified(data).then((body) => {
// Do something with `body`
}).catch((error) => {
// Handle mailgun error
});
// Async / await style
try {
const mailBody = await sendPromisified(data);
} catch (err) {
// handle mailgun error `err`
}

NodeJs TypeError: Cannot read property 'key' of undefined

i want to create a cloud function which sends email if some data where added to my database. Unfortunately while trying to deploy my function i receive this error:
TypeError: Cannot read property 'key' of undefined
Here is my function:
const functions = require('firebase-functions')
const nodemailer = require('nodemailer')
const postmarkTransport = require('nodemailer-postmark-transport')
const admin = require('firebase-admin')
// 2. Admin SDK can only be initialized once
try {admin.initializeApp(functions.config().firebase)} catch(e) {
console.log('dbCompaniesOnUpdate initializeApp failure')
}
// 3. Google Cloud environment variable used:
const postmarkKey = functions.config().postmark.key
const mailTransport = nodemailer.createTransport(postmarkTransport({
auth: {
apiKey: postmarkKey
}
}))
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-
functions
//
exports.sendingEmailForlocationsRequests =
functions.database.ref('/clubs/{clubId}/{pushId}')
.onWrite((event) => {
//I want to retrieve the pushID
return sendEmail();
})
function sendEmail() {
// 5. Send welcome email to new users
const mailOptions = {
from: '"Dave" <noreply#clate.de>',
to: 'locations#clate.de',
subject: 'Welcome!',
html: `<Test>`
}
// 6. Process the sending of this email via nodemailer
return mailTransport.sendMail(mailOptions)
.then(() => console.log('dbCompaniesOnUpdate:Welcome
confirmation email'))
.catch((error) => console.error('There was an error while
sending the email:', error))
}
It looks like 'postmark' isn't set in your firebase configuration. You can set what's retrieved by functions.config() using the CLI: https://firebase.google.com/docs/functions/config-env

Promise not returning value on request

I have been trying to get this to work, but am new to NodeJS. I suspect the issue is due to async, but am not familiar with how it works.
The idea behind this code is that it monitors a firebase database change and sends an email to the users. I am getting everything from the change snapshot, and using the values to check another table for user data. The request is not returning before the email gets sent and I am unsure why.
Edit I should specify that the email function sgMail is firing off before I get the results from the requests. I've tried putting a delay, but I am still not getting the result to return in time.
Here's my index.js
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
var requestify = require('requestify');
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database.ref('Order/{orderID}')
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = 'https://shlep-me-f516e.firebaseio.com/User/'+shipperInfo+'.json';
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
});
function getTravelerData() {
return new Promise(resolve => {
requestify.get('https://shlep-me-f516e.firebaseio.com/User/' + travelerInfo + '.json')
.then(function (response) {
resolve(response.getBody())
});
});
}
var TravelD = getTravelerData();
//Send an email
const msg = {
to: 'andrew#shlepme.com',
from: 'support#shlepme.com',
subject: 'New Follower',
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: 'd1ccfeb9-2e2d-4979-a3ca-c53975fe486e',
substitutionWrappers: ['%', '%'],
substitutions: {
'%shipper_name%': "Test",
'traveler_name': TravelD.name
// and other custom properties here
}
};
console.log('Sending email');
console.log(TravelD);
return sgMail.send(msg)
});
Any ideas? I have been trying to figure this out.
It seems that you need to understand about Promises first.
When you start using promises you will need to ALWAYS use them and chain one with the other.
So I would rewrite your code like this: (not tested)
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require("firebase-functions");
var requestify = require("requestify");
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require("#sendgrid/mail");
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database
.ref("Order/{orderID}")
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = "https://shlep-me-f516e.firebaseio.com/User/" + shipperInfo + ".json";
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
var shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
})
.then(function (shipperResult) {
//Send an email
const msg = {
to: "andrew#shlepme.com",
from: "support#shlepme.com",
subject: "New Follower",
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: "d1ccfeb9-2e2d-4979-a3ca-c53975fe486e",
substitutionWrappers: ["%", "%"],
substitutions: {
"%shipper_name%": "Test",
traveler_name: shipperResult.name
// and other custom properties here
}
};
console.log("Sending email");
console.log(shipperResult);
return sgMail.send(msg);
});
});

Mocking email function in nodejs

I've got a mailer function I've built and trying to shore up the coverage. Trying to test parts of it have proven tricky, specifically this mailer.smtpTransport.sendMail
var nodemailer = require('nodemailer')
var mailer = {}
mailer.smtpTransport = nodemailer.createTransport('SMTP', {
'service': 'Gmail',
'auth': {
'XOAuth2': {
'user': 'test#test.com',
'clientId': 'googleClientID',
'clientSecret': 'superSekrit',
'refreshToken': '1/refreshYoSelf'
}
}
})
var mailOptions = {
from: 'Some Admin <test#tester.com>',
}
mailer.verify = function(email, hash) {
var emailhtml = 'Welcome to TestCo. Click this '+hash+''
var emailtxt = 'Welcome to TestCo. This is your hash: '+hash
mailOptions.to = email
mailOptions.subject = 'Welcome to TestCo!'
mailOptions.html = emailhtml
mailOptions.text = emailtxt
mailer.smtpTransport.sendMail(mailOptions, function(error, response){
if(error) {
console.log(error)
} else {
console.log('Message sent: '+response.message)
}
})
}
I'm unsure of how to go about testing, specifically ensuring that my mailer.smtpTransport.sendMail function is passing the correct parameters without actually sending the email. I'm trying to use https://github.com/whatser/mock-nodemailer/tree/master, but I'm probably doing it wrong. Should I be mocking out the method?
var _ = require('lodash')
var should = require('should')
var nodemailer = require('nodemailer')
var mockMailer = require('./helpers/mock-nodemailer')
var transport = nodemailer.createTransport('SMTP', '')
var mailer = require('../../../server/lib/account/mailer')
describe('Mailer', function() {
describe('.verify()', function() {
it('sends a verify email with a hashto an address when invoked', function(done) {
var email ={
'to': 'dave#testco.com',
'html': 'Welcome to Testco. Click this bleh',
'text': 'Welcome to Testco. This is your hash: bleh',
'subject': 'Welcome to Testco!'
}
mockMailer.expectEmail(function(sentEmail) {
return _.isEqual(email, sentEmail)
}, done)
mailer.verify('dave#testco.com','bleh')
transport.sendMail(email, function() {})
})
})
You can use a 'Stub' transport layer on your test instead of SMTP.
var stubMailer = require("nodemailer").createTransport("Stub"),
options = {
from: "from#email.com",
to: "to#email.com",
text: "My Message!"
};
stubMailer.sendMail(options, function(err, response){
var message = response.message;
})
So, in that case, 'message' will be the email in text format. Something like this:
MIME-Version: 1.0
X-Mailer: Nodemailer (0.3.43; +http://www.nodemailer.com/)
Date: Fri, 25 Feb 2014 11:11:48 GMT
Message-Id: <123412341234.e23232#Nodemailer>
From: from#email.com
To: to#email.com
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
My Message!
For more examples, take a look at nodemailer test suite:
https://github.com/andris9/Nodemailer/blob/master/test/nodemailer-test.js
You can directly mock the sendMail function but it's not obvious how to access it from the tests. A Mailer instance is returned when you create a transport so you need to directly import that class in to your test.
const Mailer = require('nodemailer/lib/mailer')
Then you can mock or stub the sendMail method on the prototype in the usual way. Using Jasmine, you can do it like this:
beforeEach(function () {
spyOn(Mailer.prototype, 'sendMail').and.callFake(function (mailOptions, cb) {
cb(null, true)
})
})
The callFake ensures that the sendMail's callback is still executed encase you need to test what happens next. You can easily simulate an error by passing a first argument to cb: cb(new Error('Email failed'))
Now that the mock is set up, you can check everything is working as intended:
expect(Mailer.prototype.sendMail).toHaveBeenCalled()
expectEmail simply hooks into the transport layer, and expects you to identify the email ( return true if this is the email you are expecting ) by looking at the sentEmail contents.
In this case, return sentEmail.to === 'dave#testco.com' should suffice.
Keep in mind however, this module was designed in an environment where tests are ran in a random order and concurrently. You should propably randomize your data heavily to prevent collisions and false positives. BTW we use something like: var to = Date.now().toString(36) + Faker.Internet.email();
This example works fine for me
======== myfile.js ========
// SOME CODE HERE
transporter.sendMail(mailOptions, (err, info) => {
// PROCESS RESULT HERE
});
======== myfile.spec.js (unit test file) ========
const sinon = require('sinon');
const nodemailer = require('nodemailer');
const sandbox = sinon.sandbox.create();
describe('XXX', () => {
afterEach(function() {
sandbox.restore();
});
it('XXX', done => {
const transport = {
sendMail: (data, callback) => {
const err = new Error('some error');
callback(err, null);
}
};
sandbox.stub(nodemailer, 'createTransport').returns(transport);
// CALL FUNCTION TO TEST
// EXPECT RESULT
});
});

Resources