mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
shared-code-webhooks
This commit is contained in:
@@ -148,5 +148,7 @@
|
||||
"noSudoAccess": "You don't have sudo access.",
|
||||
"couponCodeRequired": "The coupon code 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"
|
||||
}
|
||||
|
||||
@@ -118,6 +118,9 @@ import purchase from './ops/purchase';
|
||||
import purchaseHourglass from './ops/hourglassPurchase';
|
||||
import readCard from './ops/readCard';
|
||||
import openMysteryItem from './ops/openMysteryItem';
|
||||
import addWebhook from './ops/addWebhook';
|
||||
import updateWebhook from './ops/updateWebhook';
|
||||
import deleteWebhook from './ops/deleteWebhook';
|
||||
|
||||
api.ops = {
|
||||
scoreTask,
|
||||
@@ -137,6 +140,9 @@ api.ops = {
|
||||
purchaseHourglass,
|
||||
readCard,
|
||||
openMysteryItem,
|
||||
addWebhook,
|
||||
updateWebhook,
|
||||
deleteWebhook,
|
||||
};
|
||||
|
||||
import handleTwoHanded from './fns/handleTwoHanded';
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
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;
|
||||
wh = user.preferences.webhooks;
|
||||
refPush(wh, {
|
||||
url: req.body.url,
|
||||
enabled: req.body.enabled || true,
|
||||
id: req.body.id
|
||||
});
|
||||
if (typeof user.markModified === "function") {
|
||||
|
||||
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 typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0;
|
||||
|
||||
return refPush(wh, {
|
||||
url: req.body.url,
|
||||
enabled: req.body.enabled,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
module.exports = function(user, req, cb) {
|
||||
|
||||
module.exports = function(user, req) {
|
||||
delete user.preferences.webhooks[req.params.id];
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('preferences.webhooks');
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, user.preferences.webhooks) : void 0;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import _ from 'lodash';
|
||||
import validator from 'validator';
|
||||
import i18n from '../i18n';
|
||||
import {
|
||||
NotFound,
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function(user, req) {
|
||||
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));
|
||||
|
||||
module.exports = function(user, req, cb) {
|
||||
_.merge(user.preferences.webhooks[req.params.id], req.body);
|
||||
if (typeof user.markModified === "function") {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -358,6 +358,10 @@ gulp.task('test:api-v3:integration', (done) => {
|
||||
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) => {
|
||||
let runner = exec(
|
||||
testBin('mocha test/api/v3/integration --recursive', 'LOAD_SERVER=0'),
|
||||
|
||||
@@ -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({});
|
||||
});
|
||||
});
|
||||
29
test/api/v3/integration/user/POST-user_add_webhook.test.js
Normal file
29
test/api/v3/integration/user/POST-user_add_webhook.test.js
Normal 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({});
|
||||
});
|
||||
});
|
||||
32
test/api/v3/integration/user/PUT-user_update_webhook.test.js
Normal file
32
test/api/v3/integration/user/PUT-user_update_webhook.test.js
Normal 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);
|
||||
});
|
||||
});
|
||||
57
test/common/ops/addWebhook.test.js
Normal file
57
test/common/ops/addWebhook.test.js
Normal 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({});
|
||||
});
|
||||
});
|
||||
});
|
||||
20
test/common/ops/deleteWebhook.test.js
Normal file
20
test/common/ops/deleteWebhook.test.js
Normal 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({});
|
||||
});
|
||||
});
|
||||
42
test/common/ops/updateWebhook.test.js
Normal file
42
test/common/ops/updateWebhook.test.js
Normal 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);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user