MongoDB check for existing value - node.js

How do I check if a mongo/mongoose value exists before continuing in a post route?
My (partial) Mongoose model looks like this:
reserved: {type: Boolean, default: false}
module.exports = mongoose.model("Rentals", rentalsSchema);
I've been trying to check to see if reserved is true in this way without any luck
NOTE: As you'll see below this needs to work for each rentals ID so it will need to findById somehow.
router.put("/:id/reserve", middleware.checkRentalsStatus, function(req, res){
//checking here for existing value
//checking here for existing value
if(Rentals.reserved == true){
res.redirect("back");
console.log('TAKEN!')
} else {
// Token is created using Stripe.js or Checkout!
// Get the payment token submitted by the form:
var token = req.body.stripeToken; // Using Express
// Charge the user's card:
var charge = stripe.charges.create({
amount: 19500,
currency: "usd",
description: "",
source: token,
}, function(err, charge) {
if(err){
req.flash("error", err.message);
res.redirect("back");
} else {
var reservedby = req.body.reservedby;
var reserved = true;
var reservedemail = req.body.reservedemail;
var newReservation = {reserved: reserved, reservedby: reservedby, reservedemail: reservedemail }
Rentals.findByIdAndUpdate(req.params.id, {$set: newReservation}, function(err, rentals){
if(err){
req.flash("error", err.message);
res.redirect("back");
} else {
req.flash("success","It's reserved for you. Thank you!");
res.redirect("/rentals/" + rentals._id);
}
});
}
});
console.log('charged')
});
How would I change my code to check if reserved is true prior to finishing the post route? (I'm trying to check if a rental is available on a reserve route in the back end. )

you can directly check if/else condition like this
if(Rentals.reserved )
{ res.redirect("back"); console.log('TAKEN!') }
else { // continue with post route
}

This is the answer. Best way is to create a middleware to check. The issue with the other answers is they do not find the renal's ID. Rentals.findByID is the key to making this work. :)
//CHECK Rentals HAS NOT BEEN RENTED
middlewareObj.checkRentalsStatus = function(req, res, next){
Rentals.findById(req.params.id, function(err, foundRentals){
if (err){
res.redirect("back");
}
if (foundRentals.reserved){
req.flash("error", 'Oh no! It looks like someone else JUST rented this location. Your credit card was not charged.');
res.redirect("back");
} else {
next();
}
});
}

Related

Post same objectID in to different table

I'm trying to post a data in my user then at the same time, post the _id of my user as a reference id in my donation table.
After I posted my data in the users table like this:
var User = require('../models/user');
var Blooddonation = require('../models/blooddonation');
router.post('/createBlooduser',function(req, res) {
var user = new User();
user.user_lastname = req.body.user_lastname;
user.status= "initial";
user.save(function(err) {});
});
How can I get the _id of the posted data and make a reference id in my donation table? Something like this:
**users.json**
{
_id:ObjectId("5c7e591eee959272cc5768cb"),
user_lastname:"Jhonson",
status:"initial"
}
**blooddonations.json**
{
donor_id:ObjectId("5c7e591eee959272cc5768cb")
}
The _id property is actually created as soon as you create new instance with a statement like new User(). So you can actually access that value before it's even stored in the collection, or at any time after instance creation really:
router.post('/createBlooduser',function(req, res) {
var user = new User();
user.user_lastname = req.body.user_lastname;
user.status= "initial";
user.save(function(err) {
if (err) throw err; // or really handle better
// You can also just create() rather than new Blooddonation({ donor_id: user._id })
Blooddonation.create({ donor_id: user._id }, function(err, donor) {
// check for errors and/or respond
})
});
});
Of if you might just want access to other properties that might "default on save", then you can access in the callback from save() or create():
router.post('/createBlooduser',function(req, res) {
User.create({
user_lastname: req.body.user_lastname;
status: "initial"
}, function(err, user) { // this time we pass from the callback
if (err) throw err; // or really handle better
Blooddonation.create({ donor_id: user._id }, function(err, donor) {
// check for errors and/or respond
});
});
});

nodejs passport - use same routes in api but return different sets of data based on permissions

Not sure of a clean way to go about his. Let's say I have this endpoint:
GET /api/Books/
For the user on the webservice, this will return only the user's resources. It might look a little something like this:
exports.getBooks = function(req, res) {
// find all books for user
BookModel.find({ userId: req.user._id }, function(err, books) {
if (err)
res.send(err);
res.json(books);
});
};
The web service using the api needs the user to be logged in first. I can achieve this using a basic passport strategy to ensure authentication. But let's say I have an admin account that needs to see ALL books ever recorded. What's more is that the admin account and user accounts have completely different properties so assigning a Boolean for permissions is not enough. Using the same endpoint:
GET /api/Books
I see no reason to write another endpoint to achieve this. However the difference would look like this:
exports.getBooks = function(req, res) {
// find all books in the database
BookModel.find({}, function(err, books) {
if (err)
res.send(err);
res.json(books);
});
};
However I cannot come up with a clean way to achieve this while also using the passport middlewear as it is intended like so:
router.route('/books')
.post(authController.isAuthenticated, bookController.postBooks)
.get(authController.isAuthenticated, bookController.getBooks);
The function isAuthenticated will will only verify whether or not the user requesting resources has permission and does not change the way the controller behaves. I'm open to ideas.
ANSWER
The user #ZeroCho suggested to check user properties in req.user object to determine what should be sent back. This was more simple than I expected. In my implementation for passport.BasicAuth strategy, I check which table has a matching doc. Once the user is found in the common user or Admin user table all you do is add a property in the isMatch return object.
// Basic strategy for users
passport.use('basic', new BasicStrategy(
function(email, password, done) {
verifyUserPassword(email, password,
function(err, isMatch) {
if(err) { return done(err); }
// Password did not match
if(!isMatch) { return done(null, false); }
// Success
var userInfo = {
email: email,
isAdmin: isMatch.isAdmin || false,
businessID: isMatch.businessID || false
};
return done(null, userInfo);
});
})
);
Then you can check if .isAdmin or .businessID is valid in your requests.
Just separate your controller by if statement
exports.getBooks = function(req, res) {
if (req.user.isAdmin) { // or some other code to check user is admin
// find all books in the database
BookModel.find({}, function(err, books) {
if (err)
res.send(err);
res.json(books);
});
} else {
BookModel.find({ userId: req.user._id }, function(err, books) {
if (err)
res.send(err);
res.json(books);
});
}
};

How to limit my collection size to only one document?

I have a API, which allows a user to add a intervalValue - which will be used in my program to determine how often my program runs. I am using node js, express and mongodb in my project.
This is the API which allows a user to add a value:
router.post('/', function(req, res, next) {
intervalValue.create(req.body, function(err, post) {
if (err) {
console.log(err);
} else {
res.json(post);
}
});
});
And this is the schema for it:
var intervalValueSchema = new mongoose.Schema({
milliseconds: {
type: Number,
min: 15000
}
}, {
capped: {
size: 1024,
max: 1,
autoIndexId: true
}
});
From my understanding, this schema will only allow milliseconds value larger than 15000 milliseconds, and because it is capped, it will only allow one document in the collection.
THE AIM : add a interval value, and then only be able to modify that value - i.e. it will not be allowed to be deleted, and no more will be able to be added. Hence I need it to be limited to one document.
However with the current code, I am able to add multiple documents to this collection (even though when I do isCapped() I get true returned), and when I update the value I can insert a value less than 15000 - which should not be allowed.
This is the update API:
router.put('/updateValue/:id', function(req, res, next) {
intervalPoll.findByIdAndUpdate(req.params.id, req.body, {
new: true
}, function(err, post) {
if (err) {
console.log(err);
} else {
res.json(post);
}
});
});
What am I doing wrong - I am a beginner with mongo so any tips would be appreciated. Also, is there a better way to achieve my aim?
Thanks!
I wouldn't expose the ID of that value and I would not implement the PUT handler with :id (which is not convenient anyway, because user has to know the ID which once set will never change and be only one).
I would implement two endpoints:
router.get('/interval', ...
and
router.post('/interval', ...
# or:
router.put('/interval', ...
The get handler would search the database for any document and if present return its value. If there is no such document it would return some default value.
The post handler would first verify the value and then modify a document if it exists or insert it if it doesn't.
I think that in this case it would be much easier to check for that number in your handler then to fight with Mongoose to do what you need. This is a specific use case.
Udate
Here are some examples:
Schema can be:
mongoose.Schema({
milliseconds: Number
});
And the handlers would be something like this - let's say that your model is called Interval:
var defaultInterval = {milliseconds: 15000};
router.get('/interval', function(req, res, next) {
Interval.find().exec(function (err, intervals) {
if (err || intervals.length === 0) {
res.json(defaultInterval);
} else {
res.json({milliseconds: intervals[0].milliseconds});
}
});
});
router.put('/interval', function(req, res, next) {
var milliseconds = // get it from request
if (!milliseconds || milliseconds < 15000) {
// respond with error
} else {
Interval.findOneAndUpdate(
{}, {milliseconds: milliseconds}, {upsert: true},
function(err, interval){
if (err) {
// respond with error
} else {
// respond with success
}
}
);
}
});
This is not tested and you need to fill in the blanks but you get the idea.

Checking whether a document present in mongodb using mongoose

I am trying to create a nodejs application using mongodb as database. I need to check whether a username exist in mongodb or not. If username present, it will output "username not available", else will list all the user details with that username.
var userSchema = require('../schemas/user');
exports.collect = function(req,res) {
userSchema.find({ username: "bob" }).exec(function(err,display) {
if(err){
console.log(err);
}
else
{
if(display=='[]'){
res.send("username not available");
}
else{
res.send(display)
}
}
});
};
Is there any alternative or simple way for performing this operation?
I would decouple the whole logic from the controller if you ask me, but I'm not going to lecture you on that. I would use findOne as that will find just one record. If the user is not available, you will know that this username is not available. Don't forget that even when an error occurs, you still want to output "some" data, as you don't want the client to wait until it times out.
var userSchema = require('../schemas/user');
exports.collect = function(req,res) {
userSchema.findOne({username: "bob"}).exec(function(err, user) {
if (err) {
console.log(err);
// Handle the error properly here, we should not continue!
return res.sendStatus(500);
}
if (!user) {
return res.send("username not available");
}
// Don't know what you want to do with it, I just display it like this
return res.json(user);
});
};

Pre-populating documents using Mongoose + Express

I am new to Node+Mongoose, and am currently in the process of creating an API using KeystoneJS. I've managed to populate all posts with the author's email and name. My question is, is there a way to populate the post with author everytime, possibly with some middlewear, without having to rewrite it in each method where I retrieve posts? My goal is to not have multiple instances of populate('author', 'email name') scattered throughout the code. For instance, in the future, I'd like to include the author's profile photo url as well, and I'd prefer to be able to make that change in a single place, that will then be reflected in every place that I retrieve posts.
Current implementation:
Post.model.find().populate('author', 'email name').exec(function (err, posts) {
if (!err) {
return res.json(posts);
} else {
return res.status(500).send("Error:<br><br>" + JSON.stringify(err));
}
});
Post.model.findById(req.params.id).populate('author', 'email name').exec(function (err, post) {
if(!err) {
if (post) {
return res.json(post);
} else {
return res.json({ error: 'Not found' });
}
} else {
return res.status(500).send("Error:<br><br>" + JSON.stringify(err));
}
});
You can create model using statics. This is example of methods for schema
PostSchema.statics = {
getAll: function(cb) {
return this
.find()
.populate('author', 'email name')
.exec(cb);
}
}
You still should use "populate", but it will be in schema file, so you will not care about it in future

Resources