how to force a AWS Lambda to complete all the code - aws-lambda

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.

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;
};

Lambda unable to read DynamoDB Table when called from another DynamoDB table as a trigger

I have two DynamoDB tables: Order and Client.
When an Order is created, a Lambda function is triggered and first seeks to read the Client associated with the Order. However, the Lambda function fails to do so and does not report any error.
The Lambda function is:
var AWS = require('aws-sdk');
AWS.config.update({region: process.env.REGION})
docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event) => {
await event.Records.forEach( async (record) => {
const { eventName, dynamodb } = record
if(eventName==="INSERT") {
console.log('A new order has been created')
const client_id = dynamodb.NewImage.client_id.S
let params = {
TableName: process.env.API_REALAPI_CLIENTTABLE_NAME,
Key: {
"id": client_id
}
}
console.log(params)
try {
const result = await docClient.get(params).promise()
console.log(result)
} catch(err) { console.log(err); throw new Error(err); }
}
})
}
When I run this, it gets to console.log(params) but then doesn't show any result nor any error.
My function was configured with the amplify cli and it appears to have read permissions to the Client table:
"Statement": [
{
"Action": [
"dynamodb:Put*",
"dynamodb:Create*",
"dynamodb:BatchWriteItem",
"dynamodb:Get*",
"dynamodb:BatchGetItem",
"dynamodb:List*",
"dynamodb:Describe*",
"dynamodb:Scan",
"dynamodb:Query",
"dynamodb:Update*",
"dynamodb:RestoreTable*",
"dynamodb:Delete*"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:123456123456:table/Client-abcdefg-dev",
"arn:aws:dynamodb:us-east-1:123456123456:table/Client-abcdefg-dev/index/*"
],
"Effect": "Allow"
},
{
Am I missing something?
Thanks!
The issue is that you are using await in a forEach loop which doesn't work. Your handler is existing before the Db call is done which is why there is no response. You will want to do something more like this:
exports.handler = async (event) => {
const jobs = event.Records.map((record) => {
const { eventName, dynamodb } = record
if (eventName === 'INSERT') {
console.log('A new order has been created')
const client_id = dynamodb.NewImage.client_id.S
let params = {
TableName: process.env.API_REALAPI_CLIENTTABLE_NAME,
Key: {
id: client_id
}
}
console.log(params)
return docClient.get(params).promise()
}
})
try {
const results = await Promise.all(jobs)
} catch (err) {
console.log(err)
throw new Error(err)
}
}

How come drive API return a result when using invalid access token

My Scenario
I'm using Google Drive API to create a file and to get a list of files.
My problem
1. No matter what value I put in my access_token the API keeps working
2. If I change the order of events and I call createDriveFile before I call listDriveFiles I get this error:
Error: Invalid Credentials
at Gaxios._request (/Users/tamirklein/superquery/bd/lambda/node_modules/googleapis-common/node_modules/google-auth-library/node_modules/gaxios/src/gaxios.ts:109:15)
at
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
My code
if (!global._babelPolyfill) {
var a = require("babel-polyfill")
}
import {google} from 'googleapis'
describe('Run query with API', async () => {
it('check Drive APIs', async () => {
process.env.x_region = 'us-east-1';
let result = await test('start')
})
async function test(p1) {
let auth = getBasicAuthObj();
auth.setCredentials({
access_token: "anyValueWork",
refresh_token: "Replace With a valid refresh Token"
});
let fileList = await listDriveFiles(auth);
let newFile = await createDriveFile(auth);
}
async function listDriveFiles(auth) {
return new Promise((resolved) => {
const {google} = require('googleapis');
const drive = google.drive({version: 'v3', auth});
drive.files.list({
pageSize: 10,
fields: 'nextPageToken, files(id, name)',
q: 'trashed=false'
}, (err, res) => {
if (err) {
console.log('The API returned an error: ' + err);
resolved([err, null]);
} else {
const files = res.data.files;
if (files.length) {
console.log(`We fetched ${files.length} Files`);
// files.map((file) => {
// console.log(`${file.name} (${file.id})`);
// });
} else {
console.log('No files found.');
}
resolved([err, res]);
}
});
});
}
async function createDriveFile(auth) {
return new Promise(async (resolved) => {
//const fs = require('fs');
const {google} = require('googleapis');
const drive = google.drive({version: 'v3', auth});
let data = {
value: 'value'
};
let fileName = 'fileName.txt';
let fileMetadata = {
'name': fileName
};
// create buffer
let stream = require('stream');
let bufferStream = new stream.PassThrough();
bufferStream.end(Buffer.from(JSON.stringify(data)));
let media = {
mimeType: 'application/json',
body: bufferStream // fs.createReadStream("test.txt") //bufferStream //
};
drive.files.create({
resource: fileMetadata,
media: media,
fields: 'id'
}, function (err, file) {
if (err) {
// Handle error
console.error("Error: savePasswordInDrive" + err);
} else {
console.log('File Id: ', file.data.id);
}
resolved([err, file]);
});
})
}
async function _wait(milliseconds) {
return new Promise(resolved => {
setTimeout(() => {
resolved()
}, milliseconds)
})
}
/**
* Create oAuth object
* #returns {OAuth2Client}
*/
function getBasicAuthObj() {
let clientId = 'Replace With a valid clientId';
let clientSecret = 'Replace With a valid clientSecret';
let redirectUrl = 'URL';
return new google.auth.OAuth2(
clientId,
clientSecret,
redirectUrl
)
}
})
Any ideas on how to resolve this?

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);
}
});
};

Resources