Part of my lambda function executes twice - aws-lambda

I'm writing AWS lambda function that should update daily statistics of a player each time when (s)he performs a drill. Function is being triggered once, with 0 retries configured, on new DynamoDB row event.
I'm testing the function alone, I'm certain I create only one row in DynamoDB that triggers lambda execution. Now to the funny part. My lambda is executed once as a whole, but some part of it is executed twice within the same request.
Here's the lambda code:
const AWS = require('aws-sdk');
var docClient = new AWS.DynamoDB.DocumentClient({region: 'us-west-2'});
const dailyStatsTableName = 'xxx';
const weeklyStatsTableName = 'yyy';
const monthlyStatsPlayerTableName = 'zzz';
const requestsTableName = 'qqq';
exports.handler = async (event, context, callback) => {
//Ensure idempotency of lambda as a whole
let requestID = context.awsRequestId;
let requestAlreadyProcessed = false;
await getRequest(requestsTableName, requestID, (err, data) => {
if (err) {
console.log(err);
} else {
if (data.Item) {
requestAlreadyProcessed = true;
}
}
});
if (requestAlreadyProcessed) {
console.log("This request has already been processed. Aborting.");
return;
}
console.log("Processing new assigned drill performance event. RequestID: " + requestID);
console.log(event);
const record = event.Records[0].dynamodb;
console.log(record);
if (!record || !record.NewImage) {
console.log("New record image undefined");
return;
}
console.log(record.NewImage);
//Get performed touches to count them in statistics
let touches = 0;
try {
touches = parseInt(record.NewImage.touches.N, 10);
} catch (error) {
console.error(error);
}
//Unpack date from record.NewImage.createdAt string
let performanceDate = new Date(Date.parse(record.NewImage.createdAt.S));
console.log("CreatedAt date: " + performanceDate);
//Get daily statistics object from table - I want to update if already exist
let dailyStats = {
drillID: record.NewImage.drillID.S,
targetUserID: record.NewImage.targetUserID.S,
performDay: performanceDate.getDate(),
performMonth: performanceDate.getMonth(),
performYear: performanceDate.getFullYear(),
performDate: performanceDate.toISOString(),
touches: 0,
id: undefined
};
let result = await getDailyStats(dailyStatsTableName, dailyStats.performDay, dailyStats.performMonth, dailyStats.performYear, dailyStats.drillID, dailyStats.targetUserID, (err, data) => {
if (err) {
console.log(err);
} else {
if (data.Items.length !== 0) {
console.log("Found daily stats object"); //this console.log is logged twice. Everything below that line is executed twice.
dailyStats = data.Items[0];
}
}
return "success";
});
//Create or update daily statistics
if (!dailyStats.id) {
console.log("DailyStats ID not found. Creating new with touches " + touches);
dailyStats.touches = touches;
result = await createDailyStats(dailyStatsTableName, dailyStats, requestID, (err, data) => {
if (err) {
console.log(err);
} else {
console.log("Success creating daily stats " + dailyStats.drillID + " " + dailyStats.targetUserID + " " + dailyStats.touches);
}
return "success";
});
} else {
console.log("DailyStats ID found. Updating existing with touches " + touches);
result = await updateDailyStats(dailyStatsTableName, dailyStats.id, touches, requestID, (err, data) => {
if (err) {
console.log(err);
} else {
console.log("Success updating daily stats " + dailyStats.drillID + " " + dailyStats.targetUserID + " " + touches);
}
return "success";
});
}
//Mark this request as processed to ensure idempotency of lambda as a whole
result = await createProcessedRequest(requestsTableName, requestID, (err, data) => {
if (err) {
console.log(err);
} else {
console.log("Success creating processed request " + requestID);
}
return "success";
});
return "success";
};
function createDailyStats(tableName, stats, requestID, callback) {
let now = new Date();
let dateTimeString = now.toISOString();
let params = {
TableName:tableName,
Item:{
"__typename": "PersonalDrillDailyStatistic",
"createdAt": dateTimeString,
"updatedAt": dateTimeString,
"id": stats.id ? stats.id : createUUID(),
"drillID": stats.drillID,
"targetUserID": stats.targetUserID,
"touches": stats.touches,
"performDay": stats.performDay,
"performMonth": stats.performMonth,
"performYear": stats.performYear,
"lastRequestID": requestID
}
};
console.log("Adding a new daily stats (with id) item... " + stats.drillID + " " + stats.targetUserID + " " + stats.touches);
return docClient.put(params, callback).promise();
}
function updateDailyStats(tableName, statsID, touches, requestID, callback) {
var params = {
TableName:tableName,
Key:{
"id": statsID
},
UpdateExpression: "set touches = touches + :val, lastRequestID = :reqID",
ExpressionAttributeValues:{
":val": touches,
":reqID": requestID
},
ConditionExpression: "lastRequestID <> :reqID", //conditional check exception is being thrown during second call, hence Exception in logs output
ReturnValues:"UPDATED_NEW"
};
console.log("Updating daily stats (with id) item... " + statsID + " " + touches);
return docClient.update(params, callback).promise();
}
function getDailyStats(tableName, performDay, performMonth, performYear, drillID, targetUserID, callback) {
console.log("Querying for daily statistics |" + performDay + "." + performMonth + "." + performYear + "| userID: |" + targetUserID + "| drillID: |" + drillID + "| from table " + tableName);
let params = {
TableName: tableName,
FilterExpression: "drillID = :drill_id and targetUserID = :user_id and performDay = :day and performMonth = :month and performYear = :year",
ExpressionAttributeValues: {
":drill_id": drillID,
":user_id": targetUserID,
":day": performDay,
":month": performMonth,
":year": performYear,
}
};
return docClient.scan(params, callback).promise();
}
function createUUID(){
(...)
}
function getRequest(tableName, requestID, callback) {
let params = {
TableName: tableName,
Key: {
"id": requestID
}
};
return docClient.get(params, callback).promise();
}
function createProcessedRequest(tableName, requestID, callback) {
let params = {
TableName:tableName,
Item:{
"id": requestID,
"name": requestID
}
};
return docClient.put(params, callback).promise();
}
Here's CloudWatch output for this lambda execution:
2021-04-16T22:24:49.754+02:00 START RequestId: 8766c005-c1f3-42fb-aee9-9e8352da67ed Version: $LATEST
2021-04-16T22:24:50.464+02:00 2021-04-16T20:24:50.464Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO Processing new assigned drill performance event. RequestID: 8766c005-c1f3-42fb-aee9-9e8352da67ed
2021-04-16T22:24:50.523+02:00 2021-04-16T20:24:50.523Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO { Records: [ { eventID: 'e09a400894c178ef66840f54e71b6c26', eventName: 'INSERT', eventVersion: '1.1', eventSource: 'aws:dynamodb', awsRegion: 'us-west-2', dynamodb: [Object], eventSourceARN: 'arn:aws:dynamodb:us-west-2:900776852541:table/FDrillPerformance-y22t7izqyvb2xiruvbf4zhadvm-fissiondev/stream/2021-04-15T21:48:06.158' } ] }
2021-04-16T22:24:50.541+02:00 2021-04-16T20:24:50.541Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO { ApproximateCreationDateTime: 1618604689, Keys: { id: { S: '7321fcf5-fed2-402a-b1cf-7667e958f73a' } }, NewImage: { createdAt: { S: '2021-04-16T20:24:49.077Z' }, touches: { N: '25' }, __typename: { S: 'FDrillPerformance' }, drillID: { S: '01adc7e6-67be-4bdf-828b-36833cbd7070' }, targetUserID: { S: 'd4a95710-c4fb-4f0f-8355-76082e41c43a' }, id: { S: '7321fcf5-fed2-402a-b1cf-7667e958f73a' }, updatedAt: { S: '2021-04-16T20:24:49.077Z' } }, SequenceNumber: '218143500000000009280556527', SizeBytes: 269, StreamViewType: 'NEW_IMAGE' }
2021-04-16T22:24:50.561+02:00 2021-04-16T20:24:50.561Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO { createdAt: { S: '2021-04-16T20:24:49.077Z' }, touches: { N: '25' }, __typename: { S: 'FDrillPerformance' }, drillID: { S: '01adc7e6-67be-4bdf-828b-36833cbd7070' }, targetUserID: { S: 'd4a95710-c4fb-4f0f-8355-76082e41c43a' }, id: { S: '7321fcf5-fed2-402a-b1cf-7667e958f73a' }, updatedAt: { S: '2021-04-16T20:24:49.077Z' } }
2021-04-16T22:24:50.581+02:00 2021-04-16T20:24:50.581Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO CreatedAt date: Fri Apr 16 2021 20:24:49 GMT+0000 (Coordinated Universal Time)
2021-04-16T22:24:50.581+02:00 2021-04-16T20:24:50.581Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO Querying for daily statistics |16.3.2021| userID: |d4a95710-c4fb-4f0f-8355-76082e41c43a| drillID: |01adc7e6-67be-4bdf-828b-36833cbd7070| from table PersonalDrillDailyStatistic-y22t7izqyvb2xiruvbf4zhadvm-fissiondev
2021-04-16T22:24:50.784+02:00 2021-04-16T20:24:50.783Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO Found daily stats object
2021-04-16T22:24:50.784+02:00 2021-04-16T20:24:50.784Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO DailyStats ID found. Updating existing with touches 25
2021-04-16T22:24:50.784+02:00 2021-04-16T20:24:50.784Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO Updating daily stats (with id) item... ab00afe1-ed4b-4895-b1bb-31ac570fe46d 25
2021-04-16T22:24:50.883+02:00 2021-04-16T20:24:50.883Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO Found daily stats object
2021-04-16T22:24:51.302+02:00 2021-04-16T20:24:51.302Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO Success updating daily stats 01adc7e6-67be-4bdf-828b-36833cbd7070 d4a95710-c4fb-4f0f-8355-76082e41c43a 25
2021-04-16T22:24:51.401+02:00 2021-04-16T20:24:51.384Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO ConditionalCheckFailedException: The conditional request failed at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:688:14) at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10) at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12) at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10 at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9) at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:690:12) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18) { code: 'ConditionalCheckFailedException', time: 2021-04-16T20:24:51.383Z, requestId: 'H8PC553OQABGJKR9KFIJMR1EHJVV4KQNSO5AEMVJF66Q9ASUAAJG', statusCode: 400, retryable: false, retryDelay: 36.82252581429517 }
2021-04-16T22:24:51.462+02:00 2021-04-16T20:24:51.462Z 8766c005-c1f3-42fb-aee9-9e8352da67ed INFO Success creating processed request 8766c005-c1f3-42fb-aee9-9e8352da67ed
2021-04-16T22:24:51.482+02:00 END RequestId: 8766c005-c1f3-42fb-aee9-9e8352da67ed
2021-04-16T22:24:51.482+02:00 REPORT RequestId: 8766c005-c1f3-42fb-aee9-9e8352da67ed Duration: 1728.11 ms Billed Duration: 1729 ms Memory Size: 128 MB Max Memory Used: 91 MB Init Duration: 413.89 ms
So, you can see that even though lambda is called once, some parts of it are called twice, and I don't know how to deal with it. I need to update weekly and monthly statistics as well, so the problem will get more complicated. Hack with ConditionExpression on last request ID works, but I'd ensure that code inside my function is not invoked twice rather than try to do such workarounds.

Following comment of #Jason Wadsworth, I changed all function invocations in exports.handler function by removing callback functions. Please note that I left callback parameters in dynamo related functions like updateDailyStats - without it, for some reason code didn't write to database.

Related

Unable to store timestamp in Dynamo DB using Nodejs 16 version Lambda

I am unable to store timestamp in Dynamo DB using Node JS Lambda. i am new to Node JS.
while run the test case, it was stored. but while trigger this lambda function getting error.
i was try to insert below timestamp
LastUpdateTimestamp
The date and time this contact was last updated, in UTC time.
Type: String (yyyy-mm-ddThh:mm:ssZ)
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();
exports.handler = async (event) => {
var surveyResults = {};
var data = event.Details.ContactData.Attributes; // extract the contact attributes from the Amazon Connect event
Object.keys(data).forEach(element => {
if (element.startsWith("survey_result_")) {
surveyResults[element] = { S: data[element] };
}
});
var params = {
TableName: process.env.TABLE,
Item: {
contactId: { S: event.Details.ContactData.ContactId},
date: { S: event.Details.ContactData.LastUpdateTimestamp},
surveyId: { S: event.Details.ContactData.Attributes.surveyId },
...surveyResults
}
}
try {
await ddb.putItem(params).promise(); // write the object to the DynamoDB table
} catch (err) {
console.log(err);
}
const response = {
statusCode: 200,
body: JSON.stringify('OK'),
};
return response;
};
Test Data:
{
"Name": "ContactFlowEvent",
"Details": {
"ContactData": {
"Attributes": {
"surveyId": "123456",
"survey_result_1": "4",
"survey_result_2": "5"
},
"Channel": "VOICE",
"ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"LastUpdateTimestamp": "2023-02-14T06:52:29Z",
"CustomerEndpoint": {
"Address": "+11234567890",
"Type": "TELEPHONE_NUMBER"
},
"InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"InitiationMethod": "API",
"InstanceARN": "arn:aws:connect:us-east-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa",
"MediaStreams": {
"Customer": {
"Audio": {
"StartFragmentNumber": "91343852333181432392682062622220590765191907586",
"StartTimestamp": "1565781909613",
"StreamARN": "arn:aws:kinesisvideo:us-east-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806"
}
}
},
"PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"Queue": null,
"SystemEndpoint": {
"Address": "+11234567890",
"Type": "TELEPHONE_NUMBER"
}
},
"Parameters": {}
}
}
Error:
2023-02-15T14:10:26.426Z 94fad25e-bcdb-443b-95c0-47640bfaba34 INFO ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes
at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:686:14)
at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:688:12)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
code: 'ValidationException',
time: 2023-02-15T14:10:26.328Z,
requestId: 'F5JJOCF20D507JCFRO7FEMAH6VVV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 15.458319111471575
}
You state that ...surveyResults contains the following:
"survey_result_1": "4",
"survey_result_2": "5"
This is not DynamoDB JSON. You need to modify the values to suit the correct format as you have done with the other values.
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();
exports.handler = async (event) => {
var surveyResults = {};
var data = event.Details.ContactData.Attributes; // extract the contact attributes from the Amazon Connect event
Object.keys(data).forEach(element => {
if (element.startsWith("survey_result_")) {
surveyResults[element] = { S: data[element] };
}
});
var contact_params = {
ContactId: event.Details.ContactData.InitialContactId, /* required */
InstanceId: 'place your instance id' /* required */
};
var connect = new AWS.Connect();
console.log('describeContact');
/*connect.describeContact(contact_params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});*/
const attributes = await connect.describeContact(contact_params).promise();
console.log(attributes)
console.log(typeof attributes.Contact.LastUpdateTimestamp)
var agent_params = {
InstanceId: 'place your instance id', /* required */
UserId: attributes.Contact.AgentInfo.Id /* required */
};
const agent = await connect.describeUser(agent_params).promise();
let agent_username = "";
if(agent_username != null){
agent_username = agent.User.Username;
}
console.log(agent)
var params = {
TableName: process.env.TABLE,
Item: {
contactId: { S: event.Details.ContactData.ContactId},
surveyId: { S: event.Details.ContactData.Attributes.surveyId },
date: { S: attributes.Contact.LastUpdateTimestamp.toLocaleDateString()},
time: { S: attributes.Contact.LastUpdateTimestamp.toLocaleTimeString()},
agentname: {S: agent_username},
...surveyResults
}
}
try {
await ddb.putItem(params).promise(); // write the object to the DynamoDB table
} catch (err) {
console.log(err);
}
const response = {
statusCode: 200,
body: JSON.stringify('OK'),
};
return response;
};

how to force a AWS Lambda to complete all the code

I have a lambda that is inserting a record into an Airtable db. This should be pretty straight forward and I have it working locally.
Once I put it into Lambda the record is not inserted. I have been messing with this for 2 days and can't figure out why the record won't load into Airtable.
The only other note that matters is that this lambda is long polling for SQS messages.
console.log('Loading function');
const Airtable = require('airtable');
const apiKey = process.env.AIRTABLE_API_KEY;
const BaseTable = process.env.AIRTABLE_BASE_TABLE;
var base = new Airtable({
apiKey: apiKey
}).base(BaseTable);
const table = base('Contacts');
exports.handler = async (event) => {
for (const {
messageId,
body
} of event.Records) {
console.log("iteration: " + iteration);
const data = JSON.parse(body);
var fields = {
"Full Name (First Middle Last)": data.firstname + ' ' + data.lastname,
"Email": data.email,
"Phone": data.phonenumber,
"Address": data.address_one + ' ' + data.address_two,
"City": data.city,
"State/Province": data.state,
"Postal Code": data.zip,
"Country": data.country,
"Birthday": data.dob,
};
insertRecord(fields);
}
console.log('before the end');
};
function insertRecord(fields) {
console.log('insert Record');
return new Promise((resolve, reject) => {
table.create([{
fields: fields
}], function(err, records) {
if (err) {
console.log('ERROR');
console.log(err);
reject(err);
} else {
console.log('SUCCESS');
resolve(records);
}
});
});
}
I have been googling, and reviewing, others' answers for multiple days now.

Node.js getitem from dynamoDB and pass variable

I'm working on some labmda code in Node.js, and I want to pass an item gotten from DynamoDB with getitem to some code. Here's what I have:
const Alexa = require('ask-sdk');
const AWS = require ('aws-sdk');
AWS.config.update({region: 'us-east-1'});
//replace dynamo later with dynamo
dynamodb = new AWS.DynamoDB();
//const appId = 'REPLACE WITH YOUR SKILL APPLICATION ID';
const date = new Date(Date.now()).toString();
const date2 = date.substring(0,16);
const time = date.substring(16,24);
const LaunchRequestHandler = {
canHandle(handlerInput) {
//first we assign the requestEnvelope request to the request variable
const request = handlerInput.requestEnvelope.request;
//conditions to determine the requests this handler can handle
//now using the request variable, we return true if it equals the one we want
//in this case LaunchRequest
return request.type === 'LaunchRequest'
},
handle(handlerInput) {
//execution logic for the handler
// ie What exactly do we do
const speechOutput =
"Welcome to Track it, you can tell me who you lent things to, \
or where you put an item. For example I lent the saw to Mike, or I put the saw in the workshop."
return handlerInput.responseBuilder
.speak(speechOutput)
.getResponse();
}
}
const GaveIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'GaveIntent';
},
handle(handlerInput,event) {
// setting variable attributes to handle things like counters
const attributes = handlerInput.attributesManager.getSessionAttributes();
// personName and itemName are pulling the slots information from the intent
const personName = handlerInput.requestEnvelope.request.intent.slots.lenderPerson.value;
const itemName = handlerInput.requestEnvelope.request.intent.slots.storedObject.value;
const currentUser = handlerInput.requestEnvelope.session.user.userId;
//begin DB code
var params = {
TableName: 'TrackItDB',
Item: {
'userID' : {S: currentUser},
'storedObject' : {S: itemName},
'lenderPerson' : {S: personName},
'objectStatus' : {S: 'lent'},
'transactionDate': {S: date},
},
ConditionExpression: 'attribute_not_exists(storedObject)'
};
console.log(params);
// putItem in database only if it doesn't already exist
dynamodb.putItem(params, function(err, data) {
if (err) {
console.log("Error", err);
console.log("That item already exists");
} else {
console.log("Success", data);
}
});
console.log(personName);
console.log(itemName);
console.log(currentUser);
const speechOutput = 'You Gave ' + personName + ' the ' + itemName;
return handlerInput.responseBuilder
.speak(speechOutput)
.withSimpleCard('Track It', speechOutput)
.getResponse();
}
};
const PutIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'PutIntent';
},
handle(handlerInput) {
const itemName = handlerInput.requestEnvelope.request.intent.slots.storedObject.value;
const LocationName = handlerInput.requestEnvelope.request.intent.slots.storageLocation.value;
const currentUser = handlerInput.requestEnvelope.session.user.userId;
//begin DB code
var params = {
TableName: 'TrackItDB',
Item: {
'userID' : {S: currentUser},
'storedObject' : {S: itemName},
'lenderPerson' : {S: LocationName},
'objectStatus' : {S: 'stored'},
'transactionDate': {S: date},
},
ConditionExpression: 'attribute_not_exists(storedObject)'
};
dynamodb.putItem(params, function(err, data) {
if (err) {
console.log("Error", err);
console.log("That item already exists");
}
else {
console.log("Success", data);
}
});
//end DB code
const speechOutput = 'You put the ' + itemName + ' in the ' + LocationName;
return handlerInput.responseBuilder
.speak(speechOutput)
.withSimpleCard('Hello World', speechOutput)
.getResponse();
}
};
const WhereIsIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'WhereIsIntent';
},
handle(handlerInput) {
const itemName = handlerInput.requestEnvelope.request.intent.slots.storedObject.value;
const currentUser = handlerInput.requestEnvelope.session.user.userId;
// begin DB query
var params = {
Key: {
"userID": {
S: currentUser
},
"storedObject": {
S: itemName
}
},
TableName: "TrackItDB"
};
// End DB Query
dynamodb.getItem(params, function(err, data) {
if (err) {
console.log("Error", err);
}// an error occurred
else {
console.log("Success", data); // successful response
const LocationName = data.Item.lenderPerson.S;
const speechOutput = 'Your ' + itemName + ' is in the ' + LocationName;
return handlerInput.responseBuilder
.speak(speechOutput)
.withSimpleCard('Hello World', speechOutput)
.getResponse();
}
});
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speechOutput = 'Welcome to Track it, you can tell me who you lent things to, or where you put an item. For example I lent the saw to Mike, or I put the saw in the workshop.';
return handlerInput.responseBuilder
.speak(speechOutput)
.reprompt(speechOutput)
.withSimpleCard('Hello World', speechOutput)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
|| handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speechOutput = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speechOutput)
.withSimpleCard('Hello World', speechOutput)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
//any cleanup logic goes here
return handlerInput.responseBuilder.getResponse();
}
};
const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
.addRequestHandlers(
LaunchRequestHandler,
GaveIntentHandler,
PutIntentHandler,
WhereIsIntentHandler,
HelpIntentHandler,
SessionEndedRequestHandler,
CancelAndStopIntentHandler
)
.lambda()
I'm trying to pass the "itemName and LocationName to the const speechOutput variable so I can have Alexa speak it for me.
It will work in the console log, but not later outside the getitem function. FYI, I know I shouldn't have the const speechOutput twice, and it isn't like that in my code; I just put it there to show what I'm trying to do.
You need to move the responseBuilder code snippet within the getItem function' else part.
Do not declare the const speechOutput twice.
handle(handlerInput) {
const itemName =
handlerInput.requestEnvelope.request.intent.slots.storedObject.value;
const currentUser =
handlerInput.requestEnvelope.session.user.userId;
// Begin database query
var params = {
Key: {
"userID": {
S: currentUser
},
"storedObject": {
S: itemName
}
},
TableName: "TrackItDB"
};
// End DB Query
//const speechOutput = 'test'
dynamodb.getItem(params, function(err, data) {
if (err) {
console.log("Error", err);
} // An error occurred
else {
console.log("Success", data); // Successful
// response
const LocationName = data.Item.lenderPerson.S;
const speechOutput = 'Your ' + itemName + ' is in the ' +
LocationName;
// Speak the output
return handlerInput.responseBuilder
.speak(speechOutput)
.withSimpleCard('Hello, World!', speechOutput)
.getResponse();
console.log(speechOutput);
}
});
//const speechOutput = 'Your ' + itemName + ' is in the ' +
LocationName;
}

retrieve a JSON element nodejs lambda aws

I got a lambda function which retrieves elements from dynamodb. When I tried to extract an individual Item or more from the JSON object which contains the elements from the database they are undefined, how can I return an individual element? for example 'date_booking'. The following is the code of the function.
Thanks in advance.
'use strict';
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-west-2'});
function close(sessionAttributes, fulfillmentState, message) {
return {
sessionAttributes,
dialogAction: {
type: 'Close',
fulfillmentState,
message,
},
};
}
// --------------- Events -----------------------
function dispatch(intentRequest, callback) {
const sessionAttributes = intentRequest.sessionAttributes;
const slots = intentRequest.currentIntent.slots;
const phone = slots.PhoneNumber;
let params = {
TableName: 'bookings',
Key: {
"phone_number": phone
},
ProjectionExpression:"date_booking, time_booking"
};
docClient.get(params, function(err, data) {
if (err) {
callback(err, null);
} else {
callback(null,data);
console.log(data);
console.log(data.date_booking);
console.log(data.time_booking);
}
});
}
// --------------- Main handler -----------------------
exports.handler = (event, context, callback) => {
try {
dispatch(event,
(response) => {
callback(null, response);
});
} catch (err) {
callback(err);
}
};
The executions results are:
Function Logs:
START RequestId: 5536b82c-c538-11e8-ad44-474e44b2f858 Version: $LATEST
2018-10-01T05:10:38.653Z 5536b82c-c538-11e8-ad44-474e44b2f858 { Item:
{ date_booking: '2018-09-18', time_booking: '15:00' } }
2018-10-01T05:10:38.712Z 5536b82c-c538-11e8-ad44-474e44b2f858
undefined
2018-10-01T05:10:38.730Z 5536b82c-c538-11e8-ad44-474e44b2f858
undefined
END RequestId: 5536b82c-c538-11e8-ad44-474e44b2f858
REPORT RequestId: 5536b82c-c538-11e8-ad44-474e44b2f858 Duration: 1301.50 ms
Billed Duration: 1400 ms Memory Size: 128 MB Max Memory Used: 30 MB
Getting data from dynamoDB as in Item.
docClient.get(params, function(err, data) {
if (err) {
callback(err, null);
console.log(err)
} else {
console.log(data.Item.date_booking);
}
});
You can use the following.
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-west-2'});
exports.handler = (event, context, callback) => {
var params = {
TableName: 'bookings',
Key:{
date_booking: event.params.path.date_booking
//date_booking is the item you want to retrieve
}
};
docClient.get(params, function(err, data) {
if (err) {
callback(err, null);
console.log(err)
} else {
callback(null, data);
console.log(data);
}
});
};

ASK error, TypeError: Cannot read property 'type' of undefined

I'm creating a skill that will call back different incidents at different dates and times from a DynamoDB table through Alexa.
My 3 columns are data, time and incident
I've defined my partition and sort key in my Lambda function as
let GetMachineStateIntent = (context, callback) => {
var params = {
TableName: "updatedincident",
Key: {
date: "2017-03-21",
time: "07:38",
incident: "Blocked Primary",
}
};
When I try to test my skill I can't seem to recall the incident correctly, the error I'm getting in Cloudwatch is:
2018-03-28T14:48:53.397Z 042319cb-4a3e-49ae-8b33-1641367107d4 Unexpected error occurred in the skill handler! TypeError: Cannot read property 'type' of undefined
at exports.handler.e (/var/task/index.js:70:16)
as well as:
2018-03-28T14:48:53.417Z 042319cb-4a3e-49ae-8b33-1641367107d4
{
"errorMessage": "Unexpected error"
}
Here is my code from index.js
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dbClient = new AWS.DynamoDB.DocumentClient();
AWS.config.update({
region: "'us-east-1'"
});
let GetMachineStateIntent = (context, callback) => {
var params = {
TableName: "updatedincident",
Key: {
date: "2018-03-28",
time: "04:23",
}
};
dbClient.get(params, function (err, data) {
if (err) {
// failed to read from table for some reason..
console.log('failed to load data item:\n' + JSON.stringify(err, null, 2));
// let skill tell the user that it couldn't find the data
sendResponse(context, callback, {
output: "the data could not be loaded from your database",
endSession: false
});
} else {
console.log('loaded data item:\n' + JSON.stringify(data.Item, null, 2));
// assuming the item has an attribute called "incident"..
sendResponse(context, callback, {
output: data.Item.incident,
endSession: false
});
}
});
};
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill. what data are you looking for?",
endSession: false
});
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "GetMachineStateIntent") {
GetMachineStateIntent(context, callback);
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about incidents that have happened",
reprompt: "what can I help you with?",
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// an unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};
and this is my handler GetMachineState.js
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill. what do you want to find?",
endSession: false
});
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "GetMachineStateIntent") {
// this is where we will wire up the dynamo call
// for now, just send a simple response and end the session
sendResponse(context, callback, {
output: "cinema not implemented yet!",
endSession: true
});
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about incidents that have happened",
reprompt: "what can I help you with?",
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// an unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! Good bye!",
endSession: true
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};
Its impossible to know for sure if this is the problem or not because you haven't shared the code from the index.js file. The error message you get is telling you that the problem occurs at line 70 in your index.js file so you should look there, and figure out what the problem is.
However, based on the fact that you also posted this as a comment on another question, I'm going to venture to guess that the issue you've run into is that you used the code snippet I provided in the answer to that question and the error is from dereferencing the request.type
You have to make sure the request variable is set to the actual request from the event, like so: var request = event.request where event is provided from exports.handler = (event, context, callback) => {
For example:
exports.handler = (event, context, callback) => {
var request = event.request;
if (request.type === "IntentRequest"
// make suret the name of the intent matches the one in your interaction model
&& request.intent.name == "GetMachineStateIntent") {
var dateSlot = request.intent.slots.Date != null ?
request.intent.slots.Date.value : "unknown date";
var timeSlot = request.intent.slots.Time != null ?
request.intent.slots.Time.value : "unknown time";
// respond with speech saying back what the skill thinks the user requested
sendResponse(context, callback, {
output: "You wanted the machine state at "
+ timeSlot + " on " + dateSlot,
endSession: true
});
} else {
// TODO: handle other types of requests..
sendResponse(context, callback, {
output: "I don't know how to handle this request yet!"
endSession: true
});
}
};
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}

Resources