UNPKG

ac-node-hipchat

Version:

A common module plugin for building Atlassian Connect add-ons for HipChat

127 lines (118 loc) 3.52 kB
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 || ''); }); };