This is a guest post written by Topher Marie, VP of Engineering at Jump Cloud, a fellow Boulder startup and VictorOps user.

WebHooks are a powerful tool. They allow developers and operations personnel to wire together applications — I can have notifications sent from GitHub into HipChat, for instance, so that whenever somebody submits a pull request, I get an immediate notification in my chat console. This kind of ad-hoc integration lets you build rich workflows that are very customized to exactly your organization’s needs.

Unfortunately, the integration isn’t always so straightforward. Many applications are preconfigured to work with other common and popular applications, but obviously this doesn’t include everything. In this post, I’ll discuss how we can setup a relay or translator of sorts, so that the output of one application can be transformed into the input of another directly. No need to depend on them to build the integration, we can do it ourselves.

Here at JumpCloud, we have an account with UserVoice to handle our support requests. When someone submits a ticket there, we’d like the appropriate person to be notified immediately. UserVoice does send an email… but email gets lost in the morass. Especially if it spams all of the engineers, not just the one on call. We need something a little more targeted.

There’s a great product we use for this – VictorOps. They allow us to automatically notify the correct on-call person, and they can choose to be notified by the app, by text, by email – whatever works for them. Perfect for this scenario.

What I’d like is for UserVoice to alert me via VictorOps when something happens, but there is not a direct predefined integration. Well, let’s wire them up. Luckily, they both have webhook interfaces defined. UserVoice will emit an HTTP request when someone creates a ticket (okay, technically it’s a POST) and VictorOps can be configured to receive these requests.

Unfortunately their payloads are not compatible. UserVoice will produce JSON that looks like

{
“ticket”: {
“id”: 2743355,
“ticket_number”: 22,
“subject”: “I can’t find my stapler”,
“state”: “closed”,
“url”: “http://initech.uservoice.com/admin/tickets/22”

}

VictorOps wants the payload to look like this:

{
“message_type”:”CRITICAL”,
“timestamp”:”1383239337″
“entity_id”:”disk space/db01.mycompany.com”,
“state_message”:”the disk is really really full”
}

Here’s where our webhook relay is going to come into play. I’m going to setup a simple node that will consume the payload from UserVoice, translate it into the format that VictorOps expects, and forward on the request to their endpoint.

First step is to set up a node.js server that will accept the HTTP POST output from UserVoice.

web.js

var express = require(“express”);
var app = express();
app.use(express.bodyParser());
app.post(‘/’, function(req, res) {
console.log(req.body);
});
var port = process.env.PORT || 5000;
app.listen(port, function() {
console.log(“Listening on ” + port);
});

Create a new payload matching the format that VictorOps would like.

web.js

var express = require(“express”);
var app = express();
app.use(express.bodyParser());
app.post(‘/’, function(req, res) {
console.log(req.body);
var payload = JSON.parse(req.body.data);
var entity_id = payload.ticket.id;
var subject = payload.ticket.subject;
var entity_display_name = “Ticket ” + payload.ticket.ticket_number;
var monitoring_tool = “UserVoice”;
var victorOpsJSON = { message_type:”CRITICAL”,
   entity_id:entity_id,
   state_message:subject,
   monitoring_tool: monitoring_tool,
   entity_display_name: entity_display_name
 };
 var victorOpsString = JSON.stringify(victorOpsJSON);
 console.log(victorOpsString);
});
var port = process.env.PORT || 5000;
app.listen(port, function() {
console.log(“Listening on ” + port);
});

Finally, POST the new payload to the VictorOps URL for its WebHook interface

web.js

var express = require(“express”);
var app = express();
app.use(express.bodyParser());
app.post(‘/’, function(req, res) {
console.log(req.body);
var payload = JSON.parse(req.body.data);
var entity_id = payload.ticket.id;
var subject = payload.ticket.subject;
var entity_display_name = “Ticket ” + payload.ticket.ticket_number;
var monitoring_tool = “UserVoice”;
var victorOpsJSON = { message_type:”CRITICAL”,
entity_id:entity_id,
state_message:subject,
monitoring_tool: monitoring_tool,
entity_display_name: entity_display_name
};
var victorOpsString = JSON.stringify(victorOpsJSON);

var headers = {
   ‘Content-Type’: ‘application/json’,
   ‘Content-Length’: victorOpsString.length
 };
 var options = {
   host: ‘alert.victorops.com’,
   port: 443,
   path: ‘/integrations/generic/20131114/alert/33aba56f-fc78-4c65-9c6d-57a49cbb6ed8/uservoicetest’,
   method: ‘POST’,
   headers: headers
 };
 var remote_request = https.request(options, function(remote_response) {
 remote_response.setEncoding(‘utf-8’);
 var responseString = ”;
 remote_response.on(‘data’, function(data) {
   responseString += data;
 });
 remote_response.on(‘end’, function() {
   var resultObject = JSON.parse(responseString);
   console.log(resultObject);
   res.send(resultObject);
 });
});
var port = process.env.PORT || 5000;
app.listen(port, function() {
console.log(“Listening on ” + port);
});

I’m going to use Heroku to spin up a quick and easy server. Some great instructions for getting started with Heroku and Node are here if you are not familiar with the process. Pushing my repo up there…

topher@ubuntu13:~/uservoice-victorops-relay$ git push heroku master
….
….
—–> Launching… done, v7
http://uservoice-victorops-relay.herokuapp.com deployed to Heroku

To git@heroku.com:uservoice-victorops-relay.git
582861c..1d50254  master -> master

 

Configuring The Relay from UserVoice

My server name there is http://uservoice-victorops-relay.herokuapp.com, so I’ll configure that in the UserVoice application.

Create a test ticket at UserVoice

 

The result? A notification from VictorOps that someone has posted a ticket to my UserVoice support queue.

 

WebHooks are such a powerful tool, allowing you to wire together products and create complex interactions — almost meta-programming. They don’t always fit together perfectly, but a simple relay like this gives DevOps engineers like you the ability to build the behavior that best suits your needs.