Run two AWS methods in the same function of AWS Lambda code - node.js

I am creating an AWS Lambda script to retrieve information of my AWS account. At present I am having an issue in the function 3, i want to retrieve the users that have a policy, in this case i run the method to retrieve the list users and then run the method to retrieve the policy based on the users input and send it to an array to the callback. The issue is that the callback is empty. I have tested by console.log(userpolicies); and it is ok in the second method but when it out of the second method the userpolicies is in blank.
I understand that the issue is because i have two aws method in the same function but I don't how to solve.
Do you have any suggestions?
Thanks in advance.
var AWS = require('aws-sdk');
var async = require('async');
var stackoverflow = (event, context, callback) => {
var iam = new AWS.IAM();
async.series([
//function1
function(callback) {
iam.listGroups(function(err, data) {
if (err) {
callback(err);
} else
callback(null, data);
});
},
//function2
function(callback) {
iam.listPolicies(function(err, data) {
if (err) {
callback(err);
} else
callback(null, data);
});
},
//function3
function(callback) {
var userpolicies = [];
iam.listUsers(function(err,data) {
if (err)
console.log(err);
else
for (var i = 0; i < data.Users.length; i++) {
var params = {UserName: data.Users[i].UserName};
iam.listAttachedUserPolicies(params, function(err1, data1){
if (err1)
console.log(err1);
else
for (var j = 0; j < data1.AttachedPolicies.length; j ++) {
var infopolicies = '{"PolicyName":"'+data1.AttachedPolicies[j].PolicyName+'"}';
var infopoliciesjson = JSON.parse(infopolicies);
userpolicies.push(infopoliciesjson);
}
});
}
callback(null, userpolicies);
});
}
], callback);
};
exports.handler = stackoverflow;

Related

Trouble in using forEach function in node.js

I have been learning about q promises and tried to build up some mock APIs to implement its functionality,While doing so I came across the following error,
Enterprise.forEach is not a function
My API code is as follows,
var mongoose = require('mongoose');
var Enterprise = mongoose.model('Enterprise_gpy');
var q = require('q');
var displayEnterprise = function(req, res) {
function displayEnterpriseName() {
var deferred = q.defer();
Enterprise.forEach(function(err, doc) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var name = Enterprise.enterprise_name;
deferred.resolve({
name: name
});
}
return deferred.promise;
});
}
function displayEnterpriseEmail() {
var deferred = q.defer();
Enterprise.forEach(function(err, doc) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var email = Enterprise.enterprise_email;
deferred.resolve({
email: email
});
}
return deferred.promise;
});
}
q.all([
displayEnterpriseName(),
displayEnterpriseEmail()
])
.then(function(success) {
console.log(500, success);
})
.fail(function(err) {
console.log(200, err);
});
}
module.exports = {
displayEnterprise: displayEnterprise
}
In your code Enterprise is a mongoose schema so when you try to do loop using forEach then got
Enterprise.forEach is not a function
you can use forEach after Enterprise.find(). so use
Enterprise.find({}, function(err, docs) {
if (err) {
console.log('Error Finding Files');
deferred.reject(err);
} else {
var names = [];
docs.forEach (function(doc) {
var name = doc.enterprise_name;
names.push(name);// pushed in names array
//.....
});
deferred.resolve({
names: names
}); // return all names
}
});
instead of
Enterprise.find().forEach
and should use
var name = doc.enterprise_name; instead of var name = Enterprise.enterprise_name;
and
var email = doc.enterprise_email; instead of var email = Enterprise.enterprise_email;
forEach only works for arrays, and you're using it on a mongoose model.
try this instead:
Enterprise.find().exec(function(err, docs) {
docs.forEach(function(doc) {
// do something with all the documents
}
// do something outside the loop
})

I am having issues with exporting in node

I am trying to make an api endpoint for data coming from dynamoDB. I believe that I have everything connected but when I run postman to check the api (api/db) it doesn't recognize the functions from the db.js in the db.js (for routes). I have run a test on api/test and am getting the information back. Here is the code from both files:
1. This scans the database and I'm trying to export it to another file.
var AWS = require('aws-sdk');
var params = {
TableName : "iotbuttonsn",
//KeyConditionExpression: "serialNumber =:serialNumber",
//ExpressionAttributeValues: {
// ":serialNumber":"*"
//},
ScanIndexForward: false,
Limit: 3,
Select: 'ALL_ATTRIBUTES'
};
AWS.config.update({
region: "us-east-1",
endpoint: "https://dynamodb.us-east-1.amazonaws.com"
});
var docClient = new AWS.DynamoDB.DocumentClient();
var getDatabase = (function(){
return {
scanDB: function(){
docClient.scan(params, onScan);
var onScan = function(err, data){
if (err) {
console.log(err.message);
} else {
console.log('scan success');
len = data.Items.length;
for (n=0; n<len; n++) {
clickTypes[n] = data.Items[n].payload.clickType;
serialNums[n] = data.Items[n].serialNumber;
}
}
};
},
clickTypes: [],
serialNums: []
};
})();
module.exports = getDatabase;
2. This is where I'm trying to input but db.scanDB() isn't working:
var router = require('express').Router();
var db = require('../routes/db.js');
router.get('/', function(req, res){
db.scanDB();
buttons =
[
iot_buttonOne = {
serialNum: db.serialNum[0],
clickType: db.clickTypes[0]
},
iot_buttonTwo = {
serialNum: db.serialNum[1],
clickType: db.clickTypes[1]
}
]
.then(
function scanSuccess(data){
res.json(data);
},
function scanError(err){
res.send(500, err.message);
}
);
});
module.exports = router;
Change your db.scan() function to properly return an asynchronous result:
// db.js
module.exports = {
scanDB: function(cb){
docClient.scan(params, function(err, data) {
var clickTypes = [], serialNums = [];
if (err) {
console.log(err.message);
cb(err);
} else {
console.log('scan success');
len = data.Items.length;
for (n=0; n<len; n++) {
clickTypes[n] = data.Items[n].payload.clickType;
serialNums[n] = data.Items[n].serialNumber;
}
cb(null, {clickTypes, serialNums});
}
});
}
};
Then, when you use it:
var db = require('../routes/db.js');
db.scanDB(function(err, data) {
if (!err) {
// data.clickTypes
// data.serialNums
} else {
// process error
}
});
It really does not good to put the scanDB result on the DB object the way you were doing because there was no way for the caller to know when the asynchronous operation was done. So, since you have to provide some notification for the caller when the async operation is done (either via callback or promise), you may as well just pass the results there too.
Also, the .then() handler in your router.get(...) handler does not belong there. I don't know why it's there at all as there are no promises involved in the code you show. Perhaps a cut/paste error when creating the question?
Note, I removed the IIFE from your getDatabase() definition since there was no benefit to it other than a little more complicated code.

unable to return in node.js from a function

I have three blocks of code where block one executes first and the result of first block is passed to bloack 2 and then the final result is then passed to the third block which has to send data to the route.
But at the end the return is undefined.
function getUserKey(userRole, callback) {
//keys value is stored and returned
var keys = base.menuModel.find({ 'name' : userRole }, function (err, result) {
if (!err) {
var menu = JSON.stringify(result);
menu = JSON.parse(menu);
var menuKeys = [];
for(i = 0;i < Object.keys(menu[0].permissions[0]).length;i++) {
menuKeys.push((Object.keys(menu[0].permissions[0])[i]));
}
callback(null,menuKeys);
//returns menukeys to be shown
}
else {
return err;
}
});
}
n is holding the menu keys
function userMenuData(n, callback) {
var filterResult = base.globalMenuModel.find({"title" : { $in : n}},function (err, result) {
if (!err) {
callback(null,result);
}
else {
return err;
}
});
}
var userMenu = function(userRole,callback) {
var userMenuTemp = async.compose(userMenuData, getUserKey);
var sendData = userRole is passed and the result is obtained
userMenuTemp(userRole,function(err,result) {
return result; // data success
});
console.log(sendData); //undefined
return sendData;
}
here i want to pass sendData to route in node.js
but at the console i am getting undefined.
Thanks for any help
It's the async nature of node that's getting you. The console.log is happening before any of those functions are returned. You want to check out a library that does promises like Q http://documentup.com/kriskowal/q/

Node.js async - build object from loop, then do something with object

I'm trying to run a function and once that function is complete, then run another function. The first function reads a CSV file, makes a GET request, and builds an object. The second function uses that newly created object to create a new CSV file.
The problem I'm having is that the new CSV file is being created prior to the GET requests finishing.
I'm using async.parallel to set the flow, but not able to get the logic right.
I'd love to know what I'm doing wrong and better understand how node thinks about these tasks.
// Require
var request = require('request');
var fs = require('fs');
var json2csv = require('json2csv');
var csv = require('csv');
var async = require('async');
// Params
var emailHunter_apiKey = '0000';
var emails = [];
var fields = ['email'];
var i = 0;
// Start
async.parallel([
function(callback){
setTimeout(function(){
var file = fs.readFileSync('file.csv');
csv.parse(file, {delimiter: ','}, function (err, data) {
for (var key in data) {
if (i < 5) {
if (data.hasOwnProperty(key)) {
var h = data[key];
if (h[5] != '') {
var url = h[5];
url = url.replace('//', '');
url = url.replace('www.', '');
request('https://api.emailhunter.co/v1/search?domain=' + url + '&api_key=' + emailHunter_apiKey + '', function (error, response, body) {
if (!error && response.statusCode == 200) {
var json = JSON.parse(body);
for (var subObj in json) {
if (json.hasOwnProperty(subObj) && subObj == 'emails') {
var emailObj = json[subObj];
for (var key in emailObj) {
var email = {
'email': emailObj[key]['value']
};
emails.push(email);
}
}
}
}
});
}
}
}
i++;
}
});
callback(null, emails);
}, 200);
console.log(emails);
}
],
function(err, results){
json2csv({data: results, fields: fields}, function (err, csv) {
if (err) console.log(err);
fs.writeFile('export.csv', csv, function (err) {
if (err) throw err;
console.log('file saved');
});
});
console.log(results);
});
As laggingreflex mentioned, you're using async incorrectly.
First you should build a an array of functions that you want to execute in parallel. And then use async to execute them.
Furthermore, your callback was getting executed immediately because csv.parse() is an async function. Therefore node fires it immediately and then executes callback(). You need to move the callback inside of parse().
Try this...
// Params
var emailHunter_apiKey = '0000';
var emails = [];
var fields = ['email'];
var i = 0;
var functionsToRunAsync = [];
var file = fs.readFileSync('file.csv');
csv.parse(file, {delimiter: ','}, function (err, data) {
for (var key in data) {
if (i < 5) {
if (data.hasOwnProperty(key)) {
var h = data[key];
if (h[5] != '') {
var url = h[5];
url = url.replace('//', '');
url = url.replace('www.', '');
// add a new function to an array, to be executed later
functionsToRunAsync.push(function(callback) {
request('https://api.emailhunter.co/v1/search?domain=' + url + '&api_key=' + emailHunter_apiKey + '', function (error, response, body) {
if (!error && response.statusCode == 200) {
var json = JSON.parse(body);
for (var subObj in json) {
if (json.hasOwnProperty(subObj) && subObj == 'emails') {
var emailObj = json[subObj];
for (var key in emailObj) {
var email = {
'email': emailObj[key]['value']
};
emails.push(email);
// callback to tell async this function is complete
callback()
}
}
}
} else {
// callback to tell async this function is complete
callback
}
});
});
}
}
}
i++;
}
// now that we have all of the functions in an array, we run them in parallel
async.parallel(
functionsToRunAsync,
function(err, results) { // all async functions complete
json2csv({data: results, fields: fields}, function (err, csv) {
if (err) console.log(err);
fs.writeFile('export.csv', csv, function (err) {
if (err) throw err;
console.log('file saved');
});
});
console.log(results);
});
});

Using npm async to run in parallel async methods and return a unified response

I have 2 async methods that can run independently one from each other. I would like to call a callback once both are finished. I have tried using async.parallel() (npm) but this seems to be for non async methods. How can I implement this?
Here is my async.parallel call(); note that asyncTasks is my function array, where the functions are async.
async.parallel(asyncTasks, function(resultFinal){
console.log("--------->>>>> message: "+JSON.stringify(resultFinal));
console.log("");
callback(new RequestResponse(true, resultFinal));
});
In short, what I really want is a way to execute multiple async methods in parallel and consider that method finished when the callback provided for that function is triggered.
UPDATE
for a better understanding, I've included the two functions I am using
getGroups
var getGroups = function (callback_async_1) { //get groups + members
pg.connect(datebasePath, function (err, client, done) {
var s = squel.select();
s.from("groups_members");
s.where("user_id = ?", userId);
console.log("query: " + s.toString());
client.query(s.toString(), function (err, result) { //get groups ids in which i am a member
if (err) {
console.error("error...1 " + err);
callback_async_1(responseData);
} else {
// console.log("init -- get from group_members " + JSON.stringify(result.rows));
var groupIds = [];
if (result.rows.length > 0) {
for (var i = 0; i < result.rows.length; i++) {
groupIds.push(result.rows[i].group_id); // create group ids list
}
// console.log("group ids : " + groupIds);
}
if (groupIds.length === 0) {
callback_async_1(responseData);
}
var s = squel.select();
s.from("groups");
s.where("id IN ?", groupIds);
client.query(s.toString(), function (err, result2) { // retrieve all the groups in which i take part
if (err) {
console.error("error...2 " + err);
callback_async_1(responseData);
return;
} else {
// var groupIds2 = [];
// console.log("init -- get from groups " + JSON.stringify(result2.rows));
var groups = [];
// var groups_members = [];
for (var i = 0; i < result2.rows.length; i++) {
groups.push(result2.rows[i]); // adding group info to list
// var groupId = result2.rows[i].id;
// groupIds2.push(groupId);
}
// console.log("");
//console.log(" ------->>>> " + JSON.stringify(groups));
// console.log("");
// responseData.groups = groups;
responseData.push({ //pushing groups into response
"groups": groups
});
var s = squel.select();
s.from("groups_members");
s.where("group_id IN ?", groupIds);
client.query(s.toString(), function (err, result3) { // get all the members in my groups
//console.log("get from group_members --- " + JSON.stringify(result3.rows));
var groupMembers = [];
for (var i = 0; i < result3.rows.length; i++) {
groupMembers.push({
groupMember: result3.rows[i] // pushing all the group members
});
}
//console.log("");
// console.log(" ------->>>> " + JSON.stringify(groupMembers));
// console.log("");
responseData.push({
"groupsMembers": groupMembers
});
// console.log("resulting json till now; Groups : " + JSON.stringify(responseData));
//fetching now events
var s = squel.select();
s.from("events");
s.where("group_id IN ?", groupIds);
client.query(s.toString(), function (err, result4) { //selecting all events that have my groups
if (err) {
console.error("error...3 " + err);
callback_async_1(responseData);
return;
} else {
var events = [];
for (var i = 0; i < result4.rows.length; i++) {
events.push(result4.rows[i]);
}
// responseData.events = events;
responseData.push({
"events": events
});
//responseData.push (events);
callback_async_1(responseData);
// asyncTasks[1](callback);
}
});
});
}
});
}
});
done();
});
};
getRegisteredContacts
var getRegisteredContacts = function (callback_async_2) { // get registered contacts
pg.connect(datebasePath, function (err, client, done) {
//get contacts that are registered
var s = squel.select();
s.from("users");
s.where("phone_number IN ?", arrayOfContacts);
client.query(s.toString(), function (err, result5) { // retriving registered contacts -- should be run with async parallel, it does not depend on the other calls
if (err) {
console.error(err);
callback_async_2(responseData);
} else {
if (result5.rows.length > 0) {
var contacts = [];
for (var i = 0; i < result5.rows.length; i++) {
contacts.push(result5.rows[i]);
}
responseData.push({
"registeredContacts": contacts
});
}
//console.log("");
//console.log(" ------->>>> " + JSON.stringify(events));
// console.log("");
// console.log("final ---> " + JSON.stringify(responseData));
callback_async_2(responseData);
}
});
done();
});
};
You need your task function to take a parameter which you then call when the task is done
var task = function(callback){
console.log('Task');
callback(null);
};
When you are then doing something async within the task then your task would look like
var task = function(callback){
console.log('Task');
request.get('http://www.google.com', function (error, response, body){
console.log('Task - ' + response.statusCode);
callback(null);
});
};
Example
var async = require('async');
var request = require('request');
var task1 = function(callback){
console.log('Task 1');
callback(null);
};
var task2 = function(callback){
console.log('Task 2');
request.get('http://www.google.com', function (error, response, body){
console.log('Task 2 - ' + response.statusCode);
callback(null);
});
};
var asyncTasks = [task1, task2];
async.parallel(asyncTasks, function(err, result){
console.log('--DONE--');
});
Outputs
Task 1
Task 2
Task 2 - 200
--DONE--
Based on your new code listing the most obvious thing is done() is called too early for both of your tasks. It needs to be like
var getRegisteredContacts = function (callback_async_2) {
pg.connect(datebasePath, function (err, client, done) {
var s = squel.select();
s.from("users");
s.where("phone_number IN ?", arrayOfContacts);
client.query(s.toString(), function (err, result5) {
done(); // <---- done() to be here
if (err) {
//
} else {
//
}
callback_async_2();
});
});
};
You should also lint your code. If you had you would have noticed that you had not checked if there was an err for callback pg.connect (and also keep it nicer to read correctly)

Resources