Async Await issues with NodeJS & AWS Lambda - node.js

I'm currently developing a Lamba function call for my AWS project, but with me not being a master at asynchronous functions it appears it's falling apart, the code I've put together is:
const AWS = require("aws-sdk");
const game = require('game-api');
const uuid = require("uuid");
AWS.config.update({
region: "us-east-1"
});
exports.handler = async (event, context, callback) => {
//set db
var documentClient = new AWS.DynamoDB.DocumentClient()
//params
const params = {
Item: {
'id': uuid.v1(),
'player_1_name': null,
'player_1_network': null,
'player_1_matches': 0,
'player_1_kills': 0,
'player_1_last_updated': 0,
'player_2_name': null,
'player_2_network': null,
'player_2_matches': 0,
'player_2_kills': 0,
'player_2_last_updated': 0,
'match_id': 0,
'status': 0
},
TableName : 'matches'
};
var matchData = JSON.parse(event.body);
//player 1
const player_1_name = matchData.player_1_name ? matchData.player_1_name : null;
const player_1_network = matchData.player_1_network ? matchData.player_1_network : null;
//player 2
const player_2_name = matchData.player_2_name ? matchData.player_2_name : null;
const player_2_network = matchData.player_2_network ? matchData.player_2_network : null;
//match data
const match_id = matchData.match_id ? matchData.match_id : 0;
//game object
let gameAPI = new game(
[
"email#email.com",
"password"
]
);
//gameAPI.login() returns a Promise()
await gameAPI.login().then(() => {
//check stats for player 1, getStats returns a Promise()
gameAPI.getStats(player_1_name, player_1_network).then(stats => {
params.Item.player_1_matches = stats.lifetimeStats.matches;
params.Item.player_1_kills = stats.lifetimeStats.kills;
}).catch(err => {
//error! we must work out what to do here!
console.log(err);
});
//example insert
documentClient.put(params, function(err, data){
return callback(err, data);
});
}).catch(err => {
console.log("We failed to login!");
console.log(err);
});
};
This logic seems flawed since nothing is being thrown to my AWS logs? my idea is to send the request to the function & have it do it as quickly as possible so I can send a 200 response back to Lambda, can anyone point me in the correct direction?

When using async/await you don't need to use callback neither you need to fall into the Promise Hell.
Just await on your promises and grab the result. The great advantage here is it looks as though your code is synchronous.
Here's your refactored code:
const AWS = require("aws-sdk");
const game = require('game-api');
const uuid = require("uuid");
AWS.config.update({
region: "us-east-1"
});
exports.handler = async (event) => {
//set db
var documentClient = new AWS.DynamoDB.DocumentClient()
//params
const params = {
Item: {
'id': uuid.v1(),
'player_1_name': null,
'player_1_network': null,
'player_1_matches': 0,
'player_1_kills': 0,
'player_1_last_updated': 0,
'player_2_name': null,
'player_2_network': null,
'player_2_matches': 0,
'player_2_kills': 0,
'player_2_last_updated': 0,
'match_id': 0,
'status': 0
},
TableName : 'matches'
};
var matchData = JSON.parse(event.body);
//player 1
const player_1_name = matchData.player_1_name ? matchData.player_1_name : null;
const player_1_network = matchData.player_1_network ? matchData.player_1_network : null;
//player 2
const player_2_name = matchData.player_2_name ? matchData.player_2_name : null;
const player_2_network = matchData.player_2_network ? matchData.player_2_network : null;
//match data
const match_id = matchData.match_id ? matchData.match_id : 0;
//game object
let gameAPI = new game(
[
"email#email.com",
"password"
]
);
//gameAPI.login() returns a Promise()
await gameAPI.login()
const stats = await gameAPI.getStats(player_1_name, player_1_network)
params.Item.player_1_matches = stats.lifetimeStats.matches;
params.Item.player_1_kills = stats.lifetimeStats.kills;
//example insert
await documentClient.put(params).promise();
};
If you need to handle the exceptions (you should) just wrap your await calls in try/catch blocks, like this:
try {
console.log(await somePromise)
} catch (e) {
console.log(e)
}
The snippet above is equivalent to:
somePromise.then(console.log).catch(console.log)
with the great difference that you don't need to chain promises/asynchronous code in order to keep the execution order, so I highly recommend you pick the async/await approach and forget about .then().catch()

Related

AWS Timestream - SDK V3 Nodejs, TimestreamWriteClient.send() - TypeError: command.resolveMiddleware is not a function. How to solve this?

I have the following lambda function in NodeJs 14.x using AWS SDK V3 for a timestream insertion process:
'use strict'
// https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-timestream-write/index.html
const { TimestreamWriteClient } = require("#aws-sdk/client-timestream-write")
const client = new TimestreamWriteClient({ region: process.env.region })
module.exports.fnPostElectricityTimestream = async event => {
try {
console.log('🚀 START fnPostElectricityTimestream')
const jsonBody = event
const topic = jsonBody.topic
const arrTopic = topic.split('/')
let dbName = arrTopic[4]
dbName = 'smaj56g' //Test
const currentTime = Date.now().toString() // Unix time in milliseconds get jsonBody.e_timestamp
const e_timestamp = (jsonBody.e_timestamp)*1000
const dimensions = [{
'Name': 'n',
'Value': 'v'
}]
const e_ch_1 = {
'Dimensions':dimensions,
'MeasureName': 'e_ch_1',
'MeasureValue': '[1,2,3]',
'MeasureValueType': 'VARCHAR',
'Time': currentTime
}
const records = [e_ch_1]
const params = {
DatabaseName: dbName,
TableName:'e_ch_1_v_w',
Records: records
}
const data = await client.send(params);
console.log('data', data)
return {
message: ''
}
} catch (error) {
console.log('🚀 fnPostElectricityTimestream - error.stack:', error.stack)
return {
message: error.stack
}
}
}
When I run the lambda this is the message I am getting:
2022-08-12T14:58:39.496Z e578a391-06b4-48a9-9f9d-9440a373c19e INFO 🚀 fnPostElectricityTimestream - error.stack: TypeError: command.resolveMiddleware is not a function
at TimestreamWriteClient.send (/var/task/node_modules/#aws-sdk/smithy-client/dist-cjs/client.js:13:33)
at Runtime.module.exports.fnPostElectricityTimestream [as handler] (/var/task/src/ElectricityTimestream/fnPostElectricityTimestream.js:38:31)
at Runtime.handleOnceNonStreaming (/var/runtime/Runtime.js:73:25)
There is something with const data = await client.send(params).
I am following the asyncawait code in this documentation.
How to solve this issue?
Your current insertion code is wrong. In order to write the records in the TimeStream, you need to use the WriteRecordsCommand command. Refer to the doc for a better understanding. Sample code:
import { TimestreamWriteClient, WriteRecordsCommand } from "#aws-sdk/client-timestream-write";
const client = new TimestreamWriteClient({ region: "REGION" }); //your AWS region
const params = {
DatabaseName: dbName, //your database
TableName: tableName, //your table name
Records: records //records you want to insert
}
const command = new WriteRecordsCommand(params);
const data = await client.send(command);
you need to create a command before calling send.
For example:
import { TimestreamWriteClient, CreateDatabaseCommand } from "#aws-sdk/client-timestream-write";
const params = {
DatabaseName: dbName,
TableName:'e_ch_1_v_w',
Records: records
}
const command = new CreateDatabaseCommand(params);
const data = await client.send(command);

Parameter obj to Document() must be an object when trying to convert array to mongoose document with redis

I have using redis to cache my queries. Its working fine with object but not when i get array. It gives me an error **"Parameter "obj" to Document() must be an object, got kids", **. It also happens with count query. Here is my code :
const mongoose = require("mongoose");
const redis = require("redis");
const util = require("util");
const client = redis.createClient(process.env.REDIS_URL);
client.hget = util.promisify(client.hget);
const exec = mongoose.Query.prototype.exec;
mongoose.Query.prototype.cache = async function (options = {}) {
this.useCache = true;
this.hashKey = JSON.stringify(options.key || "");
this.time = JSON.stringify(options.time || 36000);
return this;
};
mongoose.Query.prototype.exec = async function () {
if (!this.useCache) {
return exec.apply(this, arguments);
}
const key = JSON.stringify(
Object.assign({}, this.getQuery(), {
collection: this.mongooseCollection.name,
})
);
// client.flushdb(function (err, succeeded) {
// console.log(succeeded); // will be true if successfull
// });
const cacheValue = await client.hget(this.hashKey, key);
if (cacheValue) {
const doc = JSON.parse(cacheValue);
/*
this.model refers to the Class of the corresponding Mongoose Model of the query being executed, example: User,Blog
this function must return a Promise of Mongoose model objects due to the nature of the mongoose model object having other
functions attached once is created ( validate,set,get etc)
*/
console.log("Response from Redis");
console.log(doc);
console.log(Array.isArray(doc));
return Array.isArray(doc)
? doc.map((d) => new this.model(d))
: new this.model(doc);
}
//await the results of the query once executed, with any arguments that were passed on.
const result = await exec.apply(this, arguments);
client.hset(this.hashKey, key, JSON.stringify(result));
client.expire(this.hashKey, this.time);
console.log("Response from MongoDB");
return result;
};
module.exports = {
clearHash(hashKey) {
client.del(JSON.stringify(hashKey));
},
};
Data in redis - [ 'kids', 'men', 'women' ]
Query - const collectionType = await Product.find() .distinct("collectionType") .cache({ key: "COLLECTION_TYPE" });
can i anyone please tell me what i am doing wrong?
I have solved by directly returning the doc and its working fine. Not sure if it is the right way if i directly do return doc then sending data from redis only

AWS Sdk response not showing in Lambda Function

I am working on lambda function and creating a method for AWS-SDK historical metric report using node, js. The method is running successful but in response showing nothing. Have a look at the response.
Here is my code
function getKeyByValue(object, value) {
return Object.keys(object).find(key =>
object[key] === value);
}
exports.handler = async (event) => {
const AWS = require('aws-sdk');
var connect = new AWS.Connect({ apiVersion: '2017-08-08' });
let queueARN = event.queueARN || null;
const connectInstanceId = process.env.instanceID;
let flag =0, nextToken = null;
let queueARNsObject = {}, queueARNsArray=[], queueTypeObject={},listQueuesResult;
console.log('At line 12 entring do while loop....')
do{
console.log('How many times do I stay here???')
let listQueuesParams = {
InstanceId: connectInstanceId, /* required */
QueueTypes: [
"STANDARD",
],
NextToken: nextToken,
};
let listQueuesPromise = connect.listQueues(listQueuesParams).promise();
listQueuesResult = await listQueuesPromise;
// console.log(listQueuesResult);
listQueuesResult.QueueSummaryList.forEach(queue => {
if(queueARN != null){
if (queue.Arn == queueARN){
queueARNsArray = [queue.Arn];
queueARNsObject[queue.Name]= queue.Arn;
queueTypeObject[queue.QueueType]= queue.Arn;
flag = 1;
return;
}
}else{
queueARNsObject[queue.Name]= queue.Arn;
queueTypeObject[queue.QueueType]= queue.Arn;
queueARNsArray.push(queue.Arn);
nextToken = listQueuesResult.NextToken;
}
});
}while (flag=0 && nextToken != null);
const HistoricalMetrics = [
{
Name : "CONTACTS_HANDLED",
Unit : "COUNT",
Statistic : "SUM"
},
{
Name : "CONTACTS_ABANDONED",
Unit : "COUNT",
Statistic : "SUM"
},
];
// Metrics params
var getHistoricalMetricsParams = {
InstanceId: connectInstanceId,
StartTime: 1593099900,
EndTime: 1593129300,
Filters: {
Channels: ["VOICE"],
Queues: queueARNsArray
},
HistoricalMetrics: HistoricalMetrics,
Groupings: ["QUEUE"]
};
// console.log(getHistoricalMetricsParams);
// get current metrics by queues
var getHistoricalMetricsPromise = connect
.getMetricData(getHistoricalMetricsParams)
.promise();
var getHistoricalMetricsResult = await getHistoricalMetricsPromise;
console.log("historical metrics",getHistoricalMetricsResult);
// console.log("current |||||||| 1 metrics:", JSON.stringify(getCurrentMetricsResult));
let queueMetricsArray = [];
if(getHistoricalMetricsResult.MetricResults.length){
getHistoricalMetricsResult.MetricResults.forEach(queue => {
let queueMetrics = {
"Queue_Name" : getKeyByValue(queueARNsObject ,queue.Dimensions.Queue.Arn),
"CallsHandled": queue.Collections[0].Value,
"CallsAbanoded": queue.Collections[1].Value,
}
queueMetricsArray.push(queueMetrics);
console.log("TYPE||||", getKeyByValue(queueTypeObject ,queue.Dimensions.Queue.Arn))
});
}
const response = {
responseCode: 200,
metricResults: queueMetricsArray
};
return response;
};
I don't have any idea why it is not showing anything. if anyone of you knows please help me to fix it Thanks. I don't know what is Missing I've almost checked everything but I didn't get anything.
There are a few general areas you can look at:
Specify the region.
AWS.Connect({ apiVersion: '2017-08-08', region:'xxxxx' });
use Await directly with listQueues method
let listQueuesPromise = await connect.listQueues(listQueuesParams).promise();
Check Permissions - make sure there is sufficient authority
Lambda Configuration - increase timeout and memory size
PS: What did console log listQueuesPromise return?

my node.js code work unstable in aws-lambda, Sometimes it works fine, sometimes skipping the data persistence that piece of code

The data center uploads a csv file to our S3 bucket every five minutes, which triggers my lambda function to read the file and save the data to DynamoDB. But the code that performs data persistence is not stable, sometimes it will be executed, and sometimes it will be skipped completely. This makes me very confused. Here is my code.
var AWS = require('aws-sdk');
var csvtojson = require('csvtojson');
var encoding = require('text-encoding');
AWS.config.update({
accessKeyId: '********',
secretAccessKey: '**********',
region: 'us-west-2',
sslEnabled:false
});
var s3 = new AWS.S3();
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = async (event) => {
try {
console.log(event);
console.log(event['Records'][0]['s3']['object']['key']);
//get the file name
let key = event['Records'][0]['s3']['object']['key'];
let date = `${key}`.slice(24,36);
console.log(date);
let getObject = {Bucket: 'saas-status-mockup-data', Key: `${key}`};
//get the object
let response = await s3.getObject(getObject).promise();
//transfer to csv
let csvFile= new encoding.TextDecoder("utf-8").decode(response.Body);
//transfer to json
let res = await csvtojson().fromString(csvFile);
console.log(res);
await res.map(async(item,key) => {
console.log(item);
let putParams = {};
if(item.FARM=="SMAX Internal production Functionalities"){
putParams.TableName = 'InternalProductionDb';
} else if(item.FARM=="SMAX Trial Major Functionalities"){
putParams.TableName = 'TrialMajorDb';
} else {
console.error(item);
}
putParams.Item = {
'Date' : {
S:`${date}${item.BUSINESS_PROCESS}`
},
'StatusId':{
S:`${date}${item.BUSINESS_PROCESS}`
},
'BusinessProcess':{
S:`${item.BUSINESS_PROCESS}`
},
'Status':{
S:`${item.STATUS}`
}
};
console.log(putParams);
//put data to dynamoDB, But sometimes this code sometimes does not execute.
let putRes = await ddb.putItem(putParams).promise();
console.dir(putRes);
});
}
catch(error){
console.error(error);
return error;
}
};
Array.map() returns an array not a Promise so you cannot await it (e.g. await res.map() in your code).
First, you should collect a list of promises and the use Promise.all() to wait for all of them.
exports.handler = async (event) => {
try {
console.log(event);
console.log(event['Records'][0]['s3']['object']['key']);
//get the file name
let key = event['Records'][0]['s3']['object']['key'];
let date = `${key}`.slice(24,36);
console.log(date);
let getObject = {Bucket: 'saas-status-mockup-data', Key: `${key}`};
//get the object
let response = await s3.getObject(getObject).promise();
//transfer to csv
let csvFile= new encoding.TextDecoder("utf-8").decode(response.Body);
//transfer to json
let res = await csvtojson().fromString(csvFile);
console.log(res);
// Construct the list of promises.
const promises = res.map((item, key) => {
console.log(item);
let putParams = {};
if(item.FARM=="SMAX Internal production Functionalities"){
putParams.TableName = 'InternalProductionDb';
} else if(item.FARM=="SMAX Trial Major Functionalities"){
putParams.TableName = 'TrialMajorDb';
} else {
console.error(item);
}
putParams.Item = {
'Date' : {
S:`${date}${item.BUSINESS_PROCESS}`
},
'StatusId':{
S:`${date}${item.BUSINESS_PROCESS}`
},
'BusinessProcess':{
S:`${item.BUSINESS_PROCESS}`
},
'Status':{
S:`${item.STATUS}`
}
};
console.log(putParams);
//put data to dynamoDB, But sometimes this code sometimes does not execute.
return ddb.putItem(putParams).promise();
});
// Wait for all promises to finish.
return Promise.all(promises)
}
catch(error){
console.error(error);
return error;
}
};

Getting timestamp of individual alexa responses in dynamodb

I am creating an Alexa factskill that asks the user for some information on their health, and users respond with a score from 1-10 depending on the level of pain in different areas. I then input the data into a DynamoDB table, which stores the information(score out of 10) for the four health questions (swelling, feeling, sleeping, breathing) and the user ID. However, the entries are not giving a timestamp for when they were created. I was wondering if there was a way to make a timestamp for preferably every health question response, but also a timestamp for the entire entry would help. Would I have to use any external SDKs, as I was looking up DynamoDB documentations and didn't find out any way to add a timestamp.
Below is my index.js code for my Lambda function that is used for my Alexa skill.
'use strict';
const Alexa = require('alexa-sdk');
const SKILL_NAME = 'Home Assist';
const HELP_MESSAGE = 'You can say I want to input my data';
const HELP_REPROMPT = 'What can I help you with?';
const STOP_MESSAGE = 'Goodbye!';
const handlers = {
'LaunchRequest': function () {
this.emit('HomeAssistQuestions');
},
'HomeAssistQuestions': function () {
this.attributes.healthscores = {
'patientID' : 0,
'scores': {
'feeling': {
'score': 0
},
'sleeping': {
'score': 0
},
'breathing': {
'score': 0
},
'swollen': {
'score': 0
}
}
};
if(this.event.request.dialogState !== 'COMPLETED'){
this.emit(':delegate');
}
else{
const feelingScore = this.event.request.intent.slots.feelingRating.value;
const sleepingScore = this.event.request.intent.slots.sleepingRating.value;
const breathingScore = this.event.request.intent.slots.breathingRating.value;
const swollenScore = this.event.request.intent.slots.SwollenRating.value;
const id = this.event.request.intent.slots.id.value;
this.attributes.healthscores.patientID = id;
this.attributes.healthscores.scores['feeling'].score = feelingScore;
this.attributes.healthscores.scores['sleeping'].score = sleepingScore;
this.attributes.healthscores.scores['breathing'].score = breathingScore;
this.attributes.healthscores.scores['swollen'].score = swollenScore;
this.response.speak("Health Scores Recorded");
this.emit(':responseReady');
}
},
'AMAZON.HelpIntent': function () {
const speechOutput = HELP_MESSAGE;
const reprompt = HELP_REPROMPT;
this.response.speak(speechOutput).listen(reprompt);
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function () {
this.response.speak(STOP_MESSAGE);
this.emit(':responseReady');
},
'AMAZON.StopIntent': function () {
this.response.speak(STOP_MESSAGE);
this.emit(':responseReady');
},
'SessionEndedRequest': function(){
this.emit('saveState', true);
}
};
exports.handler = function (event, context, callback) {
const alexa = Alexa.handler(event, context, callback);
alexa.dynamoDBTableName = 'HealthScores';
alexa.APP_ID = "amzn1.ask.skill.d5b8d597-eb50-41c6-a22d-b0f18c23b544";
alexa.registerHandlers(handlers);
alexa.execute();
};
You can try something like this:
...
this.attributes.healthscores.scores['swollen'].score = swollenScore;
this.attributes.healthscores.timestamp = Date.now();
this.response.speak("Health Scores Recorded");
...

Resources