Meteor Methods doesn't update database using Autoform - meteor

Using autoform, it seems the data is being passed from autoform as the Meteor method on my server does get the data, but then doing the database update inside of my method doesn't update my database...what am I missing?
Autoform code...
{{> quickForm collection="Rooms" type="method-update"
doc=this autosave=true id=makeUniqueID
meteormethod="updateRoom"}}
Meteor method:
updateRoom: function (room) {
console.log(room);
Rooms.update({_id: room._id}, { $set: {
checkIn: room.checkIn,
checkOut: room.checkOut,
tenantID: room.tenantID,
available: room.available,
needCleaning: room.needCleaning,
}});
},
My allow/deny rules:
Rooms.allow({
insert() { return false; },
update() { return false; },
remove() { return false; }
});
Rooms.deny({
insert() { return true; },
update() { return true; },
remove() { return true; }
});
Below is what I get from the console log in my from my meteor method. So I do get the changes (in this case change the tenantID and the false to available), but it doesn't update in the database. I'm missing a little detail somewhere but can't see it at this point.

The room variable you are passing to the method is nesting everything under the modifier and $set: keys.
You could simply do:
updateRoom: function (room) {
Rooms.update({_id: room._id}, room.modifier);
},
but that's really insecure because you're passing the whole modifier to the method and a hacker could pass in anything they wanted to.
Better:
updateRoom(room) {
check(room,Object);
check(room._id,String);
{checkIn, checkOut, tenantId, available, needCleaning } = room.modifier.$set;
Rooms.update(room._id, { $set: {checkIn, checkOut, tenantId, available, needCleaning }});
},

Related

Meteor "update failed: Access denied" when user attempts to update their own profile?

Im adding a field to a user's account on creation. This is working fine:
Accounts.onCreateUser((options, user) => {
user.groups = [2];
return user;
});
I need to make a function that allows the user to change this. When I run this from the front-end I get an error "update failed: Access denied"
Meteor.users.update(
{ _id: Meteor.userId() },
{
$set: { groups: [4, 5] },
},
);
In server/main.js I have:
Meteor.publish('currentUser', function() {
return Meteor.users.find({ _id: this.userId }, { fields: { groups: 1 } });
});
Do not make db updates from the client directly. No client can ever be trusted.
Having said that, there are two ways to deal with this:
ONE
As per the documentation :
By default, the current user’s username, emails and profile are
published to the client. You can publish additional fields for the
current user with:
// Server
Meteor.publish('userData', function () {
if (this.userId) {
return Meteor.users.find({ _id: this.userId }, {
fields: { groups: 1 }
});
} else {
this.ready();
}
});
Meteor.users.allow({
update: function(userId, user) {
return true;
/**
* Don't use `return true` in production!
* You probably need something like this:
* return Meteor.users.findOne(userId).profile.isAdmin;
*/
}
});
// Client
Meteor.subscribe('userData');
Meteor allow rules
TWO
Define a meteor method in the server and have the server update the relevant data for you AFTER it does some validations. Server code is always trusted.
server.js
Meteor.method({
updateGroups: function(data){
// make changes to the user record
});

Restivus authentication for Meteor methods

I'm trying to make my meteor-app for REST services available. For this I use the package "Restivus" which also works just fine. But once I'd like to run a meteor method this.userId is undefined.
Api.addRoute('addArticle', {authRequired: true}, {
post: function () {
console.log(this.userId); //<-- hwuqtFXf8aKperJ5p
try {
Meteor.call("addArticle",this.bodyParams);
} catch (e) {
return {code:500,type:e.error,reason:e.reason};
}
}
});
the method:
new ValidatedMethod({
name: 'addArticle',
....
if (!this.userId) {
throw new Meteor.Error(...); //is thrown
}
What am I doing wrong?
In Meteor methods you get the current userId by doing
Meteor.userId()
and not
this.userId
So you would need to update your code to
if(!Meteor.userId()){
throw new Meteor.Error(403, '403:Forbidden', 'You shall not pass!')
}

find a document with the current username in a helper

I'm trying to create a helper like this:
this.helpers({
data() {
return Customers.findOne({ user: Meteor.user().username });
}
});
but an error occurs, It seems that the user is logging in when the helper is executing, How can I execute the helper after the user is logged in ?
Don't know if is the best solution but I created a deferred promise that wait for the user to login and resolve the $state.
resolve: {
currentUser: ($q) => {
var deferred = $q.defer();
Meteor.autorun(function() {
if(!Meteor.loggingIn()) {
if(!Meteor.user()) {
deferred.reject('PERMISSION_REQUIRED');
} else {
deferred.resolve();
}
}
});
I hope that it can be useful for someone else.
Try this:
data() {
if(Meteor.user()){
return Customers.findOne({ user: Meteor.user().username });
}
}
Try builtin currentUser users helper, which check whether the user is logged in. Like that:
{{#if currentUser}}
{{data}}
{{/if}}

meteor subscription manager change collection name

I'm using subscriptions manager with iron-router and my problem is this one.
I have a collection "participants" with 2 publications: allParticipants and todayParticipants.
if I go to this page:
Router.map(function () {
this.route('winners', {
waitOn: function () {
return [subs.subscribe('allWinners'),
subs.subscribe('allParticipants')];
console.log("subscribed!");
},
data: function () {
return {
winners: Winners.find(),
participants: Participants.find(),
loginBox: "True"
}
}
});
AllParticipants publication is subscribed and put in cache by the subscription manager package.
If after this, I go to this page:
Router.map(function () {
this.route('participants', {
path: '/',
waitOn: function () {
return subs.subscribe('todayParticipants');
},
data: function () {
return {
participants: Participants.find()
}
}
});
I'm expecting to subscribe only the todayParticipants but as my subscription is automatically named "Participants", It uses the cached subscription from the previous page being allParticipants.
Is there a way to change the name of my subscriptions in order to have each of them in the right cache?
Thanks.
What I do in my waitOn function is first stop my subscriptions like
if (App.subs) {
for (name in App.subs) {
App.subs[name].stop();
}
}
And then I create new subscriptions
App.subs = {
settings: Meteor.subscribe('settings', project),
...
};
return [App.sub.settings, .....];
Hope this helps!
Today, there seems to be no solution to this problem.
More explanation here: https://github.com/meteorhacks/subs-manager/issues/11
What I'm doing now is using a very limited number of subscriptions (filtered mainly on user) and then I create as much data objects as I want filtering my subscriptions in different ways.

How to explicitly unsubscribe from a collection?

I have a MongoDB with a large "messages" collection; all messages belonging to a specific groupId. So have started with a publication like this:
Meteor.publish("messages", function(groupId) {
return Messages.find({
groupId: groupId
});
});
and a subscription like this:
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
This got me into trouble because initially currentGroupId is undefined but sill mongod would use up the CPU to find messages with groupId == null (although I know there are none).
Now, I tried to rewrite the publication as follows:
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
} else {
return {}; // is this the way to return an empty publication!?
}
});
and/or to rewrite the subscription to:
Deps.autorun(function() {
if (Session.get("currentGroupId")) {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
} else {
// can I put a Meteor.unsubscribe("messages") here!?
}
});
which both helps initially. But as soon as currentGroupId becomes undefined again (because the user navigates to a different page), mongod is still busy requerying the database for the last subscribed groupId. So how can I unsubscribe from a publication such that the mongod is stopped being queried?
According to the documentation it must be http://docs.meteor.com/#publish_stop
this.stop()
Call inside the publish function. Stops this client's subscription;
the onError callback is not invoked on the client.
So something like
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
} else {
return this.stop();
}
});
And I guess on the client side you can just remove your if/else like in your first example
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
I found it more simple and straight-forward to call the .stop() function on the handler which is returned from the .subscribe() call:
let handler = Meteor.subscribe('items');
...
handler.stop();
Simply adding a condition to the publication:
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
});
and keeping the subscription:
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
does the job.
There is no need to stop the publication explicitly. Eventually, the MongoDB is not queried anymore after finishing the currently running query and issuing yet another one (which seems to be queued somewhere in the system).
in your case, you should stop the autorun
there is an example in the documentation
Your autorun is actually called with a parameter that allows you to stop it:
Deps.autorun(function (c) {
if (! Session.equals("shouldAlert", true))
return;
c.stop();
alert("Oh no!");
});

Resources