Proper pattern to set a variable via Node async call - javascript

I'm doing an HTTP post, which returns single 10-15 character string. I want to assign this value to a temporary variable and use it while building up another larger string which will contain said 10-15 digit character string.
I've gotten this to work by making the temp variable ("ticket") global, which I don't like:
var ticket; // Global. Lame.
...stuff happens
getTicket("someUser", function(err) {
if(err)
{console.log("problem");}
else
{console.log(ticket);}
});
...other stuff happens
// Helper functions down here....
var getTicket = function (userName, callback) {
var user = userName;
request.post(
{
url: 'http://somewhere',
form: { 'username': user }
},
function(err, response, body) {
if(err) {
callback(err);
return;
} else {
ticket = body;
}
callback(null);
});
}
Can someone point me to the proper pattern to return the variable ticket in the POST's callback (or the body variable, whatever) ala:
_ticket = getTicket("someuser")
Thanks much.

You'd pass ticket as a parameter into your callback:
getTicket("someUser", function(err, ticket) {
if(err)
{console.log("problem");}
else
{console.log(ticket);}
});
function getTicket (userName, callback) {
var user = userName;
request.post(
{
url: 'http://somewhere',
form: { 'username': user }
},
function(err, response, body) {
if(err) {
callback(err);
return;
}
callback(null, body); // body contains the ticket
});
}
request.post is async, so there isn't a pattern that will get you something like:
_ticket = getTicket("someuser")

Related

res.json not returning response

My res.json in my first block of code works, but in the else part of my if statement, it does not. The block that doesnt work, checks for a record in a database then im trying to return the response but im not receiving it.
I've checked and the response is a string, I thought it would have worked as the top part of the code successfully returns the string and it shows in dialogflow (where im trying to return it)
The response is successfully consoled right before the res.json but I do not receive it from the source of the request.
code:
app.post('/webhook/orderinfo', (req, res) => {
const intent = req.body.queryResult.intent.displayName;
const domain = "chatbotdemo.myshopify.com";
const order = req.body.queryResult.parameters["number-sequence"];
if (intent.includes('Order Number')) {
url = "https://test-hchat.com/api/orders/" + domain + "/" + order;
request(url)
.then(function (response) {
order_res = JSON.parse(response)
order_res["fullfillmentText"] = "Hi, Please find your order details below:";
res.json({
"fulfillmentText": JSON.stringify(order_res)
})
})
.catch(function (err) {
console.log(err)
});
// THIS PART DOESNT RETURN THE RESPONSE.
} else {
const domain = 'testStore'
db.getClientsDialog(domain, intent, (response) => {
const fullResponse = response.response
res.json({
fullResponse
})
})
}
});
The database code:
getClientsDialog: function (domain, intent, callback) {
MongoClient.connect('mongodb://efwefewf#wefwef.mlab.com:15799/wefwef', function (err, client) {
if (err) throw err;
var db = client.db('asdsad');
db.collection('dialog').findOne({ domain: domain, intent: intent }, function (err, doc) {
if (!err) {
callback(doc)
} else {
throw err;
callback(err)
}
client.close();
});
console.dir("Called findOne");
});
}
Could it be because this second use of the res.json in the else statement, is trying to call the db first and therefore the link is lost to send the data back?

Issues with reading req undefined inside some functions

I get user query information when use req.user at first time but in second inside function I got cannot read property user of null.
router.post("/orders", function (req, res) {
console.log(req.user);//here I can see user info!
orders.count({
customerInfo: req.user//here I can get user info!
}, function (req, count) {
if (count > 0) {
console.log(count);
orders.findOne({
customerInfo: req.user//here:cannot read property user of null
}, function (err, orders) {
products.findOne({
_id: req.body.id
}, function (err, products) {
if (err) {
console.log(err);
} else {
orders.productInfo.push(products);
orders.save(function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
}
});
});
}
You have req in the inner function and it's overriding the outter req with the user. You can create a var outside with the user:
Example:
post("/orders", function (req, res) {
const user = req.user;
orders.count({ customerInfo: user//here I can get user info!
}, function (req, count) {
//Here you can use user
});
});
In my opinion your code is what we call callback hell, so it's advisable to refactor It using control flow, you can use promises, generator functions and other artifacts for this cases.

Problems with Queries in cloud code

Any ideas why this isn't working? It comes back with the success message but doesn't actually update anything.
I'm adding an FB user using Parse but since it doesn't have a func to add the username and email trying to do it this way. Any help much appreciated.
JS
Parse.Cloud.run("test", { objectId: "Q8XRUcL22N", name: "Rich", email: "rich#gmail.com"}, {
success: function(results) {
console.log(results);
},
error: function(error) {
console.log(error);
}
});
CLOUD CODE:
Parse.Cloud.define("test", function (request, response) {
Parse.Cloud.useMasterKey();
var uid;
var query = new Parse.Query("_User");
query.count({
success: function (results) {
uid = parseInt(results);
},
error: function () {
response.error("UID lookup failed");
}
});
var query = new Parse.Query(Parse.User);
query.equalTo("objectId", request.params.objectId);
query.first({
success: function (object) {
object.set("username", request.params.name);
object.set("email", request.params.email);
object.set("uid", uid);
var acl = new Parse.ACL();
acl.setPublicWriteAccess(false);
acl.setPublicReadAccess(false);
object.setACL(acl);
object.save();
response.success("Success Message");
},
error: function (error) {
response.error("Error Message");
}
});
});
Thanks
Calling success() or error() on the response halts whatever is underway at the time, including the save() on the user.
Also, it looks like you want to record in the user a count of users at the time the ACL is set. Getting the count must also be serialized with the other operations. (Also, please note that count is only good as an id "uid" to the extent the user count never goes down. What's wrong with the parse objects's id as an id?).
Most of the parse functions return promises, and using them is the only way to not go nuts trying to nest callbacks. So...
Parse.Cloud.define("test", function(request, response) {
Parse.Cloud.useMasterKey();
var uid;
var query = new Parse.Query(Parse.User);
query.count().then(function(count) {
uid = count;
return query.get(request.params.objectId);
}).then(function(object) {
object.set("username", request.params.name);
object.set("email", request.params.email);
object.set("uid", uid);
var acl = new Parse.ACL();
acl.setPublicWriteAccess(false);
acl.setPublicReadAccess(false);
object.setACL(acl);
return object.save();
}).then(function (object) {
response.success(object);
},function(error) {
response.error("Error Message");
});
});

Verifing form input by making http request in Node

I have a form whose submit button calls
exports.postUpdateProfile = function(req, res, next) {
User.findById(req.user.id, function(err, user) {
if (err) return next(err);
user.email = req.body.email || '';
user.profile.name = req.body.name || '';
//check req.body.rescueTimeKey by making http request,
//and parsing response to ensure the key is good
user.profile.rescueTimeKey = req.body.rescueTimeKey;
user.save(function(err) {
if (err) return next(err);
req.flash('success', { msg: 'Profile information updated.' });
res.redirect('/account');
});
});
};
I want to make sure the req.body.rescueTimeKey is valid before saving the profile, I've tried to create a module to perform that check...
//rescue-time.js module
var request = require('request');
exports.validKey = function(key){
var options = {
url: 'https://www.rescuetime.com/anapi/data?key=' + key,
json: true
};
var cb = function callback(error, response, body){
if(error || body.error){
//Key is bad, flash message and don't allow save
}
//key is good, save profile
};
request(options, cb);
}
As you might imagine I am not fully grasping the node style of using callbacks when making async calls, any help to re-organize this code is greatly appreciated.
What you will want to do is add an extra argument to your validKey function to accept a callback which is what we will use after the request.
So your rescue-time.js will look something like this:
// rescue-time.js
var request = require('request');
exports.validKey = function(key, cb) {
var options = {
url: 'https://www.rescuetime.com/anapi/data?key=' + key,
json: true
};
request(options, function (error, response, body) {
if(error || body.error){
cb(false)
}
else {
cb(true);
}
});
};``
We're returning a boolean result of true or false if the key is valid.
Inside your controller you will want something like the following:
var rescueTime = require('./path/to/rescue-time.js');
exports.postUpdateProfile = function(req, res, next) {
User.findById(req.user.id, function(err, user) {
if (err) return next(err);
user.email = req.body.email || '';
user.profile.name = req.body.name || '';
//check req.body.rescueTimeKey by making http request,
//and parsing response to ensure the key is good
user.profile.rescueTimeKey = req.body.rescueTimeKey;
// We're sending in a callback function that will have a "valid" result as a second arg
rescueTime.validKey(user.profile.rescueTimeKey, function(valid) {
// check if valid returned true or false and act accordingly
if (!valid) {
req.flash('error', 'invalid rescueTime key');
res.redirect('/account');
}
else {
user.save(function(err) {
if (err) return next(err);
req.flash('success', { msg: 'Profile information updated.' });
res.redirect('/account');
});
}
});
});
};
Keep in mind this code wasn't tested at all, but more of an example on what you need to do to obtain your desired results.

Parse.com: getting UserCannotBeAlteredWithoutSessionError

I have an Angular service that takes in a roleId and userId and assigns the user to that role and make a pointer in User to that role.
app.service('CRUD', function () {
this.addUserToRole = function (roleId, userId) {
// first we have to find the role we're adding to
var query = new Parse.Query(Parse.Role);
return query.get(roleId, {
success: function (role) {
// then add the user to it
var Database = Parse.Object.extend("User");
var query = new Parse.Query(Database);
console.log(role);
return query.get(userId, {
success: function (user) {
console.log(user);
role.getUsers().add(user);
role.save();
// now we need to tell the user that he has this role
console.log(user);
user.attributes.role.add(role);
user.save();
return user;
},
error: function (err) {
return err;
}
});
},
error: function (err) {
console.log(err);
}
});
}
});
I'm getting {"code":206,"error":"Parse::UserCannotBeAlteredWithoutSessionError"} on user.save();
After some research, I arrived at this website. He uses this code snippet as a JS SDK example:
Parse.Cloud.run('modifyUser', { username: 'userA' }, {
success: function(status) {
// the user was updated successfully
},
error: function(error) {
// error
}
});
and mentions something about a useMasterKey() function.
I'm still unsure how to fix this error.
Add
Parse.Cloud.useMasterKey();
at the beginning of your function.
Set it up as a background job. That is the code snip you found I think and a simpler far more secure means of fondling users and roles
https://parse.com/docs/cloud_code_guide#jobs

Categories