Calling a Meteor method synchronously (server-originated call) not working

I was under the impression that on the server side, you could call a Meteor method synchronously. Yet this doesn’t seem to work: (the gc method prints out an unexpected object instead of a token)

Meteor.methods({
  gc() {
    console.log('GC RESULTS:', getCredentials('WB9NDdWqoHAok7fDq'));
  },
});

function getCredentials(userId) {
  const user = Meteor.users.findOne(userId);

  if (!user || !user.asana) return;

  // Is the token expired? If so, get a new one via the refresh token
  if ((new Date()) >= user.asana.expiresAt) {
    return Meteor.call('asanaSetToken', {
      refreshToken: user.asana.refreshToken,
      userId: user._id,
    });
  }

  return user.asana.accessToken;
}

export const asanaSetToken = new ValidatedMethod({
  name: 'asanaSetToken',

  validate(args) {
    check(args, {
      code: Match.Optional(String),
      refreshToken: Match.Optional(String),
      userId: Match.Optional(String),
    });
  },

  async run({ code, refreshToken, userId }) {
    if (!Meteor.isServer) return;

    const client = createAsanaClient();
    let credentials, values;

    if (code) {
      credentials = await client.app.accessTokenFromCode(code);
    } else {
      credentials = await client.app.accessTokenFromRefreshToken(refreshToken);
    }

    if (code) {
      values = {
        asana: {
          accessToken: credentials.access_token,
          asanaUserId: credentials.data.id.toString(),
          expiresAt: new Date(Date.now() + credentials.expires_in * 1000),
          refreshToken: credentials.refresh_token,
        },
      };
    } else {
      // Just refreshing the expiry and access token
      values = {
        'asana.accessToken': credentials.access_token,
        'asana.asanaUserId': credentials.data.id.toString(), // just in case
        'asana.expiresAt': new Date(Date.now() + credentials.expires_in * 1000),
      };
    }

    await Meteor.users.rawCollection().update({ _id: userId || this.userId }, {
      $set: values,
    });

    console.log('reftok =', refreshToken);
    if (refreshToken) return credentials.access_token;
  },
});

I get this in the server console:

I20170223-13:38:45.427(-8)? GC RESULTS: { _c: [],
I20170223-13:38:45.428(-8)?   _a: undefined,
I20170223-13:38:45.428(-8)?   _s: 0,
I20170223-13:38:45.428(-8)?   _d: false,
I20170223-13:38:45.428(-8)?   _v: undefined,
I20170223-13:38:45.428(-8)?   _h: 0,
I20170223-13:38:45.429(-8)?   _n: false }
I20170223-13:38:45.870(-8)? reftok = xxxx

I must be missing something pretty basic here. I think the gist of it is that I have no clue how to use promises within a Meteor method. :laughing: At this point I’m tempted to just stop using this Asana node package and hand-code all the auth stuff myself.

The only thing i can spot is that you use the async keyword on your run method. This explicitly tells that the method should run asynchronously. I have no experience yet woth these keyword but i remember reading that meteor will respect these keywords.

Removing async might do the trick

After reading a bit. Try this:

//Notice the "await" keyword.
return await Meteor.call('asanaSetToken', {
      refreshToken: user.asana.refreshToken,
      userId: user._id,
    });

That didn’t work either. I can’t seem to figure out the right placement of async/await keywords. Ugh.

Ah! Per another thread, Promise.await() (Meteor’s fiber-enabled promises) works great!

1 Like

This is actually another manifestation of this issue: