Dynamically accessing function arguments - javascript

Hi have the following simple ExpressJS application, where the routes are dynamically created based on a configuration. I am having a hard time trying to pass in a bunch of parameters to the handler so that the values are returned in the respective controller.
const express = require('express');
module.exports = class App {
get routes() {
return [
{
path: '/',
verb: 'get',
method: 'home',
params: ['req.query.ref', 'req.query.country'],
},
];
}
constructor() {
this.app = express();
this.register();
}
register() {
const { routes } = this;
routes.forEach((route) => {
const {
path, verb, method, params,
} = route;
// if you replace the params with [req.query.ref, req.query.country] it will work as expected
this.app[verb](path, this.handler(this[method].bind(this), (req, res, next) => params));
});
}
handler(promise, params) {
return async (req, res, next) => {
const bound = params ? params(req, res, next) : [];
console.log(bound);
try {
const result = await promise(...bound);
res.json(result);
} catch (err) {
throw err;
}
};
}
home(payload) {
console.log(payload);
return Promise.resolve({ status: 'OK' });
}
};

Most of your issues stem from the structure of your route definition. It would make more sense to create direct references to the things you want to use, not noting function references etc down as strings.
get routes() {
return [{
path: '/',
method: this.get,
endpoint: this.home,
paramMap: req => [req.query.ref, req.query.country],
}];
}
Once you make the appropriate changes elsewhere, you no longer have the original problem you described.

Maybe you can take a look to the arguments object. All functions have this object and it contains an array with all arguments received in the function. I think it could be what you are looking for.
JavaScript functions have a built-in object called the arguments object.
The argument object contains an array of the arguments used when the function was called (invoked).
This way you can simply use a function to find (for instance) the highest value in a list of numbers:
This is an example how it works:
x = findMax(1, 123, 500, 115, 44, 88);
function findMax() {
var i;
var max = -Infinity;
for (i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
More info:
https://www.w3schools.com/js/js_function_parameters.asp

well you can build the query if with split if your params has the same format
--update--
this solution is based on req or res with x params for each
const express = require('express');
module.exports = class App {
get routes() {
return [
{
path: '/',
verb: 'get',
method: 'home',
params: ['req.query.ref', 'req.query.country'], //changed
},
];
}
constructor() {
this.app = express();
this.register();
}
register() {
const { routes } = this;
routes.forEach((route) => {
let {
path, verb, method, params,
} = route;
this.app[verb](path, this.handler(this[method].bind(this), (req, res, next) => this. paramsStringToArrayValues(req, res,params))
}));
});
}
paramsStringToArrayValues(req, res,params){
return params.map(param => {
let paramArr = param.split('.');
let obj = paramArr.shift() === 'req'? req : res
paramArr.forEach(key =>{
obj = obj[key]
})
return obj
})
}
handler(promise, params) {
return async (req, res, next) => {
const bound = params ? params(req, res, next) : [];
console.log(bound);
try {
const result = await promise(...bound);
res.json(result);
} catch (err) {
throw err;
}
};
}
home(payload) {
console.log(payload);
return Promise.resolve({ status: 'OK' });
}
};

Related

When a new image is added, the old image is automatically replasing in nodejs how to fix

I have created a file from which I can successfully upload the image, But the name of the image that comes up ( undefiend.jpg ) in this project use express file upload middleware
*admin.js
var express = require("express");
const productHelpers = require("../helpers/product-helpers");
var router = express.Router();
var productHelper = require("../helpers/product-helpers");
/* GET users listing. */
router.get("/", function (req, res, next) {
productHelpers.getAllProducts().then((products) => {
console.log(products);
res.render("admin/view-products", { admin: true, products });
});
});
router.get("/add-product", function (req, res) {
res.render("admin/add-product");
});
router.post("/add-product", (req, res, next) => {
productHelpers.addProduct(req.body, (id) => {
let image = req.files.Image;
console.log(id);
image.mv("./public/product-images/" + id + ".jpg", (err) => {
if (!err) {
res.render("admin/add-product");
} else {
console.log(err);
}
});
});
});
module.exports = router;
product-helpers.js
Here i do callback using id
product-helpers.js` var db=require('../config/connection')
var collection=require('../config/collections')
module.exports={
addProduct:(product,callback)=>{
db.get().collection('product').insertOne(product).then((data)=>{
callback(data._id)
})
}
}
`
I think you should use data.insertedId,
module.exports = {
addProduct: (product, callback) => {
db.get().collection('product').insertOne(product).then((data) => {
callback(data.insertedId)
})
}
}
in the mongodb documentation https://www.mongodb.com/docs/manual/reference/method/db.collection.insertOne/, it is showed that insertOne() returns
{
"acknowledged" : true,
"insertedId" : ObjectId("56fc40f9d735c28df206d078")
}
Insert a Document without Specifying an _id Field In the following
example, the document passed to the insertOne() method does not
contain the _id field:
try {
db.products.insertOne({ item: "card", qty: 15 });
} catch (e) { print(e); };
The operation returns the following document:
{
"acknowledged" : true,
"insertedId" : ObjectId("56fc40f9d735c28df206d078")
}
But anyway, you can use the name property from req.files.image: for example:
const image = req.files.image;
const imageName = image.name;
const imagePath = './public/product-images/' + imageName;
console.log(imagePath);
image.mv(imagePath, (error) => {
//something here
})
If you want to use more properties, look at here:
https://www.npmjs.com/package/express-fileupload

Express dosn't get return of other function querying Mongodb [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm working in a simple API Key authentication, I just want to verify the given key against the user provied key.
I have a seperate file with the function querying the database, and returning true/false and the user object.
But in my route.js file, the return object is undefined even tough in my auth.js file it isn't.
I tried making the the function in router.get an async function using express-promise-router and making the function an await return var user = await auth.verify(req.params.uid, req.get("token")) but I don't realy know how async works.
router.js
[...]
router.get('/list/:uid', function(req, res) {
var user = auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user) // <-- Undefined
if (user.status) {
res.send("Success")
} else {
res.status(403)
res.json({status: 403, error: "Unkown User / Token"})
}
})
[...]
auth.js
var db = require('./db')
var ObjectId = require('mongodb').ObjectId;
module.exports = {
verify: (uid, key) => {
try {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {throw err}else{status=1}
if (user.length <= 0) {throw "NotExistingExc"; status = 0}else{
usr = user[0];
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
} /* --> Is {
status: 1,
user: {
_id: d47a2b30b3d2770606942bf0,
name: 'Sh4dow',
groups: [ 0 ],
api: 'YWFiMDI1MGE4NjAyZTg0MWE3N2U0M2I1NzEzZGE1YjE='
}
}
*/
return returnObj;
})
} catch (e) {
console.error(e)
return {
status: 0,
user: {},
error: e
}
}
}
}
db.js (Idk if needed)
var MongoClient = require('mongodb').MongoClient
var state = {
db: null,
}
exports.connect = function(url, done) {
if (state.db) return done()
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function() {
return state.db.db("database")
}
exports.close = function(done) {
if (state.db) {
state.db.close(function(err, result) {
state.db = null
state.mode = null
done(err)
})
}
}
I want to have the returnObjin auth.js in the router.get of my route.js file.
Make auth.verify return a Promise which we can then await for it inside router, You can just make the callback async no need for express-promise-router
router.get('/list/:uid', async function(req, res) {
try {
var user = await auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user)
if (user.status) {
res.send("Success")
} else {
res.status(403).json({status: 403, error: "Unkown User / Token"})
}
} catch (e) {
console.error(e)
res.status(/* */).json(/* */)
}
})
auth
module.exports = {
verify: (uid, key) => new Promise((resolve, reject) => {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {
reject(err)
return
} else {
status = 1
}
if (user.length <= 0) {
reject(new Error("NotExistingExc"))
return
} else {
usr = user[0]
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
}
resolve(returnObj);
})
}
}
In short, the reason you get undefined is because the code in auth.js is asyncronous. But you're really close. The toArray method in MongoDB returns a promise, so you need to make sure you return that promise and then use it in the router correctly.
In auth.js, make sure verify returns a promise - just add return!
return collection.find(query).limit(1).toArray(...)
And then, change your usage of the verify to the async/await you originally tried:
router.get('/list/:uid', async function(req, res) {
var user = await auth.verify(req.params.uid, req.get("token"))
// More code here...
})

High order function in express middleware

Can't send params to a high order function in JS. The function it's not getting req and res args
// In router.js
const { getAll } = require('./controller')
router.get('/coordinadores', getAll()('mentor'))
// In controller.js
exports.getAll = (req, res) => {
return (role) => {
var filter = { role }
if (req.user.role == 'mentor') filter = { role, centro: { "$in": req.user.centro } }
Model.find(filter, '-password -role -__v -createdAt -updatedAt -centro').lean().exec()
.then(list => { res.status(200).json({ list }) })
.catch(err => { errorHandler(req, res, err) })
}
}
// Result
// TypeError: Cannot read property 'user' of undefined
Here
router.get('/coordinadores', getAll()('mentor'))
you're calling getAll without any arguments. To create a HoF you should create it like this
const getAll = role => (req, res) => {
// your code
}
and then
router.get('/coordinadores', getAll('mentor'))
this will call the getAll function with mentor and it will return a function reference that will be called with req and res by express
For an alternate, express way is to pass values through middleware like:
function setDefaultRole((req, res, next) => {
if (not some check here) {
// if check fails, setting default role
req.user.role = 'mentor'
}
// call the next middleware
next();
})
function getAll((req, res, next) => {
if (req.user.role == 'mentor') {
// continue logic
filter = ???
}
})

How to return a value from within activedirectory method

I've got a method in a class which does query an ActiveDirectory.
Therefore I'm using 'activedirectory2' npm package.
I successfully authenticated and successfully logged my result to console.
Now that I have instanciated my class and have tried to call the method, I'm not abled to get a non-empty result.
I tried it with getters/setters to make the _result value available after instaciating the class.
I tried to solve my issue with research on asynchronous calls, but obviously wasn't able to ask the right question.
class Activedirectory
var ActiveDirectory = require("activedirectory2");
class AuthenticateWithLDAP {
constructor(user, password){
this._result = [];
this.user = user;
this.password = password;
this.config = {
url: "ldaps://someldap",
baseDN: "somebasdn",
username: this.user,
password: this.password,
filter: 'somefilter',
}
this.ad = new ActiveDirectory(this.config);
}
//Auth Method
auth() {
var result = this._result;
this.config.entryParser = function(entry,raw,callback){
if(entry.hasOwnProperty('info')) {
result.push(entry.info);
this._result = result;
}
callback(entry);
}
this.ad.authenticate(config.username, config.password, (err,auth)=>{
if (err) {
//some error handling
}
if (auth) {
this.ad.find(config,async (err, userDetails) => {
var result = this._result;
{
if (err) {
//some error handling
}
if(!userDetails) {
console.log("No users found.");
} else {
this._result = result[0]; //I want this result!
console.log('result: ', this._result);
return await this._result;
}
}
})
} else {
console.log("Authentication failed!");
}
});
}
//getter/setter
get result(){
return this._result;
}
set result(value) {
this._result.push(value);
}
}
module.exports = AuthenticateWithLDAP;
route module
const express = require('express');
const AuthwithLDAP = require('AuthenticateWithLDAP');
const router = express.Router();
router.post('/', async (req,res,next) => {
let x = async ()=> {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
return await authwithldap.auth();
}
x().then((res)=>{
console.log('res: ', res); //always []
})
})
I expected to be able to use the _result value of AuthenticateWithLDAP class in my router.post method handler.
Actually i only get [] (empty array) in router.post.
Could you please tell me how to alter the value _result in a way, so that the instance of the class knows it and can use it outside the class itself.
Thank you very much.
Micha
I am not 100% sure but I think this should work.
In your code you cant return the result because the return is in a callback.
There are to ways to fix that.
Pass a callback to the auth() method (This is bad since callbacks suck)
Return a promise and that resolves to the result
I've decided to go for promises.
var ActiveDirectory = require("activedirectory2");
class AuthenticateWithLDAP {
constructor(user, password){
this._result = [];
this.user = user;
this.password = password;
this.config = {
url: "ldaps://someldap",
baseDN: "somebasdn",
username: this.user,
password: this.password,
filter: 'somefilter',
}
this.ad = new ActiveDirectory(this.config);
}
//Auth Method
auth() {
return new Promise((resolve, reject) => {
this.ad.authenticate(config.username, config.password, (err,auth)=>{
if (err) {
//Call reject here
}
if (auth) {
this.ad.find(config,async (err, userDetails) => {
var result = this._result;
{
if (err) {
//some error handling
}
if(!userDetails) {
console.log("No users found.");
} else {
this._result = result[0]; //I want this result!
resolve(await this._result);
}
}
})
} else {
console.log("Authentication failed!");
}
});
});
}
}
module.exports = AuthenticateWithLDAP;
const express = require('express');
const AuthwithLDAP = require('AuthenticateWithLDAP');
const router = express.Router();
router.post('/', async (req,res,next) => {
/* This code can be simplifed
let x = async () => {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
return await authwithldap.auth();
}
x().then((res)=>{
console.log('res: ', res); //always []
})
*/
(async () => {
authwithldap = new AuthwithLDAP(req.body.user,req.body.password);
var res = await authwithldap.auth();
console.log('res: ', res);
})();
})
Could you try to add syntax "await" like this?
await x().then((res)=>{
console.log('res: ', res); //always []
})
As your "x" method is in async mode, maybe you have to wait for the Promise to be resolved...

ExpressJS Router + Promise not working

I have a problem with Express response and Promise.
My script call a MongoDB Database to get informations from the server. I use Promise to send the result only when my server answered me.
The problem is in my route. I get the response correctly, but the
res.send returns nothing. I can't figure why.
Let's review my code. I've truncated some of the files, as it is not important code.
I have first a server.js file with my router declaration :
let router = express.Router();
app.use('/mongo', MongoRouter);
Then here is my MongoRouter.js file :
import { Router } from 'express';
import { MongoController } from '../Controllers/MongoController';
class MongoRouter {
constructor() {
this.router = Router();
this.controller = new MongoController();
this.routes();
}
routes() {
let obj = this;
this.router.get('/global', (req, res) => {
let promise = obj.controller.globalInfos();
promise.then((value) => {
console.log('Resolve OK. Showing data');
console.log(value);
res.send(value);
}).catch((error) => {
res.status(500).send(error.toString());
});
});
this.router.get('*', (req, res) => {
res.json({
data: 'Invalid route'
});
});
}
}
export default new MongoRouter().router;
And the function globalInfos() from my MongoController :
globalInfos() {
var status = '';
var globalInfos = new Array();
var internalServerReached = 0;
var dbCommand = {'serverStatus': 1};
let obj = this;
return new Promise((resolve, reject) => {
obj.cnxStrings.forEach(element => {
MongoClient.connect(element['cnxString'], (err, db) => {
if (!err) {
if (element['replica'] == true) {
dbCommand = {'replSetGetStatus':1};
}
db.command(dbCommand, (err, results) => {
if (!err) {
status = results;
} else {
status = 'Status unknown';
}
globalInfos[element['name']] = status;
internalServerReached++;
db.close();
if (internalServerReached >= obj.cnxStrings.length) {
console.log('Waiting 3s for the resolve');
setTimeout(() => {
resolve(globalInfos);
},3000);
}
});
} else {
status = 'Unreachable';
}
});
});
});
}
When i go to http://localhost:3000/mongo my function is running, the resolve is called after 3sec, my data are ok in the console (in the .then() callback), but the res.send(..) show an empty array []
Thanks!

Categories