How to fix data not inserting into local dynamodb (NodeJS) - node.js

I am trying to insert data into local dynamoDB through a endpoint I created but it is not working. There were no error log in the promise itself and it seems like the function is being ignored.
This is a helper function that will assist in inserting the data
require('aws-sdk/index');
const DynamoDB = require('aws-sdk/clients/dynamodb');
const options = {
apiVersion: '2012-08-10',
region: 'ap-southeast-1',
endpoint: 'http://localhost:8000/',
};
const dynamoDBDoc = new DynamoDB.DocumentClient(options);
async function putData(tableName,insertValue) {
const param = {
TableName: tableName,
Item:insertValue,
ReturnConsumedCapacity: 'TOTAL',
};
// Mock data
// const param = {
// TableName: 'user',
// Item:{
// 'user_id':'1234',
// 'email':'memelorde'
// },
// ReturnConsumedCapacity: 'TOTAL',
// };
try {
const data = await dynamoDBDoc.put(param).promise();
console.log("Data successfully entered", data)
return data;
} catch (e) {
console.log(`failed ${e.message}`);
return false;
}
}
This is the part where I call the above function and provide it with the table name to insert into
async function createUser(data){
const tableName = "user"
data["user_id"]= uuidv4()
await dynamoDB.putData("user",data);
return await dynamoDB.putData(tableName, data);
}
This is the endpoint I created to pass in user information
if (event.httpMethod === 'POST') {
dataset = JSON.parse(event.body)
console.log('/signup POST req =>', dataset)
let res = await user.createUser(dataset)
console.log(res)
}
Expected:
If the put function executed, there will be a console log that logs the success and the data will be inserted to the table, if there is an error the error will be logged too
Actual:
No error code was produced at all

I was facing the same issue. I solved it by adding accessKeyId and secretAccessKey to the configuration option of "AWS.DynamoDB.DocumentClient"
const options = {
accessKeyId: "aaa",
secretAccessKey: "bbb",
apiVersion: '2012-08-10',
region: 'ap-southeast-1',
endpoint: 'http://localhost:8000/',
};

Related

My Lambda ends before code is complete - node.js

I know this has been asked in various ways before, but I can't figure it out. I'm still very new to node.js and lambda. This code will work if I run the lambda twice, but never runs to completion the first time. This also works fine if I run this from a local IDE by adding
exports.handler();
to the end of the code block.
The code queries DynamoDB for results and then attempts to delete those records from Dynamo. The query part seems to work every time, but the deletion part fails to happen on the first invocation.
I can't seem to figure out what changes are necessary to for lambda to wait until all of my processes are complete.
Thanks in advance.
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({ region: 'us-east-2' });
exports.handler = async (event) => {
// Create DynamoDB service object
const ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
const documentClient = new AWS.DynamoDB.DocumentClient({ region: "us-east-2" });
const tablename = process.env.table_name;
let dynapromises = [];
let params = {
ExpressionAttributeValues: {
':offNum': { S: process.env.cost_center },
':s': { N: '2' }
},
ExpressionAttributeNames: {
"#notif_status": "status"
},
KeyConditionExpression: 'officeNumber = :offNum',
TableName: tablename,
IndexName: 'officeNumberIndex',
ProjectionExpression: "notificationNumber",
FilterExpression: '(attribute_not_exists(#notif_status) or #notif_status = :s) and attribute_not_exists(statusTimes)'
};
let qresults = await ddb.query(params).promise();
console.log("Count of notifs again " + qresults.Items.length);
qresults.Items.forEach(function(element, index, array) {
console.log(element.notificationNumber.S);
let delparams = {
TableName: tablename,
ReturnValues: "ALL_OLD",
Key: {
notificationNumber: {
S: element.notificationNumber.S
}
}
};
dynapromises.push(ddb.deleteItem(delparams).promise().then(function(data) {
console.log("Deleted Record:"+ JSON.stringify(data)); // successful response
}, function(error) {
console.log(error, error.stack); // an error occurred
}));
console.log("deletion parameters " + JSON.stringify(delparams));
});
Promise.all(dynapromises).then(res => {
console.log("All promises done");
});
return qresults.Items.length;
};
The issue is in that you are returning before all the promises are completed, you need to move the return qresults.Items.length; inside the last then.
try with this code:
** UPDATE: Change the snippet with the working code **
// Load the AWS SDK for Node.js
const AWS = require('aws-sdk');
// Set the region
AWS.config.update({ region: 'us-east-2' });
exports.handler = async (event) => {
// Create DynamoDB service object
const ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
const documentClient = new AWS.DynamoDB.DocumentClient({ region: "us-east-2" });
const tablename = process.env.table_name;
let params = {
ExpressionAttributeValues: {
':offNum': { S: process.env.cost_center },
':s': { N: '2' }
},
ExpressionAttributeNames: {
"#notif_status": "status"
},
KeyConditionExpression: 'officeNumber = :offNum',
TableName: tablename,
IndexName: 'officeNumberIndex',
ProjectionExpression: "notificationNumber",
FilterExpression: '(attribute_not_exists(#notif_status) or #notif_status = :s) and attribute_not_exists(statusTimes)'
};
let qresults = await ddb.query(params).promise();
console.log("Count of notifs again " + qresults.Items.length);
const dynapromises = qresults.Items.map( async element => {
let delparams = {
TableName: tablename,
ReturnValues: "ALL_OLD",
Key: {
notificationNumber: {
S: element.notificationNumber.S
}
}
};
try {
console.log("deletion parameters " + JSON.stringify(delparams));
const data = await ddb.deleteItem(delparams).promise();
console.log( "Deleted Record:"+ JSON.stringify(data) );
} catch ( err ) {
console.log(error, error.stack); // an error occurred
}
} )
await Promise.all(dynapromises)
console.log("All promises done");
return qresults.Items.length;
};
The code that #pepo posted is performing the Dynamo deletions on the first invocation of the Lambda. Thanks for his work and the responses from everyone.

AWS Lambda using s3 getObject function and putItem function to insert it into DynamoDB but nothing happens

this is the node.js code:
'use strict';
const AWS = require("aws-sdk");
AWS.config.update({
region: 'eu-west-1'});
const docClient = new AWS.DynamoDB.DocumentClient();
const tableName = 'Fair';
const s3 = new AWS.S3();
exports.handler = async (event) => {
var getParams = {
Bucket: 'dataforfair', //s3 bucket name
Key: 'fairData.json' //s3 file location
}
const data = await s3.getObject(getParams).promise()
.then( (data) => {
//parse JSON
let fairInformations = JSON.parse(data.Body.toString());
fairInformations.forEach(function(fairInformationEntry) {
console.log(fairInformationEntry);
var params = {
TableName: tableName,
Item: {
"year": fairInformationEntry.year,
"fairName": fairInformationEntry.fairName,
"info": fairInformationEntry.info
}
};
docClient.put(params, function(err, data) {
console.log('*****test');
if (err) {
console.error("Unable to add fairInformation", fairInformationEntry.fairName, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("PutItem succeeded:", fairInformationEntry.fairName);
}
});
});
})
.catch((err) => {
console.log(err);
});
const response = {
statusCode: 200,
body: JSON.stringify(data),
};
return response;
};
Hello everyone,
I want to put the data into the Dynamo DB after getting the JSON file from the s3 Bucket. Getting the JSON works and the console.log(fairInformationEntry); is also still triggered, but the docClient.put() never gets called. I am getting no error, nothing. I do not know what is wrong and why it is not working. I have the right IAM role and access to everything I need.
I hope you can help me!
The problem is mixup of promise, callback and async/await. You are also trying to do asynchronous operation inside foreach. The code should look something like this
"use strict";
const AWS = require("aws-sdk");
AWS.config.update({
region: "eu-west-1"
});
const docClient = new AWS.DynamoDB.DocumentClient();
const tableName = "Fair";
const s3 = new AWS.S3();
exports.handler = async event => {
var getParams = {
Bucket: "dataforfair", //s3 bucket name
Key: "fairData.json" //s3 file location
};
const data = await s3.getObject(getParams).promise();
//parse JSON
let fairInformations = JSON.parse(data.Body.toString());
await Promise.all(
fairInformations.map(fairInformationEntry => {
console.log(fairInformationEntry);
var params = {
TableName: tableName,
Item: {
year: fairInformationEntry.year,
fairName: fairInformationEntry.fairName,
info: fairInformationEntry.info
}
};
return docClient.put(params).promise();
})
);
const response = {
statusCode: 200,
body: JSON.stringify(data)
};
return response;
};
Hope this helps

Decrypt text with AWS KMS in NodeJs

I am trying to decrypt some text encrypted with AWS KMS using aws-sdk and NodeJs. I started to play today with NodeJs so I am a newbie with it.
I have this problem resolved with Java but I am trying to migrate an existing Alexa skill from Java to NodeJs.
The code to decrypt is:
function decrypt(buffer) {
const kms = new aws.KMS({
accessKeyId: 'accessKeyId',
secretAccessKey: 'secretAccessKey',
region: 'eu-west-1'
});
return new Promise((resolve, reject) => {
let params = {
"CiphertextBlob" : buffer,
};
kms.decrypt(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.Plaintext);
}
});
});
};
When I run this code with a correct CiphertextBlob, I get this error:
Promise {
<rejected> { MissingRequiredParameter: Missing required key 'CiphertextBlob' in params
at ParamValidator.fail (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\param_validator.js:50:37)
at ParamValidator.validateStructure (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\param_validator.js:61:14)
at ParamValidator.validateMember (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\param_validator.js:88:21)
at ParamValidator.validate (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\param_validator.js:34:10)
at Request.VALIDATE_PARAMETERS (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\event_listeners.js:126:42)
at Request.callListeners (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\sequential_executor.js:106:20)
at callNextListener (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\sequential_executor.js:96:12)
at D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\event_listeners.js:86:9
at finish (D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\config.js:349:7)
at D:\Developing\abono-transportes-js\node_modules\aws-sdk\lib\config.js:367:9
message: 'Missing required key \'CiphertextBlob\' in params',
code: 'MissingRequiredParameter',
time: 2019-06-30T20:29:18.890Z } }
I don't understand why I am receiving that if CiphertextBlob is in the params variable.
Anyone knows?
Thanks in advance!
EDIT 01/07
Test to code the feature:
First function:
const CheckExpirationDateHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'TtpConsultaIntent';
},
handle(handlerInput) {
var fecha = "";
var speech = "";
userData = handlerInput.attributesManager.getSessionAttributes();
if (Object.keys(userData).length === 0) {
speech = consts.No_Card_Registered;
} else {
console.log("Retrieving expiration date from 3rd API");
fecha = crtm.expirationDate(cipher.decrypt(userData.code.toString()));
speech = "Tu abono caducará el " + fecha;
}
return handlerInput.responseBuilder
.speak(speech)
.shouldEndSession(true)
.getResponse();
}
}
Decrypt function provided with a log:
// source is plaintext
async function decrypt(source) {
console.log("Decrypt func INPUT: " + source)
const params = {
CiphertextBlob: Buffer.from(source, 'base64'),
};
const { Plaintext } = await kms.decrypt(params).promise();
return Plaintext.toString();
};
Output:
2019-07-01T19:01:12.814Z 38b45272-809d-4c84-b155-928bee61a4f8 INFO Retrieving expiration date from 3rd API
2019-07-01T19:01:12.814Z 38b45272-809d-4c84-b155-928bee61a4f8 INFO Decrypt func INPUT:
AYADeHK9xoVE19u/3vBTiug3LuYAewACABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREF4UW0rcW5PSElnY1ZnZ2l1bHQ2bzc3ZnFLZWZMM2J6YWJEdnFCNVNGNzEyZGVQZ1dXTDB3RkxsdDJ2dFlRaEY4UT09AA10dHBDYXJkTnVtYmVyAAt0aXRsZU51bWJlcgABAAdhd3Mta21zAEthcm46YXdzOmttczpldS13ZXN0LTE6MjQwMTE3MzU1MTg4OmtleS81YTRkNmFmZS03MzkxLTRkMDQtYmUwYi0zZDJlMWRhZTRkMmIAuAECAQB4sE8Iv75TZ0A9b/ila9Yi/3vTSja3wM7mN/B0ThqiHZEBxYsoWpX7jCqHMoeoYOkVtAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDNnGIwghz+b42E07KAIBEIA76sV3Gmp5ib99S9H4MnY0d1l............
2019-07-01T19:01:12.925Z 38b45272-809d-4c84-b155-928bee61a4f8 INFO Error
handled: handlerInput.responseBuilder.speak(...).shouldEndSession is
not a function
2019-07-01T19:01:13.018Z 38b45272-809d-4c84-b155-928bee61a4f8 ERROR Unhandled Promise
Rejection {"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"InvalidCiphertextException:
null","stack":["Runtime.UnhandledPromiseRejection:
InvalidCiphertextException: null","...
That either means you're missing key 'CiphertextBlob' or its value is undefined.
Please checkout the value you're passing in as buffer.
For reference, I also added my working code example that I used.
import { KMS } from 'aws-sdk';
import config from '../config';
const kms = new KMS({
accessKeyId: config.aws.accessKeyId,
secretAccessKey: config.aws.secretAccessKey,
region: config.aws.region,
});
// source is plaintext
async function encrypt(source) {
const params = {
KeyId: config.aws.kmsKeyId,
Plaintext: source,
};
const { CiphertextBlob } = await kms.encrypt(params).promise();
// store encrypted data as base64 encoded string
return CiphertextBlob.toString('base64');
}
// source is plaintext
async function decrypt(source) {
const params = {
CiphertextBlob: Buffer.from(source, 'base64'),
};
const { Plaintext } = await kms.decrypt(params).promise();
return Plaintext.toString();
}
export default {
encrypt,
decrypt,
};
----- ADDED -----
I was able to reproduce your issue.
decrypt("this text has never been encrypted before!");
This code throws same error.
So if you pass plain text that has never been encrypted before or has been encrypted with different key, it throws InvalidCiphertextException: null.
Now I'll give you one usage example.
encrypt("hello world!") // this will return base64 encoded string
.then(decrypt) // this one accepts encrypted string
.then(decoded => console.log(decoded)); // hello world!
I kept on getting this error in my AWS lambda when trying the accepted solution, using AWS KMS over an environment variable I had encrypted by using AWS user interface.
It worked for me with this code adapted from the AWS official solution:
decrypt.js
const AWS = require('aws-sdk');
AWS.config.update({ region: 'us-east-1' });
module.exports = async (env) => {
const functionName = process.env.AWS_LAMBDA_FUNCTION_NAME;
const encrypted = process.env[env];
if (!process.env[env]) {
throw Error(`Environment variable ${env} not found`)
}
const kms = new AWS.KMS();
try {
const data = await kms.decrypt({
CiphertextBlob: Buffer.from(process.env[env], 'base64'),
EncryptionContext: { LambdaFunctionName: functionName },
}).promise();
console.info(`Environment variable ${env} decrypted`)
return data.Plaintext.toString('ascii');
} catch (err) {
console.log('Decryption error:', err);
throw err;
}
}
To be used like this:
index.js
const decrypt = require("./decrypt.js")
exports.handler = async (event, context, callback) => {
console.log(await decrypt("MY_CRYPTED_ENVIRONMENT_VARIABLE"))
}
EncryptionContext is a must for this to work.
Let's say the name of EnvironmentVariable is Secret
The code below reads the EnvironmentVariable called Secret and returns decrypted secret as plain text in the body.
Please see the function code posted below
'use strict';
const aws = require('aws-sdk');
var kms = new aws.KMS();
exports.handler = (event, context, callback) => {
const functionName = process.env.AWS_LAMBDA_FUNCTION_NAME;
const encryptedSecret = process.env.Secret;
kms.decrypt({
CiphertextBlob: new Buffer(encryptedSecret, 'base64'),
EncryptionContext: {
LambdaFunctionName: functionName /*Providing the name of the function as the Encryption Context is a must*/
},
},
(err, data) => {
if (err) {
/*Handle the error please*/
}
var decryptedSecret = data.Plaintext.toString('ascii');
callback(null, {
statusCode: 200,
body: decryptedSecret,
headers: {
'Content-Type': 'application/json',
},
});
});
};

AWS cognito is giving serializationException(Start of structure or map found where not expected) while doing sign up in node.js How to fix this issue?

I'm trying to add signup functionality with AWS cognito, But While signing up up getting SerializationException How to resolve this issue?
My signup function look like this
const AmazonCognitoIdentity = require("amazon-cognito-identity-js");
const AWS = require("aws-sdk");
global.fetch = require("node-fetch");
const keys = require("../../config/keys");
AWS.config.update({
accessKeyId: keys.awsKeys.key,
secretAccessKey: keys.awsKeys.secret,
region: keys.region.awsRegionId
});
const poolConfig = {
UserPoolId: keys.cognito.userPoolId,
ClientId: keys.cognito.clientId
};
// create a new user pool
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolConfig);
async function signupFunc(userData) {
console.log('JSON string received : ' + JSON.stringify(userData));
const emailData = {
Name: "email",
Value: userData.email
};
const name = {
Name: "name",
Value: userData.name
}
const password = userData.password;
const familyname = {
Name: 'family_name',
Value: userData.familyname
}
return new Promise((resolve, reject) => {
try {
var attributeList = [];
attributeList.push(new AmazonCognitoIdentity.CognitoUserAttribute(name));
attributeList.push(new AmazonCognitoIdentity.CognitoUserAttribute(familyname));
userPool.signUp(emailData, password, attributeList, null, (err, result) => {
if (err) {
console.error(`ERROR : ${JSON.stringify(err)}`);
return reject({ status: 0, error: "Error!!!" });
}
return resolve({
status: "200",
message: "Check email and verify!"
});
});
} catch (error) {
console.log(`ERROR : ${JSON.stringify(error)}`);
return reject({error: error});
}
});
}
module.exports = signupFunc;
While executing this method I'm getting below exception.
{
"code":"SerializationException",
"name":"SerializationException",
"message":"Start of structure or map found where not expected."
}
any help will much appreciated.
I had the same problem, because I was mistakenly passing the password as an Object instead of a String. Make sure you password is a String:
Auth.completeNewPassword(user, password, {} ).then(data=> console.log(data)})
Here user is an Object and password is a String.

DynamoDB queries return no items within Lambda function

Basically, I have a DynamoDB connection within a Lambda function. Will post code below. This DynamoDB connection seems to be behaving properly - it's able to call the listTable and describeTable functions successfully, which means it's got the right configuration - but querying it returns nothing, even on queries I know are correct and have tested on the Dynamo console.
UPDATE: Was able to successfully query with a string on a separate index, but still unable to query based on a binary...
Here's a part of the Lambda function:
const AWS = require('aws-sdk');
const SNS = new AWS.SNS({ apiVersion: '2010-03-31', region: 'sa-east-1' });
const DDB = new AWS.DynamoDB({ apiVersion: '2012-08-10', region: 'sa-east-1' })
const Lambda = new AWS.Lambda({ apiVersion: '2015-03-31' });
const async = require('async');
const CREATE_NOTIFICATIONS = 'create-notifications'
const QUERY_TOKENS = 'query-tokens'
function getUsers(functionName, message, callback) {
var msg = JSON.parse(message);
var users = [];
console.log(DDB);
async.forEachOf(msg.targetsb64, function(value, index, cb) {
console.log("getUsers b64: ", value)
console.log(typeof(value))
DDB.describeTable({
TableName: 'tsGroups'
}, function(err, data) {
console.log(err)
console.log(data.Table.KeySchema)
})
DDB.query({
TableName: 'tsGroups',
KeyConditionExpression: "identifier = :v_user",
ExpressionAttributeValues: {
":v_user": {"B": value}
}
}, function(err, data) {
if (err) {
cb(err)
} else {
console.log("data: ", data)
console.log("items: ", data.Items)
data.Items.forEach(function(item) {
users.push.apply(users, item.users.BS)
})
cb()
}
})
}, function(err) {
if (err) {
callback(err)
} else {
console.log("getUsers users: ", users);
const promises = users.map((user) => invokeQueryTokens(functionName, msg, user));
Promise.all(promises).then(() => {
const result = `Users messaged: ${users.length}`;
console.log(result);
callback(null, result);
});
}
})
}
I've tried using KeyConditions instead of KeyConditionExpression, to no avail. Value refers to a base64 identifier string that's passed along from an earlier Lambda function - I've tried hard-coding the correct value, doesn't help. The describeTable function is only there to detail that DynamoDB is connecting properly, and in the correct region.
TL;DR: The data.Items value in the above code snippet is always an empty array, even when doing a query I know should return something. What's my error here?
Thanks, and cheers!

Resources