Generate new username available, always getting Undefined - node.js

I have a function to generate an available username, but always return undefined. I tried in many ways, in this case is a recursive function, but I'm always getting the same result, can you help me? Thanks
This is the code:
function generateNewUsernameAvailable(userName, number){
console.log('FUNCTION generateNewUsernameAvailable '+userName+' with number '+number);
User.countDocuments({ 'userName' : userName+number }, (err, count) => {
if (err) {
return `Error: ${err}`;
}
if (count == 0) {
return userName+number;
}else{
generateNewUsernameAvailable(userName, number+1);
}});
}
module.exports.existsUserName = function(req,res){
let userName = req.body.userName;
console.log('POST existsUserName '+userName);
User.countDocuments({ 'userName' : userName }, (err, count) => {
if (err) {
return res.status(500).send({message: `Error: ${err}`});
}
// Available
if (count == 0){
return res.status(200).send();
} else {
// Generate and return a new username available
console.log(generateNewUsernameAvailable('ricardo', 1));
res.status(400).send({message: 'newUsernameAvailable (Example ricardo1)'});
}
})
}

FindOne is faster than countDocuments/estimatedDocumentCount in this case. Both are promises, I'm going to add a possible solution:
function generateNewUsernameAvailable(userName, number){
return User
.findOne({ 'userName' : userName+number })
.then(function(result){
if (result){
return generateNewUsernameAvailable(userName, number+1);
}else{
return userName+number;
}
})
.catch(function(err) {
console.log(`Error: ${err}`);
throw err;
});
}
module.exports.existsUserName = function(req,res){
let userName = req.body.userName;
console.log('POST existsUserName '+userName);
User.countDocuments({ 'userName' : userName }, (err, count) => {
if (err) {
return res.status(500).send({message: `Error: ${err}`});
}
if (count == 0){
return res.status(200).send();
} else {
generateNewUsernameAvailable(userName,1)
.then(function(results){
return res.status(400).send({message: results});
})
.catch(function(err){
return res.status(500).send({message: `Error: ${err}`});
});
}
})
}

Related

MongoDb findOne query results in error - even if data is found

the below is my controller function
exports.validateUsername = async (uName) => {
console.log("inside validate username");
await User.findOne({ username: "sab2" }).then(function (err, user) {
if (err) {
console.log("inside err");
console.log("error");
console.log(err);
return true;
} else {
console.log("inside user found");
console.log("user");
console.log(user);
return false;
}
});
};
I have record in my "user" collection with username ="sab2". But still , the promise function results in err, and goes inside if(err).
But when I do console.log(err) I get the actual user data "sab2" object.
I have a doubt if the function syntax is function(data,err).
Error object printed
Please suggest a solution.
The then() on a findOne does not return a error, I would rewrite like this:
User.findOne({ username: "sab2" }, function(err, user) {
if (err) {
console.log("inside err");
console.log("error");
console.log(err);
return true;
} else {
console.log("inside user found");
console.log("user");
console.log(user);
return false;
}
});
then do not return error. Do something like this to catch error
await User.findOne({ username: "sab2" }).then((user) => {
console.log(user)
}).catch(error=>console.log(error))
or use callback. See here

How do I change the code to run synchronously

I have comparePassword function runs inside checkLogin in synchronous way, but the debugging output shows the opposite, what is my mistake here?
comparePassword = (password, salt, passwordHash) => {
crypto.pbkdf2(password, salt, 10000, 64, "sha512", (err, derivedKey) => {
//console.log('dump comparePassword: ', err);
if (err) {
return {
status: 500,
error: "Internal server error",
detail: "Server couldn't check your credential at the moment"
};
} else {
console.log(
"dump dump === : ",
passwordHash === derivedKey.toString("hex") ? true : false
);
if (derivedKey.toString("hex") == passwordHash) {
console.log("return return return1: ", true);
return true;
}
console.log("return return return2: ", true);
return false;
}
});
};
// Check credentials of a seller
exports.checkLogin = async (username, password) => {
let result = 0;
try {
const record = await Seller.findOne({
raw: true,
attributes: { exclude: ["birth", "createdAt", "updatedAt"] },
where: { email: username }
});
if (record !== null) {
let passwordRes = await comparePassword(
password,
record.salt,
record.password
);
console.log("dump passwordRes: ", passwordRes);
if (typeof passwordRes === "object") {
return passwordRes;
}
if (passwordRes) {
delete record.password;
delete record.salt;
return { seller: record };
}
}
} catch (error) {
console.log("error while checking seller credential: ", error);
result = {
status: 500,
error: "Internal server error",
detail: "Server couldn't check your credential at the moment"
};
}
return result;
};
Output:
dump passwordRes: undefined
dump dump === : true
return return return1: true
Expectation:
dump dump === : true
return return return1: true
dump passwordRes: true
comparePassword should return an promise so await can wait till the promise is resolved or rejected
comparePassword = (password, salt, passwordHash) => {
return new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, 10000, 64, "sha512", (err, derivedKey) => {
if (err) {
reject({
status: 500,
error: "Internal server error",
detail: "Server couldn't check your credential at the moment"
});
} else {
if (derivedKey.toString("hex") == passwordHash) {
resolve(true);
}
resolve(false);
}
});
});
};
I think you can not await a callback in comparePasswort without wrapping it in a promise.
Try something like
comparePassword = (password, salt, passwordHash) => new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, 10000, 64, 'sha512', (err, derivedKey) => {
// ...
resolve(true); // or reject(false)
})
})
Return a promise or simply make it async:
comparePassword = async (password, salt, passwordHash) =>{}

Filter function for req.query

at the moment i have this:
router.get('/contatos',function(req,res){
if(req.query.nome){
Contato.find({ nome: req.query.nome }, function (err, contato) {
console.log(contato);
if (JSON.stringify(contato) == "[]") {
return res.status(404).json({ Error: "that contact doesn't exist" });
}
if (err) {
return res.status(500);
}
return res.send(contato);
});
}
if(req.query.email){
Contato.find({ email: req.query.email }, function (err, contato) {
if (!contato) {
return res.status(404).json({ Error: "that contact doesn't exist" });
}
if (err) {
return res.status(500);
}
return res.send(contato);
});
}
if(Object.keys(req.query).length === 0){
Contato.find(function (err, contatos) {
if (JSON.stringify(contatos) == "{}") {
return res.status(404).json({ Error: "there are no contacts" });
}
if (err) {
return res.status(500);
}
return res.send(contatos);
}).populate('emergencia');
}
if(!req.query.nome && !req.query.email){
return res.status(400);
}
});
as you guys can see, the mongo query is almost the same the only thing that changes are the values that i pass, so to change this i tried to do a function like this:
function filtro(campo,valor,req,res){
if(arguments[0] != "undefined" && arguments[1] != "undefined"){
// i pass no parameters to filtro so here i just get all values
}
else{
Contato.find({campo:valor},function(err,contatos){
if(err){
return res.status(500);
}
return res.send(contatos);
});
}
}
then above in the first code i placed i just replace the mongo data with the return filtro("email",req.query.email)
AND
return filtro("nome",req.query.nome)
i want to know if what i am doing is correct, i just miss something because on postman it doesn't give me any response it keeps loading
To simplify it a little, you could do something like....
router.get('contatos', function(req, res){
var query = {};
if(req.query.nome) query.nome = req.query.nome;
if(req.query.email) query.email = req.query.email;
Contato.find(query, function (err, contato) {
if(err) return res.json({status : 500, error : err});
if(!contato) return res.json({status : 404, error : "Contact not found"});
return res.json(contato);
});
}

Not working with method "UPDATE" in API REST MEAN + Mongoose

exports.updateUsuarioByEmail = function (req, res) {
console.log('updateUsuarioByEmail');
console.log("PARAM ID" + req.params.email);
return Usuario.find({ email: req.params.email }, function(err, usuario) {
if(!usuario) {
res.statusCode = 404;
return res.send({ error: 'Not found' });
}
if (req.body.email != null) usuario.email = req.body.email;
if (req.body.password != null) usuario.password = req.body.password;
if (req.body.listaCardsSorting != null) usuario.listaCardsSorting = req.body.listaCardsSorting;
return usuario.save(function(err) {
if(!err) {
console.log('Updated usuario');
return res.send({ status: 'OK', usuario:usuario });
} else {
if(err.name == 'ValidationError') {
res.statusCode = 400;
res.send({ error: 'Validation error' });
} else {
res.statusCode = 500;
res.send({ error: 'Server error' });
}
console.log('Internal error(%d): %s',res.statusCode,err.message);
}
res.send(usuario);
});
});
};
The error after to execute is :
I believe that the error is the line "return usuario.save(function(err)..."
find return an Array (list of documents) . you can't do save on an array object. Instead try findOne if your email field is unique.
findOne returns a single document, so you can save that.
Replace
Usuario.find({ email: req.params.email }, function(err, usuario)
with :
Usuario.findOne({ email: req.params.email }, function(err, usuario)
To update a model it's much easier to use the findOneAndUpdate() update API. The method finds a matching document, updates it according to the update arg, passing any options, and returns the found document (if any) to the callback. The query executes immediately if callback is passed.
The syntax is:
Model#findOneAndUpdate([conditions], [doc], [options], [callback])
Parameters:
[conditions] <Object> - the query to match
[doc] <Object> - the document to update
[options] <Object> - update options
[callback] <Function> - callback
For example, the above function can be re-written to use the findOneAndUpdate() method as:
exports.updateUsuarioByEmail = function (req, res) {
console.log('updateUsuarioByEmail');
console.log("PARAM ID" + req.params.email);
var doc = {},
conditions = { "email": req.params.email },
options = { "new": true };
if (req.body.email != null) doc.email = req.body.email;
if (req.body.password != null) doc.password = req.body.password;
if (req.body.listaCardsSorting != null)
doc.listaCardsSorting = req.body.listaCardsSorting;
Usuario.findOneAndUpdate(
conditions,
doc,
options,
function(err, usuario) {
if(!err) {
console.log('Updated usuario');
return res.send({
status: 'OK',
usuario: usuario
});
} else {
if(err.name == 'ValidationError') {
res.statusCode = 400;
res.send({ error: 'Validation error' });
} else {
res.statusCode = 500;
res.send({ error: 'Server error' });
}
console.log('Internal error(%d): %s',res.statusCode,err.message);
}
}
)
};
Here remained the solution:
exports.updateUsuarioByEmail = function (req, res) {
console.log('updateUsuarioByEmail');
return Usuario.findOne({email: req.params.email}, function (err, usuario) {
usuario.email = req.body.email || usuario.email;
usuario.password = req.body.password || usuario.password;
usuario.listaCardsSorting = req.body.listaCardsSorting || usuario.listaCardsSorting;
return usuario.save(function (err) {
if (!err) {
console.log('Updated');
return res.send({status: 'OK', usuario: usuario});
} else {
if (err.name == 'ValidationError') {
res.statusCode = 400;
res.send({error: 'Validation error'});
} else {
res.statusCode = 500;
res.send({error: 'Server error'});
}
console.log('Internal error(%d): %s', res.statusCode, err.message);
}
res.send(usuario);
});
});
};

Mongoose - use findOne multiple times at once

Disclaimer: I am a newb web dev.
I am creating a registration page. There are 5 input fields, with 3 of them (username, password, and email) requiring that they pass various forms of validation. Here is the code:
router.post('/register', function (req, res, next) {
user.username = req.body.username;
user.profile.firstName = req.body.firstName;
user.profile.lastName = req.body.lastName;
user.password = req.body.password;
user.email = req.body.email;
User.findOne({email: req.body.email}, function(err, existingEmail) {
if(existingEmail) {
console.log(req.body.email + " is already in use")
} else {
User.findOne({username: req.body.username}, function(err, existingUsername) {
if(existingUsername) {
console.log(req.body.username + " is already in use");
} else {
user.validate({password: req.body.password}, function(err) {
if (err) {
console.log(String(err));
} else {
user.save(function(err, user) {
if (err) {
return next(err);
} else {
return res.redirect('/')
}
})
}
});
}
});
}
});
});
Basically it first checks to see if it is a duplicate e-mail; if it is a duplicate e-mail, it says so in the console.log. If it isn't a duplicate e-mail, it then checks the username.... and then goes onto the password.
The issue is that it does this all one at a time; if the user inputs an incorrect email and username, it will only say that the email is incorrect (it won't say that both the email and username are incorrect).
How can I get this to validate all 3 forms at the same time?
You can use async to run them in parallel and it will also make your code cleaner and take care of that callback hell:
var async = require('async');
async.parallel([
function validateEmail(callback) {
User.findOne({email: req.body.email}, function(err, existingEmail) {
if(existingEmail) {
callback('Email already exists');
} else {
callback();
}
}
},
function validateUsername(callback) {
User.findOne({username: req.body.username}, function(err, existingUsername) {
if(existingUsername) {
callback('Username already exists');
} else {
callback();
}
}
},
function validatePassword() {
user.validate({password: req.body.password}, function(err) {
if(err) {
callback(err);
} else {
callback();
}
}
}
], function(err) {
if(err) {
console.error(err);
return next(err);
} else {
user.save(function(err, user) {
if (err) {
return next(err);
} else {
return res.redirect('/');
}
});
}
}
);
This way, all the validation methods inside the array will be run in parallel and when all of them are complete the user will be saved.
If you use else statements, you choose to make checks individually (one at a time) by design.
To achieve an 'all at once' behaviour, I would not use else statements (where possible, i.e. errors ar not fatal for next checks), but would do all tests in the same block, and would fill an object like this:
errors: {
existingEmail: false,
existingUserName: false,
invalidUserName: false,
wrongPassword: false,
...
};
And then I'd use it in the form to show user all errors together...
Something like this:
var errors = {};
if (existingEmail) {
console.log(req.body.email + " is already in use");
errors.existingEmail: true;
}
User.findOne({username: req.body.username}, function(err, existingUsername) {
if (existingUsername) {
console.log(req.body.username + " is already in use");
errors.existingUsername: true;
} else {
user.validate({password: req.body.password}, function(err) {
if (err) {
console.log(String(err));
errors.invalidUsername = true;
} else {
user.save(function(err, user) {
if (err) {
return next(err);
} else {
return res.redirect('/')
}
})
}
});
}
});

Resources