Suppose that you have two tables: CARS
and VOTES
.
Now you want to enable your users to vote in the cars. They can either vote to a new car or change their previous vote, but no user can vote to the same care twice.
VOTE-MODEL
car_id
user_id
vote_value
How would you setup an Upsert process for that model? Please remember that the Upsert method does not accept remote hooks (before save), which would prevent you from getting the user_id
from access token.
Here are some approaches that I tried, but did not work:
'use strict';
module.exports = function (Votes) {
Votes.observe('before save', function setAutoData(context, next) {
if (context.instance) {
if (context.isNewInstance) {
if (context.options.accessToken) {
context.instance.user_id = context.options.accessToken.userId;
Votes.upsertWithWhere({
where: {
car_id: context.instance.car_id,
user_id: context.instance.user_id
}
}, context.instance, function (err, cb) {
if (err) return next(err);
console.log('cb', cb);
});
}
}
}
});
};
'use strict';
module.exports = function(Votes) {
Votes.observe('before save', function setAutoData(context, next) {
if (context.instance) {
if(context.isNewInstance) {
if (context.options.accessToken) {
context.instance.user_id = context.options.accessToken.userId;
Votes.find({
where: {
car_id: context.instance.car_id,
user_id: context.instance.user_id
}
}, function(err, cb) {
if (!cb.length) {
// If it does not find anything, go next, accept post
next();
} else {
// Otherwise update record
Votes.updateAll({
where: {
car_id: context.instance.car_id,
user_id: context.instance.user_id
}
}, context.instance, function(err, cb) {
console.log('cb', cb);
});
}
});
}
}
}
});
};
The issue with both attempts is that it seems that the code does not get executed for the Upsert or Udpate.
It's named operation hook not remote hook.
BTW your first attempt should work like :
module.exports = function (Votes) {
Votes.observe('before save', function setAutoData(context, next) {
if (context.instance) {
if (context.isNewInstance) {
if (context.options.accessToken) {
context.instance.user_id = context.options.accessToken.userId;
}
return next();
}else {
Votes.count({
where: {
car_id: context.instance.car_id,
user_id: context.instance.user_id,
vote_value: context.instance.vote_value
}
}, function(err, count){
if(err) return next(err);
if(count > 0) next(SuitableError);
next();
});
}
}
});
};
You just modify the user id and let the operation be completed. No need to do extra update/upsert.
And if this not new instance, check for previous votes with the same criteria, if it is existed, deny it, else allow it.
There is a simple solution too, no need to hooks
Votes.vote = function(data, options, cb){
var userId = options && options.accessToken && options.accessToken.userId;
if(!userId){
return cb(UserNotFound); //return error
}
Votes.upsertWithWhere({
car_id: data.car_id,
user_id: userId
}, {
car_id: data.car_id,
user_id: userId,
vote_value: data.vote_value
}, function(err, result){
if(err) return cb(err);
cb(null, result);
});
};
Votes.remoteMethod('vote', {
accepts: [
{
arg: 'data',
type: 'Object',
required: true,
http: {source: 'body'}
},
{
arg: 'options',
type: 'Object',
http: "optionsFromRequest"
}
],
returns: {root: true, type: 'object'},
http: {verb: 'post', status: 201, path: '/vote'}
});