Bluebird: Question: Promisifying mandrill-api

Created on 3 Oct 2014  路  7Comments  路  Source: petkaantonov/bluebird

Just getting started with Bluebird and trying to figure out the best way to promisify use of the sendTemplate call on the mandrill-api. It's not a standard node.js callback design in that it takes 2 callback as the last parameters (first as a success callback and the second being a failure callback). For example (taken from the mandril docs)

mandrill_client.messages.sendTemplate({"template_name": template_name, "template_content": template_content, "message": message, "async": async, "ip_pool": ip_pool, "send_at": send_at}, function(result) {
   // SUCCESS 
    console.log(result);
    /*
    [{
            "email": "[email protected]",
            "status": "sent",
            "reject_reason": "hard-bounce",
            "_id": "abc123abc123abc123abc123abc123"
        }]
    */
}, function(e) {
    // FAILURE
    // Mandrill returns the error as an object with name and message keys
    console.log('A mandrill error occurred: ' + e.name + ' - ' + e.message);
    // A mandrill error occurred: Unknown_Subaccount - No subaccount exists with the id 'customer-123'
});

Any help/suggestions would be great. At a minimum we'd like to call this from inside a method wrapped in a Promise.method

@pazrul to follow

Most helpful comment

The promise constructor works pretty well with dual callback apis.

// impl
function sendTemplate(opts) {
  return new Promise(function (resolve, reject) {
    mandrill_client.messages.sendTemplate(opts, resolve, reject)
  })
}

// usage
sendTemplate({
  "template_name": template_name,
  "template_content": template_content,
  "message": message,
  "async": async,
  "ip_pool": ip_pool,
  "send_at": send_at
})
.then(handleSuccess)
.catch(handleError)

All 7 comments

The promise constructor works pretty well with dual callback apis.

// impl
function sendTemplate(opts) {
  return new Promise(function (resolve, reject) {
    mandrill_client.messages.sendTemplate(opts, resolve, reject)
  })
}

// usage
sendTemplate({
  "template_name": template_name,
  "template_content": template_content,
  "message": message,
  "async": async,
  "ip_pool": ip_pool,
  "send_at": send_at
})
.then(handleSuccess)
.catch(handleError)

If it's just one method then you can do what @wavded said.

If you want to promisify whole module that has this convention, then use a custom promisifier using the promisifier option:

Promise.promisifyAll(..., {
    promisifier: function(cbApi) {
        return function() {
            var args = [].slice.call(arguments);
            var self = this;
            return new Promise(function(resolve, reject) {
                args.push(resolve, reject);
                cbApi.apply(self, args);
            });
        };
    }
});

@wavded Just wanted to say that your solution is awesome and saved me a lot of headaches.

Using npm Q:

return Q.Promise(function (resolve, reject, notify) {
  mandrill_client.messages.sendTemplate(opts, resolve, reject);
});

Thanks @wavded

This was really helpful @wavded. Shouldn't the sendTemplate function return the promise though instead of just create it? Thanks!

More generically:

Object.keys(mandrill_client.messages).forEach(function (key) {

  mandrill_client.messages[key + 'Async'] = function (opts) {

    return new Promise(function (resolve, reject) {
      mandrill_client.messages[key](opts, resolve, reject);
    });

  };

});
Was this page helpful?
0 / 5 - 0 ratings