I have the following test in mocha that yields "Uncaught AssertionError: undefined == 'Ernest'. I had a sneaking suspicion that the test was actually finding the song instance that is created in the top of the test, and I believe this proves it. That being said, I'm not sure how to fix it.
This is an api written for a MEAN stack app, with mongoose as the ODM
test.js
it('can save a song', function(done) {
Song.create({ title: 'saveASong' }, function(error, doc) {
assert.ifError(error);
var url = URL_ROOT + '/create/song/saveASong';
superagent.
put(url).
send({
title: 'saveASong',
createdBy: 'Ernest'
}).
end(function(error, res) {
assert.ifError(error);
assert.equal(res.status, status.OK);
Song.findOne({}, function(error, song) {
assert.ifError(error);
assert.equal(song.title, 'saveASong');
assert.equal(song.createdBy, 'Ernest');
done();
});
});
});
});
my route:
//PUT (save/update) song from the create view
api.put('/create/song/:title', wagner.invoke(function(Song) {
return function(req, res) {
Song.findOne({ title: req.params.title}, function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
song.save(function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
return res.json({ song: song });
});
});
};
}));
UPDATE: I put in a console.log(res.body) right after the "end", and it did not include the "createdBy: Ernest" k/v pair. So I tried to alter the object being sent to another k/v pair (that is from schema, of course) and still nothing persisted. I do not receive any errors if I comment out the "assert.equal...'Ernest'" line.
My latest version of PUT route:
api.put('/create/song/:title', wagner.invoke(function(Song) {
return function(req, res) {
Song.findOneAndUpdate({ title: req.params.title}, req.body ,{ new: true }, function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
return res.json({ song: song });
});
};
}));
the following api route
api.put('/create/song/:title', wagner.invoke(function(Song) {
return function(req, res) {
Song.findOneAndUpdate({ title: req.params.title}, req.body ,{ new: true }, function(error, song) {
if(error) {
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
}
return res.json({ song: song });
});
};
}));
passes the following mocha test
it('can save a song', function(done) {
Song.create({ title: 'saveASong' }, function(error, doc) {
assert.ifError(error);
var url = URL_ROOT + '/create/song/saveASong';
superagent.
put(url).
send({
sections:[{ name: 'intro', timeSig: 140 }]
}).
end(function(error, res) {
assert.ifError(error);
assert.equal(res.status, status.OK);
console.log(res.body);
Song.findOne({}, function(error, song) {
assert.ifError(error);
assert.equal(song.title, 'saveASong');
//console.log(song);
assert.equal(song.sections[0].name, 'intro');
done();
});
});
});
});
Related
I have a controller function that is declared like this. Based on my comment for this function, I need to call a DB method to get the currentUser data, but I want to re-use an exports.function for that.
I want to call this getme function:
// Get the profile of the current user through JWT.
exports.getme = (req, res) => {
db.User.findOne({
where: { id: req.user.id },
include: [
{
model: db.Role,
as: "role"
},
{
model: db.UserType,
as: "userType"
},
{
model: db.PushToken,
as: "pushToken"
},
{
model: db.StripeAccount,
as: "stripeAccount"
}
],
attributes: defaultAttributes
})
.then(data => {
res.send(data)
})
.catch(err => {
console.log(err)
res.status(500).send({
message: "An error has occured while retrieving data."
})
})
}
from this createStripeAccount function.
// Create Stripe Account
// If there's no stripeAccount connected to the current user,
// only then will we attempt to call stripe's create.
exports.createStripeAccount = (req, res) => {
stripe.accounts.create({
type: 'express',
country: 'US',
email: req.user.email
})
.then(account => {
console.log('Account: ', JSON.stringify(account))
stripe.accountLinks.create({
account: account.id,
refresh_url: 'https://app.com/reauth',
return_url: 'https://app.com/return',
type: 'account_onboarding',
})
.then(accountLinks => {
console.log('Account links: ', accountLinks.url)
return res.send(accountLinks)
})
.catch(err => {
console.log("Error fetching account links from Stripe: ", err.message);
return res.status(500).send({
message: err.message || "An error has occured while fetching account links from Stripe."
});
})
}).catch(err => {
console.log("Error creating Stripe account: ", err.message);
return res.status(500).send({
message: err.message || "An error has occured while creating Stripe account."
});
});
};
Define the function first, export it later.
function getme() {
// ...
}
function createStripeAccount() {
// ...
getme(...);
// ...
}
exports.getme = getme;
exports.createStripeAccount = createStripeAccount;
use this.getme(...) wherever you want to use inside createStripeAccount function.
I am doing APIs and Microservices projects on FreeCodeCamp,the "Url shortener microservice". Here is my code:
const shorturlSchema = mongoose.Schema({
originalUrl: String,
shortUrl: Number
});
const ShortUrl = mongoose.model("ShortUrl", shorturlSchema);
app.post("/api/shorturl/new", urlencodedParser, async (req, res) => {
var url = req.body.url;
console.log(url);
if (!validUrl.isWebUri(url)) {
res.json({ error: "invalid url" });
} else {
var newUrl = new ShortUrl({
originalUrl: url,
shortUrl: shortId.generator
});
try {
await newUrl.save();
} catch (err) {
console.log("error", err);
return res.json({
error: "failed to store in database"
});
}
res.json({
"origianl_url": newUrl.originalUrl,
"short_url": shortId.generate
});
}
});
app.get("/api/shorturl/:short_url?", async (req, res) => {
console.log(req.params.short_url);
const smallUrl = req.params.short_url;
if (smallUrl === undefined) res.json({ error: "undefined" });
else {
const actualUrl = await ShortUrl.findOne({ shortUrl: smallUrl });
res.redirect(actualUrl.originalUrl);
return;
}
});
It should respond with a {"original_url":"www.google.com","short_url":1}. But instead it shows {"origianl_url":"https://www.freecodecamp.org". What am I doing wrong here?
You could pass the shortUrl from the database object.
Edited code:
try {
const response = await newUrl.save();
return res.json({
"origianl_url": response.originalUrl,
"short_url": response.shortUrl
});
} catch (err) {
return res.json({
error: "failed to store in database"
});
}
Actually I made some changes in the code. In the schema:
const shorturlSchema = mongoose.Schema({
originalUrl: String,
shortUrl: {
type:String,
default:shortId.generate
}
});
Then in the app.post section
var newUrl = new ShortUrl({
originalUrl: url
});
Then finally in the res.json part of app.post
res.json({
"original_url": newUrl.originalUrl,
"short_url": newUrl.shortUrl
});
This gives me the output:
{"original_url":"https://www.freecodecamp.org","short_url":"_sAInwNXs"}
I just figured this out now. Thanks for all your help
I'm using async each to iterate over objects and perform a query to populate their children. Each site has locations which have floors which have areas. My sites and locations populate just fine, however, that is where it stops. Sails outputs that it is looking for floors and areas though but they are never assigned. Any ideas?
gg: function (req, res) {
var userID = req.param('id');
User.findOne({ id: userID }).populate('sites').exec(function afterFind(err, foundUser) {
if (err) return res.json({ status: 'failure', message: 'Server error' });
if (foundUser === undefined) return res.json({ status: 'failure', message: 'User was not found' });
var resultToJson = foundUser.toJSON();
var sites = resultToJson.sites;
async.each(sites, function (site, siteCb) {
sails.log.debug('Finding locations for ' + site.name);
Locations.find({ site: site.id }).exec(function afterFind(err, foundLocations) {
if (err) {
siteCb(err);
} else {
site['locations'] = foundLocations;
async.each(site['locations'], function (location, locCb) {
sails.log.debug('Finding floors for ' + location.name);
Floor.find({ location: location.id }).exec(function afterFind(err, foundFloors) {
if (err) {
locCb(err);
} else {
location['floors'] = foundFloors;
async.each(location['floors'], function (floor, floorCb) {
sails.log.debug('Finding areas for ' + floor.name);
Area.find({ floor: floor.id }).exec(function afterFind(err, foundAreas) {
if (err) {
floorCb(err);
} else {
floor['areas'] = foundAreas;
floorCb();
}
});
}, function (floorError) {
if (floorError) {
locCb(floorError);
}
else {
locCb();
}
});
}
});
}, function (locError) {
if (locError) {
siteCb(locError);
} else {
siteCb();
}
});
}
});
}, function (siteError) {
if (siteError) {
sails.log.debug(siteError);
return res.json({ status: 'failure', message: 'Server error' });
} else {
return res.json({ status: 'success', message: 'Sites for user retrieved', sites: sites });
}
});
});
}
This code should be fixed with use of toJSON() on foundLocations. Anytime when you're overriding a defined attribute with populated one (or something else) it will not work when using a ORM returned object, use toJSON() or something and assign to plain JS object.
Ref: https://stackoverflow.com/a/43500017/1435132
Also, any reason to not use populate with Locations.find?
I have multiple res.send in one route, how can I append them all into one and send the accumulated list at the end?
I prefer to do it in the following form:
{
"writer": {success message},
"archive": {success message},
...
}
and another one like above for the list errors.
here is the code:
router.post('/some/route', function (req, res) {
if (req.isLoggedIn()) {
return res.status(403).json({});
}
MyModel.findById(req.user._id,function (err, data) {
if(err || data.rights !== 'super'){
return res.status(403).json({});
}
if(req.body.writer){
Books.update(
{ writer : req.body.id},
{ $set : { writer : req.body.writer} },
function (err) {
if(err){
res.status(500).send(err);
}
else{
res.status(200).send('updated successfully.');
}
}
);
}else{
Books.remove({writer: req.body.id}, function(err){
if (err){ return console.log(err)}
});
}
MetaInfo.findOneAndRemove({_id: req.body.id}, function (err, data) {
console.log(err);
});
Archive.findOne({_id: req.body.id},function (err, data) {
smtpTransporter.sendMail({...}, function (error, response) {
if (error) {
console.log(error);
} else {
console.log("Mail sent");
}
smtpTransporter.close();
});
data.remove();
if (err) {
console.log(err);
return res.status(200).json({
success: false,
message: 'server error',
err: err
});
}
res.status(200).json({
success: true
});
})
});
});
I assume your problem are the asynchronous calls to the database.
So best take a library of your choice (for example async) and do your async processes, in the callback then finally send your result.
Your result could look like this:
async.parallel([
function(callback) { ... },
function(callback) { ... }
], function(err, results) {
// send your result here
});
Note that if you are using .parallel the final callback will be immediatly called if one of the promises fails. see the docu
I've turned my hand from game dev to writing a supporting back-end. Thus far everything seems to work out but when i got to writing a friend system i ran into this function flow which seemed very dirty to me. And i'm certain i'm just hacking it together at this point. Any node.js wizards about to tell me how I can improve this?
Fairly certain i should be caching player lookups in Redis as well.
acceptfriend: function(req, res){
//Find our user
User.findById( req.decoded._id, function(err, user){
//error occured
if(err){
return res.status(401).send(err);
}
//no user found
if(!user){
return res.status(401).json({
succes: false,
message: 'no user found with that id'
} );
}
//Does the request exist?
if( !_.any( user.social.friendRequests, {id: req.params.id} ) ){
return res.status(401).json( {
succes: false,
message: 'friend request not found'
} );
}
//find the user that belongs to the request
User.findById( req.params.id, function(err, friend){
//error occured
if(err){
return res.send(err);
}
//user doesnt exist
if(!friend){
return res.status(401).json({
succes: false,
message: 'no user found with that id'
} );
}
//Pull the request from the friendRequests array
user.social.friendRequests.pull( req.params.id );
//Add the friend
user.social.friends.addToSet( {
user_id: friend._id,
name: friend.username,
corp: 'n/a'
} );
//Add the user to the friends list as well
friend.social.friends.addToSet({
user_id: user._id,
name: user.username,
corp: 'n/a'
});
//save the docs
user.save();
friend.save();
} );
//return success
return res.status(200).json({
success: true,
message: 'friend succesfully added'
});
} );
}
1- First of all, you have a big function. You have to split it into some functions. Doing this you gain the possibility to test them with any testing framework.
2- Delegate the handle of error responses to the controller.
from -> return res.status(401).send(err);
to (with Promises)-> deferred.reject(err);
to (normal way) -> throw new Error(err);
3- You can use Promises to manage the asynchronous behaviour of node to clear the code.
I created an example, maybe is not working at first time, feel free to fix the incorrent references. The User ref, the 'acceptfriend' method...
Gist: https://gist.github.com/aitoraznar/b7099ad88ead0cdab256
var Promise = require('bluebird');
var _ = require('lodash');
//var User = app.models.User;
var ERRORS = {
userNotFoundError: {
code: 401,
success: false,
message: 'no user found with that id'
},
friendRequestNotFoundError: {
code: 401,
success: false,
message: 'friend request not found'
},
friendNotFoundError: {
code: 401,
success: false,
message: 'no friend found with that id'
}
}
var SUCCESS_MESSAGES= {
friendAddedSuccessfully: {
success: true,
message: 'friend succesfully added'
}
};
var userDAO = {
/*
*
*/
getUserById: function(id) {
var deferred = Promise.pending();
User.findById(id, function(err, user) {
//error occured
if (err) {
err.code = 401;
return deferred.reject(err);
}
//no user found
if (!user) {
return deferred.reject(ERRORS.userNotFoundError);
}
deferred.resolve(user);
});
return deferred.promise;
},
/*
* Does the request exist?
*/
checkFriendRequest: function(user, friendId) {
var deferred = Promise.pending();
if (userDAO.haveFriendRequestFrom(user, friendId)) {
deferred.resolve(user, friendId);
} else {
return deferred.reject(ERRORS.friendRequestNotFoundError);
}
return deferred.promise;
},
/*
*
*/
haveFriendRequestFrom: function(user, friendId) {
return _.any(user.social.friendRequests, {id: friendId });
},
/*
*
*/
getFriend: function(user, friendId) {
var deferred = Promise.pending();
userDAO.getUserById(friendId)
.then(function(friend) {
deferred.resolve(user, friend);
},
function(error) {
if (error === ERRORS.userNotFoundError) {
// Then the error is friend not found
// Override the error
error = ERRORS.friendNotFoundError;
}
return deferred.reject(error);
});
return deferred.promise;
},
/*
*
*/
makeFriendship: function(user, friend) {
var deferred = Promise.pending();
//Pull the request from the friendRequests array
user.social.friendRequests.pull(friend._id);
//Add the friend
user.social.friends.addToSet( {
user_id: friend._id,
name: friend.username,
corp: 'n/a'
} );
//Add the user to the friends list as well
friend.social.friends.addToSet({
user_id: user._id,
name: user.username,
corp: 'n/a'
});
//save the docs
user.save();
friend.save();
// Return the new friendship
var friendship = {
user: user,
friend:friend
};
deferred.resolve(friendship);
return deferred.promise;
},
/*
*
*/
friendRequestError: function(err) {
var deferred = Promise.pending();
// Propagate de error
deferred.reject(err);
return deferred.promise;
},
/*
*
*/
friendRequest: function(userId, friendId) {
var deferred = Promise.pending();
// Get user by ID
userDAO.getUserById(userId)
// Check if the user is able to add the friend
.then(userDAO.checkFriendRequest, userDAO.friendRequestError)
// Get the friend to add
.then(userDAO.getFriend, userDAO.friendRequestError)
// Make the friendship
.then(userDAO.makeFriendship, userDAO.friendRequestError)
// Response to the controller
.then(
function(friendship) {
// Resolve with new friendship
// This goes to 'success' function in controller
deferred.resolve(friendship);
}, function(error) {
// This goes to 'error' function in controller
deferred.reject(error);
})
return deferred.promise;
}
};
// Controller
var acceptfriend = function(req, res, next) {
var userId = req.decoded._id;
var friendId = req.params.id;
userDAO.friendRequest(userId, friendId)
.then(function(friendRequest) {
console.log('---> SUCCESS');
//return success
return res.status(200)
.json(SUCCESS_MESSAGES.friendAddedSuccessfully);
}, function(error) {
console.error('---> ERROR', error);
return res.status(error.code).json(error);
});
}
4- Create database indexes in the collection/table
Regards,
Aitor