What could be reasons why collections don't update?

Hi guys,

I’m totally new to debugging and testing but I guess it’s time to dive into the problem. My app won’t update any collections anymore. Whenever I do an insert or update it does the insert/update and shows the result on my app, collection.find().fetch() also shows the right results but as soon as I reload the page the inserts/updates are gone. I’ve never come across this problem before and it seems like it happened out of nowhere, I also get no errors in the console.log :confused:

How can I start debugging this? Velocity? I’m totally new to debugging or unit testing and I’ve read velocity is super unreliable so not sure if it’s beginner friendly.

Are you sure you have your collection(s) defined on both client and server? It sounds like you are using a client-only collection.

As long as it’s not inside of a

if (Meteor.isServer) {}

they should be published both server and client side, right? I’m subscribing with flowrouter like this on my route (for some reason I get access denied with template subscriptions which is why I’m not using it right now):

FlowRouter.route('/vendor_profile', {
  subscriptions: function(params) {     
    this.register('VendorProfileImages', Meteor.subscribe('VendorProfileImages'));
    this.register('VendorProducts', Meteor.subscribe('VendorProducts'));
  },  
  action: function() {
    BlazeLayout.render("applicationLayout", { main: "vendor_profile" });
  }
});

I’m cleaning up my code right now to see if there’s anything I’m missing.

Oh wait … you just gave me an idea … I have something like this in my isServer for a user signup. Is this only accessible on the server if I inserted it (on user signup) like this?

 if (Meteor.isServer) {
    Accounts.onCreateUser(function(options, user) {

      Meteor.users.update(
        {_id: this.userId },
        {$set: { "profile.vendor": true } }
      );

      VendorProducts.insert({

        'userid': user._id,
        'products': {
          some inserts here                                                                                                                                
        }
      });

      // We still want the default hook's 'profile' behavior.
      if (options.profile)
        user.profile = options.profile;
      return user;
    });

  }
}

Only if it’s not inside of a Meteor.isClient :wink:

2 Likes

Turned autopublish on. Not sure what this means. autopublish publishes and subscribes all the collections. I do have the collections available on my client so I guess you must be right that in some way it’s a client only collection.

autopublish will automatically publish a collection to all clients, unless you have specifically written publish methods for it, in which case your publish methods will over-ride autopublish.

If you start up meteor shell, do you have access to your collection?

You didn’t do anything silly like move all your collection declaration code inside a client\ folder now did you? :wink:

Sorry, I kinda wrote it wrong. I turned on autopublish and then it worked, yes. Without autopublish and using meteor shell I have access to my collection. And nope, all my collection stuff and routing (with subscription) is in root/lib/

So the subscription itself is working, I’m getting the data to my client but it seems I cannot push new data to the client for some reason which I don’t understand. Sometimes (very random) I get access denied when trying to do updates and neither client nor server update. Sometimes it seems to update the client but it isn’t pushed to the real server collection.

I wish I’d get an error code why the access is denied :confused:

By default, if you remove the insecure package (which I assume you have also done?), changes on collections on the clients are not applied to the database, unless you use allow/deny rules in your publications. Could this be what’s happening?

Is this new behavior? I was not aware of that x_x I thought allow/deny is usually just used for Meteor.user collections. Ok, I’m pretty sure this is it because I definitely haven’t created allow/deny rules!

How can I check a document is owned by the user? If I have a collection where every user writes into where every document has the user’s id written in it. That’s something I wrote myself.

What determines the doc.owner of a document? Let’s say for example as above with the onCreateUser I’ve added a new document to my VendorProducts collection. How does meteor know or not know who the doc.owner is in this case? From the meteor docs:

Posts = new Mongo.Collection("posts");

Posts.allow({
  insert: function (userId, doc) {
    // the user must be logged in, and the document must be owned by the user
    return (userId && doc.owner === userId);
  },
  update: function (userId, doc, fields, modifier) {
    // can only change your own documents
    return doc.owner === userId;
  },
  remove: function (userId, doc) {
    // can only remove your own documents
    return doc.owner === userId;
  },
  fetch: ['owner']
});

No - that’s documented, although the bit regarding insecure is right at the end and easy to miss (or misunderstand):

Meteor also has a special “insecure mode” for quickly prototyping new applications. In insecure mode, if you haven’t set up any allow or deny rules on a collection, then all users have full write access to the collection.

As long as each of your documents has the originator’s userId (doc.owner in the examples you referenced), you can write the allow rule appropriately. If your documents do not contain the userId, then you will either need to add these retrospectively to each document, or find an alternative strategy for granting write access.

Edit: if you aren’t already, consider matb33:collection-hooks for an easy, consistent way of adding standard fields to documents as they’re inserted.

Thanks, I got it now. I got confused for a while with doc.owner. For anyone else who’s getting confused. doc simply refers to the current doc and owner in this case is the field which holds the userid. So to make it work for my case as shown above it would be:

VendorProfileImages.allow({
  insert: function (userId, doc) {
    // the user must be logged in, and the document must be owned by the user
    return (userId && doc.userid === userId);
  },
  update: function (userId, doc, fields, modifier) {
    // can only change your own documents
    return doc.userid === userId;
  }, 
  remove: function (userId, doc) {
    // can only remove your own documents
    return doc.userid === userId;
  },
  //fetch: ['userid']
});

VendorProducts.allow({
  insert: function (userId, doc) {
    // the user must be logged in, and the document must be owned by the user
    return (userId && doc.userid === userId);
  },
  update: function (userId, doc, fields, modifier) {
    // can only change your own documents
    return doc.userid === userId;
  },
  remove: function (userId, doc) {
    // can only remove your own documents
    return doc.userid === userId;
  },
  //fetch: ['userid']
});

Just a question: What do I do if I want to do an upsert instead? Upsert is not allowed in allow/deny. Create a method? Why is it not allowed?

So, yes, a method is your only option.

1 Like