Unable to store timestamp in Dynamo DB using Nodejs 16 version Lambda - aws-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;
};

Related

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

MissingRequiredParameter: Missing required key 'TableName' in params : Serverless Framework

I am getting the error stack below, The code looks fine to me, don't know where is the error!
My handler code is below :
import AWS from 'aws-sdk';
import commonMiddleware from '../lib/commonMiddleware';
import createError from 'http-errors';
const dynamodb = new AWS.DynamoDB.DocumentClient();
async function placeBid(event, context) {
const { id } = event.pathParameters;
const { amount } = event.body;
console.log('**********************************');
console.log(process.env.AUCTIONS_TABLE_NAME);
console.log('**********************************');
const params = {
TableName: process.env.AUCTIONS_TABLE_NAME,
Key: {
id: { S: id}
},
UpdateExpression: "set highestBid.amount = :amount",
ExpressionAttributeValues: {
":amount": { S: amount },
},
ReturnValues: "UPDATED_NEW"
};
console.log('**********************************');
console.log(JSON.stringify(params));
console.log('**********************************');
let updatedAuction;
try {
const result = await dynamodb.update({params}).promise();
console.log({result});
updatedAuction = result.Attributes;
} catch(e) {
console.log({'error': e});
throw new createError.InternalServerError(e);
}
return {
statusCode: 200,
body: JSON.stringify(updatedAuction)
};
}
export const handler = commonMiddleware(placeBid);
2021-06-16T15:41:20.179Z 722a3b28-9f67-44eb-8e5a-85368325dae5 INFO {
error: MultipleValidationErrors: There were 2 validation errors:
* MissingRequiredParameter: Missing required key 'TableName' in params
* MissingRequiredParameter: Missing required key 'Key' in params
at ParamValidator.validate (/var/runtime/node_modules/aws-sdk/lib/param_validator.js:40:28)
at Request.VALIDATE_PARAMETERS (/var/runtime/node_modules/aws-sdk/lib/event_listeners.js:132:42)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at callNextListener (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:96:12)
at /var/runtime/node_modules/aws-sdk/lib/event_listeners.js:86:9
at finish (/var/runtime/node_modules/aws-sdk/lib/config.js:386:7)
at /var/runtime/node_modules/aws-sdk/lib/config.js:404:9
at EnvironmentCredentials.get (/var/runtime/node_modules/aws-sdk/lib/credentials.js:127:7)
at getAsyncCredentials (/var/runtime/node_modules/aws-sdk/lib/config.js:398:24)
at Config.getCredentials (/var/runtime/node_modules/aws-sdk/lib/config.js:418:9) {
code: 'MultipleValidationErrors',
errors: [ [Error], [Error] ],
time: 2021-06-16T15:41:20.176Z
}
}
You've nested params in another object. This causes the method call to receive:
{ params: { TableName: 'AUCTIONS_TABLE_NAME', Key: { id: id}}}
Try replacing dynamodb.update({params}) with just dynamodb.update(params).
Also with the document client, you don't need to specify the type.
Key: {id} will be fine.

How to create a new key and add or append an array of objects on AWS DynamoDB

I have this object in my dynamodb table which looks like
{
"id": "b31de483"
}
I want to add another object that looks like
{
"id": "b31de483",
"players": [{"playerId": "1234"}]
}
This is my code
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(if_not_exists(players, :players)',
ExpressionAttributeValues: {
':players': {
"L": [
{ "S": playerId }
]
}
}
};
return await documentClient.update(params);
}
This throws an error but I cannot understand how to fix it. I am looking at documentation here
Figured it out
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(players, :players)',
ExpressionAttributeValues: {
':players': [{
"playerId": playerId
}]
}
};
return await documentClient.update(params).promise();
}

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

Graphql-js subscriptions unit tests not working as expected

I have written integration tests for graphql-js subscriptions, which are showing weird behavior.
My graphq-js subscription works perfectly in GraphiQL. But when the same subscriptions is called from unit test, it fails.
Ggraphql-Js object, with resolve function and subscribe function
return {
type: outputType,
args: {
input: {type: new GraphQLNonNull(inputType)},
},
resolve(payload, args, context, info) {
const clientSubscriptionId = (payload) ? payload.subscriptionId : null;
const object = (payload) ? payload.object : null;
var where = null;
var type = null;
var target = null;
if (object) {
where = (payload) ? payload.object.where : null;
type = (payload) ? payload.object.type : null;
target = (payload) ? payload.object.target : null;
}
return Promise.resolve(subscribeAndGetPayload(payload, args, context, info))
.then(payload => ({
clientSubscriptionId, where, type, target, object: payload.data,
}));
},
subscribe: withFilter(
() => pubSub.asyncIterator(modelName),
(payload, variables, context, info) => {
const subscriptionPayload = {
clientSubscriptionId: variables.input.clientSubscriptionId,
remove: variables.input.remove,
create: variables.input.create,
update: variables.input.update,
opts: variables.input.options,
};
subscriptionPayload.model = model;
try {
pubSub.subscribe(info.fieldName, null, subscriptionPayload);
} catch (ex) {
console.log(ex);
}
return true;
}
),
};
Subscription query
subscription {
Customer(input: {create: true, clientSubscriptionId: 112}) {
customer {
id
name
age
}
}
}
Mutation query
mutation {
Customer {
CustomerCreate (input:{data:{name:"Atif 50", age:50}}) {
obj {
id
name
}
}
}
}
Integration Test
'use strict';
const ws = require('ws');
const { SubscriptionClient } = require('subscriptions-transport-ws');
const { ApolloClient } = require('apollo-client');
const { HttpLink } = require('apollo-link-http');
const { InMemoryCache } = require('apollo-cache-inmemory');
const Promise = require('bluebird');
const expect = require('chai').expect;
const chai = require('chai').use(require('chai-http'));
const server = require('../server/server');
const gql = require('graphql-tag');
let apollo;
let networkInterface;
const GRAPHQL_ENDPOINT = 'ws://localhost:5000/subscriptions';
describe('Subscription', () => {
before(async () => {
networkInterface = new SubscriptionClient(
GRAPHQL_ENDPOINT, { reconnect: true }, ws);
apollo = new ApolloClient({
networkInterface ,
link: new HttpLink({ uri: 'http://localhost:3000/graphql' }),
cache: new InMemoryCache()
});
});
after(done => {
networkInterface.close() ;
});
it('subscription', async () => {
const client = () => apollo;
// SUBSCRIBE and make a promise
const subscriptionPromise = new Promise((resolve, reject) => {
client().subscribe({
query: gql`
subscription {
Customer(input: {create: true,
clientSubscriptionId: 112,
options: {where: {age: 50}}}) {
customer {
name
}
}
}
`
}).subscribe({
next: resolve,
error: reject
});
});
let execGraphQL;
// MUTATE
await execGraphQL(
`mutation {
Customer {
CustomerCreate (input:{data:{name:"Atif 21", age:50}}) {
obj {
id
name
}
}
}
}`
);
// ASSERT SUBSCRIPTION RECEIVED EVENT
expect(await subscriptionPromise).to.deep.equal({});
});
});
Issue Here
When test in run, payload in the resolve function contains global data, where as it should contain the subscription payload. So the code breaks.

Resources