Meteor Wrapasync server side save api call result to collection


#1

Hello

I am trying to insert some data in a collection after an asynchronous api call in the Accounts.onCreateUser callback (API: https://github.com/Mangopay/mangopay2-nodejs-sdk).

However I am getting the error:

throw new Error("Meteor code must always run within a Fiber. " + Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

Here’s my first try:

Accounts.onCreateUser(function(options, user) {
mangoPayApi.Users.create({
"Email": options.email,
"FirstName": options.profile.firstName,
"LastName": options.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": user._id,
}, function(mpUser) {
    // User created - using callback
    console.log('User created ');
    console.log(mpUser);
    aMPData.user = mpUser;
    MPData.insert(aMPData); // Insert data into collection`

Second shot: I tried to make the api call aynchronous

let synCreateUser = Meteor.wrapAsync(mangoPayApi.Users.create, mangoPayApi.Users ); user = handleCharge.create({ "Email": post.emails[0], "FirstName": post.profile.firstName, "LastName": post.profile.lastName, "CountryOfResidence": "FR", "Nationality": "FR", "Birthday": new Date(aBirthDateTimestamp).getTime() / 1000, "PersonType": "NATURAL", "Tag": post._id, });

But now I get the following error:

 Exception in queued task: TypeError: Object function (/* arguments */) {
 ar self = context || this;
var newArgs = _.toArray(arguments);
var callback;
for (var i = newArgs.length - 1; i >= 0; --i) {
  var arg = newArgs[i];
  var type = typeof arg;
  if (type !== "undefined") {
    if (type === "function") {
      callback = arg;
    }
    break;
  }
}

if (! callback) {
 if (Meteor.isClient) {
   callback = logErr;
 } else {
   var fut = new Future();
   callback = fut.resolver();
 }
 ++i; // Insert the callback just after arg.
}

newArgs[i] = Meteor.bindEnvironment(callback);
var result = fn.apply(self, newArgs);
return fut ? fut.wait() : result;
 } has no method 'create'
at Object.added (server/main.js:102:30)
at [object Object].observeChangesCallbacks.added (packages/minimongo/observe.js:153:1)
at self.applyChange.added (packages/minimongo/observe.js:53:1)

`
Now my question is, how can I insert the data I get from an async api call into a collection ?

UPDATE: On my second try, I made a mistake
Following @robfallows advice I tried:

try {
 let mangopayuser = Meteor.wrapAsync(   mangoPayApi.Users.create, mangoPayApi.Users );
 be = mangopayuser({
  "Email": options.email,
  "FirstName": options.profile.firstName,
  "LastName": options.profile.lastName,
  "CountryOfResidence": "FR",
  "Nationality": "FR",
  "Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
  "PersonType": "NATURAL",
  "Tag": user._id,
});
  } catch (err) {
      console.log('2');
      console.log(err);
      console.log('3');  }

which yields the following:

[details=Console output]I20160630-20:29:12.229(2)? onCreateUser
I20160630-20:29:13.512(2)? 2
I20160630-20:29:13.517(2)? { Address:
I20160630-20:29:13.518(2)? { AddressLine1: null,
I20160630-20:29:13.518(2)? AddressLine2: null,
I20160630-20:29:13.519(2)? City: null,
I20160630-20:29:13.520(2)? Region: null,
I20160630-20:29:13.520(2)? PostalCode: null,
I20160630-20:29:13.521(2)? Country: null },
I20160630-20:29:13.521(2)? FirstName: ‘AA’,
I20160630-20:29:13.522(2)? LastName: ‘BB’,
I20160630-20:29:13.523(2)? Birthday: 689814000,
I20160630-20:29:13.523(2)? Nationality: ‘FR’,
I20160630-20:29:13.524(2)? CountryOfResidence: ‘FR’,
I20160630-20:29:13.525(2)? Occupation: null,
I20160630-20:29:13.525(2)? IncomeRange: null,
I20160630-20:29:13.526(2)? ProofOfIdentity: null,
I20160630-20:29:13.527(2)? ProofOfAddress: null,
I20160630-20:29:13.527(2)? PersonType: ‘NATURAL’,
I20160630-20:29:13.528(2)? Email: ‘b@b.fr’,
I20160630-20:29:13.529(2)? KYCLevel: ‘LIGHT’,
I20160630-20:29:13.529(2)? Id: ‘14407486’,
I20160630-20:29:13.530(2)? Tag: ‘S8d4zPBpYfjsxDkEH’,
I20160630-20:29:13.531(2)? CreationDate: 1467311353 }
I20160630-20:29:13.542(2)? 3[/details]

I gotta say that blew my mind. In err, there’s my api response??

UPDATE 2
Following @youngone suggestion, I managed to make it work ! Since, there are some nested callbacks here the full solution as someone may find it useful

/* Create MangoPay user */
mangoPayApi.Users.create({
  "Email": options.email,
  "FirstName": options.profile.firstName,
  "LastName": options.profile.lastName,
  "CountryOfResidence": "FR",
  "Nationality": "FR",
  "Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
  "PersonType": "NATURAL",
  "Tag": user._id,
}, Meteor.bindEnvironment( function(mpUser) {
  // User created - using callback
  console.log('User created ');
  console.log(mpUser);
  aMPData.user = mpUser;
  if(mpUser)
  {
    mangoPayApi.Wallets.create({
      "Owners": [mpUser.Id],
      "Description": "User Wallet",
      "Currency": "EUR"
    }, Meteor.bindEnvironment( function(wallet) {
      console.log('wallet created');
      console.log(wallet);

      aMPData.wallet = wallet;
      /* MangoPay wallet created */
      /* Create MangoPay CardRegistrations for user */
      if(wallet)
      {
        mangoPayApi.CardRegistrations.create({
          "UserId": wallet.Owners[0],
          "Currency": "EUR",
          "CardType": "CB_VISA_MASTERCARD"
        }, Meteor.bindEnvironment( function(cardRegistration) {
          console.log('cardRegistration');
          console.log(cardRegistration);
          if (cardRegistration)
          {
            aMPData.cardRegistration = cardRegistration;
            /* Everything is OK, insert it in DB */
            MPData.insert(aMPData);
          }
        })); // callback mangoPayApi.CardRegistration
      } // end if wallet
    })); // callback mangoPayApi.Wallets  // Meteor.bindEnvironment callback angoPayApi.Wallets  // mangoPayApi.Wallets.create
  } // end if mpUser
})); // callback Users.create // Meteor.bindEnvironment callback Users.create// mangoPayApi.Users.create;

Now, how can may I catch exceptions thrown by the api call ?


Call function (mongo.insert) inside async await promise chain
#2

Someone please help us with this question. :sweat:


#3

I made some progress now.

let synCreateUser = Meteor.wrapAsync(mangoPayApi.Users.create, mangoPayApi.Users ); user = synCreateUser({ // Notice that I remove the .create as I have already passed it to the wrapAsync. "Email": post.emails[0], "FirstName": post.profile.firstName, "LastName": post.profile.lastName, "CountryOfResidence": "FR", "Nationality": "FR", "Birthday": new Date(aBirthDateTimestamp).getTime() / 1000, "PersonType": "NATURAL", "Tag": post._id, });

I am now getting:

W20160630-00:20:56.624(2)? (STDERR) /home/yanis/.meteor/packages/meteor-tool/.1.3.4_1.1qcu7p2++os.linux.x86_64+web.browser+web.cordova/mt-os.linux.x86_64/dev_bundle/server-lib/node_modules/fibers/future.js:313
W20160630-00:20:56.625(2)? (STDERR) throw(ex);
W20160630-00:20:56.626(2)? (STDERR) ^

Still on it !


#4

Have you tried this? (In your original code)
function(mpUser) {

}

to
Meteor.bindEnvironment(function(mpUser) {

})

so, basically when you pass this callback function, wrap it with Meteor.bindEnvironment…


#5

Your basic code structure looks okay, so I would suggest wrapping in a try/catch and logging the exact error. Something like:

const synCreateUser = Meteor.wrapAsync(mangoPayApi.Users.create, mangoPayApi.Users);
try {
  const user = synCreateUser({
    Email: post.emails[0],
    FirstName: post.profile.firstName,
    LastName: post.profile.lastName,
    CountryOfResidence: 'FR',
    Nationality: 'FR',
    Birthday: new Date(aBirthDateTimestamp).getTime() / 1000,
    PersonType: 'NATURAL',
    Tag: post._id,
  });
  aMPData.user = user;
  MPData.insert(aMPData);
} catch (err) {
  console.log(err);
}


#6

Found a solution thanks to all you help:

/* Create MangoPay user */
      mangoPayApi.Users.create({
        "Email": document.emails[0].address,
        "FirstName": document.profile.firstName,
        "LastName": document.profile.lastName,
        "CountryOfResidence": "FR",
        "Nationality": "FR",
        "Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
        "PersonType": "NATURAL",
        "Tag": document._id,
      }, Meteor.bindEnvironment( function(mpUser) {

        // User created - using callback
        console.log('User created ');
        console.log(mpUser);

        /* Mangopay Id */
        aMPData.Id = mpUser.Id;

        if(mpUser)
        {
          mangoPayApi.Wallets.create({
            "Owners": [mpUser.Id],
            "Description": "Client Wallet",
            "Currency": "EUR"
          }, Meteor.bindEnvironment( function(clientWallet) {
            console.log('wallet created');
            console.log(clientWallet);

            aMPData.clientWallet.Id = clientWallet.Id;
            aMPData.clientWallet.Owner = clientWallet.Owners[0];

            /* MangoPay clientWallet wallet created */
            if(clientWallet)
            {
              mangoPayApi.Wallets.create({
                "Owners": [clientWallet.Owners[0]],
                "Description": "mw Wallet",
                "Currency": "EUR"
              }, Meteor.bindEnvironment(function(mw) {
                if(mw)
                {
                  console.log(mw);
                  aMPData.mw.Id = mw.Id;
                  aMPData.mw.Owner = mw.Owners[0];
                  // Mangopay.insert(aMPData);
                  Meteor.users.update(document._id, { $set: { mangopay: aMPData } });
                }
              }));
            }
          })); // callback mangoPayApi.Wallets  // Meteor.bindEnvironment callback angoPayApi.Wallets  // mangoPayApi.Wallets.create
        } // end if mpUser
      })); // callback Users.create // Meteor.bindEnvironment callback Users.create// mangoPayApi.Users.create;
    }