This code pulls field values from a form, and I can see the values being passed properly when I debug in the console. However, due to the extra object added with my first value, Meteor throws a sanitize error...any ideas?
Template.sendInvitationModal.events({
'submit form': function submitForm(event, template) {
event.preventDefault();
var firstName = template.find("[name='firstName']").value,
lastName = template.find("[name='lastName']").value,
email = template.find("[name='emailAddress']").value,
store = template.find("[name='store'] option:selected").value,
position = template.find("[name='position'] option:selected").value,
roles = template.find("[name='roles'] option:selected").value;
debugger;
if (email && roles && position !== "") {
Meteor.call("sendInvitation", {
firstName: firstName,
lastName: lastName,
email: email,
store: store,
position: position,
roles: roles
}, function (error, response) {
if (error) {
toastr["warning"]( error.reason);
} else {
$("#send-invitation-modal").modal('hide');
$('.modal-backdrop').hide();
toastr["success"]( "Invitation sent!" );
}
});
} else {
toastr["warning"]( "Please set an email and at least one user type!" );
}}});
This is the value being passed with firstName when calling "sendInvitation"
firstName = "Richard", template = B…e.TemplateInstance {view: B…e.View, data: Object, firstNode: div#send-invitation-modal.modal.fade.in, lastNode: div#send-invitation-modal.modal.fade.in, _allSubsReadyDep: T…r.Dependency…}
And then Meteor throws a sanitize error. Any suggestions?
We managed (thanks Ryan Glover for your assistance) to find a solution by simply converting the variables assignment from the values pulled out of the form into an object instead. Solved it! So the correct code is...
Template.sendInvitationModal.events({
'submit form': function submitForm(event, template) {
event.preventDefault();
var data = {
firstName: template.find("[name='firstName']").value,
lastName: template.find("[name='lastName']").value,
email: template.find("[name='emailAddress']").value,
store: template.find("[name='store'] option:selected").value,
position: template.find("[name='position'] option:selected").value,
roles: template.find("[name='roles'] option:selected").value
};
if (data.email && data.roles && data.position !== "") {
Meteor.call("sendInvitation", data, function (error, response) {
if (error) {
toastr["warning"]( error.reason);
} else {
$("#send-invitation-modal").modal('hide');
$('.modal-backdrop').hide();
toastr["success"]( "Invitation sent!" );
}
});
} else {
toastr["warning"]( "Please set an email and at least one user type!" );
}
}
});
Related
I'm trying to update a document in MongoDB using NodeJS (NextJS). My current code is:
import connect from "../../util/mongodb";
async function api(req, res) {
if (req.method === "POST") {
const { id } = req.body;
const { name } = req.body;
const { email} = req.body;
const { anything1 } = req.body;
const { anything2 } = req.body;
if (!id) {
res.status(400).json({ "error": "missing id param" });
return;
}
const { db } = await connect();
const update = await db.collection("records_collection").findOneAndUpdate(
{ id },
{
$set: {
name,
email,
anything1,
anything2
}
},
{ returnOriginal: false }
);
res.status(200).json(update);
} else {
res.status(400).json({ "error": "wrong request method" });
}
}
export default api;
Everything is working. But I would like to request only the ID as mandatory, and for the other information, leave optional.
In this code, passing the id and name for example, the other three fields (email, anything1 and anything2) will be null in the document.
It is possible to implement the update without requiring all document information and ignore when body fields are null? (As a beginner in NodeJS and MongoDB, the only way to do that that comes to my head now is to surround it all by a lot of if...)
If I've understood your question correctly you can achieve your requirement using the body object in $set stage.
If there is a field which not exists in the object, mongo will not update that field.
As an example check this example where only name field is updated and the rest of fields are not set null.
Another example with 2 fields updated and 3 fields.
You can see how only is updated the field into object in the $set.
So you only need to pass the object received into the query. Something like this:
const update = await db.collection("records_collection").findOneAndUpdate(
{ id },
{
$set: req.body
},
{ returnOriginal: false }
);
I want to set roles for a new user.
I tried updating roles array in metadata during signup but I get an error. If I remove the roles metadata new user is created just fine.
db.signUp(userId, 'pass', {
metadata: {
email: 'robi434n#boywonder.com',
birthday: '1932-03-27T00:00:00.000Z',
likes: ['acrobatics', 'short pants', 'sidekickin\'']
roles: ['basic']
}
}, function (err, response) {
if (err) {
if (err.name === 'conflict') {
console.log('batman" already exists, choose another username')
// "batman" already exists, choose another username
} else if (err.name === 'forbidden') {
console.log('invalid username')
// invalid username
} else {
console.log('sign up error')
// HTTP error, cosmic rays, etc.
}
} else {
console.log('user signed up')
// login()
}
})
So I figured it out first modify the pouchdb-authentication index.js code to accept roles.
var signUp = pouchdbUtils.toPromise(function (username, password,roles, opts, callback) {
var db = this;
if (typeof callback === 'undefined') {
callback = typeof opts === 'undefined' ? (typeof password === 'undefined' ?
username : password) : opts;
opts = {};
}
if (['http', 'https'].indexOf(db.type()) === -1) {
return callback(new AuthError('This plugin only works for the http/https adapter. ' +
'So you should use new PouchDB("http://mysi3te.org:5984/mydb") instead.'));
} else if (!username) {
return callback(new AuthError('You must provide a username'));
} else if (!password) {
return callback(new AuthError('You must provide a password'));
}
var userId = 'org.couchdb.user:' + username;
var user = {
name: username,
password: password,
roles: roles,
type: 'user',
_id: userId,
};
updateUser(db, user, opts, callback);
});
Then you can send the roles in the sign up. I'm sending basic below
signUp()
function signUp () {
db.signUp(userId, 'pass', ['basic'], {
metadata: {
email: 'robi434n#boywonder.com',
birthday: '1932-03-27T00:00:00.000Z',
likes: ['acrobatics', 'short pants', 'sidekickin\'']
}
}, function (err, response) {
if (err) {
if (err.name === 'conflict') {
console.log('batman" already exists, choose another username')
// "batman" already exists, choose another username
} else if (err.name === 'forbidden') {
console.log('invalid username', err)
// invalid username
} else {
console.log('sign up error', err)
// HTTP error, cosmic rays, etc.
}
} else {
console.log('user signed up', err)
login()
}
})
}
now you have to go to couchdb _user database _design/_auth document modify
else if (newDoc.roles.length > 0 ) {\n
set this to
else if (newDoc.roles.length > 0 && newDoc.roles[0] !== 'basic' ) {\n
Now you will have basic in your session and can add more roles my adjusting the code a bit. This allows me to set member role permissions easily to limit access to other databases. Or a simpler solution i found and tested is to add a new design doc to your database with the following code. It will only allow users that are logged in to access your database
{
"_id": "_design/usersOnly",
"_rev": "17-6fb7e6c0ccfca8b2e56738ad63e26107",
"language": "javascript",
"validate_doc_update": "\n function(newDoc, oldDoc, userCtx){ \n // check if user is logged in \n if(!userCtx.name){ throw({forbidden : 'No way.. login man!'});} \n //reqired fields to update \n function require(field){ var message = field + ' is required'; if(!newDoc[field]){ throw({'forbidden':message}) }} require('name'); }"
}
The validate function design documentation uses the _users design document as the primary example:
Example: The _design/_auth ddoc from _users database uses a validation function to ensure that documents contain some required fields and are only modified by a user with the _admin role:
...
} else if (newDoc.roles.length > 0) {
throw({forbidden: 'Only _admin may set roles'});
}
...
In fauxton (for example) you can login as the couchdb admin user and go to _users database and change the design (at your own peril), for example:
} else if (newDoc.roles.length > 0 && !("basic" === newRoles[0] && newDoc.roles.length === 1)) {
throw({forbidden: 'Only _admin may set roles'});
}
saving it as you would any other document. (I say peril, since subtle accidents in this design document can potentially allow unauthorized users to drastically raise their permissions.)
With such a change, new users are allowed to be created with the basic role by couchdb, so your client code works if it sets roles correctly. In pouchdb-authenticate this seems to be with opt.roles and not opt.metadata.roles, i.e.:
db.signUp(userId, 'pass', {
metadata: {
email: 'robi434n#boywonder.com',
birthday: '1932-03-27T00:00:00.000Z',
likes: ['acrobatics', 'short pants', 'sidekickin\'']
},
roles: ['basic'] }, ... )
I am writing an API that takes data and stores it into DB, also edits it and deletes it, the add/delete works fine, but when I update I want to be able to update only certain attributes, and if the user doesn't send an attribute I want the code to keep the data already in the DB. Instead if I don't send an attribute it's overwritten by empty data.
Here's an example:
router.post('/update', function (req, res) {
var first_name = req.body.first_name,
last_name = req.body.last_name,
email = req.body.email,
phone_number = req.body.phone_number,
clas = req.body.clas,
subject = req.body.subject,
teacher_id = req.body.teacher_id;
req.assert('teacher_id', 'Invalid teacher_id').notEmpty();
var errors = req.validationErrors();
if (errors) {
res.json(400, {success: false, message: "please enter your teacher_id "});
return;
}
Teacher.findOne({_id: teacher_id}, function (err, teacher) {
if (err) {
console.log(err);
} else {
teacher.first_name = first_name != null || first_name
!= undefined ? first_name : teacher.first_name;
teacher.last_name = last_name != null || last_name
!= undefined ? last_name : teacher.last_name;
teacher.email = email != null || email
!= undefined ? email : teacher.email;
teacher.phone_number = phone_number != null || phone_number
!= undefined ? phone_number : teacher.pickup_points;
teacher.clas = clas != null || clas
!= undefined ? clas : teacher.clas;
teacher.subject = subject != null && subject
!= undefined ? subject : teacher.subject;
teacher.save(function (err, teacher) {
if (err) {
console.log(err);
} else {
res.json({success: true, message: "teacher successfully updated"});
}
});
}
});
});
This may not be the only way, but I use the Lodash library to make this kind of thing easy. I also use the 'param' route to populate the request with my object for me to save callbacks.
const _= require("lodash ")
router.param("teacherId", function(id, req,res,next){
Teacher.findOne({_id : id}, function(e, teacher){
//add error handling of your choice
req.teacher = teacher;
next();
})
})
router.put("teacher/:teacherId", function(req,res){
var updates = _.pick(req.body, ["name","age"]; // whitelist allowed attribute names to change
var teacher = _.merge(req.teacher, updates);
teacher.save(function(e){
// handle your response.
})
})
/* Edit */
Also, please note that in this solution, I'm assuming the use of (reasonably standard) REST-formatted routes. If you want to keep using router.post('/update'... that's fine, but you won't be able to separate out the teacher query like I did.
The advantage of the way I did it is that anytime you want to find a thing first (e.g. viewing a teacher, deleting a teacher, etc), you have the logic for doing so in one place, and don't have to repeat it in other handlers.
This method is a little different from what you are doing but I'd recommend this way to you so that you can keep your document models in one place and call them whenever you need them. Validations part can be handled using a separate module which is for mongoose.
The way you are updating it expects other variables to be assigned as it is, like I am fetching the document using findone then objectname.value = newobjectname.value
A simple way to solve this would be using findoneandupdate.
modelname.findOneAndUpdate({
primarykey: primarykey
}, {
$set: {
nameindocument: valuetobereplace
//you can add here values that you would like to change
}
}, {
new: true //new:true here helps to get updated object in return
}, function(err, doc) {
if (err) {
console.error("Error:" + err);
} else {
}
});
I am using express-validator to validate POST data in my express application. I have a form which has a select where in user can select multiple options:
<select name="category" multiple id="category">
<option value="1">category 1 </option>
.......
</select>
The payload after submitting form shows me this if I select multiple values:
...&category=1&category=2&....
Now, in my Express application I try to validate it like this:
req.checkBody('category', 'category cannot be empty').notEmpty();
But, even after I send multiple values I always get the error - category cannot be empty. If I print my variable as req.body.category[0] - I get the data. But, somehow not able to understand the way I need to pass this to my validator.
You may need to create your own custom validator;
expressValidator = require('express-validator');
validator = require('validator');
app.use(expressValidator({
customValidators: {
isArray: function(value) {
return Array.isArray(value);
},
notEmpty: function(array) {
return array.length > 0;
}
gte: function(param, num) {
return param >= num;
}
}
}));
req.checkBody('category', 'category cannot be empty').isArray().notEmpty();
Try this:
router.post('/your-url',
[
check('category').custom((options, { req, location, path }) => {
if (typeof category === 'object' && category && Array.isArray(category) && category.length) {
return true;
} else {
return false;
}
})
],
controller.fn);
Answering a bit late but hope this might help somebody.
If you are using express-validator, You can pass minimum / maximum length option to check whether Array is empty or exceeds a certain limit
req.checkBody('category', 'category cannot be empty').isArray({ min: 1, max: 10 });
If you are using schema, you can do it this way:
const schema = {
someArray: { isArray: true, notEmpty: true }
};
This maybe a bit late but for others who wants a clean solution without using the customValidator, I created a validator that can dig up to the deepest array and validate its content.
https://github.com/edgracilla/wallter
In your case validation would be:
const halter = require('wallter').halter
const Builder = require('wallter').builder // validation schema builder
const builder = new Builder()
server.use(halter())
server.post('/test', function (req, res, next) {
let validationSchema = builder.fresh()
.addRule('category.*', 'required')
.build()
// validationSchema output:
// { 'category.*': { required: { msg: 'Value for field \'category.*\' is required' } } }
req.halt(validationSchema).then(result => {
if (result.length) {
res.send(400, result)
} else {
res.send(200)
}
return next()
})
})
Error message can be override in builder initialization. Check the repo README.md for an in depth usage.
Mailchimp is almost a perfect company, except their Node API documentation is non-existent. How can I add a subscriber to my new list and include their first name and last name? The code below successfully adds the subscriber, but first and last names are not being added.
var MCapi = require('mailchimp-api');
MC = new MCapi.Mailchimp('***********************-us3');
addUserToMailchimp = function(user, callback) {
var merge_vars = [
{ EMAIL: user.email },
{ LNAME: user.name.substring(user.name.lastIndexOf(" ")+1) },
{ FNAME: user.name.split(' ')[0] }
];
MC.lists.subscribe({id: '1af87a08af', email:{email: user.email}, merge_vars: merge_vars, double_optin: false }, function(data) {
console.log(data);
}, function(error) {
console.log(error);
});
}; // addUserToMailchimp
The supplied merge variables should be passed as a single object, not an array of objects. Please see my example below:
var mcReq = {
id: 'mail-chimp-list-id',
email: { email: 'subscriber-email-address' },
merge_vars: {
EMAIL: 'subscriber-email-address',
FNAME: 'subscriber-first-name',
LNAME: 'subscriber-last-name'
}
};
// submit subscription request to mail chimp
mc.lists.subscribe(mcReq, function(data) {
console.log(data);
}, function(error) {
console.log(error);
});
It looks like you supplied your actual mail chimp API key in your question. If so, you should remove it immediately.