I am trying to test a constructor in node.js that uses asynchronous code, to test out a feature in it. I know the asynchronous code works because I have already ran it, before I put in the feature, as if I were an end-user. In fact, I got it thanks to some user who answered another question I had
The feature
The User constructor has a userRole member, and some async code that checks userType specified against User.userTypes. If userType found, this.userRole is set to userType. Else, exception is thrown.
The code looks like this:
async.forEachSeries(
User.userTypes,
function(user_type, callback)
{
if (user_type == userType)
{
this.userRole = user_type;
return;
}
if (user_type == User.userTypes[User.userTypes.length - 1])
{
callback(helpers.invalidData("user_role"));
return;
}
callback(null);
},
function(err)
{
if (err)
{
if (DEBUG)
{
console.log("Error from constructor...");
console.log(JSON.stringify(err, null, '\t') + "\n");
}
throw err;
}
}
);
The rest of the constructor looks like:
function User(id, email, displayName, password, userType, deleted, cb)
{
var DEBUG = true;
var error = null;
this.userID = id;
this.email = email;
this.displayName = displayName;
this.deleted = deleted;
var self = this;
async.forEachSeries(
User.userTypes,
function(user_type, callback)
{
if (user_type == userType)
{
this.userRole = user_type;
return;
}
if (user_type == User.userTypes[User.userTypes.length - 1])
{
callback(helpers.invalidData("user_role"));
return;
}
callback(null);
},
function(err)
{
if (err)
{
if (DEBUG)
{
console.log("Error from constructor...");
console.log(JSON.stringify(err, null, '\t') + "\n");
}
throw err;
}
}
);
if (User.connectedToDatabase) this._password = password;
else
{
bcrypt.genSalt(10, function (e, salt) {
bcrypt.hash(password, salt, function (e, hash) {
if (!e)
{
self._password = hash;
if (DEBUG)
{
console.log("this._password ==" + self._password);
console.log("this.userID == " + self.userID);
}
if (typeof cb === 'function')
cb(null, this);
}
else
{
console.log("Error occurred: ");
console.log(e);
if (typeof cb === 'function')
cb(e);
}
})
});
}
}
User.connectedToDatabase = false;
User.BASIC = "basic user";
User.INVENTORY_MANAGEMENT = "inventory";
User.ADMIN = "admin";
User.userTypes = [ User.BASIC, User.INVENTORY_MANAGEMENT, User.ADMIN ];
User.prototype.userID = 0;
User.prototype.email = null;
User.prototype.displayName = null;
User.prototype._password = null;
User.prototype.userRole = User.BASIC;
User.prototype.deleted = false;
User.prototype.responseObject = function() {
return {
id: this.userID,
email: this.email,
displayName: this.displayName,
userType: this.userRole
};
}
My test
I write test() function that takes parameters to pass to User. If User constructed without error, it, along with some of its members are printed to console. Else, error is printed. Here is code:
function test(email, name, pass, type)
{
try
{
var a = new User(Math.round(Math.random() * 32),
email,
name,
pass,
type
);
console.log("Test user created: " + JSON.stringify(a.responseObject(), null, '\t') + "\n");
console.log("User._password == " + a._password);
console.log("\n");
}
catch (e)
{
console.log("User could not be created.\n" + JSON.stringify(e, null, '\t') + "\n");
}
/*async.waterfall([
function(callback){
var a = new User(Math.round(Math.random * 32),
email,
name,
pass,
type);
callback(null, a);
},
function(a, callback) {
console.log("Test user created: " + JSON.stringify(a, null, '\t') + "\n");
console.log("User._password == " + a._password);
console.log("User.userID == " + a.userID);
console.log("\n");
callback(null, a);
}
],
function(err, results)
{
console.log("results of test: " + JSON.stringify(results, null, '\t'));
if (err)
{
console.log("User could not be created.\n" + JSON.stringify(err, null, '\t') + "\n");
}
})*/
}
My test cases are as follows:
userType matching first element of User.userTypes
userType matching another element of User.userTypes (I picked the last one)
userType not matching any of User.userTypes
At runtime, after new User created, User._password is default value and not the value my asynchronous code comes up with for it. I suspect I have some async-sync error, but haven't been able to fix it.
Got it fixed, thanks to #Bergi's advice.
First thing I did: in that async.forEachSeries(), I changed any instance of this to self.
Second thing I did: replaced that asynchronous code (that was working!) to synchronous code. Instead of this:
bcrypt.genSalt(10, function (e, salt) {
bcrypt.hash(password, salt, function (e, hash) {
if (!e)
{
self._password = hash;
if (DEBUG)
{
console.log("this._password ==" + self._password);
console.log("this.userID == " + self.userID);
}
if (typeof cb === 'function')
cb(null, this);
}
else
{
console.log("Error occurred: ");
console.log(e);
if (typeof cb === 'function')
cb(e);
}
})
});
I simply said: this._password = bcrypt.hashSync(password, bcrypt.genSaltSync(10)); and all was good!
Related
I want to integrate Stripe into my application. I have collected all necessary Data in the req.body.
Now an undefined error is being thrown after creating the customer Id and while trying to pass it to addCustomerToCard. After that, createToken is being successfully logged.
So two questions:
1.Why is the order of functions not being invoked as I would expect?
2.Why does the customer not get passed in addCustomerToCard?
router.post("/checkout", async function (req, res, next) {
if (!req.session.cart) {
return res.redirect("/shopping-cart");
}
let createCustomer = function () {
var param ={};
param.email = req.body.email;
param.name= req.body.name;
param.description ="";
return stripe.customers.create(param, function (err, customer) {
if (err) {
console.log("err:" + err);
}
if (customer) {
console.log("success: " + JSON.stringify(customer, null, 2));
} else {
console.log("something went wrong");
}
});
};
let createToken = function () {
let param ={};
param.card = {
number: req.body.card,
exp_month: req.body.exp_month,
exp_year: req.body.exp_year,
cvc: req.body.security
}
return stripe.tokens.create(param, function (err, token) {
if (err) {
console.log("err:" + err);
console.log(param);
}
if (token) {
console.log("success: " + JSON.stringify(token, null, 2));
console.log(req.body);
} else {
console.log("something went wrong");
}
});
};
let addCardToCustomer = function () {
console.log(createdCustomer);
return stripe.customers.createSource(customer.id, {source: token.id}, function (err, card) {
if (err) {
console.log("err:" + err);
console.log(param);
}
if (card) {
console.log("success: " + JSON.stringify(card, null, 2));
} else {
console.log("something went wrong");
}
});
};
let chargeCustomerThroughCustomerID = function () {
let param = {
amount: cart.totalPrice,
currency: 'eur',
description: 'First payment',
customer: customer.id
}
stripe.charges.create(param, function (err, charge) {
if (err) {
console.log("err: " + err);
}
if (charge) {
console.log("success: " + JSON.stringify(charge, null, 2));
} else {
console.log("Something wrong")
}
})
}
try {
const createdCustomer = await createCustomer(); // promise 1
const createdToken = await createToken();
const addedCardToCustomer = await addCardToCustomer(createdCustomer,createdToken ); //
// const chargeCustomerThroughCustomerID = await chargeCustomerThroughCustomerID(); // promise 4
res.send("success");
} catch (e) {
console.log(`error ${e}`)
};
});
//LOG OUTPUT
//success
//error ReferenceError: createdCustomer is not defined
//success
You should break this down into smaller pieces to verify the behaviour you're expecting, for starters. There's quite a lot going on here.
One problem is that you are trying to await functions which are not async -- you should have each function marked:
let createCustomer = async function () {...}
This is likely the reason the sequence is not what you expect.
You're hitting an error in addCardToCustomer because createCustomer is undefined in the scope of that function. You need to have a parameter:
let addCardToCustomer = async function (createdCustomer) { ... }
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I have a function in typescrypt in file Service.ts:
export const doCallAuth = (username, password) => {
var auth = new Auth({
url: '...',
});
var status;
auth.authenticate(username, password, function (err, user) {
if (err) {
console.log(err);
status = 'no';
} else if (!user.uid) {
console.log("user not found Error");
status = 'no';
} else if (user.uid) {
console.log("success : user " + user.uid + " found ");
status = 'yes';
}
});
return status;
}
I call this method by :
var result = Service.doCallAuth('test', 'test');
And variable result is undefined
I don't know why result is undefined
Can anybody help me out ?
Thanks in advance. :)
You can use Promise or callback patterns
1. Promise
export const doCallAuth = (username, password) => {
return new Promise((resolve, reject) => {
var auth = new Auth({ url: "..." });
auth.authenticate(username, password, (err, user) => {
if (err) {
reject(err);
} else if (!user.uid) {
reject(new Error("user not found Error"));
} else if (user.uid) {
console.log("success : user " + user.uid + " found ");
resolve(user);
}
});
});
};
Service.doCallAuth("test", "test")
.then(user => {
console.log("success : user " + user.uid + " found ");
})
.catch(e => {
console.log(e);
});
2. Callback
export const doCallAuthCallBack = (username, password, callback) => {
var auth = new Auth({ url: "..." });
auth.authenticate(username, password, (err, user) => {
if (err) {
callback(err);
} else if (!user.uid) {
callback(new Error("user not found Error"));
} else if (user.uid) {
callback(null, user);
}
});
};
Service.doCallAuthCallBack("test", "test", (err, user) => {
if (err) {
console.log(err);
} else {
console.log("success : user " + user.uid + " found ");
}
});
Try this one.
export const doCallAuth = (username, password, callback) => {
var auth = new Auth({
url: '...',
});
var status;
auth.authenticate(username, password, callback);
}
var result;
Service.doCallAuth('test', 'test', function (err, user) {
if (err) {
console.log(err);
status = 'no';
} else if (!user.uid) {
console.log("user not found Error");
status = 'no';
} else if (user.uid) {
console.log("success : user " + user.uid + " found ");
status = 'yes';
}
result = status;
});
To get value from AJAX call need to Use async and await
export const doCallAuth = async function(username, password) => {
var auth = new Auth({
url: '...',
});
var status;
auth.authenticate(username, password, function (err, user) {
if (err) {
console.log(err);
status = 'no';
} else if (!user.uid) {
console.log("user not found Error");
status = 'no';
} else if (user.uid) {
console.log("success : user " + user.uid + " found ");
status = 'yes';
}
return status;
});
}
call this method by :
var result = await Service.doCallAuth('test', 'test');
I have a bot. It can input some text and return some word.
I would like to use MongoDB. Because Heroku can't store data.
So I add function.js that use mongoose.
console.log('data.functionswitch = ' + data.functionswitch);
console log is work fine. It can reply what i want.
return data.functionswitch;
but return data.functionswitch only return undefined when i call it in input.js/.
I have try async/await.
But it only stops working.
How can I improve it and make it work? Thank you.
-
-
2018/03/15 updated
function.js
function switchfind(id, name, callback) {
mongodb.functionSwitch.findOne({
groupid: id, functionname: name
}, function (err, data) {
if (err) {
console.log(err);
callback(null);
return;
}
else if (!data) {
console.log("No record found")
callback(null);
return;
}
console.log('date = ' + data);
console.log('data.functionswitch = ' + data.functionswitch);
callback(data.functionswitch);
return;
})
};
input.js
function parseInput(rplyToken, inputStr) {
//console.log('InputStr: ' + inputStr);
_isNaN = function (obj) {
return isNaN(parseInt(obj));
}
let msgSplitor = (/\S+/ig);
let mainMsg = inputStr.match(msgSplitor);
let trigger = mainMsg[0].toString().toLowerCase();
exports.mongoose.switchfind(mainMsg[1], mainMsg[2], function (functionswitch) {
console.log('functionswitch = ' + functionswitch)
if (functionswitch === null) {
console.log('HERE === NULL ')
}
if (functionswitch == 0) {
console.log('HERE != 0')
return;
}
else if (functionswitch != 0 ) {
console.log('HERE != 0')
if (inputStr.match(/\w/) != null && inputStr.toLowerCase().match(/\d+d+\d/) != null) return exports.rollbase.nomalDiceRoller(inputStr, mainMsg[0], mainMsg[1], mainMsg[2]);
}
})
}
update
const mongoose = require('mongoose');
let uristring = process.env.mongoURL ||
'mongodb://XXXXXXX';
mongoose.connect(uristring);
mongoose.connect(uristring, function (err, res) {
if (err) {
console.log('ERROR connecting to: ' + uristring + '. ' + err);
} else {
console.log('Succeeded connected to: ' + uristring);
// console.log('allswitch: ' + allswitch);
}
});
var functionSchema = new mongoose.Schema({
groupid: String,
functionname: String,
functionswitch: String
});
// Compiles the schema into a model, opening (or creating, if
// nonexistent) the 'PowerUsers' collection in the MongoDB database
var functionSwitch = mongoose.model('functionSwitchs', functionSchema);
The problem in your code is that you are using findOne as it was synchronous. You cannot simply return the data, you have to use a callback.
Here is a tutorial about callbacks.
Example of what it should look like :
// The find function
function switchfind(id, name, callback) {
mongodb.functionSwitch.findOne({
groupid: id,
functionname: name
}, function (err, data) {
// Handle error
if (err) {
callback(null);
return;
}
// Handle empty data
if (data == null) {
callback(null);
return;
}
// Handle with data
callback(data.functionswitch);
})
};
// How to call it
funcX() {
switchfind(id, name, function (functionswitch) {
if (functionswitch === null) {
// Handle the error
}
// Handle the data
});
}
I'm getting this array of user emails from the post data. I want to find the _id related to each email. I tried this for loop:
var studentIds = [];
for (var i = studentEmails.length - 1; i >= 0; i--) {
var email = studentEmails[i];
User.findOne({"email": email}, (err, student) => {
if (err) {
console.log("ERROR" + err);
}
if (student) {
var id = student._id;
studentIds.push(id);
console.log("STUDENT: " + student);
}
});
}
// Outside for loop
console.log('END');
However, this logs the following:
END
STUDENT: { _id: 5a11e667d7333203337cd9a4,
name: 'Patrick Jacobs',
email: 'windvaan#live.nl',
password: '$2a$10$CiSw/VH1HCaPtW6Sjz0X4.4avVoLsAH6iyF3FhidorahwLt1WDXoC',
__v: 0 }
STUDENT: { _id: 5a0f7dfb64b5a6000417c662,
name: 'Carlo Jacobs',
email: 'carlojacobs91#gmail.com',
password: '$2a$10$fiIosS4Jo5ehuCp3TfltSOnpypPMWSMvzlb7phRWmNGBtDz5W1rCG',
__v: 0 }
As you can see, the END is being printed first. I don't want that. I'm assuming the for loop is asynchronous? How can I make it synchronous?
Thx in advance!
The solution would be
var studentIds = [];
var loopRecords = function (records, cb) {
if (!records.length) return cb();
var email = records.shift();
User.findOne({"email": email}, (err, student) = > {
if (err) {
console.log("ERROR" + err);
}
if (student) {
var id = student._id;
studentIds.push(id);
console.log("STUDENT: " + student);
}
return loopRecords(records, cb);
}
}
loopRecords(studentEmails,function () {
console.log('END');
})
It is not the for loop, but the User.findOne call that is asynchronous.
Following this answer, you could use streamline.js; try the following to make the call synchronous.
var result = User.findOne({"email": email}, _);
if (result === null) {
console.log("ERROR" + err);
}
var id = result._id;
studentIds.push(id);
console.log("STUDENT: " + result);
I have this code whereby i want to check if code input data is the same in the local db. This code works fine until it gets to where i have marked as code hangs or stops here. Once the code gets to the condition it runs perfectly and assigns notifier to be true but it doesnt come out of that function and is stuck there hence the remaining part of the code is not executed. Can anyone explain to me why ? I am building an Ionic, AngularJS app.
function checklocalDB(localdb, result) {
var d= $q.defer();
var identifier = false;
var notifier = false;
// var f = function(localdb, result){
if(localdb === false) {
console.log("inside localdb false")
var insert_into_table = "INSERT INTO preferences(description, value) VALUES ('username','" + result[0].username + "'), ('token','" + result[0].token.toString() + "')";
$cordovaSQLite.execute(db, insert_into_table).then(function (res) {
console.log("executedd")
var updateDB = "UPDATE preferences SET value='true' WHERE description='registered'";
$cordovaSQLite.execute(db, updateDB).then(function (res) {
console.log("executed")
identifier = true;
notifier = true;
//d.resolve(identifier)
var query = "SELECT id, description, value FROM preferences";
$cordovaSQLite.execute(db, query).then(function (res) {
}, function (err) {
console.error(err);
});
}, function (err) {
console.error(err);
});
});
}
else {
console.log("inside localdb true")
var dbNew = null;
var query = "SELECT id, description, value FROM preferences";
console.log(localdb)
$cordovaSQLite.execute(db, query).then(function (res) {
console.log("hhhhhhhhhhhhhh")
console.log(res.rows.item(2).value)
console.log(result[0].username)
if(res.rows.item(2).value != result[0].username) {
console.log("username different")
$cordovaSQLite.deleteDB("loanstreet_partners.db");
dbNew = $cordovaSQLite.openDB("loanstreet_partners.db");
$cordovaSQLite.execute(dbNew, "CREATE TABLE IF NOT EXISTS preferences (id integer primary key, description string, value string)").then(function (res) {
console.log("done")
var insert_into_table = "INSERT INTO preferences (description, value) SELECT 'registered' as registered, 'false' as value UNION SELECT 'logged_in', 'false'";
$cordovaSQLite.execute(db, insert_into_table).then(function (res) {
console.log("1st")
var insert_into_table = "INSERT INTO preferences(description, value) VALUES ('username','" + result[0].username + "'), ('token','" + result[0].token.toString() + "')";
$cordovaSQLite.execute(db, insert_into_table).then(function (res) {
console.log("2nd")
identifier = true;
notifier = true;
var updateDB = "UPDATE preferences SET value='true' WHERE description='registered'";
$cordovaSQLite.execute(db, updateDB).then(function (res) {
}, function (err) {
console.error(err);
});
});
}, function (err) {
console.error(err);
});
}, function (err) {
console.error(err);
});
}
else {
notifier = true;
console.log("im here")
return notifier;
// ***code hangs or stops here***
}
}, function (err) {
console.error(err);
});
}
// ***this is never executed because it still remains false***
if(notifier === true) {
console.log(identifier)
console.log(notifier)
d.resolve(identifier)
}
return d.promise;
// watch identifier when value change then only resolve
//d.resolve(identifier)
//return d.promise;
}
Any help appreciated