shared-code-webhooks

This commit is contained in:
Victor Piousbox
2016-04-04 16:05:07 +00:00
parent d133fc08f2
commit 14b0ec8b03
13 changed files with 305 additions and 20 deletions

View File

@@ -148,5 +148,7 @@
"noSudoAccess": "You don't have sudo access.", "noSudoAccess": "You don't have sudo access.",
"couponCodeRequired": "The coupon code is required.", "couponCodeRequired": "The coupon code is required.",
"eventRequired": "\"req.params.event\" is required.", "eventRequired": "\"req.params.event\" is required.",
"countRequired": "\"req.query.count\" is required." "countRequired": "\"req.query.count\" is required.",
"invalidUrl": "invalid url",
"invalidEnabled": "the \"enabled\" parameter should be a boolean"
} }

View File

@@ -118,6 +118,9 @@ import purchase from './ops/purchase';
import purchaseHourglass from './ops/hourglassPurchase'; import purchaseHourglass from './ops/hourglassPurchase';
import readCard from './ops/readCard'; import readCard from './ops/readCard';
import openMysteryItem from './ops/openMysteryItem'; import openMysteryItem from './ops/openMysteryItem';
import addWebhook from './ops/addWebhook';
import updateWebhook from './ops/updateWebhook';
import deleteWebhook from './ops/deleteWebhook';
api.ops = { api.ops = {
scoreTask, scoreTask,
@@ -137,6 +140,9 @@ api.ops = {
purchaseHourglass, purchaseHourglass,
readCard, readCard,
openMysteryItem, openMysteryItem,
addWebhook,
updateWebhook,
deleteWebhook,
}; };
import handleTwoHanded from './fns/handleTwoHanded'; import handleTwoHanded from './fns/handleTwoHanded';

View File

@@ -1,15 +1,22 @@
import refPush from '../libs/refPush'; import refPush from '../libs/refPush';
import validator from 'validator';
import i18n from '../i18n';
import {
NotFound,
BadRequest,
} from '../libs/errors';
module.exports = function(user, req, cb) { module.exports = function(user, req) {
var wh; var wh;
wh = user.preferences.webhooks; wh = user.preferences.webhooks;
refPush(wh, {
if(!validator.isURL(req.body.url)) throw new BadRequest(i18n.t('invalidUrl', req.language));
if(!validator.isBoolean(req.body.enabled)) throw new BadRequest(i18n.t('invalidEnabled', req.language));
user.markModified('preferences.webhooks');
return refPush(wh, {
url: req.body.url, url: req.body.url,
enabled: req.body.enabled || true, enabled: req.body.enabled,
id: req.body.id
}); });
if (typeof user.markModified === "function") {
user.markModified('preferences.webhooks');
}
return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0;
}; };

View File

@@ -1,7 +1,5 @@
module.exports = function(user, req, cb) {
module.exports = function(user, req) {
delete user.preferences.webhooks[req.params.id]; delete user.preferences.webhooks[req.params.id];
if (typeof user.markModified === "function") { user.markModified('preferences.webhooks');
user.markModified('preferences.webhooks');
}
return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0;
}; };

View File

@@ -1,9 +1,16 @@
import _ from 'lodash'; import _ from 'lodash';
import validator from 'validator';
import i18n from '../i18n';
import {
NotFound,
BadRequest,
} from '../libs/errors';
module.exports = function(user, req, cb) { module.exports = function(user, req) {
_.merge(user.preferences.webhooks[req.params.id], req.body); if(!validator.isURL(req.body.url)) throw new BadRequest(i18n.t('invalidUrl', req.language));
if (typeof user.markModified === "function") { if(!validator.isBoolean(req.body.enabled)) throw new BadRequest(i18n.t('invalidEnabled', req.language));
user.markModified('preferences.webhooks');
} user.markModified('preferences.webhooks');
return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0; user.preferences.webhooks[req.params.id].url = req.body.url;
user.preferences.webhooks[req.params.id].enabled = req.body.enabled;
}; };

View File

@@ -358,6 +358,10 @@ gulp.task('test:api-v3:integration', (done) => {
pipe(runner); pipe(runner);
}); });
gulp.task('test:api-v3:integration:watch', () => {
gulp.watch(['website/src/controllers/api-v3/**/*', 'test/api/v3/integration/**/*', 'common/script/ops/*'], ['test:api-v3:integration']);
});
gulp.task('test:api-v3:integration:separate-server', (done) => { gulp.task('test:api-v3:integration:separate-server', (done) => {
let runner = exec( let runner = exec(
testBin('mocha test/api/v3/integration --recursive', 'LOAD_SERVER=0'), testBin('mocha test/api/v3/integration --recursive', 'LOAD_SERVER=0'),

View File

@@ -0,0 +1,23 @@
import {
generateUser,
} from '../../../../helpers/api-integration/v3';
let user;
let endpoint = '/user/webhook';
describe('DELETE /user/webhook', () => {
beforeEach(async () => {
user = await generateUser();
});
it('succeeds', async () => {
let id = 'some-id';
user.preferences.webhooks[id] = { url: 'http://some-url.com', enabled: true };
await user.sync();
expect(user.preferences.webhooks).to.eql({});
let response = await user.del(`${endpoint}/${id}`);
expect(response).to.eql({});
await user.sync();
expect(user.preferences.webhooks).to.eql({});
});
});

View File

@@ -0,0 +1,29 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
let user;
let endpoint = '/user/webhook';
describe('POST /user/webhook', () => {
beforeEach(async () => {
user = await generateUser();
});
it('validates', async () => {
await expect(user.post(endpoint, { enabled: true })).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidUrl'),
});
});
it('successfully adds the webhook', async () => {
expect(user.preferences.webhooks).to.eql({});
let response = await user.post(endpoint, { enabled: true, url: 'http://some-url.com'});
expect(response.id).to.exist;
await user.sync();
expect(user.preferences.webhooks).to.not.eql({});
});
});

View File

@@ -0,0 +1,32 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
let user;
let url = 'http://new-url.com';
let enabled = true;
describe('PUT /user/webhook/:id', () => {
beforeEach(async () => {
user = await generateUser();
});
it('validation fails', async () => {
await expect(user.put('/user/webhook/some-id'), { enabled: true }).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidUrl'),
});
});
it('succeeds', async () => {
let response = await user.post('/user/webhook', { enabled: true, url: 'http://some-url.com'});
await user.sync();
expect(user.preferences.webhooks[response.id].url).to.not.eql(url);
let response2 = await user.put(`/user/webhook/${response.id}`, {url, enabled});
expect(response2).to.eql({});
await user.sync();
expect(user.preferences.webhooks[response.id].url).to.eql(url);
});
});

View File

@@ -0,0 +1,57 @@
import addWebhook from '../../../common/script/ops/addWebhook';
import {
BadRequest,
} from '../../../common/script/libs/errors';
import i18n from '../../../common/script/i18n';
import {
generateUser,
} from '../../helpers/common.helper';
describe('shared.ops.addWebhook', () => {
let user;
let req;
beforeEach(() => {
user = generateUser();
req = { body: {
enabled: true,
url: 'http://some-url.com',
} };
});
context('adds webhook', () => {
it('validates req.body.url', (done) => {
delete req.body.url;
try {
addWebhook(user, req);
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUrl'));
done();
}
});
it('validates req.body.enabled', (done) => {
delete req.body.enabled;
try {
addWebhook(user, req);
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidEnabled'));
done();
}
});
it('calls marksModified()', () => {
user.markModified = sinon.spy();
addWebhook(user, req);
expect(user.markModified.called).to.eql(true);
});
it('succeeds', () => {
expect(user.preferences.webhooks).to.eql({});
addWebhook(user, req);
expect(user.preferences.webhooks).to.not.eql({});
});
});
});

View File

@@ -0,0 +1,20 @@
import deleteWebhook from '../../../common/script/ops/deleteWebhook';
import {
generateUser,
} from '../../helpers/common.helper';
describe('shared.ops.deleteWebhook', () => {
let user;
let req;
beforeEach(() => {
user = generateUser();
req = { params: { id: 'some-id' } };
});
it('succeeds', () => {
user.preferences.webhooks = { 'some-id': {} };
deleteWebhook(user, req);
expect(user.preferences.webhooks).to.eql({});
});
});

View File

@@ -0,0 +1,42 @@
import updateWebhook from '../../../common/script/ops/updateWebhook';
import {
BadRequest,
} from '../../../common/script/libs/errors';
import i18n from '../../../common/script/i18n';
import {
generateUser,
} from '../../helpers/common.helper';
describe('shared.ops.updateWebhook', () => {
let user;
let req;
let newUrl = 'http://new-url.com';
beforeEach(() => {
user = generateUser();
req = { params: {
id: 'this-id',
}, body: {
url: newUrl,
enabled: true,
} };
});
it('validates body', (done) => {
delete req.body.url;
try {
updateWebhook(user, req);
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUrl'));
done();
}
});
it('succeeds', () => {
let url = 'http://existing-url.com';
user.preferences.webhooks = { 'this-id': { url } };
updateWebhook(user, req);
expect(user.preferences.webhooks['this-id'].url).to.eql(newUrl);
});
});

View File

@@ -764,4 +764,62 @@ api.userOpenMysteryItem = {
}, },
}; };
/**
* @api {post} /user/webhook
* @apiVersion 3.0.0
* @apiName UserAddWebhook
* @apiGroup User
* @apiSuccess {}
**/
api.addWebhook = {
method: 'POST',
middlewares: [authWithHeaders()],
url: '/user/webhook',
async handler (req, res) {
let user = res.locals.user;
let result = common.ops.addWebhook(user, req);
await user.save();
res.respond(200, {id: result.id});
},
};
/**
* @api {put} /user/webhook/:id
* @apiVersion 3.0.0
* @apiName UserUpdateWebhook
* @apiGroup User
* @apiSuccess {}
**/
api.updateWebhook = {
method: 'PUT',
middlewares: [authWithHeaders()],
url: '/user/webhook/:id',
async handler (req, res) {
let user = res.locals.user;
common.ops.updateWebhook(user, req);
await user.save();
res.respond(200, {});
},
};
/**
* @api {delete} /user/webhook/:id
* @apiVersion 3.0.0
* @apiName UserDeleteWebhook
* @apiGroup User
* @apiSuccess {}
**/
api.deleteWebhook = {
method: 'DELETE',
middlewares: [authWithHeaders()],
url: '/user/webhook/:id',
async handler (req, res) {
let user = res.locals.user;
common.ops.deleteWebhook(user, req);
await user.save();
res.respond(200, {});
},
};
module.exports = api; module.exports = api;