Node Js Chaining the response - node.js

I am trying to make a db call insides a loop and want to add that data into one object and then send this object to user, but I am getting only a empty object at the user end.
I already checked this one asynchronous response into a loop in javascript NodeJS
router.get('/collection', (req, res) => {
const Data = {}
Section.find({})
.then(sections => {
sections.map(section => {
let id = section._id;
Product.find({section: id})
.then(products => {
// console.log(products)
Data[section.title] = {
title: section.title,
routeName: section.title,
id,
items: products
}
console.log(Data)
})
.catch(err => {
return res.status(500).json(err)
})
})
return res.json(data)
})
.catch(err => {
return res.status(500).json(err)
})
})
I want the output to be like :-
{
food : {
items: [...]
},
vegetable: {
items: [...]
}
}
food and vegetable are keys which will be obtained from a Database Call and items in each keys are returned from a seperate call to database.

return res.json(data) is executed before any of the mapped Product-promises are resolved (also there's a typo since you're returning data instead of Data). One way to do this is to map the find-promises and to use Promise.all on the mapped array. Something like:
router.get('/collection', (req, res) => {
const Data = {}
Section.find({})
.then(sections => {
const sectionProductPromises = sections.map(section => {
let id = section._id;
return Product.find({
section: id
})
.then(products => {
Data[section.title] = {
title: section.title,
routeName: section.title,
id,
items: products
}
});
});
return Promise.all(sectionProductPromises);
})
.then(() => {
res.json(Data)
})
.catch(err => {
res.status(500).json(err)
});
});

Related

Writing Mocha Chai Test cases for NodeJs Controllers

I am new to unit testing. I am trying to write test cases for controller.js files for nodejs microservices files. I am unable to understand where I am going wrong. Always throws an error "TypeError: Cannot read property 'empId' of undefined" for 2 of these properties.
This is the controller code:
const crmgDetails = db.crmgResource_details;
const employeeProposal = db.employee_Proposal;
const Op = db.Sequelize.Op;
const raDetails = db.crmgRaSheet_entity;
let results = [];
Sequelize = require('sequelize')
exports.findOne = (req, res) => {
console.log(req.body.empId);
crmgDetails.findAll({where: {
resEmployeeNumber: req.body.empId
}
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving tutorials."
});
});
};
exports.findMatchingDemandsForRmg = (req,res) => {
let proposedDemands = [];
employeeProposal.findAll({
where: {
emp_id: req.body.empId,
demandSbu : req.body.sbu
}
}).then(proposedEmployee => {
console.log('proposedEmployee',proposedEmployee);
if(proposedEmployee.length === 0){
crmgDetails.findAll({
where: {
resEmployeeNumber: req.body.empId,
demandSbu: req.body.sbu
}
}).then(matchingDemands => {
console.log('matchingDemands ',matchingDemands)
proposedDemands = matchingDemands;
})
}
else{
console.log("crmg Employee")
console.log(proposedEmployee)
for(let employee of proposedEmployee){
crmgDetails.findOne({
where: {
demandUid: employee.demandUid,
resEmployeeNumber: req.body.empId,
demandSbu: req.body.sbu
}
}).then( crmgProposed=> {
proposedDemands.push(crmgProposed);
})
}
}
setTimeout(() => {
console.log(proposedDemands)
res.send(proposedDemands);
}, 3000);
}).catch((err)=>{
res.status(500).send({
message:
err.message || "Some error occurred while retrieving tutorials."
});
})
}
exports.getResourceAllocationDetails = (req,res) => {
employeeProposal.findAll({
include: {
model: raDetails
},
where: Sequelize.and(
{activeFlag : true},
Sequelize.or({status:"Accepted By RMG"},
{status:"Rejected"}
))
}).then(employees => {
res.send(employees)
})
}
This is the test file I tried to write without my head:
const CrmgRaSheetModel = require('../controllers/crmgResource_Details.controller')
describe('Check for succcessful fetech API call', () => {
it('property getResourceAllocationDetails should be called', async () => {
CrmgRaSheetModel.getResourceAllocationDetails((res) => {
expect(res).to.be.an('object')
return res.json()
})
});
it('property findMatchingDemandsForRmg should be called', async () => {
CrmgRaSheetModel.findMatchingDemandsForRmg((res) => {
expect(res).to.be.an('object')
return res.json()
})
});
it('property findOne should be called', async () => {
CrmgRaSheetModel.findOne((res) => {
expect(res).to.be.an('object')
return res.json()
})
})
})
from test file you are calling controller method with only res, so no chance to send your input as your body.
So pass req,res both and pass your input value in req

Optimization requests/responses in react/nodejs

so I have problem with optimizing code, accordingly to DRY rule.
I have 2 requests, 2 responses, 2 GETS, 2 POSTS,
I think I should use loop, which changing the number of iterator, but I have problem with syntax.
Here is my fetch from axios:
componentDidMount() {
fetch('http://localhost:3001/getcounterEbook1')
.then(response => {
console.log(response);
return response.json();
})
.then(count => {
console.log(count);
console.log(count.count);
this.setState({counterebook1: count.count});
}).catch(err => {
});
fetch('http://localhost:3001/getcounterEbook2')
.then(response => {
console.log(response);
return response.json();
})
.then(count => {
console.log(count);
console.log(count.count);
this.setState({counterebook2: count.count});
}).catch(err => {
});
}
Here is handleClick1,2 function:
handleClick1(e) {
console.log("Sending ...")
let data = {
clicked: true,
}
axios.post('http://localhost:3001/counterEbook1', data)
.then( res => {
console.log('ok')
})
.catch( () => {
console.log('Message not sent')
})
}
handleClick2(e) {
console.log("Sending ...")
let data = {
clicked: true,
}
axios.post('http://localhost:3001/counterEbook2', data)
.then( res => {
console.log('ok')
})
.catch( () => {
console.log('Message not sent')
})
}
How can I transform it into loop?
One possibility is to use an Object, call it myObject, and use map like so:
const myObject = {
'counterebook1' : 'http://localhost:3001/getcounterEbook1',
'counterebook2' : 'http://localhost:3001/getcounterEbook2',
};
class Foo extends Component {
....
for (let [key, url] of Object.entries(myObject)) {
fetch(url)
.then(response => {
console.log(response);
return response.json();
})
.then(count => {
console.log(count);
console.log(count.count);
this.setState({key: count.count});
}).catch(err => {
});
}
...
You can consolidate handleClicks by first retrieving the source that triggered the event, and then using that same object
handleClick = (e) => {
console.log("Sending ...")
const url = myObject[e.target.id];
let data = {
clicked: true,
}
axios.post(url, data)
.then( res => {
console.log('ok')
})
.catch( () => {
console.log('Message not sent')
})
}
Note that we're pulling the id prop frome.targetand referencing the key/value pair inmyObject. This means that the element rendered must have theid` prop set to some key in myObject:
For example:
<button onClick={this.handleClick} id={key}>
`Button for ${key}`
</button>
Hope this help!

How to return the result of multiple promises in an express route?

Below is a route to retrieve information for my user's dashboard. I'm trying to avoid nesting all the promises (i hear its called callback hell) and still get the correct outcome. I'm assuming the code snippet below doesn't return anything because at run time it's blowing past all the promises and simply returning the three empty arrays before the promises have a chance to resolve. I tried to use async/await with no success and am not sure if that's even what I need. In this given situation what is the best practice for handling these promises and returning all the results all at once?
// #route GET api/user/dashboard
// #desc Retrieve all of the user's dashboard info
// #access Private
router.get("/dashboard",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const donations = [];
const events = [];
const teams = [];
req.user.donations.forEach(donation_id => {
Donation.findOne({ _id: donation_id })
.then(donation => {
if (donation) {
donations.push(donation);
}
})
.catch(err => console.log(err));
});
req.user.events.forEach(event_id => {
Event.findOne({ _id: event_id })
.then(event => {
if (event) {
events.push(event);
}
})
.catch(err => console.log(err));
});
req.user.teams.forEach(team_id => {
Team.findOne({ _id: team_id })
.then(team => {
if (team) {
teams.push(team);
}
})
.catch(err => console.log(err));
});
return res.status(200).json({ data: { donations, events, teams } });
}
);
Use Promise.all to wait for all of the Promises to resolve first. You'll need to map each donation (and event, and team) to a Promise so you can call Promise.all on each group of Promises, and then you need to call Promise.all on those three groups.
Because the donations, events, and teams all need something similar (extract the _id property from an array, call .findOne with that _id, and filter all results), it would be good to abstract that all away into a single function, to keep your code DRY:
const getItems = async (arr, finder) => {
const foundItems = await Promise.all(
arr.map(({ _id }) => finder.findOne({ _id }))
);
return foundItems.filter(Boolean);
};
async (req, res) => {
const [donations, events, teams] = await Promise.all([
getItems(req.user.donations, Donation),
getItems(req.user.events, Event),
getItems(req.user.teams, Team),
])
.catch(err => console.log(err));
return res.status(200).json({ data: { donations, events, teams } });
};
Without async and await:
const getItems = (arr, finder) => (
Promise.all(
arr.map(({ _id }) => finder.findOne({ _id }))
)
.then((foundItems) => foundItems.filter(Boolean))
);
(req, res) => {
Promise.all([
getItems(req.user.donations, Donation),
getItems(req.user.events, Event),
getItems(req.user.teams, Team),
])
.then(([donations, events, teams]) => {
res.status(200).json({ data: { donations, events, teams } });
})
.catch(err => console.log(err));
};

POST request for many to many in express

I have 2 tables: user and material which have a m:m relationship. The intersection entity is journalMaterials. I am trying to send a POST request to insert into journalMaterials. Also, this table has 2 attributes: recycledQuantity and recycleDate. I tried something, but if i insert with a materialId which doesn't exist it doesn't give me "not found".
app.post('/users/:uid/materials/:mid', (req, res, next) => {
User.findById(req.params.uid)
.then((user) => {
if (user){
let journalMaterial = req.body
journalMaterial.userId = user.id
Material.findById(req.params.mid)
.then((material) => {
if (material){
journalMaterial.materialId = material.id
return JournalMaterial.create(journalMaterial)
}
else{
res.status(404).send('not found')
}
})}
else{
res.status(404).send('not found')
}
})
.then(() => {
if (!res.headers){
res.status(201).json('created')
}
})
.catch((err) => next(err))
})
I've solved it. Here is the correct code.
app.post('/users/:uid/materials/:mid', (req, res, next) => {
const { uid, mid } = req.params;
Promise.all([
User.findById(uid),
Material.findById(mid)
])
.then(([user, material]) => {
if (user && material) {
let journalMaterial = req.body
journalMaterial.userId = user.id
journalMaterial.materialId = material.id
res.status(201).json('created')
return JournalMaterial.create(journalMaterial)
}
res.status(404).send('not found')
})
.catch(err => next(err));
})
Slightly re-wrote this to make it a bit more readable. Removed your nested promise calls... (let's not dive into promise hell when they try to get rid of callback hell..)
app.post('/users/:uid/materials/:mid', (req, res, next) => {
const { journalMaterial } = req.body;
const { uid, mid } = req.params;
Promise.all([
User.findById(uid),
Material.findById(mid)
])
.then(([user, material]) => {
if (user && material) {
journalMaterial.userId = user.id;
journalMaterial.materialId = material.id;
return JournalMaterial.create(journalMaterial);
}
res.status(404).send('not found');
})
.then(() => {
if (!res.headers) {
res.status(201).json('created');
}
})
.catch(err => next(err));
});
Your check against if(user) currently passes. It seems that if that's what is happening, you're always getting an object back. Lots of databases generally don't simply return a null or false value, but rather an object with a bunch of meta data. In that object is generally the data you requested (ie, user.data.id, but it may be that user.data is NULL). Can you verify what the exact contents of Users is? It's evaluating to truthy, thus it must have something in it.

Display all collections in mongodb+express

This is my code for sending data to a database:
app.post('/thanks', function(req, res) {
if (atendees.checkin === req.body.dbstring) {
dbConn.then(client => {
delete req.body._id;
const db = client.db('mydata')
db.collection(atendees.checkin).insertOne(req.body);
})
(...)
This is how I display on the page after clicking on a href link:
app.get('/view-feedbacks', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.collection(atendees.checkin).find({}).toArray().then(function(feedbacks) {
res.status(200).json(feedbacks);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
});
That works fine. How can I do something similar to display all collections from the database instead of just the individual ones?
This is what I tried to do:
app.get('/view-history', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.listCollections().toArray().then(function(collInfos) {
res.status(200).json(collInfos);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
});
But it just gives me the name of each collection. I want to show all collections and all of their elements.
Edit: my question is different from this one: MongoDB Show all contents from all collections .I'm trying to do this on express.js, not on the terminal
Edit2: Using db.collection:
app.get('/view-history', function(req, res) {
dbConn.then(client => {
const db = client.db('mydata')
db.collections().then(function(feedbacks) {
res.status(200).json(feedbacks);
atendees.checkin = ' '
}).catch(err => {
throw(err);
})
});
But this gives the error: TypeError: converting circular structure to JSON
With async/await, this could be done:
app.get('/view-history', async (req, res) => {
try {
const client = await dbConn;
const db = client.db('mydata');
let collections = await db.collections();
let documents = await Promise.all(collections.map(async (collection) => {
let documents = await collection.find({}).toArray();
return Promise.resolve([collection.collectionName, documents]); // Retain collectionName
}));
// Format into an object that looks like `collectionName: documents`
let formatted = documents.reduce((obj, collection) => {
obj[collection[0]] = collection[1];
return obj;
}, {});
res.json(formatted);
} catch (e) {
console.error(e);
res.sendStatus(500);
}
});
A Promise-only approach:
app.get('/view-history', (req, res) => {
dbConn.then((client) => {
const db = client.db('mydata');
return db.collections();
}).then((collections) => {
return Promise.all(collections.map((collection) => {
return new Promise((resolve, reject) => {
collection.find({}).toArray().then((documents) => {
resolve([collection.collectionName, documents]);
}).catch(reject);
});
}));
}).then((documents) => {
let formatted = documents.reduce((obj, collection) => {
obj[collection[0]] = collection[1];
return obj;
}, {});
res.json(formatted);
}).catch((e) => {
console.error(e);
res.sendStatus(500);
});
});
The main reason this code is unnecessarily verbose is because instead of just returning a big array filled with arrays of documents, you probably want an object that retains the name of the collection, like so:
{
collection1: [...documents...],
collection2: [...documents...],
...
}
Instead of:
[
[...documents...],
[...documents...],
...
]
If you do want just a big array of each collection without caring about the names of the collections, it becomes much simpler:
async/await version:
app.get('/view-history', async (req, res) => {
try {
const client = await dbConn;
const db = client.db('mydata');
let collections = await db.collections();
let documents = await Promise.all(collections.map((collection) => collection.find({}).toArray()));
res.json(documents);
} catch (e) {
console.error(e);
res.sendStatus(500);
}
});
Promise-only version:
app.get('/view-history', (req, res) => {
dbConn.then((client) => {
const db = client.db('mydata');
return db.collections();
}).then((collections) => {
return Promise.all(collections.map((collection) => collection.find({}).toArray()));
}).then((documents) => {
res.json(documents);
}).catch((e) => {
console.error(e);
res.sendStatus(500);
});
});
Have you tried just db.collections()? If that also doesn't give what you need, you might have to invoke db.collection(<name>) on each of the names you get from listCollections.

Resources