diff --git a/tasks/gulp-console.js b/tasks/gulp-console.js index 95a1962fd4..768df81e3b 100644 --- a/tasks/gulp-console.js +++ b/tasks/gulp-console.js @@ -2,7 +2,7 @@ import 'coffee-script'; import mongoose from 'mongoose'; import autoinc from 'mongoose-id-autoinc'; -import logging from '../website/src/libs/logging'; +import logging from '../website/src/libs/api-v2/logging'; import nconf from 'nconf'; import utils from '../website/src/libs/utils'; import repl from 'repl'; diff --git a/test/api/v3/unit/libs/webhooks.test.js b/test/api/v3/unit/libs/webhooks.test.js new file mode 100644 index 0000000000..62aeee3922 --- /dev/null +++ b/test/api/v3/unit/libs/webhooks.test.js @@ -0,0 +1,136 @@ +import request from 'request'; +import { sendTaskWebhook } from '../../../../../website/src/libs/api-v3/webhook'; + +describe('webhooks', () => { + + beforeEach(() => { + sandbox.stub(request, 'post'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('sendTaskWebhook', () => { + let task = { + details: { _id: 'task-id' }, + delta: 1.4, + direction: 'up' + }; + + let data = { + task: task, + user: { _id: 'user-id' } + }; + + it('does not send if no webhook endpoints exist', () => { + let webhooks = { }; + + sendTaskWebhook(webhooks, data); + + expect(request.post).to.not.be.called; + }); + + it('does not send if no webhooks are enabled', () => { + let webhooks = { + 'some-id': { + sort: 0, + id: 'some-id', + enabled: false, + url: 'http://example.org/endpoint' + } + }; + + sendTaskWebhook(webhooks, data); + + expect(request.post).to.not.be.called; + }); + + it('does not send if webhook url is not valid', () => { + let webhooks = { + 'some-id': { + sort: 0, + id: 'some-id', + enabled: true, + url: 'http://malformedurl/endpoint' + } + }; + + sendTaskWebhook(webhooks, data); + + expect(request.post).to.not.be.called; + }); + + it('sends task direction, task, task delta, and abridged user data', () => { + let webhooks = { + 'some-id': { + sort: 0, + id: 'some-id', + enabled: true, + url: 'http://example.org/endpoint' + } + }; + + sendTaskWebhook(webhooks, data); + + expect(request.post).to.be.calledOnce; + expect(request.post).to.be.calledWith({ + url: 'http://example.org/endpoint', + body: { + direction: 'up', + task: { _id: 'task-id' }, + delta: 1.4, + user: { + _id: 'user-id' + } + }, + json: true + }); + }); + + it('sends a post request for each webhook endpoint', () => { + let webhooks = { + 'some-id': { + sort: 0, + id: 'some-id', + enabled: true, + url: 'http://example.org/endpoint' + }, + 'second-webhook': { + sort: 1, + id: 'second-webhook', + enabled: true, + url: 'http://example.com/2/endpoint' + } + }; + + sendTaskWebhook(webhooks, data); + + expect(request.post).to.be.calledTwice; + expect(request.post).to.be.calledWith({ + url: 'http://example.org/endpoint', + body: { + direction: 'up', + task: { _id: 'task-id' }, + delta: 1.4, + user: { + _id: 'user-id' + } + }, + json: true + }); + expect(request.post).to.be.calledWith({ + url: 'http://example.com/2/endpoint', + body: { + direction: 'up', + task: { _id: 'task-id' }, + delta: 1.4, + user: { + _id: 'user-id' + } + }, + json: true + }); + }); + }); +}); diff --git a/test/server_side/analytics.test.js b/test/server_side/analytics.test.js index 7cde7aede8..56dd8f1787 100644 --- a/test/server_side/analytics.test.js +++ b/test/server_side/analytics.test.js @@ -30,7 +30,7 @@ describe('analytics', function() { }); describe('init', function() { - var analytics = rewire('../../website/src/libs/analytics'); + var analytics = rewire('../../website/src/libs/api-v2/analytics'); it('throws an error if no options are passed in', function() { expect(analytics).to.throw('No options provided'); @@ -62,7 +62,7 @@ describe('analytics', function() { describe('track', function() { var analyticsData, event_type; - var analytics = rewire('../../website/src/libs/analytics'); + var analytics = rewire('../../website/src/libs/api-v2/analytics'); var initializedAnalytics; beforeEach(function() { @@ -370,7 +370,7 @@ describe('analytics', function() { var purchaseData; - var analytics = rewire('../../website/src/libs/analytics'); + var analytics = rewire('../../website/src/libs/api-v2/analytics'); var initializedAnalytics; beforeEach(function() { diff --git a/website/src/controllers/api-v2/challenges.js b/website/src/controllers/api-v2/challenges.js index 72f28d9f3d..21104f2885 100644 --- a/website/src/controllers/api-v2/challenges.js +++ b/website/src/controllers/api-v2/challenges.js @@ -7,7 +7,7 @@ var shared = require('../../../../common'); var User = require('./../../models/user').model; var Group = require('./../../models/group').model; var Challenge = require('./../../models/challenge').model; -var logging = require('./../../libs/logging'); +var logging = require('./../../libs/api-v2/logging'); var csv = require('express-csv'); var utils = require('../../libs/utils'); var api = module.exports; diff --git a/website/src/controllers/api-v2/user.js b/website/src/controllers/api-v2/user.js index b3a83dd80c..ed8aa05f25 100644 --- a/website/src/controllers/api-v2/user.js +++ b/website/src/controllers/api-v2/user.js @@ -12,11 +12,11 @@ var analytics = utils.analytics; var Group = require('./../../models/group').model; var Challenge = require('./../../models/challenge').model; var moment = require('moment'); -var logging = require('./../../libs/logging'); +var logging = require('./../../libs/api-v2/logging'); var acceptablePUTPaths; var api = module.exports; var firebase = require('../../libs/firebase'); -var webhook = require('../../libs/webhook'); +var webhook = require('../../libs/api-v2/webhook'); // api.purchase // Shared.ops diff --git a/website/src/controllers/payments/paypal.js b/website/src/controllers/payments/paypal.js index 30970f72cf..19f9cc4136 100644 --- a/website/src/controllers/payments/paypal.js +++ b/website/src/controllers/payments/paypal.js @@ -5,7 +5,7 @@ var _ = require('lodash'); var url = require('url'); var User = require('mongoose').model('User'); var payments = require('./index'); -var logger = require('../../libs/logging'); +var logger = require('../../libs/api-v2/logging'); var ipn = require('paypal-ipn'); var paypal = require('paypal-rest-sdk'); var shared = require('../../../../common'); diff --git a/website/src/index.js b/website/src/index.js index 3f21cedbfb..9454b0c46b 100644 --- a/website/src/index.js +++ b/website/src/index.js @@ -2,12 +2,12 @@ require('babel/register'); // TODO remove this once we've fully converted over -require('coffee-script'); +require('coffee-script'); // Only do the minimal amount of work before forking just in case of a dyno restart var cluster = require('cluster'); var nconf = require('nconf'); -var logging = require('./libs/logging'); +var logging = require('./libs/api-v2/logging'); // Initialize configuration var setupNconf = require('./libs/api-v3/setupNconf'); @@ -33,4 +33,4 @@ if (cores !== 0 && cluster.isMaster && (IS_DEV || IS_PROD)) { }); } else { module.exports = require('./server.js'); -} \ No newline at end of file +} diff --git a/website/src/libs/analytics.js b/website/src/libs/api-v2/analytics.js similarity index 100% rename from website/src/libs/analytics.js rename to website/src/libs/api-v2/analytics.js diff --git a/website/src/libs/logging.js b/website/src/libs/api-v2/logging.js similarity index 100% rename from website/src/libs/logging.js rename to website/src/libs/api-v2/logging.js diff --git a/website/src/libs/webhook.js b/website/src/libs/api-v2/webhook.js similarity index 100% rename from website/src/libs/webhook.js rename to website/src/libs/api-v2/webhook.js diff --git a/website/src/libs/api-v3/webhook.js b/website/src/libs/api-v3/webhook.js new file mode 100644 index 0000000000..f1c6f13df2 --- /dev/null +++ b/website/src/libs/api-v3/webhook.js @@ -0,0 +1,30 @@ +import { each } from 'lodash'; +import { post } from 'request'; +import { isURL } from 'validator'; + +let _sendWebhook = (url, body) => { + post({ + url, + body, + json: true, + }); +}; + +let _isInvalidWebhook = (hook) => { + return !hook.enabled || !isURL(hook.url); +}; + +export function sendTaskWebhook (webhooks, data) { + each(webhooks, (hook) => { + if (_isInvalidWebhook(hook)) return; + + let body = { + direction: data.task.direction, + task: data.task.details, + delta: data.task.delta, + user: data.user, + }; + + _sendWebhook(hook.url, body); + }); +} diff --git a/website/src/libs/utils.js b/website/src/libs/utils.js index c8944c9ccc..1016a35daf 100644 --- a/website/src/libs/utils.js +++ b/website/src/libs/utils.js @@ -16,7 +16,7 @@ module.exports.sendEmail = function(mailData) { } }); smtpTransport.sendMail(mailData, function(error, response){ - var logging = require('./logging'); + var logging = require('./api-v2/logging'); if(error) logging.error(error); else logging.info("Message sent: " + response.message); smtpTransport.close(); // shut down the connection pool, no more messages @@ -172,7 +172,7 @@ module.exports.setupConfig = function(){ //if (nconf.get('IS_PROD')) //require('newrelic'); - var analytics = IS_PROD && require('./analytics'); + var analytics = IS_PROD && require('./api-v2/analytics'); var analyticsTokens = { amplitudeToken: nconf.get('AMPLITUDE_KEY'), googleAnalytics: nconf.get('GA_ID') diff --git a/website/src/middlewares/errorHandler.js b/website/src/middlewares/errorHandler.js index 9a82316a75..36daacdf2b 100644 --- a/website/src/middlewares/errorHandler.js +++ b/website/src/middlewares/errorHandler.js @@ -1,4 +1,4 @@ -var logging = require('../libs/logging'); +var logging = require('../libs/api-v2/logging'); module.exports = function(err, req, res, next) { //res.locals.domain.emit('error', err); diff --git a/website/src/models/group.js b/website/src/models/group.js index 7ba8dac173..b2da0011d7 100644 --- a/website/src/models/group.js +++ b/website/src/models/group.js @@ -4,7 +4,7 @@ var User = require('./user').model; var shared = require('../../../common'); var _ = require('lodash'); var async = require('async'); -var logging = require('../libs/logging'); +var logging = require('../libs/api-v2/logging'); var Challenge = require('./../models/challenge').model; var firebase = require('../libs/firebase'); diff --git a/website/src/server.js b/website/src/server.js index 74c8e87e5e..224022b192 100644 --- a/website/src/server.js +++ b/website/src/server.js @@ -1,7 +1,7 @@ // TODO cleanup all comments when finished API v3 import nconf from 'nconf'; -import logging from './libs/logging'; +import logging from './libs/api-v2/logging'; import utils from './libs/utils'; import express from 'express'; import http from 'http'; @@ -161,4 +161,4 @@ server.listen(app.get('port'), () => { return logging.info(`Express server listening on port ${app.get('port')}`); }); -export default server; \ No newline at end of file +export default server;