ac-node-hipchat
Version:
A common module plugin for building Atlassian Connect add-ons for HipChat
127 lines (118 loc) • 3.52 kB
JavaScript
var check = require('check-types');
var verify = check.verify;
var urls = require('url');
var crypto = require('crypto');
var _ = require('lodash');
var param = encodeURIComponent;
var events = [
'room_enter',
'room_exit',
'room_message',
'room_notification',
'room_topic_change'
];
var self = exports;
exports.normalize = function (definition, baseUrl, defaultMountPath, token) {
check.map(definition, {
event: verify.string,
name: verify.maybe.string,
url: verify.maybe.string
});
verify.webUrl(baseUrl);
if (events.indexOf(definition.event) === -1) {
throw new Error('Unrecognized webhook event: ' + definition.event);
}
definition = _.extend({}, definition);
if (definition.event === 'room_message') {
verify.defined(definition.pattern)
if (_.isRegExp(definition.pattern)) {
definition.pattern = exports.regExpToPattern(definition.pattern);
} else {
verify.string(definition.pattern);
}
}
// parse salient path info from the url
if (definition.url) {
definition.url = exports.stripNameParam(definition.url);
}
var parsed = self.parseUrl(definition.url);
// re-assemble the webhook url with the base url, as a sanity check
var fullPath = parsed ? parsed.fullPath : defaultMountPath;
definition.url = urls.resolve(baseUrl, fullPath);
if (token) {
definition.url += '?token=' + param(token);
}
if (!definition.name) {
definition.name = exports.digest(definition);
}
definition.url += (definition.url.indexOf('?') > 0 ? '&' : '?') + 'name=' + param(definition.name);
return definition;
};
exports.parseUrl = function (url) {
var result;
if (check.webUrl(url)) {
// full urls
parsed = urls.parse(url);
result = {
fullPath: parsed.path,
mountPath: parsed.pathname
};
} else if (check.string(url)) {
// path-only shorthand
result = {
fullPath: url,
mountPath: url.split('?')[0]
};
}
return result;
};
exports.regExpToPattern = function (re) {
var pattern = re.toString();
pattern = pattern.replace(/\\u([\da-fA-F]{4,4})/g, function ($0, $1) {
return '\\x{' + $1 + '}';
});
var match = /^\/(.*)\/([gimy]+)?/.exec(pattern);
if (match) {
pattern = match[1];
if (match[2]) {
pattern = '(?' + match[2] + ')' + pattern;
}
}
return pattern;
};
exports.patternToRegExp = function (pattern) {
var match = /\(\?([gimy])+\)(.*)/.exec(pattern);
var flags;
if (match) {
flags = match[1];
pattern = match[2];
}
pattern = pattern.replace(/\\x\{([\da-fA-F]{4,4})\}/g, function ($0, $1) {
return '\\u' + $1 + '';
});
return new RegExp(pattern, flags);
};
exports.digest = function (webhook) {
webhook = _.extend({}, webhook);
if (webhook.url) {
webhook.url = exports.stripNameParam(webhook.url);
}
// url encode other webhook params to generate a stable hash as the name that
// will change if any of the fields change
var fields = ['event', 'url'];
if (webhook.event === 'room_message') {
verify.defined(webhook.pattern)
fields.push('pattern');
}
var str = fields.filter(function (key) {
return !!webhook[key];
}).map(function (key) {
return key + '=' + encodeURIComponent(webhook[key].toString());
}).join('&');
return crypto.createHash('sha1').update(str).digest('hex');
};
exports.stripNameParam = function (url) {
return url.replace(/(.*)(\?|&)name=[^&]+&?(.*)/, function ($0, $1, $2, $3) {
return $1 + ($3 ? $2 : '') + ($3 || '');
});
};