I have a PUT request that I'm trying to have hit the backend, but for some reason, it never reaches it. What's odd is the if(req.body.bidderId){} hits no problem, but not the if(req.body.watchingGroup){}
The watching angular service uses identical code to the bidderId so I don't know what's different between the two where only one would reach the endpoint? Whats wrong with the addToWatchList call? I did testing and both console.log statements in code block return the correct value. So the data is ready to be passes, but is never received.
console.log("MADE IT TO LISTINGS BACKEND");
never outputs for watchingGroup scenario
watching.service.ts
addToWatchList(id: string, watchingGroup: string[]) {
const watching: Watching = {
id: id,
watchingGroup: watchingGroup
};
console.log("Here are the values being passed to backend");
console.log(watching.id);
console.log(watching.watchingGroup);
console.log(watching);
return this.http.put(`http://localhost:3000/api/listings/${watching.id}`, watching,
);
}
app.js
app.put("/api/listings/:id", (req, res) => {
console.log("MADE IT TO LISTINGS BACKEND");
if (req.body.biddingGroup) {
console.log("bidding has been received");
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
currentBid: req.body.currentBid,
lastBidTimeStamp: Date.now(),
bidderId: req.body.bidderId,
auctionEndDateTime: req.body.auctionEndDateTime,
biddingGroup: req.body.biddingGroup,
lastBidTimeStamp: req.body.lastBidTimeStamp
},
function(err, docs) {
if (err) res.json(err);
else {
console.log(docs);
}
}
);
}
if (req.body.watchingGroup) {
console.log("watching has been received");
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
watchingGroup: req.body.watchingGroup
},
function(err, docs) {
if (err) res.json(err);
else {
console.log(docs);
}
}
);
}
});
addToWatchList
addToWatchList(
auctionId: string,
watchingGroup: string[]
) {
this.watchItStatus = true;
this.userId = localStorage.getItem("userId: ");
var unique = watchingGroup.filter(function(elem, index, self) {
return index === self.indexOf(elem);
});
this.uniqueResult = unique;
watchingGroup.push(this.userId);
this.watchListService.addToWatchList(auctionId, this.uniqueResult);
}
As i suspected you're not subscribing to it. It's weird but you need to subscribe to it.
this.watchListService.addToWatchList(auctionId, this.uniqueResult).subscribe(
(res) => {
// Handle success response
console.log("SUCCESS");
},
(err) => {
// Handle error response
console.log("ERROR");
}
);
Related
I'm trying to learn how to implement transactions for a simple app in Mongoose with express for a RESTful API.
I've managed to put together a small one change function using the mongoDB session.withTransaction() helper function.
This code block runs with no errors and the document appears in the DB as I would expect.
function createCourse() {
try {
return Course.createCollection()
.then(() => mongoose.startSession())
.then(session => {
session.withTransaction(() => {
return Course.create(
[
{
author: "Larry",
category: "web",
tags: ["tag1", "tag2"],
isPublished: false,
price: 15,
name: "Tester series 3"
}
],
{ session }
);
});
});
} catch (error) {
console.error(error);
}
}
I feel like I'm missing something with how to structure my operations with this function.
Putting everything together into an Express POST method, looks like this works but I'd appreciate a code-review of it.
Also, how can I go about testing that the code is correctly handling errors/roll-backs? Put session.abortTransaction(); somewhere within the sesson.withTransaction() block?
router.post("/", async (req, res) => {
try {
validateRental(req.body);
} catch (error) {
return res.status(400).send(error.details[0].message);
}
let customer = null;
try {
customer = await Customers.findById(req.body.customerId);
} catch (err) {
return res.status(400).send("Invalid customer.");
}
let movie = null;
try {
movie = await Movies.findById(req.body.movieId);
} catch (err) {
return res.status(400).send("Invalid movie.");
}
if (movie.numberInStock === 0)
return res.status(400).send("Movie not in stock.");
try {
mongoose.startSession().then(session => {
session.withTransaction(async () => {
let rental = new Rentals({
renter: {
_id: customer._id,
name: customer.name,
phone: customer.phone,
isGold: customer.isGold
},
movie: {
_id: movie._id,
title: movie.title,
dailyRentalRate: movie.dailyRentalRate
}
});
await rental.save({ session });
movie.numberInStock--;
await movie.save({ session });
res.send(rental);
});
});
} catch (err) {
res.status(500).send(err.message);
}
});
I am trying to replace a string in url . Here is image of it
in this image I want to replace lssplalpha with lssplprod which are in pics array. For that I created an api . Here is a code
apiRoutes.get('/SchoolListing_lssplalpha_Replace',function(req, res) { schoolListModel.find({},function(err,check){
if(err){
return console.log(err);
}
else{
for(var i=0;i<check.length;){
var f=0;
for(var j=0;j<check[i].pics.length;j++){
f++;
var newTitle = check[i].pics[j].replace("lssplalpha","lsslprod");
check[i].pics[j] = newTitle;
console.log("after change",check[i].pics[j]);
check[i].save(function (err) {
if(err) {
console.error('ERROR!');
}
});
}
if(j==check[i].pics.length&&j==f){
i++;
}
}
console.log("i value",i);
console.log("check length",check.length);
if(i==check.length){
return res.json({status:true,message:"Updated Schools"}); }
}
});
});
I am getting success response . When I go and check database nothing changed in db. To know the reason I write log of it. When I see logs it was replacing correctly. But I didn't understand why those are not reflecting in database? Here is an image of log in console
Please help me to come out of this
The issue here is you are running a for loop (synchronous) where you are calling the model.save() operation which is asynchronous and the loop keeps iterating but the results of the async calls come later. The process of saving a database item in an array takes some time and Node.js knows this, so it starts the update and then just moves on trying to update the next item in the array. Once the write operation is complete a callback function is run, but by that point the loop has completed and there is no way to know which items finish in what order.
You could use the Bulk Write API to update your models. This allows you to sends multiple write operations to the MongoDB server in one command. This is faster than sending multiple independent operations (like) if you use create()) because with bulkWrite() there is only one round trip to MongoDB.
The following examples show how you can use the bulkWrite.
Using async/await:
apiRoutes.get('/SchoolListing_lssplalpha_Replace', async (req, res) => {
try {
let ops = [];
const docs = await schoolListModel.find({}).lean().exec();
docs.forEach(doc => {
const pics = doc.pics.map(pic => pic.replace("lssplalpha", "lsslprod"));
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { pics }
}
}
});
});
const result = await schoolListModel.bulkWrite(ops);
console.log('Bulk update complete.', result);
res.status(200).json({
status: true,
message: "Updated Schools"
});
} catch (err) {
res.status(400).send({
status: false,
message: err
});
}
});
Using Promise API:
const bulkUpdate = (Model, query) => (
new Promise((resolve, reject) => {
let ops = [];
Model.find(query).lean().exec((err, docs) => {
if (err) return reject(err);
docs.forEach(doc => {
const pics = doc.pics.map(pic => (
pic.replace("lssplalpha", "lsslprod")
));
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": {
"$set": { pics }
}
}
});
if (ops.length === 500) {
Model.bulkWrite(ops).then((err, result) => {
if (err) return reject(err);
ops = [];
resolve(result);
});
}
});
if (ops.length > 0) {
Model.bulkWrite(ops).then((err, result) => {
if (err) return reject(err);
resolve(result);
});
}
});
})
);
apiRoutes.get('/SchoolListing_lssplalpha_Replace', (req, res) => {
bulkUpdate(schoolListModel, {}).then(result => {
console.log('Bulk update complete.', result);
res.status(200).json({
status: true,
message: "Updated Schools"
});
}).catch(err => {
res.status(400).send({
status: false,
message: err
});
});
});
You are running asynchronous call model.save() in for loop(synchronous)
To make your for loop synchronous you can use for...of loop which works asynchronous, also you will not need to add multiple checks like you have done in your code.
Try following code, it will work
apiRoutes.get('/SchoolListing_lssplalpha_Replace', function (req, res) {
schoolListModel.find({},function (err, check) {
if (err) {
return console.log(err);
}
else {
for (let checkObj of check) {
let newPicArr=[];
for (let pic of checkObj.pics) {
pic = pic.replace("lssplalpha", "lsslprod");
newPicArr.push(pic);
}
checkObj.pics=newPicArr;
checkObj.save(function (err) {
if (err) {
console.error('ERROR!');
}
});
}
return res.json({ status: true, message: "Updated Schools" });
}
});
});
I want to display chatbot and facebook data at the same time. how to display it? because when I try to run in the browser but it does not appear anything. I've tried to look it up on stackoverflow but did not get the right reference
route.js
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
CB.getAllRecords( function(e, chatbot) {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : chatbot });
});
FBM.getAllRecords( function(e, facebook) {
res.render('cpanel/facebook', { udata : req.session.user, facebook : facebook });
});
}
});
facebook.js
var facebook = db.collection('facebook');
exports.addNewFacebook = function(newData, callback) {
facebook.findOne({accesstoken:newData.accesstoken}, function(e, o) {
if (o) {
callback('accesstoken-taken');
} else {
facebook.insert(newData, {safe: true}, callback);
}
});
}
exports.getAllRecords = function(callback) {
facebook.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
chatbot.js
var chatbot = db.collection('chatbot');
exports.addNewChatBot = function(newData, callback) {
chatbot.insert(newData, {safe: true}, callback);
}
exports.getAllRecords = function(callback) {
chatbot.find().toArray(
function(e, res) {
if (e) callback(e)
else callback(null, res)
}
);
}
The easier way to manage asynchronous operations in node.js, especially when you have more than one operation that you want to coordinate is to use Promises instead of plain callbacks. And, fortunately, mongodb supports a promise-based interface for all its asynchronous operations now.
So, first change your methods to return a promise instead of taking a callback:
var chatbot = db.collection('chatbot');
exports.getAllRecords = function() {
return chatbot.find().toArray();
}
var facebook = db.collection('facebook');
exports.getAllRecords = function() {
return facebook.find().toArray();
}
Then, you can use those promises with Promise.all() to coordinate:
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
Promise.all([CB.getAllRecords(), FBM.getAllRecords()]).then(results => {
res.render('cpanel/facebook', { udata : req.session.user, chatbot : results[0], facebook: results[1]});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});
For a call to just a single function that returns a promise, you can use .then():
app.get('/cpanel/facebook', function(req, res) {
if (req.session.user == null) {
res.redirect('/cpanel/login');
} else {
FBM.getAllRecords().then(results => {
res.render('cpanel/facebook', { udata : req.session.user, facebook: results});
}).catch(err => {
// render some error page here
res.sendStatus(500);
});
}
});
I am a little confused with the req, res parameters for node and the best way to handle these when I'm using asynchronous calls. A function I currently have is supposed to add an Item to my DB, update some DB models accordingly, and then send a response saying the updates were successful. However, the functions that this function asynchronously calls can send an erroneous response if an error happens. If this happens, I get the error Can't set headers after they are sent, since I am trying to call res.send twice. Would appreciate someone's help in figuring out a more optimal way to handle errors. Thanks!
the main function:
item.author = req.user._id;
item.description = req.query.description;
item.rating = req.query.rating;
item.save()
.then(result => {
// update user's items
UserController.updateItems(req.user._id, result._id);
ItemController.updateItemRating(req.query.outingId, req.query.rating, res);
res.send(result);
})
.catch(error => {
res.send(error);
});
updateItemRating:
export const updateItemRating = (itemId, rating, res) => {
Item.findOne({ _id: itemId }).exec((err, item) => {
if (item === undefined || item === null) {
return res.status(404).send('Item not found; check item ID');
}
Item.findOneAndUpdate(
{ _id: itemId },
{ $set: { rating: rating },
},
(error, item) => {
if (error) {
res.status(404).send('Error updating item with new rating');
}
});
});
};
updateItems:
export const updateItems = (userId, itemId) => {
User.findOneAndUpdate(
{ _id: userId },
{ $push: { items: [itemId] } },
(err, user) => {
console.log('user' + user);
if (err) {
console.log('got an error in updateItems');
}
});
};
Function call to updateItems and updateItemRating both are asynchronous . Response send is getting called multiple time , and also not sure which method send is getting called first . To fix you problem I can suggest you to apply below technique :
Callback : You can pass callback as argument which will do res.send and same callback you can call on error or success condition .
UserController.updateItems(req.user._id, result._id,function(status,message){res.status(status).send(message);});
You can update item rating method like :
export const updateItemRating = (itemId, rating, callback) => {
Item.findOne({ _id: itemId }).exec((err, item) => {
if (item === undefined || item === null) {
callback(404,'Item not found; check item ID');
}
Item.findOneAndUpdate(
{ _id: itemId },
{ $set: { rating: rating },
},
(error, item) => {
if (error) {
callback(404,'Error updating item with new rating');
}else{
callback(200);
}
});
});
};
Async Module : You can synchronize you method call using this module.
Instead of having your update functions send the result, you should throw() an error, that way your outer function will catch the error and you can use that to return it.
Another way to think about this is that your outer function is handling the success case with a res.send(), so it should also be responsible for the res.send of the error case.
The less your database layer knows about the caller the easier it will be to reuse our code.
Create a custom error type to encapsulate the 404:
function NotFoundError(message) {
this.message = (message || "");
}
NotFoundError.prototype = new Error();
Then use that in your inner function:
export const updateItemRating = (itemId, rating, res) => {
Item.findOne({ _id: itemId }).exec((err, item) => {
if (item === undefined || item === null) {
throw new NotFoundError('Item not found; check item ID');
}
Item.findOneAndUpdate(
{ _id: itemId },
{ $set: { rating: rating },
},
(error, item) => {
if (error) {
throw new NotFoundError('Error updating item with new rating');
}
});
});
};
And your main becomes:
item.save()
.then(result => {
// update user's items
UserController.updateItems(req.user._id, result._id);
ItemController.updateItemRating(req.query.outingId, req.query.rating, res);
res.send(result);
})
.catch(error => {
if (error instanceof NotFoundError) {
res.status(404).send(error.message);
}
else {
res.send(error);
}
});
i'm new with node and mongoose.
I do multiple queries to find my datas so I use async to populate an array and then send it back the the client.
I try to get all the stats from the games I found.
Here is my Schema : [Team] 1 ---> N [Game] 1 ---> N [Stat]
var json = []; // The variable I will send back
async.waterfall([
function( team, next ) { // Find all games from a team (with its id)
dbGame.find({ team_id: req.params._id }).exec( next );
},
function( games, next ) {
for (key in games) { // For all games, finds their stats
dbStat.find({ game_id: games[key]._id }).exec(function(err, stats) {
json.push({
_id : games[key]._id,
stats : stats
}); // The json I need to return
});
if ( Number(key) === Number(games.length -1) ) {
next(json);
}
}
}
], function () {
res.status(200);
res.json(json);
});
The variable json send is always empty because of the asynchronous and I don't know how to have the full one.
update #1
Ok it's cool it's working.
But just a trouble, all the stats are in all json in the object :
Juste a problem : The stats from all games are stored in all json.
[{
_id:"57cb15f73945ac9808fe5422",
stats:Array[13]
}, {
_id:"57ff76fd78ca91a17b25dd1b",
stats :Array[13]
}]
But I think i can sort then. Here's my code now :
async.waterfall([
function(next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
jsonResponse(res, 200, json);
});
Try below:
var json = []; // The variable I will send back
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function() {
res.status(200);
res.json(json);
});
Comment if it has any issue or I have missed something as I have not tested it.
You are not passing the results of the first find to the next step of waterfall, and when you call next(json); you are passing json as error, as the first argument is an error. Try:
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next(err, games));
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
res.status(200);
res.json(json);
});