Parse Server set User ACL and assign Role - node.js

I am converting my code from parse.com to parse-server with little success. The idea of the afterSave function is to assign user only ACL and assign the user to the 'user' role.
The ACL part works fine, however I am unable to assign the user to the role.
Parse.Cloud.afterSave(Parse.User, function(req, res) {
if (!req.object.existed()) {
var user = req.object;
var acl = new Parse.ACL(user);
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
user.setACL(acl);
return user.save({}, { useMasterKey: true }).then(function() {
var query = new Parse.Query(Parse.Role);
query.equalTo("name", 'user');
return query.first({useMasterKey: true});
}).then(function(roles) {
if (roles.length < 1) return Parse.Promise.error("no such role");
roles[0].getUsers({useMasterKey: true}).add(user);
return roles[0].save({}, { useMasterKey: true });
}).then(function() {
return user;
});
}
});

"however I am unable to assign the user to the role". add function(err) to see which step failed, and see error message.
The code you shows is incorrect.
try this one
return user.save({}, { useMasterKey: true }).then(function() {
var query = new Parse.Query(Parse.Role);
query.equalTo("name", 'user');
return query.first({useMasterKey: true});
//first will return one object or null
}).then(function(role) {
//.getUsers() is equal .relation('users')
if(role){
role.getUsers().add(user);
return role.save(null, { useMasterKey: true });
}else{
return Parse.Promise.error("no such role");
}
}, console.error).then(function() {
return user;
}, console.error);

query.first() return Object not Array, so you should query, the returned result, without accessing array index. Second last .then block code should be
if (!roles) return Parse.Promise.error("no such role");
roles.getUsers({useMasterKey: true}).add(user);
return roles.save({}, { useMasterKey: true });

Related

Keystonejs get insterted id value

I am using updateItem method to save new record.
Following is the code example :
var vv = new vvmodel.model();
vvmodel.updateItem(vv, obj, function (error) {
if(error)
console.log('error :: ', error);
});
This is not sending any object from which we can have inserted id record. How can we get the inserted record id ?
Thanks
you should use update handler in keystoneJs
once updateHandler.process runs without error, you can access .id field on vv
var vv = new vvmodel.model({
// .... initial fields
}),
var updater = vv.getUpdateHandler(req, res, {
errorMessage: 'There was an error creating your new model:'
});
updater.process(req.body, {
flashErrors: true,
logErrors: true,
fields: 'field1, field2, field3'
}, function(err) {
if (err) {
locals.validationErrors = err.errors;
} else {
req.flash('success', 'Your model has been added');
return res.redirect('/vv/detail/' + vv.id); // here you can access the id if request was successful.
}
next();
});
see more detailed example use with sydjs site source

Sequelize synchronous ORM

Hello I'm using sequelize and I would like to make a synchronous query, but this is made asynchronously and return an wrong response.
I have this code:
function(user) {
var allow = false;
return User.find({ where: { id: user.id},
include: [{
model: Plan,
attributes: ['id', 'name']
}]
}).then(function(u) {
if(!u) throw new Error("0 user not found");
user = u.dataValues;
var plan = user.plan.dataValues.name;
else if (plan == "Premium") allow = true;
console.log("allow", allow);
return allow;
}).catch(function(error){
console.log("error:::", error);
});
}
And the console.log('allow', allow); is printing true but when I call the function, the function is returning false.
Thanks for help.
You can't make asynchronous code synchronous, so you're going to have to deal with the asynchronicity.
However, it's not too difficult, since your function is returning a promise already. You can use that to pass the allow variable to a caller:
yourFunction(user).then(function(allow) {
console.log('allow', allow);
});
I think the return allow; statement is the return value to function function(u) {} inside .then(), not the function (user) {}.
Furthermore, User.find() returns a promise. If you read about Promise, you will see there are some ways to deal with your problem.
Here is one:
var myFunc = function(user, callback) {
var allow = false;
User.find({ where: { id: user.id},
include: [{
model: Plan,
attributes: ['id', 'name']
}]
}).then(function(u) {
if(!u) throw new Error("0 user not found");
user = u.dataValues;
var plan = user.plan.dataValues.name;
else if (plan == "Premium") allow = true;
callback(null, allow);
}).catch(function(error){
callback(error);
});
}
Then you can use this function:
myFunc(user, function (err, allow) {
// Do not forget to handle error
// `allow` now could be used here
console.log(allow);
});

Create/update with Sequelize on an array of items

I created a function to:
take an array of 'labels' and look for whether they have a record in the db already
create those which don't exist,
and update those which do exist
return a json array reporting on each item, whether they were updated/created, or resulted in an error
I managed to make it work but I feel like I just made some ugly dogs' dinner!
var models = require("../models");
var Promise = models.Sequelize.Promise;
module.exports = {
addBeans: function (req, callback) {
Promise.map(req.body.beansArr, function (bean) {
return models.Portfolio.findOrCreate({where: {label: bean}}, {label: bean});
}).then(function (results) { // Array of 'instance' and 'created' for each bean "findOrCreate(where, [defaults], [options]) -> Promise<Instance>"
var promisesArr = [];
results.forEach(function (result) {
if (result[1]) { // result[1] = wasCreated
promisesArr.push(Promise.resolve([result[0].dataValues.label, "created"]));
} else {
promisesArr.push(
models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).then(function () {
return Promise.resolve([result[0].dataValues.label, "updated"])
})
);
}
});
return promisesArr;
// When it's all done create a JSON response
}).then(function (results) {
var resultObj = {items: []}; // JSON to return at the end
Promise.settle(results).then(function (promiseinstances) {
for (var i = 0; i < promiseInstances.length; i++) {
if (promiseInstances[i].isFulfilled()) {
resultObj.items.push({
item: {
label: promiseInstances[i].value()[0],
result: promiseInstances[i].value()[1],
error: ''
}
});
}
else if (promiseInstances[i].isRejected()){
resultObj.items.push({
label: promiseInstances[i].value()[0],
result: 'error',
error: promiseInstances[i].reason()
});
}
}
// Send the response back to caller
}).then(function () {
return callback(null, resultObj);
}, function (e) {
return callback(e, resultObj);
});
});
}
};
Question:
Is there an easier or more obvious way to create/update values with Sequelize?
Is my use of Promise.settle() appropriate for this case? I have the feeling I made this more complicated than it needs to be.
I am new to Sequelize and using Promises, I'd appreciate if someone could advise on this.
I feel like this would work better on CodeReview.SE but I can see a few issues.
Is there an easier or more obvious way to create/update values with Sequelize?
Well, for one thing:
.then(function(array){
var newArr = [];
array.forEach(function(elem){
newArr.push(fn(elem);
}
return newArr;
});
Is just
.map(fn)
Additionally, promises assimilate so you can return val; from a .then you don't have to return Promise.resolve(val);.
So:
).then(function (results) { // Array of 'instance' and 'created' for each bean "findOrCreate(where, [defaults], [options]) -> Promise<Instance>"
var promisesArr = [];
results.forEach(function (result) {
if (result[1]) { // result[1] = wasCreated
promisesArr.push(Promise.resolve([result[0].dataValues.label, "created"]));
} else {
promisesArr.push(
models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).then(function () {
return Promise.resolve([result[0].dataValues.label, "updated"])
})
);
}
});
return promisesArr;
})
Is just
.map(function(result){
if(result[1]) return [result[0].dataValues.label, "created"];
return models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).
return([result[0].dataValues.label, "updated"]);
});
However, since you want it to work regardless of it being resolved, you'd have to do:
.then(function(results){
return results.map(function(result){
if(result[1]) return [result[0].dataValues.label, "created"];
return models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).
return([result[0].dataValues.label, "updated"]);
});
});
Which means it'll resolve regardless, then you'd call .settle():
.settle().then(function(results){
// your settle logic here
});
Note that the last:
}).then(function () {
return callback(null, resultObj);
}, function (e) {
return callback(e, resultObj);
});
Is simply:
.nodeify(callback);
However, I recommend sticking to promises.
I use Promise.settle for sequelize.update, and can get affect rows number by _settledValueField .
promise.push(...update...)
db.sequelize.Promise.settle(promise).then(function (allresult) {
var affectcnt = 0
allresult.forEach(function (singlecnt) {
if (undefined !== singlecnt._settledValueField[1]) {
affectcnt += parseInt(singlecnt._settledValueField[1])
}
})
unfortunately, it's only work for update.
You can insert array in database using sequelize. You want to change in model like below. I am trying to add multiple languages in database through array.
language: { type: DataTypes.STRING,
allowNull: false,
get()
{
return this.getDataValue('language').split(';')
},
set(val)
{
this.setDataValue('language',Array.isArray(val) ? val.join(','):val);
}
}

Mongoose custom validation failing

In my application I am trying to run some custom validations on mongoose, all I want is to be able to make sure that ratings from a particular user should not exceed more than once, I have tried a couple of things, and the code to begin with return true and false correctly but the error is not triggered. Here is my code
RatingSchema.path('email').validate(function (email) {
var Rating = mongoose.model('Rating');
//console.log('i am being validated')
//console.log('stuff: ' + this.email+ this.item)
Rating.count({email: this.email, item: this.item},function(err,count){
if(err){
console.log(err);
}
else{
if(count===0){
//console.log(count)
return true;
}
else {
//console.log('Count: in else(failing)'+ count)
return false;
}
}
});
},'Item has been already rated by you')
When defining a validator that performs an asynchronous operation (like your Rating.count call), your validator function needs to accept a second parameter which is a callback that you call to provide the true or false result as you can't just return async results.
RatingSchema.path('email').validate(function (email, respond) {
var Rating = mongoose.model('Rating');
Rating.count({email: this.email, item: this.item},function(err,count){
if(err){
console.log(err);
}
else{
if(count===0){
respond(true);
}
else {
respond(false);
}
}
});
},'Item has been already rated by you');

nested loops asynchronously in Node.js, next loop must start only after one gets completed

Check below algorithm...
users = getAllUsers();
for(i=0;i<users.length;i++)
{
contacts = getContactsOfUser(users[i].userId);
contactslength = contacts.length;
for(j=o;j<contactsLength;j++)
{
phones = getPhonesOfContacts(contacts[j].contactId);
contacts[j].phones = phones;
}
users[i].contacts = contacts;
}
return users;
I want to develop such same logic using node.js.
I have tried using async with foreach and concat and foreachseries functions. But all fail in the second level.
While pointer is getting contacts of one user, a value of i increases and the process is getting started for next users.
It is not waiting for the process of getting contacts & phones to complete for one user. and only after that starting the next user. I want to achieve this.
Actually, I want to get the users to object with proper
Means all the sequences are getting ruined, can anyone give me general idea how can I achieve such a series process. I am open to change my algorithm also.
In node.js you need to use asynchronous way. Your code should look something like:
var processUsesrs = function(callback) {
getAllUsers(function(err, users) {
async.forEach(users, function(user, callback) {
getContactsOfUser(users.userId, function(err, contacts) {
async.forEach(contacts, function(contact, callback) {
getPhonesOfContacts(contacts.contactId, function(err, phones) {
contact.phones = phones;
callback();
});
}, function(err) {
// All contacts are processed
user.contacts = contacts;
callback();
});
});
}, function(err) {
// All users are processed
// Here the finished result
callback(undefined, users);
});
});
};
processUsers(function(err, users) {
// users here
});
You could try this method without using async:
function getAllUserContacts(users, callback){
var index = 0;
var results = [];
var getUserContacts = function(){
getContactsOfUser(users[index].userId, function(contacts){
var index2 = 0;
var getContactsPhones = function(){
getPhonesOfContacts(contacts[index2].contactId, function(phones){
contacts[index2].phones = phones;
if(index2 === (contacts.length - 1)){
users[index].contacts = contacts;
if(index === (users.length - 1)){
callback(users)
} else {
index++;
getUserContacts();
}
}else{
index2++;
getContactsPhones();
}
});
}
getContactsPhones();
});
}
getUserContacts();
}
//calling the function
getAllUsers(function(users){
getAllUsersWithTheirContacts(users, function(usersWithContacts){
console.log(usersWithContacts);
})
})
//Asynchronous nested loop
async.eachSeries(allContact,function(item, cb){
async.eachSeries(item,function(secondItem,secondCb){
console.log(secondItem);
return secondCb();
}
return cb();
},function(){
console.log('after all process message');
});

Resources