Console log promise nodejs - javascript

How can I console.log the promise stored in a promise variable? I want to later assign that to a variable.
function getMetricFilters() {
const promise = new Promise(function(resolve, reject) {
exec("aws cloudwatch list-metrics --namespace AWS/ApiGateway | jq -r .Metrics[].Dimensions[].Name | sort -u", (error, stdout, stderr) => {
if (error) {
console.error(`error: ${error.message}`);
//return errorResponse(500, 'Error running migration.');
}
if (stderr) {
console.log(`stderr: ${stderr}`);
//return errorResponse(500, 'Error running migration.');
}
console.log(`stdout: ${stdout}`);
//return okResponse(200, 'Migration successfully.');
})
})
return promise
}
getMetricFilters() // I want the result of promise to be printed out or store it in a variable.
If I don't use the getMetricFilters function then it prints out stdout value. But if use the function, then nothing is printed out.

Make a re-usable helper function that encapsulates exec() in a promise:
function execAsync(cmdline) {
return new Promise((resolve, reject) => {
exec(cmdline, (error, stdout, stderr) => {
if (error) reject({error, stdout, stderr});
else resolve({stdout, stderr});
});
});
}
Make a function for your cloudwatch command:
function getMetricFilters() {
return execAsync("aws cloudwatch list-metrics --namespace AWS/ApiGateway | jq -r .Metrics[].Dimensions[].Name | sort -u");
}
Use it:
getMetricFilters()
.then(({stdout, stderr}) => {
okResponse(200, 'Migration completed successfully.');
})
.catch(({error, stdout, stderr}) => {
console.error(`error: ${error.message}`);
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
errorResponse(500, 'Error running migration.');
});
Mixing node-style callbacks and promises usually does not end well.
Since your exports.handler is a callback-based function, and all your other functions (like exec() and lambda.invoke()) are callback-based, I would keep the entire code callback-based.
The async module offers useful helpers for this, such as waterfall(), which executes a list of asynchronous functions one after another, passing the result(s) of the previous to the next.
exports.handler = function (event, context, callback) {
async.waterfall([
// 1. parse SNS message (throw if invalid/unexpected)
(callback) => {
const sns = JSON.parse(event.Records[0].Sns.Message);
console.log(1, sns);
const Namespace = sns.Trigger.Namespace;
if (Namespace !== "AWS/S3") throw new Error('Unexpected Namespace: ' + Namespace);
const msgData = {
Namespace: Namespace,
MetricName: sns.Trigger.MetricName,
BucketName: sns.Trigger.Dimensions.find(where('name', 'BucketName')).value,
StorageType: sns.Trigger.Dimensions.find(where('name', 'StorageType')).value
};
callback(null, msgData);
},
// 2. run cloudwatch command
(msgData, callback) => {
console.log(2, msgData);
const cmdline = "aws cloudwatch list-metrics --Namespace AWS/ApiGateway | jq -r .Metrics[].Dimensions[].Name | sort -u";
exec(cmdline, (err, stdout, stderr) => {
if (err) return callback(err);
callback(null, msgData, stdout, stderr);
});
},
// 3. run zabbixPy
(msgData, stdout, stderr, callback) => {
console.log(3, msgData, stdout);
lambda.invoke({
FunctionName: 'zabbixPy',
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: JSON.stringify({
Host: msgData.Namespace.replace("/", "_"),
key: `AWS[${msgData.BucketName}_${msgData.StorageType}_${msgData.MetricName}]`,
Value: "1"
})
}, callback);
},
// 4. next step...
(zabbixResponse, callback) => {
console.log(4, zabbixResponse.Payload);
// ...
callback(null, 'All done.');
},
], (err, result) => {
if (err) {
console.error(err);
context.fail(err);
callback(err);
return;
}
console.log(result); // 'All done.'
// don't forget to call the main callback here
});
};
The alternative would be to convert all asynchronous functions in that code path to promise-based functions. Not sure if that's less work.

Related

How to use child-process-promise

var promise = require('child-process-promise').spawn;
promise('some_command_producing_output')
.then(function (result) {
...
})
.catch(function (err) {
...
});
What I want is to add some processing after command produced output in stdout. So finally I want to create a function to use like this:
RunCommandAndProcess('some_command_producing_output')
.then(function (result) {
...
})
.catch(function (err) {
...
});
The function should use promise from child-process-promise, wait until successful result produced and return promise for processing data.
Welcome to Stack Overflow #ScHoolboy.
I cansuggest you use a basic child-process module from Node.js and promising it yourself in the following way
const spawn = require('child_process').spawn;
const spawnPromise = (cmd, args) => {
return new Promise((resolve, reject) => {
try {
const runCommand = spawn(cmd, args);
runCommand.stdout.on('data', data => resolve(data.toString()));
runCommand.on('error', err => {
throw new Error(err.message);
});
} catch (e) {
reject(e);
}
});
};
Where:
cmd - command, for example, "echo"
args: array of arguments, for example ["Hello world"]
You can call the function as RunCommandAndProcess if you need :)
Example of usage:
spawnPromise('ls', ['-la']).then(data => console.log('data: ', data));
Or
const result = await spawnPromise('ls', ['-la']);
You can do it by
var spawn = require('child-process-promise').spawn;
var promise = spawn('echo', ['hello']);
var childProcess = promise.childProcess;
console.log('[spawn] childProcess.pid: ', childProcess.pid);
childProcess.stdout.on('data', function (data) {
console.log('[spawn] stdout: ', data.toString());
});
childProcess.stderr.on('data', function (data) {
console.log('[spawn] stderr: ', data.toString());
});
promise.then(function () {
console.log('[spawn] done!');
})
.catch(function (err) {
console.error('[spawn] ERROR: ', err);
});
For the more information please check the documentation

DynamoDB updateItem in Lambda fails silently

I'm attempting to implement a simple counter with a Lambda function, but whenever I test it, the updateItem below simply does not work: none of the log statements in the callback are run at all, and of course the relevant counter in the table is never updated. Here's my lambda function:
'use strict';
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
let params = {
TableName: 'Counters',
Key: {
'name': { S: 'global' }
},
UpdateExpression: 'SET val = val + :inc',
ExpressionAttributeValues: {
':inc': { N: '1' }
},
ReturnValues: 'ALL_NEW'
};
exports.handler = async(event) => {
console.log("Invoked counter-test");
dynamodb.updateItem(params, function(err, data) {
console.log("In updateItem callback");
if (err)
console.log(err, err.stack);
else
console.log(data);
});
console.log("Updated counter");
const response = {
statusCode: 200,
body: JSON.stringify('Counter updated'),
};
return response;
};
And here's the output of the test:
Response:
{
"statusCode": 200,
"body": "\"Counter updated\""
}
Request ID:
"80e92299-2eea-45e4-9c68-54ccf87199c5"
Function Logs:
START RequestId: 80e92299-2eea-45e4-9c68-54ccf87199c5 Version: $LATEST
2019-05-07T11:34:21.931Z 80e92299-2eea-45e4-9c68-54ccf87199c5 Invoked counter-test
2019-05-07T11:34:21.934Z 80e92299-2eea-45e4-9c68-54ccf87199c5 Updated counter
END RequestId: 80e92299-2eea-45e4-9c68-54ccf87199c5
REPORT RequestId: 80e92299-2eea-45e4-9c68-54ccf87199c5 Duration: 275.91 ms Billed Duration: 300 ms Memory Size: 128 MB Max Memory Used: 67 MB
As you can see, no log statements from the updateItems callback ran.
If I try to update the counter from the command line using aws dynamodb it does work, however:
$ aws dynamodb update-item \
--table-name Counters \
--key '{"name": { "S": "global" }}' \
--update-expression 'SET val = val + :inc' \
--expression-attribute-values '{":inc": {"N": "1"}}' \
--return-values ALL_NEW \
--output json
{
"Attributes": {
"name": {
"S": "global"
},
"val": {
"N": "129"
}
}
}
This is due to the asynchronous nature of Javascript.
The method updateItem is asynchronous and you don't wait for the callback to be fired before returning (you start the updateItem operation and then immediately return a response).
If you want to maintain the callback pattern, you should do:
exports.handler = (event, context, callback) => {
console.log("Invoked counter-test");
dynamodb.updateItem(params, function(err, data) {
console.log("In updateItem callback");
if (err) {
console.log(err, err.stack);
callback(err);
} else {
console.log(data);
console.log("Updated counter");
const response = {
statusCode: 200,
body: JSON.stringify('Counter updated'),
};
callback(null, response);
}
});
};
Using promises:
exports.handler = (event, context, callback) => {
console.log("Invoked counter-test");
dynamodb.updateItem(params).promise()
.then((data) => {
console.log(data);
console.log("Updated counter");
const response = {
statusCode: 200,
body: JSON.stringify('Counter updated'),
};
callback(null, response);
});
.catch((err) => {
console.log(err, err.stack);
callback(err);
})
};
Using await (recommended):
exports.handler = async (event) => {
try {
console.log("Invoked counter-test");
const data = await dynamodb.updateItem(params).promise();
console.log(data);
console.log("Updated counter");
const response = {
statusCode: 200,
body: JSON.stringify('Counter updated'),
};
return response;
} catch (err) {
console.log(err, err.stack);
throw err;
}
};
See also Understanding Asynchronous JavaScript, Deeply Understanding JavaScript Async and Await with Examples and AWS SDK for Javascript - Using JavaScript Promises.

How to return a list of SQS queues in a module exports function?

I'm very new to node.js so I think I'm missing something obvious here.
I'm simply trying to get a list of SQS queues using aws-sdk and return them from a module to be accessible to other code. list_queues is the function in question.
The code below works to an extent, I see a "success" log and a log of a string array of all my queues, however, the function does not return that array to the caller and I don't understand why.
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
var sqs;
var sts = new AWS.STS();
sts.assumeRole({
RoleArn: 'arn:aws:iam::xxxxx:role/UserRole',
RoleSessionName: 'NodeDeveloperRoleSession'
}, function(err, data) {
if (err) { // an error occurred
console.log('Cannot assume role :(');
console.log(err, err.stack);
} else { // successful response
console.log('Assumed role success :)');
AWS.config.update({
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken
});
sqs = new AWS.SQS({apiVersion: '2012-11-05'});
}
});
exports.list_queues = function() {
sqs.listQueues({}, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("success");
console.log(data.QueueUrls);
return data.QueueUrls;
}
});
}
Any help is appreciated
exports.list_queues = function() { // 2. but you actually want to return from this one
sqs.listQueues({}, function(err, data) { <-----------------
if (err) { |
console.log("Error", err); |
} else { |
console.log("success"); |
console.log(data.QueueUrls); |
return data.QueueUrls; // 1. you are returning from this one
}
});
}
there are two ways you can make it work
Promise based
exports.list_queues = function() {
return sqs.listQueues({}).promise().then((data) => data.QueueUrls);
}
// and in another file you would:
const {list_queues} = require('./list_queues.js');
list_queues.then((queues) => console.log(queues));
Callback based
exports.list_queues = function(cb) { // notice I added callback here
sqs.listQueues({}, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("success");
console.log(data.QueueUrls);
cb(data.QueueUrls);
}
});
}
// and in another file you would:
const {list_queues} = require('./list_queues.js');
list_queues(function(queues) {
console.log(queues);
});
I strongly recommend you to use promise based approach, since it's much more readable and you can make use of async/await on it, which is great.

node.js ignores awaitZip building with express

I want to fetch icon PNGS from gridfs out of our mongodb database with mongoose. These icons then should be zipped and served at a specific route.
My current code is as follows:
var zip = require("node-native-zip");
async function getZipFile() {
//get the events out of the DB
db.Category.find({}).populate('icons.file').exec(async function (err, cats) {
if (err) {
//oh oh something went wrong, better pass the error along
return ({
"success": "false",
message: err
});
}
else {
//all good, build the message and return
try {
const result = await buildZip(cats);
return ({
"success": "true",
message: result
});
}
catch (err) {
console.log("ZIP Build Failed")
}
}
});
}
async function buildZip(cats) {
let archive = new zip();
for (let i = 0; i < cats.length; i++) {
cats[i].icons.forEach(function (icon) {
if (icon.size === "3x") {
db.Attachment.readById(icon.file._id, function (err, buffer) {
if (err)
return;
archive.add(cats[i]._id + ".png", buffer);
});
}
});
//return when everything is done
if (i === cats.length - 1) {
return archive.toBuffer();
}
}
}
module.exports =
{
run: getZipFile
};
I don't want to build the zip before runtime, as I want to rename the icons acording to the category ID. I tried going for a async/await structure, but my callback is being returned before the building of the zip file even started.
I'm calling the function with
case 'categoryZip':
categoryHelper.getZipFile.run().then((result) => {
callback(result);
});
break;
This should (as far as I understood it) fire the callback when the zipping is done, but I think I'm missing something essential here.
I wrapped both your callback methods into promises, and also awaited your double for-loop of callbacks in parallel using Promise.all() since they don't rely on each other and I assume they don't need to be in any particular order in the zip file:
async function getZipFile() {
//get the events out of the DB
return new Promise((resolve, reject) => {
db.Category.find({}).populate('icons.file').exec(async function(err, cats) {
if (err) {
//oh oh something went wrong, better pass the error along
reject({
success: false,
message: err
});
} else {
//all good, build the message and return
try {
const result = await buildZip(cats);
resolve({
success: true,
message: result
});
} catch (err) {
console.log("ZIP Build Failed")
reject({
success: false,
message: err
});
}
}
});
});
}
async function buildZip(cats) {
let archive = new zip();
await Promise.all(
cats.map(cat => Promise.all(cat.icons
.filter(icon => icon.size === '3x')
.map(icon => new Promise((resolve, reject) => {
db.Attachment.readById(icon.file._id, function(err, buffer) {
if (err) return reject(err);
archive.add(cat._id + ".png", buffer);
resolve();
});
}))
))
);
return archive.toBuffer()
}

how to return array in Node.js from module

getting undefined all the time "main.js":
var dbAccess = require('../dao/dbAccess');
dbaInstance = new dbAccess();
var wordPool = dbaInstance.getWordPool();
console.log (wordPool);
and "dbAccess.js" contains:
var DatabaseAccess = function() {}
DatabaseAccess.prototype.getWordPool = function () {
RoundWord.find({},'words decoys', function(err, wordPoolFromDB) {
if (err) throw err;
//console.log(wordPoolFromDB); -working ok
return (wordPoolFromDB);
});
}
module.exports = DatabaseAccess;
why is it not working?
DatabaseAccess.prototype.getWordPool is not returning any result.
Since you are using an asynchronous function, you need do one of these things:
a) Take a callback as parameter and invoke the callback with a result
DatabaseAccess.prototype.getWordPool = function (cb) {
RoundWord.find({}, 'words decoys', function(err, results) {
if (err) {
return cb(err, null);
}
cb(null, results);
});
}
The callback convention is: cb(error, results...)
b) Use promises
DatabaseAccess.prototype.getWordPool = function () {
return RoundWord.find({}, 'words decoys', function (err, results) {
if (err) {
throw err; // however you might want to sanitize it
}
return results;
});
}
To consume this result you will need to do it as a promise
databaseAccess.getWordPool()
.catch(function (err) {
// process the error here
})
.then(function (results) {
// something with results
});
It will work if you change to this:
var dbAccess = require('../dao/dbAccess');
dbaInstance = new dbAccess();
dbaInstance.getWordPool(function(wordPool){console.log (wordPool);});
And:
var DatabaseAccess = function() {}
DatabaseAccess.prototype.getWordPool = function (cb) {
RoundWord.find({},'words decoys', function(err, wordPoolFromDB) {
if (err) throw err;
//console.log(wordPoolFromDB); -working ok
cb(wordPoolFromDB);
});
}
module.exports = DatabaseAccess;
If the function is Asynchronous you need to pass a callback to find to get the result:
DatabaseAccess.prototype.getWordPool = function (callback) {
RoundWord.find({},'words decoys', function(err, wordPoolFromDB) {
if (err) throw err;
callback(err, wordPoolFromDB);
});
}
and call it as follows in main:
dbaInstance.getWordPool(function (err, wordPool) {
console.log (wordPool);
// wordPool is only available inside this scope,
//unless assigned to another external variable
});
// cannot access wordPool here

Categories

Resources