[EDIT]
I am new to AWS and I've been trying to make a real-time chat application. I saw this code, so I might as well try this. I tried to follow everything but it seems there is a problem.
[END]
I got an error with this code stating, Cannot read "domainName" of undefined which got me confused. Is this because of my API Gateway? I already connected this to another route named "Message" and I already have the integration request of my connect and disconnect.
Below is my code:
const AWS = require('aws-sdk');
const db = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
require('./index.js');
let send = undefined;
function init(event) {
console.log(event);
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
});
send = async (connectionId, data) => {
await apigwManagementApi.postToConnection({
ConnectionId: connectionId,
Data: `Echo: ${data}` }).promise();
}}
exports.handler = (event, context, callback) => {
init(event);
let message = JSON.parse(event.body).message;
getConnections().then((data) => {
console.log(data.Items);
callback(null, {
statusCode: 200,
});
data.Items.forEach(function(connection) {
console.log("Connection " +connection.connection_id);
send(connection.connection_id, message);
});
});
return {};
};
function getConnections(){
const params = {
TableName: 'GroupChat',
}
return db.scan(params).promise();
}
Related
In our scalable AWS serverless application (DynamoDB, Lambda, API Gateway WebSockets) we want to broadcast messages from a Lambda to very many connected browser sessions simultaneously.
We have many topics, and to limit the load on the system we want to limit each browser session to subscribe to max. two topics.
The AWS example app linked in this official tutorial does this by looping through the connectionIds and doing an await postToConnection for each of them, which doesn't scale well as each postToConnection takes 40 ms (average):
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = async function (event, context) {
let connections;
try {
connections = await ddb.scan({ TableName: process.env.table }).promise();
} catch (err) {
return {
statusCode: 500,
};
}
const callbackAPI = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint:
event.requestContext.domainName + '/' + event.requestContext.stage,
});
const message = JSON.parse(event.body).message;
const sendMessages = connections.Items.map(async ({ connectionId }) => {
if (connectionId !== event.requestContext.connectionId) {
try {
await callbackAPI
.postToConnection({ ConnectionId: connectionId, Data: message })
.promise();
} catch (e) {
console.log(e);
}
}
});
try {
await Promise.all(sendMessages);
} catch (e) {
console.log(e);
return {
statusCode: 500,
};
}
return { statusCode: 200 };
};
How can we make this broadcast scalable?
I am trying to implement fleet provisioning in AWS Lambda function. As a starting point, I have this code:
'use strict';
var AWS = require('aws-sdk');
var iot = new AWS.Iot({
endpoint: 'apiendpoint',
accessKeyId: "AAAABBBBBCCCCDDDDD",
secretAccessKey: "AAAAABBBBCCCDD/1234122311222",
region: 'ap-south-1'
});
exports.handler = async (event, context) => {
var params = {
setAsActive: true
};
return {
statusCode: 200,
body:JSON.stringify(await createCertAndKey(params))
}
}
const createCertAndKey = async (params) => {
return new Promise((resolve, reject) => {
iot.createKeysAndCertificate(params, function(err, data){
if(err){
console.log(err);
reject(err)
}
else{
console.log("success?");
resolve(data)
}
})
})
}
I get a ResourceNotFound exception for calling createKeysAndCertificate. I also tried calling other functions of iot, but it gives the same exception.
What am I doing wrong here?
endpoint passed when intialzing an object should be generic AWS service in format https://{service}.{region}.amazonaws.com. we don't need to pass it, AWS will assume based on region and object we are initializing.
var iot = new AWS.Iot({
endpoint: 'iot.ap-south-1.amazonaws.com',
accessKeyId: "AAAABBBBBCCCCDDDDD",
secretAccessKey: "AAAAABBBBCCCDD/1234122311222",
region: 'ap-south-1'
});
So I have 3 lambdas that connects to the routes of the API Gateway. One(connectFunction)for $connect, One(disconnectFunction) for $disconnect, and another one(onMessageFunction) for my added route named onMessage.
My problem is I declared const usernameid=event.queryStringParameters.usernameid in my lambda for $connect and I will use that usernameid in my onMessage route. How can I do this?
Below is my snippet code for connectFunction lambda and onMessageFunction lambda:
connectFunction
exports.handler = (event, context, callback) => {
const connection_Id = event.requestContext.connection_Id;
const usernameid = event.queryStringParameters.usernameid;
addConnection(connection_Id,usernameid).then(() => {
callback(null, {
statusCode: 200,
});
});};
onMessageFunction
let sending = undefined;
function init(event) {
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
});
sending = async (connection_Id, data) => {
await apigwManagementApi.postToConnection({
Connection_Id: connection_Id,
Data: `Echo: ${data}`}).promise();
}}
The code is correct but the output is Echo: data and I want to be the username which I inputted and the data so dev(this is the username):data should be the output
I am trying to scan the Dynamodb table form my following code, can anyone please guide me what is wrong here.
const AWS = require("aws-sdk");
const dynamodb = new AWS.DynamoDB({
region: "eu-west-1",
apiVersion: "2012-08-10"
});
exports.handler = async (event, callback) => {
const params = {
TableName: "job_Status"
};
dynamodb.scan(params, (err, data) => {
if (err) {
console.log(err);
callback(err);
} else {
console.log(data);
callback(null, data);
}
});
};
I have given full dynamodb access role to the function but still it gives me the null response. Any idea what can be wrong here?
Response:
I tried with dynaomClient which not working too.
const AWS = require("aws-sdk");
const db = new AWS.DynamoDB.DocumentClient({
region : 'eu-west-1'
});
exports.handler = async (event, callback) => {
const params = {
TableName: "job_Status"
};
db.scan(params, (err, data) => {
if (err) {
console.log(err);
callback(err);
} else {
console.log(data);
callback(null, data);
}
});
};
Your Lambda function is async but your code uses callbacks. By the time the callback is reached, your function has already been terminated because it ran asynchronously. I'd speculate that the null output you see is the return value from the Lambda function, not your console.log.
Replace your call to scan with the following:
try{
let results = await db.scan(params).promise()
console.log(results);
} catch(err){
console.log(err)
}
For more info, check out the AWS documentation about working with promises.
I've created a lambda and cloud formation template which grants a lambda access to the parameter store and secrets manager. When I test the lambda I have the following functions outside of the export.handler function:
function getParameterFromStore(param){
let promise = new Promise(function(resolve, reject){
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, function(err, data){
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
} else {
resolve(data);
}
});
});
let parameterResult = promise.then(function(result){
console.log('---- result: '+ JSON.stringify(result));
return result;
});
return parameterResult;
};
servmgr is instantiated as var servmgr = new AWS.SSM();
When I call this function from the export.handler function I do so as:
myFirstParam = { Path : '/myPath/Service/servicesEndpoint'};
let endpointResult = getParameterFromStore(myFirstParam);
In the lambda I have the function retrieve the parameter defined outside of the export.handler function bt wrapped in a promise.
When I run/test this lambda the object returned is always undefined... I get Parameters[] back but no values.
2019-02-20T21:42:41.340Z 2684fe88-d552-4560-a477-6761f2de6717 ++ /myPath/Service/serviceEndpoint
2019-02-20T21:42:41.452Z 2684fe88-d552-4560-a477-6761f2de6717 ---- result: {"Parameters":[]}
How do you get parameter values returned back to a lambda at run time?
update
based upon the suggestion/answer from Thales I've simplified the lambda to just this:
const getParameterFromStoreAsync = (param) => {
return new Promise((resolve, reject) => {
servmgr.getParametersByPath(param, (err, data) => {
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
}
return resolve(data);
});
});
};
exports.handler = async(event, ctx, callback) => {
console.log('INFO[lambda]: Event: [' + JSON.stringify(event, null, 2) + ']');
console.log('this is the event' + JSON.stringify(event));
sfdcEndPointParam = { Path : '/PartnerBanking/Service/SfdcEndpoint'};
let myendpoint = await getParameterFromStoreAsync(sfdcEndPointParam);
console.log('### endpoint path: ' + JSON.stringify(myendpoint));
done = ()=>{}
callback(null, done());
};
I am still seeing an empty array being returned in my tests:
### endpoint path: {"Parameters":[]}
I've also moved the function into the callback as
exports.handler = (event,ctx, callback){
done = async()=>{
console.log('this is the event' + JSON.stringify(event));
sfdcEndPointParam = { Path : '/PartnerBanking/Service/SfdcEndpoint'};
let myendpoint = await getParameterFromStoreAsync(sfdcEndPointParam);
console.log('### endpoint path: ' + JSON.stringify(myendpoint));}
}
callback(null, done());
Same result ... empty array. Any additional things to try?
This is because your getParameterFromStore returns before your then() code is executed, thus parameterResult is undefined. If you don't want to change your code too much, I would return the Promise you create, like this:
function getParameterFromStore(param){
return new Promise(function(resolve, reject){
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, function(err, data){
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
} else {
resolve(data);
}
});
});
};
And finally, on your function's client, you can get the result like this:
const myFirstParam = { Path : '/myPath/Service/servicesEndpoint'}
getParameterFromStore(myFirstParam).then(console.log)
When coding in NodeJS, however, I highly recommend you use async/await instead, so you'll be able to escape the Promise Hell (chaninig Promise after Promise in order to achieve something "synchronously")
When using async/await, you can design your code as though it was synchronous. Here's a refactored version of your example, using async/await as well as arrow functions:
const getParameterFromStore = param => {
return new Promise((resolve, reject) => {
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, (err, data) => {
if (err) {
console.log('Error getting parameter: ' + err, err.stack)
return reject(err);
}
return resolve(data);
});
})
}
exports.handler = async (event) => {
const endpointResult = await getParameterFromStore(event.someAttributeFromTheEventThatYouWantToUse)
console.log(endpointResult)
};
EDIT: After the OP fixed the first issue, I created a working example on my own. It turned out that the way the OP was invoking the API was incorrect.
Here's the full working example:
'use strict';
const AWS = require('aws-sdk')
AWS.config.update({
region: 'us-east-1'
})
const parameterStore = new AWS.SSM()
const getParam = param => {
return new Promise((res, rej) => {
parameterStore.getParameter({
Name: param
}, (err, data) => {
if (err) {
return rej(err)
}
return res(data)
})
})
}
module.exports.get = async (event, context) => {
const param = await getParam('MyTestParameter')
console.log(param);
return {
statusCode: 200,
body: JSON.stringify(param)
};
};
Mind the Name attribute which must be provided as part of the API call to the ServiceManager.getAttribute method.
This attribute is stated in the official docs
I have run this myself and here's the output in CloudWatch Logs:
As you can see, the value was returned successfully.
Hope this helps!
If your lambda is deployed on VPC, make sure that Security Group is attached to it and outbound traffic is allowed. It will be able to access parameter store automatically.
https://aws.amazon.com/premiumsupport/knowledge-center/lambda-vpc-parameter-store/
A simpler solution would be:
const getParameterFromStore = (params) => servmgr.getParametersByPath(params).promise();
const myFirstParam = { Path : '/myPath/Service'};
getParameterFromStore(myFirstParam).then(console.log);
As you can see, the SDK itself provides utility functinality that you can use depending on your needs to use in an async or syncronious fashion.
Hope it helps.