I am trying to validate if an email or username already exist in my mongoDB users collection in nodejs in my User Model.
But whenever i try to do that i get this error callback is not a function, when it finds that a username already exists in the next process that i am pushing to my processes array. meaning that the first process gets executed normally and there are no errors, but if an email doesnt already exists it goes to the next process and the error happens.
What am i doing wrong? How can I validate more than one field and return a response for that specific error?
here is my code :
register(params) {
return new Promise((resolve, reject) => {
var processes = [];
//check if the email is already registered
let user = new models.User()
processes.push((callback) => {
user.emailExists(params.email,null).then((response) => {
if(response && response.length) {
callback({errorCode: 403, msg: 'User Already Exists.'});
} else {
db.collection("Users").insertOne(options, function (error, response) {
if (error) {
callback(error)
} else {
console.log(response.ops[0])
if(response.ops[0]) {
callback(null, {result: response.ops[0]});
} else {
callback({errorCode: 403, msg: 'User sign-up failed.'})
}
}
})
}
}, (error) => {
reject(error);
});
});
processes.push((callback) => {
user.usernameExists(params.username).then((response) => {
if(response && response.length) {
callback({errorCode: 403, msg: 'username Already Exists.'});
} else {
db.collection("Users").insertOne(options, function (error, response) {
if (error) {
callback(error) //this is line 91
} else {
console.log(response.ops[0])
if(response.ops[0]) {
callback(null, {result: response.ops[0]});
} else {
callback({errorCode: 403, msg: 'User sign-up failed.'})
}
}
})
}
}, (error) => {
reject(error);
});
});
async.waterfall(processes, function (error, data) {
if (error) {
reject(error);
} else {
resolve(data);
}
})
})
}
emailExists(email, password) {
// check that required params are set
if (!email) {
return new Promise(function (resolve, reject) {
console.log("Parameters not set")
reject({errorCode: 403, msg: 'invalid.'});
});
}
return new Promise(function (resolve, reject) {
db.collection("Users").find({
"email": email.toLowerCase()
}).limit(1).toArray(function (error, response) {
if (error) {
reject(error);
} else {
if (response && response.length) {
if (!password) {
resolve(response);
} else {
bcrypt.compare(password, response[0].password, function (err, success) {
if (err) {
reject(err);
} else {
if (success) {
resolve(true);
} else {
resolve(false);
}
}
});
}
} else {
resolve(false);
}
}
});
});
}
usernameExists(params) {
if (!params || params.username) {
return new Promise(function (resolve, reject) {
console.log("params not set");
reject({errorCode: 403, msg: 'Invalid.'});
});
}
return new Promise(function (resolve, reject) {
db.collection("Users").find({
"username": params.username
}).limit(1).toArray(function (error, response) {
if (error) {
reject(error);
} else {
if (response && response.length) {
resolve(true)
} else {
resolve(false);
}
}
});
})
}
error log:
There are so many things wrong in the code that it's hard to point to something specific tbh.
Please read how to use waterfall:
An array of async functions to run. Each function should complete with any number of result values. The result values will be passed as arguments, in order, to the next task.
It means when you call
callback(null, {result: response.ops[0]});
the next process is being called with 2 arguments:
process({result: response.ops[0]}, callback)
you define your processes with only 1 argument, the rest are ignored. The first one is a json, so you get the error that it is not a function when you try to invoke it.
Now, how to fix it. Don't use waterfall. You don't need it. Bare async/await is more than enough and will make code much more readable.
Related
I am using Slack API and I want to test does it work fine with response status code. Here is sending function :
sendMsg(msg) {
return this.slack.webhook({text: msg}, (err, res) => {
if (err) {
throw err;
}
console.log(res.statusCode) // = 200
return res.statusCode;
});
}
And my test:
it('Checks connection with Slack', (() => {
let slack = new Slack();
let res = slack.sendMsg('test');
expect(res).to.equal(200);
}));
But ofc. it's giving me request object to slack. I want to wait for response object from slack API. Thanks in advance.
It looks like slack.webhook takes in a callback, which is how you retrieve the status. The problem is that the caller of sendMsg has no way of getting that status.
One way to solve this is to have sendMsg take in a callback:
sendMsg(msg, onStatusReceived) {
this.slack.webhook({text: msg}, (err, res) => {
if (err) {
throw err;
}
console.log(res.statusCode) // = 200
onStatusReceived(res.statusCode);
});
}
Then in your test, use done to end the test when the callback is invoked:
it('Checks connection with Slack', (done) => {
let slack = new Slack();
slack.sendMsg('message', status => {
expect(status).to.equal(200);
done();
});
});
Another way is to have sendMsg wrap slack.webhook in a promise, so the caller can do sendMsg().then(...).
one of the ways I handled a returning callback to test is as follows:
it('receives successful response', async () => {
nock('https://localhost')
.persist()
.log(console.log)
.post(‘/getData’, (unitData, callback) => {
return true;
})
.delayBody(1000)
.reply(200, {statusCode: 'Some Status'});
const getSomeData = await getResponse(unitData, function callBack(unitData, error, data){
expect(data.statusCode).to.be.equal(200);
}) })
getResponse Function (returning callback):
getResponse(unitData, function callBack(unitData, error, data){
try {
return request.post(unitData, function (err, resp) {
if (!err && resp.statusCode === 200) {
if (resp.body.error) {
return callback(obj, JSON.stringify(resp.body.error), null);
}
return callback(obj, null, resp);
} else {
if (err == null) {
err = { statusCode: resp.statusCode, error: 'Error occured.' };
}
return callback(obj, err, null);
}
});
} catch (err) {
return callback(obj, err, null);
}
}
I am trying to write a test case for jwt token validation in node js.I am able to cover failure cases but not successful case.
isValid: function (request, reply) {
if (request.query && request.query.token) {
var token = request.query.token;
validateTok(token)
.then(function (credentials) {
reply(true);
})
.catch(function (err) {
reply(false);
})
} else {
reply(false);
}
}
function validateT(jwt) {
return new Promise(function (resolve, reject) {
Security.validate(jwt, function (err, success, credentials) {
if (err || !success) {
reject(err)
} else {
resolve(credentials);
}
});
});
};
I want to mock Security.validate(jwt, function (err, success, credentials) to return success. Following is my test case.
it('should pass token validation', async () => {
const data = {
token: '1512598739676174ae69792b81583fd210c381c50f',
};
const request = generateRequest({query: data,});
const response = await awaitHandler(users.isValid, request);
(response).should.eql( true );
});
I have the following code in use attempting to download a file over HTTP. I'm attempting to use streams as the file is quite large. The problem I am having is that after I pipe the response to a created WritableStream, sometimes the file isn't created and thus the code will error out later when checking the checksum.
In other words the 'finish' event of the WritableStream is firing even though the file doesn't exist/has not been created.
exports.downloadHttpFile = function(url, path, mimeType, checksum) {
return exports.get(url, mimeType, false).then(function(res) {
if (res.statusCode != 200) {
return Promise.reject(new Error('Invalid response from server when trying to download file'))
} else {
return new Promise(function(resolve, reject) {
console.log('creating file at ' + path)
const writable = fs.createWriteStream(path);
writable.once('open', () => res.pipe(writable).once('error', reject)).on('finish', function() {
writable.end();
try {
if (checksum) {
console.log(checksum)
if (!checksum.hashType || !checksum.hashEncoding || !checksum.hashValue) return reject(new Error('Invalid checksum object'))
fs.createReadStream(path)
.once('error', function() {
console.log('ERRORS OUT HERE, FILE NO EXIST (path not created by fs.createWriteStream)')
})
.pipe(require('crypto').createHash(checksum.hashType).setEncoding(checksum.hashEncoding))
.once('finish', function() {
const fileHash = this.read();
if (fileHash != checksum.hashValue) {
try {
fs.unlinkSync(path);
} catch (err) {}
return reject(new Error('Downladed file checksum did not match given checksum'));
} else {
return resolve(path);
}
}).once('error', reject)
} else {
return resolve(path);
}
} catch (err) {
console.log('File did not exist after piping to writable stream..')
return reject(err);
}
}).once('error', reject);
});
}
})
}
Can someone be so kind as to tell me where I'm going wrong? Thanks
I managed to fix this by checking if the file exists and if it doesn't recalling downloadHttpFile. I'm aware this isn't a very good solution and I'm not sure why it's firing the finish event when the file hasn't been created..but it will do for now unless anyone has a better solution.
Code is below:
exports.downloadHttpFile = function (url, path, mimeType, checksum) {
return exports.get(url, mimeType, false).then(function (res) {
if (res.statusCode != 200) {
return Promise.reject(new Error('Invalid response from server when trying to download file'))
} else {
return new Promise(function (resolve, reject) {
const writable = fs.createWriteStream(path)
writable.on('finish', function () {
let pathExists = false
try {
fs.statSync(path)
pathExists = true
} catch (err) {}
if (pathExists) {
if (checksum) {
console.log(checksum)
if (!checksum.hashType || !checksum.hashEncoding || !checksum.hashValue) return reject(new Error('Invalid checksum object'))
console.log('creating read stream for path ' + path)
fs.createReadStream(path)
.pipe(require('crypto').createHash(checksum.hashType).setEncoding(checksum.hashEncoding).once('error', function (err) {
console.log('Error creating crc code')
return reject(err)
}))
.once('finish', function () {
console.log('Finished reading stream')
const fileHash = this.read()
if (fileHash != checksum.hashValue) {
console.log('Checksums did not match')
console.log(fileHash)
console.log(checksum.hashValue)
return reject(new Error('Downladed file checksum did not match given checksum'))
} else {
console.log('Checksums matched')
return resolve(path)
}
}).once('error', reject)
} else {
return resolve(path)
}
} else {
console.log('File did not exist.. attempting to redownload' + path)
return resolve(exports.downloadHttpFile(url, path, mimeType, checksum))
}
}).on('error', function (err) {
console.log(err)
return reject(err)
})
res.pipe(writable).on('error', function (err) {
console.log('Error reading response')
return reject(err)
})
})
}
})
}
I am refactoring my code using promises. I am running into a problem. I have two API routes. First one is api.js and the second is account.js. I also have 4 controllers(CommentController, ZoneController, ProfileController, AccountController) .
CommentController, ZoneController, and ProfileController share the same API route(api.js).
account.js uses AccountController. But AccountController's method uses ProfileController's method.
I ended up having Promise calling another Promise, but I am not returning data properly. It is leaving the server hanging. How can I return data when one Promise is calling another Promise?
Basically account.js is calling AccountController.js that has a method that calls ProfileController.js, but both AccountController and ProfileController are refactored to Promise. I am not getting data back. Please help.
AccountController.js
var ProfileController = require('./ProfileController');
module.exports = {
currentUser: function(req) {
return new Promise(function(resolve, reject) {
if (req.session == null) {
reject({message: 'User not logged in'});
return;
}
if (req.session.user == null) {
reject({message: 'User not logged in'});
return;
}
ProfileController.findById(req.session.user, function(err, result) {
if (err) {
reject({message: 'fail'});
return;
}
resolve(result);
return;
});
});
}
ProfileController.js
findById: function(id) {
return new Promise(function(resolve, reject){
Profile.findById(id, function(err, profile){
if(err){
reject(err);
return;
}
resolve(profile);
return;
});
})
},
account.js
router.get('/:action', function(req, res, next) {
var action = req.params.action;
if (action == 'logout') {
req.session.reset();
res.json({
confirmation: 'success',
message: 'Bye!'
});
return;
}
if (action == 'login') {
res.json({
confirmation: 'success',
action: action
});
return;
}
if (action == 'currentuser') {
AccountController.currentUser(req)
.then(function(result){
res.json({
confirmation: 'success',
user: result
});
return;
})
.catch(function(err){
res.json({
confirmation: 'fail',
message: err.message
});
return;
});
}
});
you should modify AccountController.js
AccountController.js
var ProfileController = require('./ProfileController');
module.exports = {
currentUser: function(req) {
return new Promise(function(resolve, reject) {
if (req.session == null) {
reject({message: 'User not logged in'});
return;
}
if (req.session.user == null) {
reject({message: 'User not logged in'});
return;
}
ProfileController.findById(req.session.user).then(
function (profile){
resolve(profile);
},function (err) {
reject(err);
});
}
Hi this works for the first execution but then when I try again it seems to break, does anything seem wrong with the way the promises are used here
socket.on('new user', function(data, callback) {
return getUsersFromDb()
.then(function() {
if (users.name.indexOf(data) > -1) {
callback(false);
reject(err);
}
else {
callback(true);
// socket.nickname = data;
// people[socket.nickname] = socket;
updateNicknames();
var user = new User();
user.name = data;
user.save(function(err, data) {
if (err) {
console.log(err)
}
else {
return getUsersFromDb()
.then(function() {
updateNicknames()
})
.catch(function(e) {
console.log("Error", e)
})
}
});
socket.broadcast.emit('user join', {nick: socket.nickname});
}
})
.catch(function(e) {
console.log("Error", e)
})
});
Sublime seems to colour the then and catch inside the second else differently as if it was used wrongly
I'm assuming the following function returns a promise!?
getUsersFromDb()
Have you tried jQuery promise, using $.when(functionWhichReturnsAPromise()).then( "the rest of the code")?