Validating user using .pre save - javascript

My API accepts users POSTed as JSON. I would like to validate certain fields only if they are included as part of the JSON object.
For example, a user might look like this:
{
"email" : "test#test.com",
"username" : "testing",
"name" : "Test User"
}
or it might not have a name field:
{
"email" : "test#test.com",
"username" : "testing"
}
and I would like to make sure name has at least 6 characters if it is an included field.
I'm trying to build the validation process into my model using .pre but things aren't quite as I'd expect them.
var UserSchema = new Schema({
id : String,
name : String,
email : String,
username : String
},{ timestamps: { createdAt: 'created_at',updatedAt: 'updated_at' } });
UserSchema.pre('save', function(next) {
console.log(this); //no evidence of name property here
if("name" in this){
console.log("Name found"); //this is always the output
} else {
console.log("Name not found");
}
next();
});
The above code is for testing. Using either of the JSON objects above, the output is always "Name found" even though the object doesn't have a name property when I output to the console. Is this because the model has a name property?

You see the user object in the terminal and there is no property for name because the posted json might not have a name field as you mentioned, so you may do your logic/condition just when the name property is existed as follow:
UserSchema.pre('save', function(next) {
if (this.name !== undefined) {
if (this.name.length <= 6) {
// throw error or whatever
}
}
next();
});

Related

How to use request body value in the .withMessage() function in express validator chain

I want to display my express validator errors with the dynamic value user entered.
For example, a user enters an invalid username (lets say "$##") pattern (I will attach my regex somewhere) I want to send my error message as a response like this :
{
"errorCode" : "234",
"field" : "username",
"value " : "$##",
"msg" : "Username : [$##] is an invalid username pattern, please check the rules for valid usernames"
}
I want to achieve this with .withMessage() in the validation chain.
My current chain code :
check('username')
.trim()
.not()
.isEmpty()
.withMessage('username can\'t be empty')
.bail()
.matches("^[a-zA-Z0-9]([._-](?![._-])|[a-zA-Z0-9]){3,18}[a-zA-Z0-9]$")
.withMessage(
{
errorCode: '234',
field : 'username',
value : ? ,
msg: 'Username : [?] is an invalid username pattern, please check the rules for valid usernames'
})
Since I can't access my req.body inside how can I achieve it?
Thanks
You can use a function to get the value from a parameter:
.withMessage((value) => { return `Invalid value: ${value}` })
Adapting that to your solution looks like this:
.withMessage(
(value) => {
return {
"errorCode": 234,
"field": 'username',
"value": value,
"msg": `Username: ${value} is an invalid username pattern, please check the rules for valid usernames`
}
})
And, if you want, you can sanitize the input value first with something like:
.escape().withMessage(...)

Mongoose: CastError on querying incorrect ObjectId for nested object

I have following schema
var Topic= new Schema({
text: String,
topicId: String,
comments: [{type: Schema.Types.ObjectId, ref:'Comment'}]
});
var Comment = new Schema({
text: String
});
I am writing RESTFul API that will give me the Comment details as per topic ID and Comment ID
/topics/{id}/comments/{id}
Following is the function that gets data from Mongo
getCommentsById: function(req, resp){
req.db.Topic.findOne({"topicId": req.params.topicId})
.populate({path:"Comments", match:{"_id": req.params.commentId}})
.exec(function(err, topic){
if(err) {
return resp.status(500).json({
message: 'Error when getting Topic.',
error: err
});
}
if (!topic) {
return resp.status(404).json({
message: 'No such Topic'
});
}
if (!topic.comments || topic.comments.length==0) {
return resp.status(404).json({
message: 'No such Comment'
});
}
resp.json(topic.comments[0]);
});
}
The code works fine if I specify the right comment ID, but if I specify non-existing comment ID in URL then I get following error
{
"message": "Error when getting Topic.",
"error": {
"message": "Cast to ObjectId failed for value \"57c738b66d790f0c1bdb179\" at path \"_id\"",
"name": "CastError",
"kind": "ObjectId",
"value": "57c738b66d790f0c1bdb179",
"path": "_id"
}
}
What is the issue here and how to fix it?? Is there better way to query the required object?
The issue isn't that your specifying a non-existing comment ID. It's that you're specifying a string that can't be converted into a valid ObjectId. Your test string, "57c738b66d790f0c1bdb179" is a 23 character hex string. It should be length 24.
If you want to validate before attempting your query, there are several different ways you could go about it. Here's one example: Can I determine if a string is a MongoDB ObjectID?

Accessing a sub-document ID after save - mongoose

When a user is created on my app their details are saved on the MongoDB using mongoose. The user schema contains sub-documents and I am trying to access the _id if the sub-document after using the user.save function.
The schema is below:
{
name: String,
email: String,
address: String,
phone:[
{landLine: Number,
mobile: Number}
]
}
I can access the name, email and address easily like so:
console.log(user.name + user.email + user.address)
I tried user.phone._id but it returns undefined. I think because phone is an array of objects.
user.save(function(err) {
if (err)
throw err;
else {
console.log("user ID " + user._id); // SUCCESS!!
console.log("user sub-document ID " + user.phone._id); // UNDEFINED!!
return (null, user);
}
});
How can I access the _id of the sub-document inside the save function right after the user is created and saved into mongoDB?
There are a couple of approaches to getting this information, but personally I prefer the "atomic" modification method using $push.
The actual implementation here is helped by mongoose automatically including an ObjectId value which is "monotonic" and therefore always increasing in value. So this means that my method for handling this even works with a $sort modifier applied to the $push.
For example:
// Array of objects to add
var newNumbers = [
{ "landline": 55555555, "mobile": 999999999 },
{ "landline": 44455555, "mobile": 888888888 }
];
User.findOneAndUpdate(
{ "email": email },
{ "$push": { "phone": { "$each": newNumbers } } },
{ "new": true },
function(err,user) {
// The trick is to sort() on `_id` and just get the
// last added equal to the length of the input
var lastIds = user.phone.concat().sort(function(a,b) {
return a._id > b._id
}).slice(-newnumbers.length);
}
)
And even if you used a $sort modifier:
User.findOneAndUpdate(
{ "email": email },
{ "$push": { "phone": { "$each": newNumbers, "$sort": { "landline": 1 } } } },
{ "new": true },
function(err,user) {
var lastIds = user.phone.concat().sort(function(a,b) {
return a._id > b._id
}).slice(-newnumbers.length);
}
)
That little trick of "sorting" a temporary copy on the _id value means that the "newest" items are always at the end. And you just need to take as many off the end as you added in the update.
The arguable point here is that it's actually mongoose that is inserting the _id values in the first place. So in fact those are being submitted in the request made to the server for each array item.
You "could" get fancy and use "hooks" to record those ObjectId values that were actually added to the new array members in the update statement. But it's really just a simple process of returning the last n "greatest" _id values from the array items anyway, so the more complex approach is not needed.

How to update user.services for Jasmine testing in Meteor

I am trying to mock out a user for testing out my application, and I have gotten to the point where I can create a test user and log them into the mirror instance of my app.
I need to compare the gmail addresses for peoples accounts, and to test this functionality, I want to add a test email address under user.services.google.email within the Meteor users account database (which is where the accounts-google package stores it, I don't need to mock out an entire user account yet).
What I can't figure out is how to append this information, instead of just overwriting what is already there, this is what my code looks like:
if (Meteor.users.find().count() === 0) {
var testUserDetails = {
email: 'testEmail#gmail.com',
password: 'testPassword'
};
console.log("Creating the Test User");
var newUserId = Accounts.createUser(testUserDetails);
Meteor.users.update({
_id: newUserId
}, {
$set: {
services: {
google: {
email: "testEmail#gmail.com"
}
}
}
});
} else {
console.log("There are already users in the Test database");
}
console.log('***** Finished loading default fixtures *****');
},
And this is what a user looks like:
{
"_id" : "Dw2xQPDwKp58RozC4",
"createdAt" : ISODate("2015-07-30T04:02:03.261Z"),
"services" : {
"password" : {
"bcrypt" : "asdfasdfasdfdsafsadfasdsdsawf"
},
"resume" : {
"loginTokens" : [ ]
}
},
"emails" : [
{
"address" : "testEmail#gmail.com",
"verified" : false
}
]
}
Now $set just rewrites everything within services, and there is no $push operation for mongo or for js, so how should I go about doing this? Should I consume the object and parse it manually?
*Note I have also tried using Meteor's Accounts.onCreateUser(function(options, user) but facing the same issue.
[...] there is no $push operation for mongo [...]
Sure, there is a $push operator, which appends a specified value to an array.
However, I think what you are trying to do is to update a document and keep all values which are already set.
Here is how you can do that:
Query the document first to get the object you want to set.
Update the respective object.
Run the MongoDB update operation to set the new object.
For instance:
var user = Meteor.users.findOne({
_id: newUserId
});
var servicesUserData = user.services;
servicesUserData.google.email = "your_new_email#gmail.com";
Meteor.users.update({
_id: newUserId
}, {
$set: {
"services": {
servicesUserData
}
}
});

Underscore's findWhere and MongoDB object

I use MongoDB and I want to use Underscore.js in my Angular app.
I have a model "Email" with the fields: "subject", "body" and "id".
This Email object looks like this in the browser console:
$$hashKey
"005"
_id
Object { $oid="5478774a6a61734d8a000000"}
body
"Here is the sample conte...le ble ble. Nice email."
subject
"This is first template"
This is the controller code in Angular:
$scope.viewEmail = function(emailId) {
var email = _.findWhere(emailData.data.emails, { _id: emailId });
console.log(email);
};
So, I just want to find an email with the specific id - the id I want to get is saved in a local variable emailId.
I should do something like that: _id.$oid: emailId but it causes a syntax error in the javascript console: "missing : after property id".
This will do the job:
_.find(list, predicate, [context])
Here is a fiddle:
http://jsfiddle.net/bqegdy2t/
var emailData = {
data: {
emails: [
{ _id: { $oid: "someId" }, body: "Hello Every Body1" },
{ _id: { $oid: "someOtherId" }, body: "Hello Every Body2" },
]
}
};
var emailId = "someId";
var email = _.find(emailData.data.emails, function(email) {
return email._id.$oid === emailId;
});
console.log(email);
the difficulty lies in the mongo objectid format that is an unusual JSON object containing string.
try _id: emailId versus _id.$oid: emailId
another option: dot notation often needs quotes around it "_id.$oid" : emailId especially those with dollar signs

Categories