Writing a function to adapt for two different cases? - javascript

Maybe I am not thinking hard enough but lets say I have code that is 99% similar what would be the most minimal way of building a function for it?
// this is just an express route, not the function I am building
// doPay() is the function I am trying to build properly
function(req,res) {
if(req.user) {
// I can use req.user.id in my function
doPay(req,res);
} else {
passport.authenticate('local-signup', function(err, user, info) {
// instead of req.user.id I would just need user.id
doPay();
});
}
}
doPay()
// My doPay() function, above I need to pass either req.user.id or user.id
// based on the boolean, so how do I adjust this to adapt to that?
gateway.customer.find(req.user.id, function(err, customer) {
//do payment stuff for an existing user, if the user is new I need to use
//user.id above
});

Here are two alternate designs.
You could make the function take 3 parameters
function doPay(req, res, user)
and then test in that function to select what is available
var userId;
if (req && req.user && req.user.id) userId = req.user.id;
if (!userId && user && user.id) userId = user.id;
if (!userId) throw "error...";
or you could choose to make the 3rd parameter the userId to use
function doPay(req,res,userId)
and put the userId source logic in the routing code instead of the doPay code. The doPay would just use the userId it was told to use. I think this design is what #Bergi was suggesting in the first comment.

Related

How to make a get route like "router.get('/region/:id')" but with a limited range for the id?

Sorry for the bad title, I didn't really know how to phrase it.
So I have a DB with some regions. Each of them has an unique code. I want all of them to be available in this route, but obviously the ID can be, let's say, from 10-19. If the ID is something else, then 404 should be rendered.
I have an idea on how to do this and it's a middleware. This is how I'd do it:
middleware.doesRegionExist = function(req, res, next)
{
var regions = [10, 11, 12];
if(regions.includes(req.params.id))
{
return next();
}
res.redirect("/404");
}
router.get('/regiune/:id', middleware.doesRegionExist, function(req, res, next)
{
res.send(req.params.id);
});
So my questions are: is this the best way to achieve this? I feel like it's not because I have to hardcode an array with the regions available in the middleware.
And why does my idea not work? (I get 404 everytime)
////edit: I had to change the if statement with this: if(regions.includes(Number(req.params.id))). Now it works. Is it the best way to do this though?
////edit2: I have this middleware for checking if the user has access to this variable:
middleware.access = function(req, res, next)
{
if(!req.isAuthenticated())
{
User.findOne({'username': req.body.username, "privileges.region": {$in: [1, req.body.regiune]}}, function(err, usr)
{
if(err)
{
console.log("middleWare.access - " + err);
return res.redirect("404");
}
else
{
console.log(usr);
if(usr === null)
{
return res.redirect("404");
}
else
{
return next();
}
}
})
}
}
so I can't use another middleware because I have to use this (and as far as I know you can't use more than 1 middleware in a route). Should I just copy middleware.doesRegionExist into the route?
You need two things here:
restricting route shape to prevent requests like: GET /region/<very-long-alphanumeric-string>
checking region existence in database and returning 404 if it not exists
Routing
Let's start from routing. Express' routes are support regex, so you can configure them very widely. For ex. if you expect to have id in 00-99 range only you can do the following:
router.get('/region/:id([0-9]{2})', function(req, res, next){
const numericId = parseInt(req.params.id);
// ... your code
next();
});
You can find more details on this topic here: https://expressjs.com/en/guide/routing.html, https://github.com/pillarjs/path-to-regexp
Database handling
Since your data are not constant over time and stored in database (am I right?), the only robust way to check if data really exists in the moment of making request is to perform database query.
All in one
If Region model is mongoose-based, your express handler should be something like this:
router.get('/region/:id([0-9]{2})', access, function(req, res, next){
const numericId = parseInt(req.params.id);
Region.findOne({regionId: numericId}, function (err, region){
if(err){
return res.redirect("404");
} else {
if(usr === null){
return res.redirect("404");
} else {
return next();
}
}
});
});
Everything depends on the need you want to solve, for example:
If the id you are going to analyze is a series of continuous numbers you can verify if the id is in the correct range.
const id = Number(req.params.id);
if (id >= 10 && id <= 12) {
return next();
}
res.redirect("/404");
In other cases you can have valid ids in an array or in some other data source.
You can have multiple middleware nesting one after another.
router.get('/', middleware1, middleware2, middleware3, function (req, res) {
var model = new IndexModel();
res.send('<code><pre>' + JSON.stringify(model, null, 2) + '</pre></code>');
});
I leave the documentation of Routing in Express, so you can review the section of Route handlers.

How to implement query parameters in Postman with mongoose

I have a driver.js that contains a driver schema. Also the driverController.js, which contains my rest methods. GET, POST, DELETE, and PUT.
What i would like to do is
GET - http://localhost:3000/drivers?available=true
and have it return all of the drivers that are available.
My driver schema simply looks like this:
var mongoose = require('mongoose');
var DriverSchema = new mongoose.Schema({
name: String,
available: Boolean,
latitude: Number,
longitude: Number
});
mongoose.model('Driver', DriverSchema);
module.exports = mongoose.model('Driver');
I looked at some documentation, but I haven't been able to do anything.
Here's my GET method in which I'm attempting to add parameters
// GETS ALL DRIVERS FROM THE DATABASE
router.get('/', function (req, res) {
Driver.find({}, function (err, driver) {
if (err) return res.status(500).send("There was a problem finding the drivers.");
var available = req.query.available;
if (available == driver.available )
res.status(200).send(available );
else
res.status(200).send("Nice! " + driver.available);
});
});
This comparison doesn't ever work. It always goes to the else statement. I'm not quite sure why but the output is "Nice! undefined" Even though I have plenty of drivers in my database, and if I only put inside the else statement
res.status(200).send("Nice! " + driver);
Then it gives me the list of drivers.
Nonetheless, I would like to be able to use query parameters in order to find drivers.
Any hints or tips would be greatly appreciated, as this is a project and I have never worked with restAPI, or javascript before. Thanks!
NOTE: Mongoose, express. node.js, and mongoDB are being used.
if I only put inside the else statement res.status(200).send("Nice! " + driver); Then it gives me the list of drivers.
it's a list of drivers, with if (available == driver.available ) you're comparing a boolean with an array of objects,
instead of fetching all the drivers and checking if they have availabe == true , add the condition to the .find() and return the result :
// GETS ALL DRIVERS FROM THE DATABASE
router.get('/', function (req, res) {
Driver.find({ available : req.query.available }, function (err, drivers) {
if (err) return res.status(500).send("There was a problem finding the drivers.");
res.status(200).send(drivers);
});
});
EDIT :
you can do this to add filter depending on the query string :
// GETS ALL DRIVERS FROM THE DATABASE
router.get('/', function (req, res) {
var params = {};
Object.keys(req.query).forEach((v, k) => params[k] = v);
Driver.find(params, function (err, drivers) {
if (err) return res.status(500).send("There was a problem finding the drivers.");
res.status(200).send(drivers);
});
});
having ?name=Wario&available=true will create an object like { name : 'wario', available : true and pass it to the .find()

Javascript Insert Function "arrayName.insert(item, function)"?

I am puzzled as to where or how the below function call for .insert() works? I've tried googling for javascript insert functions and looked through the entire directory for a defined insert function but there's nothing. Here is the code:
router.post('/new', function(req, res) {
var username = req.session.username;
var nextNote, note;
var reportInserted = function(err, notesInserted) {
if (err) {
res.redirect("/notes?error=Could not create a new note");
} else {
res.redirect("/edit/" + notesInserted[0]._id);
}
}
if (username) {
newNote = {title: "Untitled",
owner: username,
content: "No content"};
notesCollection.insert(newNote, reportInserted);
} else {
res.redirect("/?error=Not Logged In");
}
});
I'm going out on a limb, and downvote me if you have to, but if you have no familiarity with what you see and it's not in the current directory, it's part of a pre-existing Node module. Idiomatically, chances are much better than 50-50 that it is:
http://mongodb.github.io/node-mongodb-native/api-generated/collection.html
The function right below the constructor is indeed insert and MongoDB is a very reasonable tool for a collection of notes, given that it's a document-oriented dynamic database.

Load and save document in mongodb with mongoose

I have node running with express as the server side framework.
I have created the following endpoint:
app.post('/post/save', auth.auth, function (req, res) {
Post.findById(req.body._id, function (err, post) {
post = post || new Post();
post.author.name = req.user.getName();
post.author.id = req.user._id;
post.title = req.body.title;
post.body = req.body.body;
post.save(function (err, object) {
err && res.send(500);
res.status(200).send({ /*id: object._id*/ });
});
});
});
When I call this the first time, it works.
When I call this the second time, it fails. The request just keeps pending, and the object returned from the save function call is undefined.
req.body._id is undefined in both the requests. I try to create 2 new posts in a row.
What I want to do is to check if a document exist, if it does, update it and then save it, or create a new document.
I know stuff like upsert exist, but I cant use it because I need the pre-save middleware to trigger, and it only triggers before .save.
Can anyone see the error?
What if you put your logic to a callback, and then - either create or find a Post based on the request query value, passing your callback function? Just dont forget to remove this assignment: post.author.id = req.user._id;
app.post('/post/save', auth.auth, function (req, res) {
var callback = function(post) {
post.author.name = req.user.getName();
post.title = req.body.title;
post.body = req.body.body;
post.save(function (err, object) {
err && res.send(500);
res.status(200).send({ /*id: object._id*/ });
});
};
if (req.body._id) {
Post.findById(req.body._id, function (err, post) {
callback(post);
});
} else {
var post = new Post();
callback(post);
}
});
My original post worked, once I removed the unique field from the model, and dropped the collections in the database.
It might have been enough to drop the indexes; see Leonid Beschastnys comment;
when you're setting a field to be unique, Mongoose creates an unique
index on this field. This index persist in MongoDB even after removing
unique: true flag. Dropping collection indexes should resolve your
problem

How can I render content in my Jade template using functions in express?

I've got a working user register/login page, the only thing left is making content based on whether the user is logged in or not. After reading I came up with this:
app.use(function(req, res, next) {
db.users.find({rememberToken: req.cookies.rememberToken}, function(err, foundUser) {
if (err) { next(); }
if (foundUser[0].length === 0) {
res.locals.isLoggedIn = false;
next();
} else {
res.locals.isLoggedIn = true;
next();
}
});
});
and then just to test it I did this in my Jade template:
!!!
html
head
#{isLoggedIn()}
but all that happens is it says isLoggedIn is not defined.
I can't find much documentation on the subject so any information would be great. Thanks!
I basically want to make the function return true or false so that I can load content based on it.
First of all to get the result of the function you have to call the function not render the element as you would do. To call the function use this syntax:
p #{isLoggedIn()}
to render a portion of HTML/jade you can use something like
- if (isLoggedIn())
p User is logged in
The real problem of your code example is that you're accessing the database from your view and you're using a callback (next) from within your view that will not work.
A better and working way is to use some middleware to set a isLoggedIn field in your res.locals.
var isLoggedIn = function(req, res, next) {
db.users.find({rememberToken: req.cookie.rememberToken}, function(err, foundUser) {
if (err) { console.log('We have an error ' + err ); next();}
if (foundUser[0].length === 0) {
console.log('We could not find the user with the rememberToken ' + req.cookie.rememberToken);
res.locals.isLoggedIn = false;
next();
} else {
console.log('We found the user with the rememberToken ' + req.cookie.rememberToken);
res.locals.isLoggedIn = true;
next();
}
});
};
app.get('/yourroute', isLoggedIn, routes.yourroute);
You could get it to work:
!!!
html
head
| #{isLoggedIn()}
Or in a conditional statement:
!!!
html
head
- if (isLoggedIn())
// do something
- else
// do something else
But why not make isLoggedIn a boolean variable instead of a function? I'm pretty sure that calling asynchronous functions from Jade isn't going to work.

Categories