function not returning with promise - javascript

I'm trying to call a function (refresh_access_token) from another one and create a Promise chain from that. But the return function inside refresh_access_token is not working. The refresh_access_token doesn't return to it's caller when it completes.
I'm receiving this message:
Unhandled rejection TypeError: Cannot read property 'then' of undefined
How can I fix this?
These are the 2 function code:
exports.refresh_access_token = function(environment_hash) {
var MercadoLibre = require('../../models/index').MercadoLibre;
var needle = require('needle');
const load = require('express-load');
var meli = require('mercadolibre');
var request=require('request');
const mysql = require('mysql');
const dotenv = require('dotenv').config({path: '../../.env'});
var oauth_url = 'https://api.mercadolibre.com/oauth/token';
var env_hash = environment_hash;
where = { environment_hash: env_hash };
MercadoLibre.findOne({where: where}).then(function (account) {
if (!account) {
// Item not found
} else {
// Found an item, update it
needle.post(oauth_url, {
grant_type: 'refresh_token',
client_id: process.env.MERCADOLIBRE_CLIENT_ID,
client_secret: process.env.MERCADOLIBRE_SECRET,
refresh_token: account.refresh_token
}, {
}, function (err, res, body) {
if (body) {
expires_in = new Date();
expires_in.setUTCHours(0);
expires_in.setTime(expires_in.getTime() + 21600*1000);
values = {
refresh_token: body.refresh_token,
expires_in: expires_in
};
where = { environment_hash: env_hash };
return MercadoLibre.update(values, {where: where});
}
});
}
});
}
exports.run_mercadolibre_jobs = function() {
var MercadoLibre = require('../../models/index').MercadoLibre;
var values = {
attributes: ['environment_hash'],
raw: true
};
MercadoLibre
.findAll(values)
.then(function(accounts) {
Promise.all(accounts.map(function(account) {
module.exports.refresh_access_token(account.environment_hash)
.then(function(response) {
console.log(response);
})
.catch(function(response){
console.log(response);
});
}));
});
}

Your function refresh_access_token is not returning anything. Your only return statement is inside the needle.post callback. You should be first returning:
return MercadoLibre.findOne(...);
However, you are mixing promises, with callbacks (used in your needle.port call). I would suggest reviewing Promises vs Callbacks and how to use them together. Here is a good thread on how to convert callback-apis to promises: How do I convert an existing callback API to promises?.
Another alternative would be to replace using needle with a promise-supported node library:
Axios
Request Promise

Related

Why does this verified JSON Web Token (JWT) output as undefined?

I'm trying to decode a JWT id_token using jwks-rsa and jsonwebtoken but the result is returning as undefined.
I know this has something to do with callbacks, and the fact that I need to wait for a response from the getKey function but I can't wrap my head around how to structure the code to make that happen.
This is what I have so far...
function do_thing(properties, context) {
const id_token = "REDACTED";
// Verify using getKey callback
var jwksClient = require('jwks-rsa');
var client = jwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
var jwt = require('jsonwebtoken');
jwt.verify(id_token, getKey, { algorithms: ['RS256'] }, function(err, decoded) {
if (err) {
console.log(err);
} else {
return decoded;
}
});
const bubble_obj = do_thing();
console.log(bubble_obj); //This is `undefined`
The console.log(bubble_obj); outputs as undefined.
I know the problem with the above code is due to the nature of callbacks and asynchronous code, because if I move the console.log inside the jwt.verify call it will show the correctly decoded token.
See here for that example...
function do_thing(properties, context) {
const id_token = "REDACTED";
// Verify using getKey callback
var jwksClient = require('jwks-rsa');
var client = jwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
var jwt = require('jsonwebtoken');
jwt.verify(id_token, getKey, { algorithms: ['RS256'] }, function(err, decoded) {
if (err) {
console.log(err);
} else {
console.log(decoded); //When moved here, it prints the correctly decoded token
return decoded;
}
});
const bubble_obj = do_thing();
So how do I make it return the correctly decoded token?
You're not handling the asynchronous code correctly. The jwt.verify method returns a Promise if you do not pass it the callback method.
If you use return jwt.verify(id_token, getKey, { algorithms: ['RS256'] }) inside the do_thing function and call it like this do_thing().then((decodedToken) => console.log(decodedToken)), it should work as expected.
you need to handle promise returned by jwt.verify. either use promise.then. or go with async/await.
async function do_thing(properties, context) {
const id_token = "REDACTED";
// Verify using getKey callback
var jwksClient = require('jwks-rsa');
var client = jwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, function(err, key) {
var signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
const jwt = require('jsonwebtoken');
return jwt.verify(id_token, getKey, { algorithms: ['RS256'] });
}
const decodedToken = await do_thing();
console.log("decoded token:", decodedToken);
You can use a promise to verify the JWT with a JWK callback and promise as follows. You will need to wrap the following in an async function to use the result of the verify_jwks() function:
const token = "REDACTED";
var jwksClient = require('jwks-rsa');
// Creates a JWKS Client with a rate limit that
// limits the number of calls to our JWKS endpoint
var client = new JwksClient({
jwksUri: 'https://REDACTED.com/.well-known/jwks.json',
rateLimit: true,
jwksRequestsPerMinute: 10, // Default Value
cache: true, // Default Value
cacheMaxEntries: 5, // Default value
cacheMaxAge: 600000, // Defaults to 10m
});
// Verifies the JWKS asynchronously, returns Promise
async function verify_jwks() {
function getKey(header, callback) {
// Callback that returns the key the corresponding key[kid]
client.getSigningKey(header.kid, function(err, key) {
const signingKey = key.getPublicKey() || key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
// Returns a Promise with verification result or error
return new Promise((resolve,reject) =>
jsonwebtoken.verify(token,getKey, {
algorithms: ["HS256", "RS256"]
},
function(err,decoded) {
return err ? reject(err) : resolve(decoded);
}
));
}
let result;
await verify_jwks()
.then(decoded => result = decoded)
.catch(error => console.log(error));
console.log(result);

Access Vue Instance Using this

When I try to use this in my VueJs methods I get the following error
this is undefined
I think that I shouldn't use arrow functions because their this does not bind to the context I expect it to.
I try with a regular function and get the error above.
What I've tried so far
methods: {
connection(){
new elasticsearch.Client({...});
client.search({...})
.then(function (resp) {
var hits = resp.aggregations;
this.tmp = hits[1].value;
}, function (err) {
console.trace(err.message);
});
}
}
I cannot use the this that I want to in the functions passed to .search and .then . How can I have this bind to my VueJs instance so I can access data, computed, etc...?
You should use arrow function to save this context, and don't forget that inside Vue methods this refers to the current instance.
data() {
return {
counter:0,
connections:2,
tmp: 0,
}
},
methods: {
connection() {
// ...
var client = new elasticsearch.Client({
host: 'xxxxxxxxxxxx'
});
client.search({
[...]
}).then((resp) => {
var hits = resp.aggregations;
this.tmp = hits[1].value;
}, (err) => {
console.trace(err.message);
});
}
}
You can assign this variable to local variable(self) and use it in .then function
data () {
return {
counter:0,
connections:2
}
},
methods: {
connection(){
var self = this;
var tmp=0
var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
host: 'xxxxxxxxxxxx'
});
client.search({
"index":"400000",
[...]
}
}).then(function (resp) {
var hits = resp.aggregations;
self.tmp=hits[1].value;
}, function (err) {
console.trace(err.message);
});
console.log("tmp:",tmp)
}
}

How can I convert the following JavaScript Promise into regular asychronous code?

Hi I'm working with the following code snippet:
imaps.connect(config).then(function (connection) {
connection.openBox('INBOX').then(function () {
// Fetch emails from the last 24h
var delay = 24 * 3600 * 1000;
var yesterday = new Date();
yesterday.setTime(Date.now() - delay);
yesterday = yesterday.toISOString();
var searchCriteria = ['UNSEEN', ['SINCE', yesterday]];
var fetchOptions = { bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)'], struct: true };
// retrieve only the headers of the messages
return connection.search(searchCriteria, fetchOptions);
}).then(function (messages) {
var attachments = [];
messages.forEach(function (message) {
var parts = imaps.getParts(message.attributes.struct);
attachments = attachments.concat(parts.filter(function (part) {
return part.disposition && part.disposition.type.toUpperCase() === 'ATTACHMENT';
}).map(function (part) {
// retrieve the attachments only of the messages with attachments
return connection.getPartData(message, part)
.then(function (partData) {
return {
filename: part.disposition.params.filename,
data: partData
};
});
}));
});
return Promise.all(attachments);
}).then(function (attachments) {
console.log(attachments);
// =>
// [ { filename: 'cats.jpg', data: Buffer() },
// { filename: 'pay-stub.pdf', data: Buffer() } ]
});
I'm trying to remove Promises and turn the code into code using callbacks.
At the moment, I'm looping through all the attachments and when I print an individual attachment to the console I get:
Promise { <pending> }
How can I convert the code to regular callback code such that I can access the 'filename' and 'data' attributes of the attachment?
Actually, I don't understand why you need to go back from a promise to a callback but you can use the following code. It just a promise converter:
const promiseToCallback = function (promise) {
if (!isFn(promise.then)) {
throw new TypeError('Expected a promise');
}
return function (cb) {
promise.then(function (data) {
setImmediate(cb, null, data);
}, function (err) {
setImmediate(cb, err);
});
};
};
To use it
promiseToCallback(promise)(function(err, data) {
...
});

Chaining function calls using promises

Hi Im trying to call subsequent functions of the same types passing parameters as shown in the example:
The problem is that im getting random output like, Line3, Line4, Line1, Line2. Am I doing something wrong with the chaining of is it a problem with the latency of the rendering of each text to screen? Should i use npm sleep?
var function1 = (senderID,req,FB_ID) => {
return new Promise((resolve,reject) => {
var line1 = 'Line1';
var line2 = 'Line2';
var line3 = 'Line3';
var line4 = 'Line4';
// Display lines of text
sendTextMessage(senderID,line1)
.then(sendTextMessage(senderID,line2))
.then(sendTextMessage(senderID,line3))
.then(sendTextMessage(senderID,line4))
.then(resolve());
});
};
Each sendTextMessage call calls this function below:
var sendTextMessage = (recipientId, messageText) => {
return new Promise((resolve,reject) => {
var messageData = {
recipient: {
id: recipientId
},
message: {
text: messageText
}
};
callSendAPI(messageData).then(function(){
resolve();
});
});
};
CALLSENDAPI :
var callSendAPI = (messageData) => {
var sleepSec = 1;
return new Promise((resolve,reject) => {
request({
uri: 'https://graph.facebook.com/v2.6/me/messages',
qs: { access_token: process.env.FB_PAGE_TOKEN },
method: 'POST',
json: messageData
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
//var recipientId = body.recipient_id;
//var messageId = body.message_id;
sleep.sleep(sleepSec);
resolve();
} else {
console.error("Unable to send message.");
console.error(response);
console.error(error);
reject(error);
}
});
});
}; // func
The problem is that what you have put as then argument gets executed immediately, while instead you should pass a function.
You could use bind to avoid overly verbose anonymous functions:
sendTextMessage(senderID,line1)
.then(sendTextMessage.bind(null, senderID,line2))
.then(sendTextMessage.bind(null, senderID,line3))
.then(sendTextMessage.bind(null, senderID,line4))
.then(resolve);
Also, note the resolve at the end, without parentheses: you want to pass that function, not execute it.

Node.js: What techniques are there for writing clean, simple callback code?

node.js code is known for turning into callback spaghetti.
What are the best techniques for overcoming this problem and writing clean, uncomplex, easy to understand callback code in node.js?
Take a look at Promises: http://promises-aplus.github.io/promises-spec/
It is an open standard which intended to solve this issue.
I am using node module 'q', which implements this standard: https://github.com/kriskowal/q
Simple use case:
var Q = require('q');
For example we have method like:
var foo = function(id) {
var qdef = Q.defer();
Model.find(id).success(function(result) {
qdef.resolve(result);
});
return (qdef.promise);
}
Then we can chain promises by method .then():
foo(<any-id>)
.then(function(result) {
// another promise
})
.then(function() {
// so on
});
It is also possible to creating promise from values like:
Q([]).then(function(val) { val.push('foo') });
And much more, see docs.
See also:
http://jeditoolkit.com/2012/04/26/code-logic-not-mechanics.html#post
http://wiki.commonjs.org/wiki/Promises/A
Several things can be done to avoid the 'matrioska-style'.
You can store callbacks to variables:
var on_read = function (foo, bar) {
// some logic
},
on_insert = function (err, data) {
someAsyncRead(data, on_read);
};
someAsyncInsert('foo', on_insert);
You can use some modules that help in those scenarios.
// Example using funk
var funk = require('funk');
for(var i = 0; i < 10; i++) {
asyncFunction(i, funk.add(function (data) {
this[i] = data;
}));
}
funk.parallel(function () {
console.log(this);
});
I'd suggest 1) using CoffeeScript and 2) using named callbacks and passing state between them in a hash, rather than either nesting callbacks or allowing argument lists to get very long. So instead of
var callback1 = function(foo) {
var callback2 = function(bar) {
var callback3 = function(baz) {
doLastThing(foo, bar, baz);
}
doSomethingElse(bar, callback3);
}
doSomething(foo, callback2);
}
someAsync(callback1);
you can instead simply write
callback1 = (state) -> doSomething state.foo, callback2
callback2 = (state) -> doSomethingElse state.bar, callback3
callback3 = (state) -> doLastThing state
someAsync callback1
once your doSomething, doSomethingElse and doLastThing have been rewritten to use/extend a hash. (You may need to write extra wrappers around external functions.)
As you can see, the code in this approach reads neatly and linearly. And because all callbacks are exposed, unit testing becomes much easier.
Try node-line
https://github.com/kevin0571/node-line
Usage:
var line = require("line");
line(function(next) {
obj.action1(param1, function(err, rs) {
next({
err: err,
rs: rs
});
});
}, function(next, data) {
if (data.err) {
console.error(err);
return;
}
obj.action2(param2, function(err, rs) {
if (err) {
console.error(err);
return;
}
next(rs);
});
}, function(rs) {
obj.finish(rs);
});
For the most part, working Twitter OAuth2 application-only example, using Kris' Q promise library with https.request, Nodejs Express api route. First attempt user timeline GET. If 401 response, refreshing bearer-token then retry user timeline. I had to use Q.when to handle a promise that returns another promise (chaining) or a value.
/**
* Using Rails-like standard naming convention for endpoints.
* GET /things -> index
* POST /things -> create
* GET /things/:id -> show
* PUT /things/:id -> update
* DELETE /things/:id -> destroy
*/
'use strict';
// var _ = require('lodash');
var http = require('http');
var https = require('https');
var querystring = require('querystring');
var Q = require('q')
// Get list of twtimelines
exports.index = function(req, res) {
var tid = req.query.tid
if (tid) {
Q.when(reqTimeline(tid, true, res), function(value) {
// > value
// 404
// > body1
// '{"errors":[{"code":34,"message":"Sorry, that page does not exist."}]}'
})
} else {
res.json({
errors: [{
message: 'no tid specified in query'
}]
});
}
};
function reqPromise(options, postData) {
var deferred = Q.defer()
var req = https.request(options, function(res) {
// console.log("statusCode: ", res.statusCode);
// console.log("headers: ", res.headers);
var statusCode = res.statusCode
deferred.notify(res)
res.on('data', function(d) {
//process.stdout.write(d);
deferred.notify(d)
}).on('end', function() {
deferred.resolve(statusCode)
});
});
req.on('error', function(e) {
console.error(e);
deferred.reject(e)
});
req.write(postData);
req.end();
return deferred.promise
} // deferRequest
function isIncomingMessage(ot) {
return ot instanceof http.IncomingMessage
}
function isBuffer(ot) {
return ot instanceof Buffer
}
function reqTimeline(screen_name, reqBearerTokenOn401, res) {
var optionsUserTimeline = {
hostname: 'api.twitter.com',
path: '/1.1/statuses/user_timeline.json?' + querystring.stringify({
count: '3',
screen_name: screen_name
}),
method: 'GET',
headers: {
//'Authorization': 'Bearer ' + JSON.parse(body1).access_token
'Authorization': 'Bearer ' + process.env.BEARER_TOKEN
} // headers
};
console.log("optionsUserTimeline", optionsUserTimeline)
var statusCode;
var body1 = new Buffer(''); // default utf8 string buffer ?
return reqPromise(optionsUserTimeline, '')
.then(function(value) { // done
if (reqBearerTokenOn401 && value === 401) {
console.log("reqTimeline - requesting bearer token")
return reqBearerToken(screen_name, res)
}
console.log("reqTimeline - done done:", value)
res.end()
return value
},
function(reason) { // error
console.log("reqTimeline - error:", body1)
},
function(progress) {
console.log("reqTimeline - progress:", body1)
if (isIncomingMessage(progress)) {
body1 = body1.slice(0, 0) // re-set buffer
statusCode = progress.statusCode;
if (reqBearerTokenOn401 && statusCode === 401) {
// readyn for retry
} else {
res.writeHead(statusCode)
}
} else if (isBuffer(progress)) {
if (reqBearerTokenOn401 && statusCode === 401) {
body1 += progress
} else {
res.write(progress)
}
} else {
throw "reqTimeline - unexpected progress"
}
});
} // reqTimeline
function reqBearerToken(screen_name, res) {
var postData = querystring.stringify({
'grant_type': 'client_credentials'
})
var optionsBearerToken = {
hostname: 'api.twitter.com',
path: '/oauth2/token',
method: 'POST',
headers: {
'Authorization': 'Basic ' + new Buffer(
process.env.CONSUMER_KEY + ":" + process.env.CONSUMER_SECRET
).toString('base64'),
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'Content-Length': postData.length
} // headers
}
// console.log("key", process.env.CONSUMER_KEY)
// console.log("secret", process.env.CONSUMER_SECRET)
// console.log("buf", new Buffer(
// process.env.CONSUMER_KEY + ":" + process.env.CONSUMER_SECRET
// ).toString())
console.log("optionsBearerToken", optionsBearerToken)
var body2 = new Buffer(''); // default utf8 string buffer ?
return reqPromise(optionsBearerToken, postData)
.then(function(value) { // done
console.log("reqBearerToken - done:", body2)
if (value === 200) {
console.log("reqBearerToken - done done")
process.env.BEARER_TOKEN = JSON.parse(body2).access_token;
return reqTimeline(screen_name, false, res)
}
return value
}, function(reason) {
throw "reqBearerToken - " + reason
}, function(progress) {
if (isIncomingMessage(progress)) {
body2 = body2.slice(0, 0) // reset buffer
} else if (isBuffer) {
body2 += progress
} else {
throw "reqBearerToken - unexpected progress"
}
});
} // reqBearerToken

Categories