Problems handling calls to mongo on Meteor 3.0-alpha.15

Hi @pbeato, could you try again in the Beta version?

You can update the project by running the following:

meteor update --release 3.0-beta.0

We have the new syntax there, and it looks like this:

// Method created on the client
Meteor.methods({
  getData() {
    return "not data for the client";
  }
})

// Method created on the server
Meteor.methods({
  getData() {
    return LinksCollection.find().fetch();
  }
})

// Calling method on the client
const promise = Meteor.callAsync('getData');
console.log('getData SERVER', await promise); // Logs data returned from the server
console.log('getData CLIENT', await promise.stubPromise); //  Logs "not data for the client"

So, your code in the client could be something like this:

    collection.insertAsync({ _id: key, test: 1 })
      .stubPromise
      .then(() => collection.find({ _id: key, test: 1 }).countAsync())
      .then((docs) => {
        assert.equal(docs, 1)
      })
      .then(() => done())
      .catch(done)

Now, instead of providing a flag, you can wait only for the client with the new stubPromise property.

I had a look at https://github.com/meteor/meteor/blob/release-3.0/packages/allow-deny/allow-deny.js#L75 and this comment https://github.com/meteor/meteor/blob/release-3.0/packages/allow-deny/allow-deny.js#L196 kind of muddles it a little bit further.

Would be good to have a clarification on this, because, since the sync methods do not exist on the server anymore, one may assume that simply writing something like this in a common server/client file should suffice:

MyCollection.deny({
  insertAsync() {
    return true;
  },
  updateAsync() {
    return true;
  },
  removeAsync() {
    return true;
  },
});

The changelog at https://github.com/meteor/meteor/blob/release-3.0/docs/generators/changelog/versions/3.0.md only mentions

  • allow-deny@2.0.0:

    • Updated to accept async functions.

If both sync and async methods are recommended/required, it should be highlighted more prominently, since leaving some methods out may open security holes.

I am assuming both have to be considered, due to the latency compensation stuff in Meteor, whereby you can perform an insert via de client and on server it’s an insertAsync (though why one would allow/deny one and not the other is a mystery to me).

May be simply because it makes it easier to revert to previous code once all the *Async methods are removed in a future version of Meteor.

Still, some form of explanation would be good.

Thank you @denyhs, I have tried, but there is something I don’t understand: I am running the test below, which is a simple insert on a remote collection with a observe connected:

/* eslint-env mocha */

import { Mongo } from 'meteor/mongo'
import { Meteor } from 'meteor/meteor'
import { Random } from 'meteor/random'

const collection = new Mongo.Collection('test_mocha', {})

describe('testMocha', function () {
  it('Test insert', async function () {
    const allow = {
      insertAsync: function () {
        return true
      },
      updateAsync: function () {
        return true
      },
      removeAsync: function () {
        return true
      }
    }

    // Full permissions on collection
    collection.allow(allow)

    const key = `test_${Random.id()}`
    collection.find({ _id: key }).observeChanges({
      added: function (id, fields) {
        console.log('observeChanges added', id, fields)
      },
      changed: function (id, fields) {
        console.log('observeChanges changed', id, fields)
      },
      removed: function (id) {
        console.log('observeChanges removed', id)
      }
    })

    if (Meteor.isServer) {
      await collection.insertAsync({ _id: key, test: 1 })
    } else {
      await collection.insertAsync({ _id: key, test: 1 }).stubPromise
    }
  })
})

The result in console is:

observeChanges added test_7R7TiCQDzwdkhhQQb {"test":1}
Method stub (/test_mocha/insertAsync) took too long and could cause unexpected problems. Learn more at ...
observeChanges removed test_7R7TiCQDzwdkhhQQb

As you can see, I get a remove trigger with a single insert and it is not clear why. The result is that, after a while, the document disappears from the client’s local collection. Is there an explanation for this?

In addition, the introduction of stubPromise forces the code to be specialized by differentiating the server-side from the client-side, forcing further revisiting of all shared code.

What’s the behavior in version 2?

I can’t tell for sure right now why, but if this is unexpected behavior, we can have a look later.

Thank you @denyhs, I understand that. The problem is that I didn’t subscribe to the collection before. With a previous subscription it works.

Well, I just need an explanation regarding the introduction of stubPromise only client-side that force us to consider the side when using the MongoDB method. Is it not possible to have a dummy method to make the two implementations symmetric?

We’ll have docs explaining this better, but the idea of stubPromise is to imitate something like:

const stubResult = Meteor.call(name);

Where you don’t care about the server result, so you don’t provide a callback to Meteor.call.

But in situations where you want to display the stub result as soon as it’s ready and do something about it if the server fails, it’ll look like this:

const callPromise = Meteor.callAsync(name)

callPromise.catch(/* Handle the error */)

// [...]
const stubResult = await callPromise.stubPromise;
// [...]

So we don’t have to choose between one or another. Both of them will always be there.

2 Likes