I am getting issue in mongoose calback in custom validation - node.js

I am using express-validator and have made one custom validation with mongoose database find, now I can't send withMessage. Here is my code althought process stop at Error but it does not show message in Json, but when user created it is show all.
body('mobile', 'Mobile number is required')
.custom((value, {req, loc, path}) => {
User.countDocuments({mobile: value}, function (err, c) {
if (err) {
console.log('An error happen in db')
}
if (c > 0) {
throw new Error('Mobile already exists finally')
} else {
return value
}
})
return value
})
.withMessage('Mobile already exists'),
Following is log of console
functions: Beginning execution of "app"
> events.js:298
> throw er; // Unhandled 'error' event
> ^
>
> Error: Mobile already exists finally
> at /Users/kamal/Documents/personal/asghar/codes/functions/app/controllers/users_controller.js:117:23
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4849:16
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4849:16
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/helpers/promiseOrCallback.js:24:16
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4872:21
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/query.js:4379:11
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/kareem/index.js:135:16
> at processTicksAndRejections (internal/process/task_queues.js:79:11)
> Emitted 'error' event on Function instance at:
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/model.js:4851:13
> at /Users/kamal/Documents/personal/asghar/codes/functions/node_modules/mongoose/lib/helpers/promiseOrCallback.js:24:16
> [... lines matching original stack trace ...]
I need to add if condition near return value but problem is call back does not bring me value of c here, in above it is comign correct and even stop due to Error raising, but I don't want to raise error instead want to go further withMessage('Mobile already exists') I am sure doing mistake in callback. Please suggest solution

This should work
body("mobile").custom(value => {
return User.countDocuments({ mobile: value })
.then(count => {
if (count > 0) return Promise.reject("Mobile number already exists");
})
.catch(error => console.error(error.message));
});
From express-validator
documentation
Custom validators may return Promises to indicate an async validation
(which will be awaited upon), or throw any value/reject a promise to
use a custom error message. Note: if your custom validator returns a
promise, it must reject to indicate that the field is invalid.

After playing and seeing many many documentation I solved this by doing following, in my User model I put following code, as my model is actualy mongoose module.
UserSchema.statics = {
isValidMobile(mobile) {
console.log(` Searching obile ${mobile} number `)
return this.findOne({mobile: mobile}).then((result) => {
if (result) throw new Error('Mobile Number already exists')
})
},
}
Now in my validation I put following line instead of above lines.
body('mobile', 'Mobile number is required')
.custom((val) => User.isValidMobile(val))
It worked, and I am getting proper messages with whole JSON, as it seem custom require proper true/false reply so my method is replying just true or false and it is working fine with correct message, but the message is being used came from User model class not from validation, but that works. Thank you for responses.

Related

result prints undefined after invoking a new method

I am trying to loop though all users data in my mongo database and check and see if an email is in the database. The loop currently works and correctly identifies if an email is in a database but the problem is once I verify the email exist I get the id of the same object and use findById() to find the specific object the email was found in then update it. Once I find the object when I try and print the result I got from the first find() it logs undefined but when I log it before the findById() method it logs the result without no problem. Why is this happening and how can I log the previous result after invoking findById(). Take a look at the comments on my code to understand the question better. Thanks in advance.
const Users = require('pathToSchema')
const email = 'test#gmail.com'
Users.find()
.then(async(result) => {
for (var i = 0; i < result.length; i++) {
if (result[i].email == email) {
//this prints result successfully
console.log(result[i])
Users.findById(result[i].id)
.then((result2) => {
//this prints undefiend
console.log(result[i])
})
.catch((err) => {
console.log(err)
})
} else {
if (i === result.length - 1) {
console.log('email not found')
}
}
}
})
.catch((err) => {
console.log(err)
})
From the code snippet it looks like you are trying to print a value from result and not result2. result is not available inside the findById() method callback handler.
Continuing the discussion from the comments, you can use the findOneAndUpdate method in mongodb to find a user with a given email and update. With this, you will not have to find the user before you update. You can do that in a single DB command.
Users.findOneAndUpdate({email: 'test#gmail.com'},{updates},{options});
This will return the original document before update. If you need the updated document in the response, pass returnNewDocument: true in the options.
Link to the documentation for this function
https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndUpdate/

Discord.js, how to handle error sending private message to user with disabled privacy setting and return right after error?

Is there possible way to handle error that will return right back after error is triggered?
if(command === 'test') {
message.author.send('Dm Test').catch(error => {
message.reply('I failed to send you a private message!')
return;
})
//some code below, should not trigger after sending message error.
The problem is that .catch will respond as last, how to handle this error and immediately return instead of running also code below? I tried to use try { but that didn't work.
message.author.send('👌')
.catch(() => message.reply("Can't send DM to your user!"));
Would like to know if there is another way to handle error. Any help will be much appreciated.
The reason why .catch() executes after the rest of your code is because .send() is asynchronous and returns a Promise. Think of it this way: each time you send a message, discord.js has to send an HTTP request to the Discord API, wait for a response, and then give you the data from the response. That process is not instantaneous and takes some time, which is why using the Promise structure is very useful.
As for the specific problem, you simply want to await the response of your .catch(). This can be done by making the function you are running this code in async. Here is an example:
client.on("messageCreate", async (message) => {
let response = await message.author.send('👌').catch(() => {
message.reply("Can't send DM to your user!"));
return false;
});
// (If error occurred, 'response' will be false)
if (!response) return; // Return if the error occurred
// ... Code for if error did not occur
// (If no error occurred, 'response' will contain sent message)
});
The await keyword will wait until the asynchronous line you are executing has been fulfilled (i.e. until the return value has been obtained or an error has been caught) before continuing on with your code. Note the use of the async keyword. You can only use await within functions marked async.
If you place a return before sending the error message, JavaScript will read that as you're returning the message so if you do the following:
message.author.send('👌')
.catch(() => return message.reply("Can't send DM to your user!"));
you'll have the error message be the final command run and nothing else will follow.
You can use try inside of if & else statement to know if you can dm the person or not.
if(command === 'test') {
try {
message.author.send("Something right here")
} catch {
message.channel.send("I can't dm this person.")
} else {
//something exploration code here
}
}

Cannot read property 'catch' of undefined

I am trying to use Argon2 encryption in Node, but when I try to encrypt a string, I get this error:
Cannot read property 'catch' of undefined
I have tried handling the errors from the promise returned by the argon2.hash function, but it still does not work.
This is my code so far:
argon2.hash('password', {type: argon2.argon2id})
.then(hash => {
// do something with the hash
}).catch(err => {
// Handle the error
});
Could anyone please help me with fixing this error?
In my case I got that error message because I
a) spied on some async method
spyOn(sut,'myAsyncMethod')
b) later appended .catch() to the original method call and forgot to extend the spy to return a value/promise.
Returning a promise from the spy solved my issue:
spyOn(sut,'myAsyncMethod').and.returnValue(new Promise(resolve=>resolve()));
It throws an exception, it does not return a promise. As such, there is no promise object on which the then(…).catch(…) methods could be invoked.
To catch it, you would need an actual try/catch block
from argon2 github page, you should do this:
const argon2 = require('argon2');
try {
const hash = await argon2.hash("password");
} catch (err) {
//...
}
Try the following instead:
argon2.hash('password', {type: argon2.argon2id})
.then(hash => {
// do something with the hash
}, err => {
// Handle the error
});
The second parameter to a then clause is the onError handler.

Firebase function error; "Maximum call stack size exceeded"

I've made a function that toggles a boolean value in the database via a transaction. Take a look:
export const toggleDoneState = functions.https.onCall((data, context) => {
const userId = getUserIdFromCallableContext(context)
let togglePath: admin.database.Reference
if (!isUndefined(data.subtaskId)) {
const subtaskId = data.subtaskId
if (isValidString(subtaskId)) {
togglePath = admin.database().ref("userSubtasks").child(userId).child(subtaskId).child("done")
} else {
throw new functions.https.HttpsError('invalid-argument', 'Expected valid Subtask Id')
}
} else {
throw new functions.https.HttpsError('invalid-argument', 'Expected valid toggle type')
}
return togglePath.transaction(currentValue => {
return !(currentValue || false)
}).then(value => {
return { done: value }
})
})
This is the only function I have deployed! I used to have about 15 functions running, but I've removed them to make this test cleaner.
When I call the function from an iOS app, I see that the value in the database is toggled as expected, but I receive an error from the Functions SDK in iOS:
Domain=com.firebase.functions Code=13 "INTERNAL" UserInfo={NSLocalizedDescription=INTERNAL}
When I look at the functions logs in the console I see the following error:
Unhandled error RangeError: Maximum call stack size exceeded
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13395:23)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:204:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13400:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:3010:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13399:7)
at encode (/user_code/node_modules/firebase-functions/lib/providers/https.js:204:18)
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13400:38
at /user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:3010:24)
at Function.mapValues (/user_code/node_modules/firebase-functions/node_modules/lodash/lodash.js:13399:7)
The value in the database is toggled as expected, but I would like to not get and error and understand why. Any clues?
I suspect you have a misunderstanding about what value is in the then callback after your transaction. If you look at the API docs for transaction(), you'll see that it returns a promise that contains a couple properties, one of which is a DataSnapshot. You're effectively trying to serialize that DataSnapshot object, and I think that lodash is encountering a problem with that, maybe a circular reference.
First of all, try fixing the return value of your then callback and see if that clears things up. Then, figure out how to use the DataSnapshot yielded by the transaction to return to the client the value you intend.

Returning response from Mongoose promise

Followup from this question > Stopping response if document isn't found since it was recommended I use Promise.
So basic premise, I want node to return "Can't find ID" message if we can't find the id in our database.
v1.post("/", function(req, res) {
// If the project_id isn't provided, return with an error.
if ( !("project_id" in req.body) ) {
return res.send("You need to provide Project ID");
}
// Check if the Project ID is in the file.
helper.documentExists( ProjectsData, {project_id: req.body.project_id} )
.then(function(c) {
if ( c == 0 ) {
return res.send("The provided Project Id does not exist in our database.");
} else {
var gameDataObj = req.body;
GameData.addGameId(gameDataObj, function (err, doc) {
if (err) {
if (err.name == "ValidationError") {
return res.send("Please send all the required details.");
}
throw err;
};
res.json(doc);
})
};
});
});
And helper.documentExists
module.exports = {
documentExists: function(collection, query) {
return collection.count( query ).exec();
},
};
But the script continues to run after this and prints the "required data not found".
Output:
required data not found
1
I am using native ES6 Promises.
var mongoose = require("mongoose");
mongoose.Promise = global.Promise;
EDIT: Included the entire get route. (will fix those throw err later)
#######POINT 1#########
ProjectsData.count( {project_id: req.body.project_id} )
.then(function(c) {
#######POINT 3#########
if ( c == 0 ) {
console.log("1");
return res.send("The provided Project Id does not exist in our database.");
console.log("2");
}
});
#######POINT 2#########
//some other logic
console.log("required data not found");
Following async workflow: after POINT 1, the promise is created and your handler is attached. Now POINT 2 will continue, while (at some future clock the promise is resolved and you reach POINT 3.
With my limited understanding of your workflow/purpose I'd say simply put POINT 2 code in the else{} of the if at POINT 3 (as you rightly guessed in the comments).
EDIT: thanks to #jfriend00 for pointing out a serious mistake in the previous version of my answer.
Your code essentially results in this:
ProjectsData.count().then(...);
console.log("required data not found");
So, of course the second console.log() is going to run and print. Nothing that happens in the .then() handler runs until long after the console.log() has already run. And, even then, it can't stop other code from running. Promises don't make the interpreter "wait". They just provide structure for you to coordinate your asynchronous operations.
If you want to branch with promises, then you have to branch inside the .then() handler, not after it.
You don't show enough of the rest of what you're doing to know how to recommend a complete solution. We need to see the rest of your request in order to help you with the proper branching based on asynchronous results.
You probably need something like this:
ProjectsData.count( {project_id: req.body.project_id} ).then(function(c) {
if ( c == 0 ) {
return res.send("The provided Project Id does not exist in our database.");
} else {
// put other logic here
}
}).catch(function(err) {
// handle error here
});

Resources