NodeMailer Gmail API in AWS Lambda not working - amazon-web-services

I'm using nodemailer with gmail API to send mails.
Following code :-
var nodemailer = require('nodemailer');
var transporter = await nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'xx#usr.com',
pass: 'xxx'
}
});
console.log("Starting");
await transporter.sendMail({
from: 'xx#google.com',
to: 'xx#google.com',
subject: 'Hello !',
text: "Hello"
}, function(data, info){
});
Code is working perfectly on local & sending mails.
But, when used inside lambda nothing is happening. Function getting executed successfully.

The error would probably be in the (1) code, (2) IAM permissions (for SES, see Nodemailer SES doc, Connecting to the Amazon SES SMTP Endpoint and SES FAQ) or
(3) networking (if lambda runs from private VPC and networking isn't fully configured).
Or maybe its working fine (see #TheProgrammer's comment in this question)
To view and understand the error, try the following:
Adding logging to the error callback of sendMail method (see this):
transporter.sendMail({
from: 'sender#example.com',
to: 'recipient#example.com',
subject: 'Message',
text: 'I hope this message gets delivered!'
}, (err, info) => {
console.log(info.envelope);
console.log(info.messageId);
console.log(err);
});
Viewing the error in CloudWatch logs (accessed directly from View logs in CloudWatch, docs)

Related

Get Google Business Notifications from push Pub/Sub

I'm trying to be notified when a new review is added on my Google Business Profile.
According to the documentation, I have setup the notification but I got nothing when a new review is added.
First of all, I have created a Pub/Sub Topic projects/my-project/topics/business-profile-notifications.
Then, I have created a Push subscription projects/my-project/subscriptions/business-profile-notifications-push attached to the previous created Topic. I have also defined an endpoint: https://my-endpoint/webhook. This endpoint is listening POST requests
Finally, I have added the service account mybusiness-api-pubsub#system.gserviceaccount.com into IAM with Pub/Sub admin role.
On the code side, I'm using NPM googleapis client in a TypeScript Node.js server.
I'm updating the account settings to setup the notifications:
const { data }: GaxiosResponse<mybusinessnotifications_v1.Schema$NotificationSetting> = await google.mybusinessnotifications({
version: 'v1',
auth,
}).accounts.updateNotificationSetting({
name: `accounts/${params.accountID}/notificationSetting`,
updateMask: 'notification_types',
requestBody: {
name: `accounts/${params.accountID}/notificationSetting`,
pubsubTopic: 'projects/my-project/topics/business-profile-notifications',
notificationTypes: [
'NEW_REVIEW',
'UPDATED_REVIEW',
],
},
});
At this point, nothing happens when a new review is added.
When I'm sending a POST request on my endpoint via curl command curl -X POST -H "Content-Type: application/json" -i "https://my-endpoint/webhook", the request is successfully catched.
In the other hand, when I'm getting notifications settings from the configured account, I have the notifications types but not any subscribed topic:
const { data }: GaxiosResponse<mybusinessnotifications_v1.Schema$NotificationSetting> = await google.mybusinessnotifications({
version: 'v1',
auth,
}).accounts.getNotificationSetting({
name: `accounts/${accountID}/notificationSetting`,
fields: 'pubsubTopic,notificationTypes',
});
Response:
{
"notificationTypes": [
"NEW_REVIEW",
"UPDATED_REVIEW"
]
}
What I forgot to do ?
I resolved the issue by myself 😁
In the documentation of updateNotificationSetting method, is it stated that "The only editable field is notificationSetting" about the updateMask field. But it's wrong. I had to add pubsubTopic as value.
Finally, the parameter values of this method are:
const opts = {
name: `accounts/${params.accountID}/notificationSetting`,
updateMask: 'notificationTypes,pubsubTopic',
requestBody: {
name: `accounts/${params.accountID}/notificationSetting`,
pubsubTopic: params.pubsubTopic,
notificationTypes: params.notificationTypes,
},
};
const { data }: GaxiosResponse<mybusinessnotifications_v1.Schema$NotificationSetting> = await google.mybusinessnotifications({
version: 'v1',
auth,
}).accounts.updateNotificationSetting(opts);

gmail users.watch API 400 Bad Request using domain wide delegation

Getting 400 Bad Request when calling users.watch Gmail API from GCP Cloud Function
I'm trying to automate the call to watch() on a users GSuite email account. I have already given domain-wide access to the default service account of the Cloud Function by following the steps outlined in the link below
https://developers.google.com/admin-sdk/reports/v1/guids/delegation
I have authorized the following scopes:
profile,email, https://mail.google.com/,https://www.googleapis.com/auth/gmail.metadata, https://www.googleapis.com/auth/gmail.modify, https://www.googleapis.com/auth/gmail.readonly
Deployed Cloud Function code:
exports.watchEmail = async () => {
console.info('authenticating');
const auth = await google.auth.getClient({
scopes: ['profile',
'email',
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.metadata',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.readonly'],
});
console.info('<-- authenticated');
console.info('watch on email', 'no-reply#telico.ca');
const api = google.gmail({ version: 'v1', auth });
const response = await api.users.watch({
userId: '<USER_EMAIL>',
requestBody: {
topicName: 'projects/<PROJECT_ID>/topics/processEmail',
labelIds: ["INBOX"]
}
});
console.info('<-- watch on file', JSON.stringify(response));
};
When executing the CloudFunction, I am seeing Error: Bad Request at Gaxios.request printed in the logs.

Not able to send SMS using AWS SNS

I want to send SMS to Indian numbers using AWS SNS service. I am using node.js code to trigger SNS APIs to send SMS. Success rate of SMS delivery is too low.
Not sure what can be done.
I checked post AWS SNS is not sending SMS anymore and Amazon SNS -- Change Sender ID
Also, raised query on AWS Forum - https://forums.aws.amazon.com/thread.jspa?threadID=303941
but could not get any pointers. Few more things done:
increased SMS maxPrice to $5 USD
Cloudwatch too does not show real time logs.
SMS API does not give any error in response
This is my code:
exports.sendSMS = function(msg, phoneNumber){
phoneNumber = "+91" + phoneNumber;
setMessageAttributes();
createSubscription(phoneNumber);
var params = {
Message: msg,
PhoneNumber: phoneNumber
};
// Create promise and SNS service object
var publishTextPromise = sns.publish(params).promise();
// Handle promise's fulfilled/rejected states
publishTextPromise.then(
function(data) {
console.log("MessageID is , SMS sent ------" + data.MessageId);
console.log("JSON.stringify ------" + JSON.stringify(data));
}).catch(
function(err) {
console.error(err, err.stack);
});
}
function setMessageAttributes(){
var messageParams = {
attributes: {
'DefaultSMSType': 'Transactional',//Also tried setting this to OTP, no use
'DefaultSenderID': 'DevCare',
}
};
function createSubscription(endpoint){
// Create subscribe/email parameters
var subscribeParams = {
Protocol: 'SMS',
TopicArn: 'arn:aws:sns:us-east-1:xxxxxxx:US-xxxx',
Endpoint: endpoint
};
This is how response looks like for failed SMS:
Subscription ARN is arn:aws:sns:us-east-1:351564314200:US-sendSMS:0725db58-xxxc-4764-af31-6825xxxxxx
MessageID is ------d5212bf1-9183-5da4-826e-7e96a48xxxxx
JSON.stringify ------{"ResponseMetadata":{"RequestId":"5d9bxxx9-319d-52b4-8979-xxxxxxxx"},"MessageId":"d521xxxxf1-91xx-5da4-826e-xxxxxxxx"}
Any help is highly appreciated. So that I can make a choice to go ahead or not with AWS SNS API.

AWS Lambda using firebase-admin initializeApp timeout

I use Lambda to Firebase message. I ref this. But the lambda function still timeout because it cannot connect to google server.
Handler.js
/ [START imports]
const firebase = require('firebase-admin');
const serviceAccount = require("../serviceAccount.json");
module.exports.message = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const registrationToken = "xxxxxxx";
const payload = {
data: {
score: "850",
time: "2:45"
}
};
// [START initialize]
if(firebase.apps.length == 0) { // <---Important!!! In lambda, it will cause double initialization.
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: 'https://messaging-xxxxx.firebaseio.com'
});
}
// Send a message to the device corresponding to the provided
// registration token.
firebase.messaging().sendToDevice(registrationToken, payload)
.then(function(response) {
// See the MessagingDevicesResponse reference documentation for
// the contents of response.
console.log("Successfully sent message:", response);
callback(null, {
statusCode: 200,
body: JSON.stringify("Successful!"),
});
})
.catch(function(error) {
console.log("Error sending message:", error);
callback(null, {
statusCode: 500,
body: JSON.stringify({
"status": "error",
"message": error
})
})
});
};
CloudWatch
[Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "connect ETIMEDOUT 172.217.26.45:443".]
But I use same serviceAccount.json to run on my ec2 and work find.
Does someone encounter this?
After a couple hours struggling, I finally find the reason.
Because my Lambda using VPC to connect RDS and the network interface of VPC only have private IP.
AWS document:
When you add VPC configuration to a Lambda function, it can only access resources in that VPC. If a Lambda function needs to access both VPC resources and the public Internet, the VPC needs to have a Network Address Translation (NAT) instance inside the VPC.
So I need to create NAT inside the VPC.
I follow this Blog and problem solved.

How can i confirm the subscription request HTTP from amazon SNS

I have been searching all over the web and nothing gives a clear answer to confirm the subscription request from amazon SNS. I already send the subscription from the amazon console to my website, but what's next? I am using amazon EC2 as my server with PHP.
Before you even configure the HTTP/HTTPS endpoint subscription through AWS management console, you need to make sure that the HTTP or HTTPS endpoint of your PHP web site has the capability to handle the HTTP POST requests that Amazon SNS generates. There are several types of SNS messages: SubscriptionConfirmation, Notification and UnsubscribeConfirmation. Your PHP code needs to get the header x-amz-sns-message-type from request and process it based on the message type. For SubscriptionConfirmation message, your PHP application needs to process the POST message body, which is a JSON document. In order to subscribe the topic, your PHP code needs to visit the "SubscriberURL" specified in the JSON body. Optionally, you should verify the signature to make sure the authenticity of message before subscribing the topic.
You can find more details on AWS documentation: http://docs.aws.amazon.com/sns/latest/dg/SendMessageToHttp.html
Here is an express application (Node.js) which confirms the SNS subscription:
const express = require('express')
const request = require('request')
// parse urlencoded request bodies into req.body
const bodyParser = require('body-parser')
const app = express()
const port = 8080
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post('/', (req, res) => {
let body = ''
req.on('data', (chunk) => {
body += chunk.toString()
})
req.on('end', () => {
let payload = JSON.parse(body)
if (payload.Type === 'SubscriptionConfirmation') {
const promise = new Promise((resolve, reject) => {
const url = payload.SubscribeURL
request(url, (error, response) => {
if (!error && response.statusCode == 200) {
console.log('Yess! We have accepted the confirmation from AWS')
return resolve()
} else {
return reject()
}
})
})
promise.then(() => {
res.end("ok")
})
}
})
})
app.listen(port, () => console.log('Example app listening on port ' + port + '!'))
To use it one needs to install required packages:
yarn add express request body-parser
Once you confirm the subscription AWS will send a POST request to the server with the following content:
{
"Type": "SubscriptionConfirmation",
"MessageId": "XXXXXXXX-1ee3-4de3-9c69-XXXXXXXXXXXX",
"Token": "SECRET_TOKEN",
"TopicArn": "arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test",
"Message": "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test. To confirm the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL": "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test&Token=SECRET_TOKEN",
"Timestamp": "2018-11-21T19:48:08.170Z",
"SignatureVersion": "1",
"Signature": "SECRET",
"SigningCertURL": "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pem"
}
The payload contains SubscribeURL which is requested by the server.
The end point you have specified will get data from AWS SNS endpoint verification service, The same end point will be used to verify the end point and to get notifications from aws,
Simply dump the input sent by AWS SNS into one text file like,
$json_write_to_text = json_decode(file_get_contents("php://input"));
You will find all data sent by AWS SNS, but just find SubscriptionUrl (which will be specific for endpoint having valid token), Open this in browser you will have SubscriptionConfirmation status. That's it
Enjoy.
Spring cloud SNS subscription with annotation
spring cloud AWS has support for auto confirmation of subscriber, you just need to put this annotation "#NotificationSubscriptionMapping"
#Controller
#RequestMapping("/topicName")
public class NotificationTestController {
#NotificationSubscriptionMapping
public void handleSubscriptionMessage(NotificationStatus status) throws IOException {
//We subscribe to start receive the message
status.confirmSubscription();
}
#NotificationMessageMapping
public void handleNotificationMessage(#NotificationSubject String subject, #NotificationMessage String message) {
// ...
}
#NotificationUnsubscribeConfirmationMapping
public void handleUnsubscribeMessage(NotificationStatus status) {
//e.g. the client has been unsubscribed and we want to "re-subscribe"
status.confirmSubscription();
}
}
http://cloud.spring.io/spring-cloud-aws/spring-cloud-aws.html#_sns_support
I solved this using NodeJS backend. Lets say you have an API like this in HapiJS (Well it doesnt matter you can have another tech)
{
method: 'POST',
path: '/hello',
handler: ( request, reply ) => {
reply( Hello.print(request.payload) );
},
config: {
tags: ['api']
}
}
Just pass the payload you receive, on to your business logic.
In the business logic process it like this
'use strict';
const request = require('request');
exports.print = (payload) => {
payload = JSON.parse(payload);
if(payload.Type === 'SubscriptionConfirmation'){
return new Promise((resolve, reject) => {
const url = payload.SubscribeURL;
request(url, (error, response) => {
if (!error && response.statusCode == 200) {
console.log('Yess! We have accepted the confirmation from AWS');
return resolve();
}
else
return reject();
});
});
}
I am using request module from NPM to automatically accept such requests.
Another way would be to print the contents of payload and then click on the URL given in payload.SubscribeURL.
Once AWS accepts it you check the confirmation on the Subscriptions page where Subscription ARN would be changed from Pending Confirmation to a complex name-cum-SHA having your Topic name.