Reputation: 47
Writing a lambda function to perform some DB queries and then email certain groups of users.
Members are receiving the email twice.
The app is a music streaming app where users can create songs. They can also create groups, invite members to those groups and share their songs to these groups.
Here is the lambda called via API:
const shareWithGroup = async event => {
const { songCuid, groupCuids } = JSON.parse(event.body);
const shareSongDB = await query(
sql.queryShareWithGroup(songCuid, groupCuids),
); //share to group in DB
if (!shareSongDB) {
return corsUtil.failureWithCors("Couldn't Share Song with group");
}
const song = await query(sql.queryRead(songCuid));
if (!song) {
return corsUtil.failureWithCors('Song doesnt exist');
}
const songTitle = song.rows[0].songTitle; //retrieve songTitle
const promises = groupCuids.map(async groupCuid => {
console.log('GROUP_CUID', groupCuid);
const emailResults = await query(sql.queryReadGroupEmails(groupCuid)); // get emails for group + groupName
const results = emailResults.rows;
const groupName = results[0].groupName;
let emails = [];
results.map(row => {
emails.push(row.email); //push email address into array
});
const payload = JSON.stringify({ groupName, emails, songTitle }); //send groupName, emails list and songTitle to SNS to trigger email
console.log(payload);
await publishSNS(payload)
//send the topic
});
//Resolve all promises
await Promise.all(promises);
console.log(promises);
return corsUtil.successWithCors('Success');
};
const publishSNS = async payload => {
console.log('publishing sns topic');
//SEND EMAILS
const params = {
Message: payload,
TopicArn: `arn:aws:sns:eu-west-1:${process.env.AWS_ACC_ID}:${process.env.STAGE}-songShareTrigger`,
};
return await sns
.publish(params, async error => {
if (error) {
console.error(error);
//TODO: Actually fail the function - can't do with lambdaFactory
}
})
.promise();
};
And a sample request (share a song to 1 group):
{"songCuid":"XXX","groupCuids":["XXX"]}
The issue is, that somehow, even though it should only send 1 SNS topic, it sends 2. When I share a song to 2 groups, it sends 4 SNS topics.
Here is the lambda that is triggered by the SNS topic:
const aws = require('aws-sdk');
const ses = new aws.SES();
const corsUtil = require('../utils/corsUtil');
exports.songShareEmail = (event, context) => {
console.log('EVENT : ', event.Records[0].Sns); // LOG SNS
const body = JSON.parse(event.Records[0].Sns.Message);
const { groupName, emails, songTitle } = body;
console.log('Body : ', body);
const groupInviteEmailData = `
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<div class="email-body" style="max-width:900px; margin:auto;" >
<div class="content" style="background-color:white; margin:0 auto;
display:block;">
<p>Hello<p>
</br>
A new song has been shared to the group : <u>${groupName}</u><br>
<h3>Song Title: ${songTitle}</h3>
</div>
</div>
</body>
</html>
`;
var params = {
Destination: {
ToAddresses: emails,
},
Message: {
Body: {
Html: {
Charset: 'UTF-8',
Data: groupInviteEmailData,
},
},
Subject: {
Charset: 'UTF-8',
Data: `New song shared to ${groupName}`,
},
},
Source: 'xxx',
};
console.log('sendingEmail');
ses.sendEmail(params, function(err) {
if (err) {
console.log(err);
const response = corsUtil.failureWithCors(err);
context.fail(response);
} else {
context.succeed('Done');
}
});
};
I have logged the SNS object from the event, and I can see that it is a new MessageId each time meaning that it is in fact sending 2 topics each time, and it's not that the lambda is getting triggered twice from the same SNS.
Is there any way around this?
Upvotes: 2
Views: 2021
Reputation: 2186
The selected answer is half way right.
I also had this same issue. You need to either use SNS with a callback or use SNS setting the api version in the constructor and using promise()
const sns = new SNS({ apiVersion: '2010-03-31' });
await sns
.publish(
{
Message: 'Message',
TopicArn: <Arn>,
},
)
.promise();
If you do not pass in the apiVersion in the constructor you have to pass in a callback and not use promise()
Upvotes: 5
Reputation: 31
I had the same pb for an iotData, i use something like this to solve my problem
const request = iotdata.publish(mqttParams);
request
.on('success', () => console.log("Success"))
.on('error', () => console.log("Error"))
return new Promise(() => request.send());
Upvotes: 0
Reputation: 624
I know this issue is old, but I found myself having the same problem. I solved my mistake and I believe you have the same exact problem.
The .promise()
in your publishSNS
method is not needed, just remove it (commented out below).
const publishSNS = async payload => {
console.log('publishing sns topic');
//SEND EMAILS
const params = {
Message: payload,
TopicArn: `arn:aws:sns:eu-west-1:${process.env.AWS_ACC_ID}:${process.env.STAGE}-songShareTrigger`,
};
return await sns
.publish(params, async error => {
if (error) {
console.error(error);
//TODO: Actually fail the function - can't do with lambdaFactory
}
});
//.promise();
};
Upvotes: 3