I have an AWS Lambda function that sends an email through SNS, with a Node.js 10.x runtime. The event object contains a variable called client_id that corresponds to a Raspberry Pi device id in IoT Core service. I see the following in the Cloudwatch logs for the Lambda function:
console.log("EVENT: \n" + JSON.stringify(event, null, 2));
2020-09-05T00:21:35.587Z 229b8110-852a-4abe-8b9c-4d06a121d63f INFO EVENT:
{
"client_id": "RaspberryPi001"
}
I need to extract the client_id from the event and include it in the Message text for the email, in order to specify which device is issuing an email alert. How do I extract the client_id and include it in the email Message text?
'use strict';
var AWS = require('aws-sdk');
AWS.config.update({region: 'us-west-2'});
exports.handler = async (event, context, callback) => {
// Create publish parameters
var params = {
Message: `The air quality in your building has triggered a High Risk Alert.`,
Subject: `Air Quality - High Risk Alert`,
TopicArn: `arn:aws:sns:us-west-2:xyz`,
};
// Create promise and SNS service object
var publishTextPromise = new AWS.SNS({apiVersion: '2010-03-31'}).publish(params).promise();
// Handle promise's fulfilled/rejected states
publishTextPromise.then(
function(data) {
console.log("Message: " + params.Message);
console.log("Sent to the topic: " + params.TopicArn);
console.log("MessageID is " + data.MessageId);
console.log("EVENT: \n" + JSON.stringify(event, null, 2));
}).catch(
function(err) {
console.error(err, err.stack);
});
const data = await publishTextPromise;
return data;
};
Thanks!
Related
I am trying to use SNS located in São Paulo (sa-east-1) from a lambda function (Node.js 8.10) on Ohio (us-east-2). This is the first time I try to use a AWS service located in another region. So far, this is what I am doing:
//init aws resources
const AWS = require('aws-sdk');
const sns = new AWS.SNS({apiVersion: '2010-03-31', region: 'sa-east-1'});
//promisefy AWS.SNS.createPlatformEndpoint method
snsCreatePlatformEndpoint = params => new Promise(
(resolve, reject)=>{
sns.createPlatformEndpoint(params, function(error, data){
if (error) { reject(error); }
else { resolve(data); }
});
}
);
exports.handler = (awsEvent, context, callback) => {
//parse stuff in here
...
HandleToken(token, callback);
};
async function HandleToken(token, callback){
try{
let params = {
PlatformApplicationArn: process.env.PlatAppArn,
Token: token,
};
console.log('params:', params); // this prints as expected
let {EndpointArn} = await snsCreatePlatformEndpoint(params);
console.log('It should pass through here'); // it is not printed
//returns a success response
...
} catch (error) {
//returns an error response
...
}
}
I have set a really high timeout for my lambda function: 5mins.
I also have tested the same code on a lambda function located in São Paulo(sa-east-1), and it works.
I have been receiving the following error on my client:
"Request failed with status code 504"
"Endpoint request timed out"
Question: How can I use SNS in another AWS region correctly?
You shouldn't need to do any special setup beyond setting the region.
E.g., I use the following pattern to send notifications from us-east-1 to Tokyo (ap-northeast-1):
// this lambda runs in us-east-1
let AWS = require("aws-sdk");
AWS.config.update({ region: "ap-northeast-1" }); // asia-pacific region
exports.handler = async (event, context) => {
var params = {
Message: 'my payload',
TopicArn: 'arn:aws:sns:ap-northeast-1:xxxxxx:tokyoSNS'
};
let SNS = new AWS.SNS({apiVersion: '2010-03-31'});
var data = await SNS.publish(params).promise();
// check if successful then return
}
No endpoints, etc., was setup. Are you required to run your lambda in a VPC? That's the only complication I can think of at the moment.
i can't get the Credentials for my CognitoIdentity. When the User is successfully authenticated, he needs to get a Identity to access other AWS Services. In my case thats AWS IoT. But for somehow, i can't get any credentials.
This is the Error Message:
Error retrieving credentials: NotAuthorizedException: Access to
Identity 'eu-central-1:XXXXXXXXXX' is
forbidden.
My Code is almost exactly like the Tutorial on Github:
var cognitoUser = new AWSCognito.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log("Logged in");
console.log('access token + ' + result.getAccessToken().getJwtToken());
// window.location.href = "index.html";
AWS.config.region = AWSConfiguration.region;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: AWSConfiguration.IdPoolId,
Logins : {
'cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXX' : result.getIdToken().getJwtToken()
}
});
var cognitoIdentity = new AWS.CognitoIdentity();
AWS.config.credentials.get(function(err, data) {
if (!err) {
console.log('retrieved identity: ' + AWS.config.credentials.identityId);
var params = {
IdentityId: AWS.config.credentials.identityId
};
cognitoIdentity.getCredentialsForIdentity(params, function(err, data) {
if (!err) {
thingShadows.updateWebSocketCredentials(data.credentials.AccessKeyId,
data.credentials.SecretKey,
data.credentials.SessionToken);
}
else {
console.log('error retrieving credentials: ' + err);
}
});
}
else {
console.log('error retrieving identity:' + err);
}
});
}
});
Please note that i skipped not related code.
authenticated users have full access to all AWS services i'm using.
I don't think you need to call cognitoIdentity.getCredentialsForIdentity(). Your IAM keys should be put into the AWS.config.credentials object when you call AWS.config.credentials.get(). You can access them directly in the callback you provide when you call it.
In other words, when you're logging out the retrieved identity: to the console, the credentials object should already have your secret key, access key id, and session token in it.
All of this (give or take a curly brace):
var params = {
IdentityId: AWS.config.credentials.identityId
};
cognitoIdentity.getCredentialsForIdentity(params, function(err, data) {
if (!err) {
thingShadows.updateWebSocketCredentials(data.credentials.AccessKeyId,
data.credentials.SecretKey,
data.credentials.SessionToken);
}
else {
console.log('error retrieving credentials: ' + err);
}
});
Can probably be replaced with something like this:
thingShadows.updateWebSocketCredentials(AWS.config.credentials.accessKeyId,
AWS.config.credentials.secretKey,
AWS.config.credentials.sessionToken);
If you pass in a Logins map with the user pool id and access token in it, the getCredentialsForIdentity() call might succeed; I didn't test it. I haven't yet run into a use case where I needed to use this particular API, and I suspect you don't need it either.
Source: I work on a 100% javascript application that uses both authenticated and unauthenticated Cognito identities. We don't call getCredentialsForIdentity() anywhere, and trying to insert it produced the same error you're getting.
I am using the following function from Google Gmail APIs to get all messages from users
/**
* Retrieve Messages in user's mailbox matching query.
*
* #param {String} userId User's email address. The special value 'me'
* can be used to indicate the authenticated user.
* #param {String} query String used to filter the Messages listed.
* #param {Function} callback Function to call when the request is
complete.
*/
function listMessages(userId, query, callback) {
var getPageOfMessages = function(request, result) {
request.execute(function(resp) {
result = result.concat(resp.messages);
var nextPageToken = resp.nextPageToken;
if (nextPageToken) {
request = gapi.client.gmail.users.messages.list({
'userId': userId,
'pageToken': nextPageToken,
'q': query
});
getPageOfMessages(request, result);
} else {
callback(result);
}
});
};
var initialRequest = gapi.client.gmail.users.messages.list({
'userId': userId,
'q': query
});
getPageOfMessages(initialRequest, []);
}
I get the following error:
var initialRequest = gapi.client.gmail.users.messages.list({
^
ReferenceError: gapi is not defined
I tried replacing gapi with google since I have initialized var google for api module but that didn't work. Where am I going wrong?
I was facing the same problem..
GAPI is used for Client-side javascript only, not server side with Nodejs.
1) After you set up the sample from Node.js Quickstart
https://developers.google.com/gmail/api/quickstart/nodejs
2) Try to use this function to List and Get the recent email from your account:
function getRecentEmail(auth) {
const gmail = google.gmail({ version: 'v1', auth });
gmail.users.messages.list({ auth: auth, userId: 'me', maxResults: 1, q: 'subject:()' }, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
// Get the message id which we will need to retreive tha actual message next.
var message_id = response['data']['messages'][0]['id'];
// Retreive the actual message using the message id
gmail.users.messages.get({ auth: auth, userId: 'me', 'id': message_id }, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
// console.log(response['data']);
var to = response['data']['payload']['headers'][4].value;
var from = response['data']['payload']['headers'][5].value;
var subject = response['data']['payload']['headers'][6].value;
var email_body_raw = response['data']['payload']['body']['data'];
// Convert encoded email body message into human readable text message
data = email_body_raw;
buff = new Buffer.from(data, 'base64');
email_body_readable = buff.toString();
console.log('content:\n' + email_body_readable + '\n__________\nto: ' + to + '\nfrom: ' + from + '\nsubject: ' + subject + '\n__________\n');
});
});
}
fs.readFile('credentials.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
//authorize(JSON.parse(content), sendMessage);
authorize(JSON.parse(content), getRecentEmail);
});
https://developers.google.com/gmail/api/v1/reference/users/messages/list
https://developers.google.com/gmail/api/v1/reference/users/messages/get
Hope this helps!
This is the code I have for publishing to the topic (I have changed the target and topic arn for security reasons):
var AWS = require("aws-sdk");
var sns = new AWS.SNS();
var targetArn = 'arn:aws:sns:us-east-1:4363657289:endpoint/GCM/APP_NAME/3185sfdnfe283925sgSeaa0e';
var topicArn = 'arn:aws:s-s:us-east-1:4363657289436:TOPIC_NAME';
var payload = {
GCM: {
data: {
message: "test"
}
}
};
payload.GCM = JSON.stringify(payload.GCM);
payload = JSON.stringify(payload);
var params= {
TopicArn: topicArn,
TargetArn: targetArn,
Message: payload,
MessageStructure: 'json'
};
var responsefromSNS = sns.publish(params , function(error, data) {
if (error) {
console.log("ERROR: " + error.stack);
}
else {
console.log("SENT DATA: " + JSON.stringify(data));
context.done(null, data);
}
});
console.log(responsefromSNS);
My issue is that I never see log statements from either the if or else block and the push notification never reaches the mobile app. I have consulted both the AWS JavaScript SDK Documentation and countless stack overflow posts about this and nothing that I have tried works. And, I have given the lambda function permission to publish to the topic.
---UPDATE-----
I have changed my code a bit and now it looks like this:
var AWS = require("aws-sdk");
AWS.config.update({region:'us-east-1'});
var topicarn = 'arn:aws:s-s:us-east-1:927579412028:alexapushdemo';
var targetarn = 'arn:aws:sns:us-east-1:927579412028:endpoint/GCM/automation.home.visa.com.homeautomation/3af761b2-1955-34d8-b66a-85e232e0aa0e';
var payload = {
default: "test",
GCM: {
data: {
message: "test"
}
}
};
payload.GCM = JSON.stringify(payload.GCM);
payload = JSON.stringify(payload);
var sns = new AWS.SNS();
console.log('start of sns function')
sns.publish({
TargetArn: targetarn,
Message: payload,
MessageStructure: 'json'
}, function(err, data) {
if (err) {
console.log(err.stack);
// Notify Lambda that we are finished, but with errors
context.done(err, 'sns function finished with errors!');
return;
}
console.log('push sent');
console.log(data);
// Notify Lambda that we are finished
context.done(null, 'sns function finished!');
});
console.log('end of sns functions');
The error I get is:
ConfigError: Missing region in config\\n
at Request.VALIDATE_REGION (/node_modules/aws-sdk/lib/event_listeners.js:81:45)\\n
at Request.callListeners (/node_modules/aws-sdk/lib/sequential_executor.js:105:20)\\n
at callNextListener (/node_modules/aws-sdk/lib/sequential_executor.js:95:12)\\n
at /node_modules/aws-sdk/lib/event_listeners.js:75:9\\n
at finish (/node_modules/aws-sdk/lib/config.js:228:7)\\n
at /node_modules/aws-sdk/lib/config.js:268:9\\n
at resolveNext (/node_modules/aws-sdk/lib/credentials/credential_provider_chain.js:84:9)\\n
at /node_modules/aws-sdk/lib/credentials/credential_provider_chain.js:97:11\\n
at /node_modules/aws-sdk/lib/credentials.js:123:23\\n
at /node_modules/aws-sdk/lib/credentials/ec2_metadata_credentials.js:66:7\\"\",\"ip\":\"127.0.0.1\"}",
Why am I getting this even though I'm calling AWS.config.update.
iram,
If I take your exact code and paste it into a Lambda Node.js 4.3 function and execute a test from the Lambda Console, this is the result:
ERROR: InvalidParameter: Invalid parameter: TopicArn Reason: Both TopicArn and TargetArn specified. Use only one or the other
This means that in your params, you need to comment out either TopicArn or TargetArn or put in some logic to determine if the incoming payload contains an Arn that is a target endpoint or a topic endpoint.
You could still have permissions issues with Lambda execution role to SNS or to CW Logs, however, regardless if you have permission to publish or send logs to CloudWatch from your Lambda function, running a test from the console will always spit out some logging of what's going on.
Good luck.
const AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
specify the regeion to be used by the aws sdk like this
I am streaming data to Amazon Kinesis, and I use Amazon Lambda to handle data and write it to DynamoDB.
My Lambda code:
var doc = require('dynamodb-doc');
var dynamo = new doc.DynamoDB();
exports.handler = function(event, context) {
//console.log('Received event:', JSON.stringify(event, null, 2));
event.Records.forEach(function(record) {
// Kinesis data is base64 encoded so decode here
var payload = new Buffer(record.kinesis.data, 'base64').toString('ascii');
console.log('Decoded payload:', payload);
var tableName = "_events";
var datetime = new Date().getTime().toString();
dynamo.putItem({
"TableName": tableName,
"Item" : {
"eventID" : record["eventID"],
"eventName" : payload
}
}, function(err, data) {
if (err) {
console.log("dynamodb error: " + err);
context.done('error putting item into dynamodb failed: '+err);
}
else {
console.log('great success: '+JSON.stringify(data, null, ' '));
context.succeed('K THX BY');
}
});
});
// context.succeed("Successfully processed " + event.Records.length + " records.");
};
When I run test, data is successfully saved to DynamoDB. But when I stream the real data, it doesn't happen, while logs show that data was received by lambda function.
Also console.log() function doesn't work in putItem() block, so I have no idea how to debug this problem.
The problems were:
1. I didn't set the correct permissions
2. I just did't wait enough so the data could be processed by the lambda function.