I can not send a specific data (JavaScript) - javascript

I'm having trouble with my code I can not recover this data my array. Do you have an idea? Thank you.
I can not get my data inside my array outside my loop..
Node.js/Mongoose :
Controllers :
exports.newsfeedContent = async (req, res, next) => {
try {
const decoded = req.decoded;
await newsfeedContentQueries(decoded, req, res, next);
} catch (e) {
console.log(['erreur'],e)
}
};
Queries :
exports.newsfeedContentQueries = function (decoded, req, res, next) {
const sub = decoded;
User.findOne({
'_id': sub
}).exec().then((user) => {
User.findOne({
'amis': user.amis
}).exec()
.then((user) => {
let tab = []
for (let index = 0; index < user.amis.length; index++) {
MessageUser.find({'userID': user.amis[index]}).exec().then((UserMessage)=>{
for (let index2 = 0; index2 < UserMessage.length; index2++) {
tab.push(UserMessage[index2].message) //<= The data I want to recover.
console.log(tab); // <= Here my array to all the data.
}
})
}
console.log(tab) // <= Here my array sends me = []
req.newsfeedUser = tab // <= what I would like to do, but my array is empty..
next()
})
}).catch((err) => {
console.log(err);
});
};
User.js :
router.get('/newsfeed', verify, newsfeedContent, (req, res) => {
console.log(req.newsfeedUser); // Here my array sends me = []
});

I successful .. I did not know that when I supply a array as a value, it would be automatically by mongoose iterated.
exports.newsfeedContentQueries = async function (decoded, req, res, next) {
const user = await User.findOne({
'_id': decoded
}).exec().then((data) => {
return data
});
const messageList = await MessageUser.find({
'userID': user.amis
}).exec().then((data) => {
return data
});
res.json(messageList);
};

Related

Sending through variables in `res.render` that are conditionally empty

I'm trying to send through multiple variables into a res.render but I'm unsure of how to do it without an undefined error. Either one or the other will have an object to pass through but never both at the same time. Here's my current code that will give an undefined error.
app.get('/', async (req, res) => {
if (req.query.search) {
const search = req.query.search
const regex = new RegExp(search, 'i')
const searchedblogposts = await BlogPost.find({title: {$regex: regex}})
console.log(searchedblogposts)
} else {
const blogposts = await BlogPost.find({})
}
res.render('index', {
blogposts,
searchedblogposts
})
})
Well, this is because one of blogPosts or searchBlogPosts remains undefined at any point of time and you're passing both in res.render . What you can do is this:
app.get('/', async (req, res) => {
let blogPosts;
if (req.query.search) {
const search = req.query.search
const regex = new RegExp(search, 'i')
blogPosts = await BlogPost.find({title: {$regex: regex}})
} else {
blogposts = await BlogPost.find({})
}
res.render('index', {
blogposts,
})
})
OR this:
app.get('/', async (req, res) => {
const blogPosts = req.query.search? await BlogPost.find({title: {$regex: new RegExp(req.query.search, 'i')}})
: await BlogPost.find({})
res.render('index', {
blogposts,
})
})

create middlware with input argument in nodejs

i want to create a misslware in nodejs for access Level , i create this middlware :
class AccessUser extends middlware {
async AccessUser(access,req, res, next) {
const getTokenFrom = (req) => {
const authorization = req.headers["authorization"];
if (authorization && authorization.toLowerCase().startsWith("bearer ")) {
return authorization.substring(7);
}
return null;
};
const token = getTokenFrom(req);
if (token) {
jwt.verify(token, "shhhhh", (err, decoded) => {
if (err) return new ForbiddenResponse().Send(res);
let permission = decoded.info.permission;
let item = permission.find((x) => x.permissionId == access);
if (!item) {
return new ForbiddenResponse().Send(res);
} else {
next();
}
});
}
}
}
i add the argument name access to input of AccessUser in this middlware :
async AccessUser(access,req, res, next)
and i want to need compare the access with x.permissionId . but it show me this error :
(node:2168) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'authorization' of undefined
this error for this line :
const authorization = req.headers["authorization"];
and i use this middlware by this :
router.post(
"/Create",
access.AccessUser("Role:Add")
);
now whats the problem ? how can i use the middllware with input argument ?????
AccessUser is not a express middleware, the method signature has to be (req,res,next).
You can get around this if you define AccessUser as a factory function (there's no need to define it async as you're not awaiting any async call):
class AccessUser {
accessUser(access) {
return function (req, res, next) {
const getTokenFrom = (req) => {
const authorization = req.headers["authorization"];
if (authorization && authorization.toLowerCase().startsWith("bearer ")) {
return authorization.substring(7);
}
return null;
};
const token = getTokenFrom(req);
if (token) {
jwt.verify(token, "shhhhh", (err, decoded) => {
if (err) return new ForbiddenResponse().Send(res);
let permission = decoded.info.permission;
let item = permission.find((x) => x.permissionId == access);
if (!item) {
return new ForbiddenResponse().Send(res);
} else {
next();
}
});
}// TODO: handle case if no token exists?
}
}
}
module.exports = AccessUser;
Then use it like this:
const AccessUserMiddleware = require('./path-to-access-middleware');
const AccessUser = new AccessUserMiddleware();
app.get('/', AccessUser.accessUser("Role:Add"));

How to convert string "req.body" to data (function)?

I make a function checkCompanyPermit with paramater companyIdSource and array allowed.
Example:
companyIdSouce: "req.body.companyId", "req.params.companyId"...
allowed: "user", "admin"...
With parameter companyIdSource as string, I want to convert it to data. It's worked if I use eval(companyIdSource) but it's bad. How can I do another?
I try use Function("return " + companyIdSource)() but it return an error: req is not defined.
const checkCompanyPermit = (companyIdSource, ...allowed) => {
return async (req, res, next) => {
try {
const companyId = eval(companyIdSource) //Bad code, change another code
const company = await Company.findById(companyId)
//... some code
} catch (error) {
next(error)
}
}
}
checkCompanyPermit("req.body.companyId", "manager")
Since you already have access to the req object in your middleware, there is no need to pass the full string representation for req.body.companyId, just the property you need to check will suffice. Use the bracket notation to access the value from req.body object i.e.
const checkCompanyPermit = (companyIdSource, allowed) => {
return async (req, res, next) => {
try {
const companyId = req.body[companyIdSource]
const company = await Company.findById(companyId)
//... some code
} catch (error) {
next(error)
}
}
}
checkCompanyPermit("companyId", "manager")
It's Working For You.
const ObjectId = require('mongodb').ObjectId;
const checkCompanyPermit = (companyIdSource, ...allowed) => {
return async (req, res, next) => {
try {
const companyId = ObjectId('companyIdSource') //Replace here new code
const company = await Company.findById(companyId)
//... some code
} catch (error) {
next(error)
}
}
}
checkCompanyPermit("req.body.companyId", "manager")

Check multi permission by middleware

I already resolve this problem. I find Express.js role-based permissions middleware and use it. It's great and I will re-write my code!
I'm want to check multi permistion but it not working for me.
I create 3 middlwares to check Permistion: requiredAuth, checkAdmin and checkCompanyManager.
Model: User: {name, permistion, isBlocked, company: {id, userPermistion}}
requiredAuth funtion will check and find signedUser, and set it tores.locals.user
const checkAdmin = (req, res, next) => {
let user = res.locals.user
if (user.permission === 2) next()
else res.json({errors: "Only admin can do this action"})
}
const checkCompanyManager = (req, res, next) => {
let user = res.locals.user
let companyId = req.body.companyId ? req.body.companyId : req.query.companyId
if (user.company.id && user.company.id.equals(companyId)
&& user.company.userPermistion === 1) next()
else res.json({errors: "Only company member can do this action"})
}
And last, I use all in router to check action block user (Only admin or company manager can block user)
router.post('/admin/block-by-ids',
requiredAuth,
checkAdmin || checkCompanyManager,
userController.blockByIds
)
But it's not working, because if checkAdmin wrong, it's break and return json, not run checkCompanyManager I can solve this problem as follows:
router.post('/admin/block-by-ids',
requiredAuth,
(req, res, next) => {
let user = res.locals.user
let companyId = req.body.companyId
if ((user.permission === 2) ||
(user.company.id && user.company.id.equals(companyId) &&
user.company.userPermistion === 1)) {
next()
} else next("Only admin or company manager can do this action")
},
userController.blockByIds
)
But it's not fun! I want only use middleware to check and do not want to write code again. How can I do this? I want an idea from you!
The || operator does not do what you think it does. It returns the first truthy value:
var a = 1 || 2; // a is 1
What you need is an OR middleware. Something like:
function or (middleware1, middleware2) {
return function (req, res, next) {
var alreadyCalledNext = false;
function resolve () {
if (!alreadyCalledNext) {
alreadyCalledNext = true;
next();
}
}
middleware1(req,res,resolve);
middleware2(req,res,resolve);
}
}
router.post('/admin/block-by-ids',
requiredAuth,
or(checkAdmin, checkCompanyManager),
userController.blockByIds
)
But the above implementation runs into another problem. Once you've sent res.json you cannot send another response. So if either checkAdmin or checkCompanyManager fails you need to stop them from sending res.json unless both fails. So you need to stub res and pass a fake res (just like what we did with next above):
function or (middleware1, middleware2) {
return function (req, res, next) {
var alreadyCalledNext = false;
function resolve () {
if (!alreadyCalledNext) {
alreadyCalledNext = true;
next();
}
}
var jsonCount = 0;
var fakeRes = {
locals: res.locals,
json: function (data) {
jsonCount ++;
if (jsonCount >= 2) { // both must fail for OR to fail
res.json(data);
}
}
}
middleware1(req,fakeRes,resolve);
middleware2(req,fakeRes,resolve);
}
}
This should work.
IMHO the solution above feels over-engineered. I would personally make checkAdmin and checkCompanyManager regular functions returning boolean then wrap them in a checkPermissions middleware:
const isAdmin = (req,res) => {
let user = res.locals.user
return user.permission === 2
}
const isCompanyManager = (req,res) => {
let user = res.locals.user
let companyId = req.body.companyId ? req.body.companyId : req.query.companyId
return user.company.id && user.company.id.equals(companyId) && user.company.userPermistion === 1
}
const checkPermissions = function (checks) {
return (req, res, next) => {
// Call next if any check passes:
for (let i=0; i<checks.length; i++) {
if (checks[i](req,res)) return next();
}
res.json({errors: "You don't have authorization to do this action"})
}
}
router.post('/admin/block-by-ids',
requiredAuth,
checkPermissions([isAdmin, isCompanyManager]),
userController.blockByIds
)

Node.js Call a method after another method is completed

I would like to call my "app.get('/news/news-desc', (req, res)" method after "app.get('/news/api/:newsName', function(req, res)" is completed.
Here is my code:
let articleUrlArray = [];
app.get('/news/api/:newsName', function(req, res) {
const API_KEY = 'example';
let data = '';
const techCrunchURL = `https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${API_KEY}`
switch(req.params.newsName) {
case 'tech-crunch':
request(techCrunchURL, function(err, response, html) {
let formattedData = JSON.parse(response.body);
for(let i = 0; i < formattedData.articles.length; i++) {
articleUrlArray.push(formattedData.articles[i].url);
}
data = response.body;
res.setHeader('Content-Type', 'application/json');
res.send(data);
});
break;
default:
data = 'Please type in correct news source';
break;
}
})
const checkBody = res => (err, response, html) => {
const $ = cheerio.load(html);
const articleContent = $('.article-content').children('p')
const bodyOne = articleContent.eq(0).text()
const bodyTwo = articleContent.eq(1).text()
const isExtensive = bodyOne.split(' ').length > 50
res(isExtensive ? { bodyOne } : { bodyOne, bodyTwo })
}
const getArticle = article => new Promise(res => request(article, checkBody(res)))
app.get('/news/news-desc', (req, res) => {
Promise.all(articleUrlArray.map(getArticle)).then(data => res.send(JSON.stringify(data)))
})
As you can see, the first method calls the "newsapi.org" and gets 10 articles. Then it would only extract the urls of those articles and push them into articleUrlArray.
After the urls have been pushed into the articleUrlArray, it would look like this:
let articleUrlArray = [ 'https://techcrunch.com/2018/05/19/shared-housing-startups-are-taking-off/',
'https://techcrunch.com/2018/05/19/shared-housing-startups-are-taking-off/',
'https://techcrunch.com/2018/05/19/my-data-request-lists-guides-to-get-data-about-you/',
'https://techcrunch.com/2018/05/19/siempos-new-app-will-break-your-smartphone-addiction/',
'https://techcrunch.com/2018/05/19/la-belle-vie-wants-to-compete-with-amazon-prime-now-in-paris/',
'https://techcrunch.com/2018/05/19/apple-started-paying-15-billion-european-tax-fine/',
'https://techcrunch.com/2018/05/19/original-content-dear-white-people/',
'https://techcrunch.com/2018/05/19/meet-the-judges-for-the-tc-startup-battlefield-europe-at-vivatech/',
'https://techcrunch.com/2018/05/18/nasas-newest-planet-hunting-satellite-takes-a-stellar-first-test-image/',
'https://techcrunch.com/video-article/turning-your-toys-into-robots-with-circuit-cubes/',
'https://techcrunch.com/2018/05/18/does-googles-duplex-violate-two-party-consent-laws/' ];
It would just be filled up with urls.
Then the second method, would use the filled up articleUrlArray to do its own thing.
However, currently for my code, the second method runs first before the articleUrlArray has been filled up.
I would like to run the second method after the first method completes and the articleUrlArray has been filled up with urls.
Could you please help me with this?
let articleUrlArray = [];
const addArticleUrl = url => articleUrlArray.push(url)
const checkBody = res => (err, response, html) => {
const $ = cheerio.load(html);
const articleContent = $('.article-content').children('p')
const bodyOne = articleContent.eq(0).text()
const bodyTwo = articleContent.eq(1).text()
const isExtensive = bodyOne.split(' ').length > 50
res(isExtensive ? { bodyOne } : { bodyOne, bodyTwo })
}
const getArticle = article => new Promise(res => request(article, checkBody(res)))
const newsDescMiddleware = app.get('/news/news-desc', (req, res) => {
Promise.all(articleUrlArray.map(getArticle)).then(data => res.send(JSON.stringify(data)))
})
const techCrunch = res => url => request(url, (err, response, html) => {
let formattedData = JSON.parse(response.body);
formattedData.articles.forEach(article => addArticleUrl(article.url))
res(response.body)
})
const getNewsByName = (newsName, url) => new Promise((res, reject) => ({
'tech-crunch': techCrunch(res)(url)
}[newsName])) || reject()
const getNewsByNameMiddleware = (req, res) => {
const API_KEY = 'example';
const techCrunchURL = `https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${API_KEY}`
getNewsByName(req.params.newsName, url)
.then(body => {
res.setHeader('Content-Type', 'application/json');
res.send(body)
})
.catch(() => res.send('Please type in correct news source'))
}
app.get('/news/api/:newsName', getNewsByNameMiddleware, newsDescMiddleware)
Here, I made you some middlewares.
I am assuming that you don't need the response of the previous middleware.
I like to split the code by its responsibilities and write it functionally.
You can separate the core logic of the first route to a function and re-use it in both places, if you please. however you still need to provide newsName parameter to GET '/news/news-desc' endpoint.
Example for your code.
let articleUrlArray = [];
function getNewsNames(newsName, callback) {
const API_KEY = 'example';
let data = '';
const techCrunchURL = `https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${API_KEY}`
switch (newsName) {
case 'tech-crunch':
request(techCrunchURL, function (err, response, html) {
let formattedData = JSON.parse(response.body);
for (let i = 0; i < formattedData.articles.length; i++) {
articleUrlArray.push(formattedData.articles[i].url);
}
data = response.body;
callback(null, data);
});
break;
default:
data = 'Please type in correct news source';
callback('Error', data);
break;
}
}
app.get('/news/api/:newsName', function (req, res) {
getNewsNames(req,params.newsName, (err, data) => {
if (!err) {
res.setHeader('Content-Type', 'application/json');
}
return res.send(data);
})
})
const checkBody = res => (err, response, html) => {
const $ = cheerio.load(html);
const articleContent = $('.article-content').children('p')
const bodyOne = articleContent.eq(0).text()
const bodyTwo = articleContent.eq(1).text()
const isExtensive = bodyOne.split(' ').length > 50
res(isExtensive ? { bodyOne } : { bodyOne, bodyTwo })
}
const getArticle = article => new Promise(res => request(article, checkBody(res)))
app.get('/news/news-desc/:newsName', (req, res) => {
getNewsNames(req.params.newsName, (err, data) => {
// by now, the articleUrlArray array will be filled
Promise.all(articleUrlArray.map(getArticle)).then(data => res.send(JSON.stringify(data)))
})
})

Categories

Resources