From 0a834d42232ca1531c23724dbd875aa195f9815b Mon Sep 17 00:00:00 2001 From: Rish Date: Fri, 12 Oct 2018 00:22:38 +0530 Subject: [PATCH] Added webhooks controller to API v2 refs #9866 - Added new webhooks controller to v2 API - Added webhooks tests to v2 API --- core/server/api/v2/index.js | 4 + .../api/v2/utils/serializers/output/index.js | 4 + .../v2/utils/serializers/output/webhooks.js | 17 +++ core/server/api/v2/webhooks.js | 46 ++++++++ core/server/web/api/v2/admin/routes.js | 4 +- .../functional/api/v2/admin/webhooks_spec.js | 104 ++++++++++++++++++ 6 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 core/server/api/v2/utils/serializers/output/webhooks.js create mode 100644 core/server/api/v2/webhooks.js create mode 100644 core/test/functional/api/v2/admin/webhooks_spec.js diff --git a/core/server/api/v2/index.js b/core/server/api/v2/index.js index 2fc739e894..6136fff462 100644 --- a/core/server/api/v2/index.js +++ b/core/server/api/v2/index.js @@ -21,5 +21,9 @@ module.exports = { get slugs() { return shared.pipeline(require('./slugs'), localUtils); + }, + + get webhooks() { + return shared.pipeline(require('./webhooks'), localUtils); } }; diff --git a/core/server/api/v2/utils/serializers/output/index.js b/core/server/api/v2/utils/serializers/output/index.js index 6813947bfe..d2cbe88eb7 100644 --- a/core/server/api/v2/utils/serializers/output/index.js +++ b/core/server/api/v2/utils/serializers/output/index.js @@ -9,5 +9,9 @@ module.exports = { get slugs() { return require('./slugs'); + }, + + get webhooks() { + return require('./webhooks'); } }; diff --git a/core/server/api/v2/utils/serializers/output/webhooks.js b/core/server/api/v2/utils/serializers/output/webhooks.js new file mode 100644 index 0000000000..c843191bd5 --- /dev/null +++ b/core/server/api/v2/utils/serializers/output/webhooks.js @@ -0,0 +1,17 @@ +const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:webhooks'); + +module.exports = { + all(models, apiConfig, frame) { + debug('all'); + // CASE: e.g. destroy returns null + if (!models) { + return; + } + + frame.response = { + webhooks: [models.toJSON(frame.options)] + }; + + debug(frame.response); + } +}; diff --git a/core/server/api/v2/webhooks.js b/core/server/api/v2/webhooks.js new file mode 100644 index 0000000000..e1a182d494 --- /dev/null +++ b/core/server/api/v2/webhooks.js @@ -0,0 +1,46 @@ +const models = require('../../models'); +const common = require('../../lib/common'); + +module.exports = { + docName: 'webhooks', + add: { + statusCode: 201, + headers: {}, + options: [], + permissions: true, + query(frame) { + return models.Webhook.getByEventAndTarget( + frame.data.webhooks[0].event, + frame.data.webhooks[0].target_url, + frame.options + ).then((webhook) => { + if (webhook) { + return Promise.reject( + new common.errors.ValidationError({message: common.i18n.t('errors.api.webhooks.webhookAlreadyExists')}) + ); + } + + return models.Webhook.add(frame.data.webhooks[0], frame.options); + }); + } + }, + destroy: { + statusCode: 204, + headers: {}, + options: [ + 'id' + ], + validation: { + options: { + id: { + required: true + } + } + }, + permissions: true, + query(frame) { + frame.options.require = true; + return models.Webhook.destroy(frame.options).return(null); + } + } +}; diff --git a/core/server/web/api/v2/admin/routes.js b/core/server/web/api/v2/admin/routes.js index bfc6089c24..f9c34610db 100644 --- a/core/server/web/api/v2/admin/routes.js +++ b/core/server/web/api/v2/admin/routes.js @@ -210,8 +210,8 @@ module.exports = function apiRoutes() { ); // ## Webhooks (RESTHooks) - router.post('/webhooks', mw.authAdminAPI, api.http(api.webhooks.add)); - router.del('/webhooks/:id', mw.authAdminAPI, api.http(api.webhooks.destroy)); + router.post('/webhooks', mw.authAdminAPI, apiv2.http(apiv2.webhooks.add)); + router.del('/webhooks/:id', mw.authAdminAPI, apiv2.http(apiv2.webhooks.destroy)); // ## Oembed (fetch response from oembed provider) router.get('/oembed', mw.authAdminAPI, api.http(api.oembed.read)); diff --git a/core/test/functional/api/v2/admin/webhooks_spec.js b/core/test/functional/api/v2/admin/webhooks_spec.js new file mode 100644 index 0000000000..f57067a3a6 --- /dev/null +++ b/core/test/functional/api/v2/admin/webhooks_spec.js @@ -0,0 +1,104 @@ +const should = require('should'); +const supertest = require('supertest'); +const testUtils = require('../../../../utils'); +const localUtils = require('./utils'); +const config = require('../../../../../../core/server/config'); +const ghost = testUtils.startGhost; +let request; + +describe('Webhooks API', function () { + var ghostServer; + + describe('As Owner', function () { + var ownerAccessToken = ''; + + before(function () { + return ghost() + .then(function (_ghostServer) { + ghostServer = _ghostServer; + request = supertest.agent(config.get('url')); + }) + .then(function () { + return localUtils.doAuth(request); + }); + }); + + describe('Add', function () { + var newWebhook = { + event: 'test.create', + target_url: 'http://example.com/webhooks/test/1' + }; + + it('creates a new webhook', function (done) { + request.post(localUtils.API.getApiQuery('webhooks/')) + .set('Origin', config.get('url')) + .send({webhooks: [newWebhook]}) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(201) + .end(function (err, res) { + if (err) { + return done(err); + } + + var jsonResponse = res.body; + + should.exist(jsonResponse.webhooks); + + testUtils.API.checkResponse(jsonResponse.webhooks[0], 'webhook'); + + jsonResponse.webhooks[0].event.should.equal(newWebhook.event); + jsonResponse.webhooks[0].target_url.should.equal(newWebhook.target_url); + + done(); + }); + }); + }); + + describe('Delete', function () { + var newWebhook = { + event: 'test.create', + // a different target_url from above is needed to avoid an "already exists" error + target_url: 'http://example.com/webhooks/test/2' + }; + + it('deletes a webhook', function (done) { + // create the webhook that is to be deleted + request.post(localUtils.API.getApiQuery('webhooks/')) + .set('Origin', config.get('url')) + .send({webhooks: [newWebhook]}) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(201) + .end(function (err, res) { + if (err) { + return done(err); + } + + var location = res.headers.location; + var jsonResponse = res.body; + + should.exist(jsonResponse.webhooks); + testUtils.API.checkResponse(jsonResponse.webhooks[0], 'webhook'); + + jsonResponse.webhooks[0].event.should.equal(newWebhook.event); + jsonResponse.webhooks[0].target_url.should.equal(newWebhook.target_url); + + // begin delete test + request.del(localUtils.API.getApiQuery('webhooks/' + jsonResponse.webhooks[0].id + '/')) + .set('Origin', config.get('url')) + .expect(204) + .end(function (err, res) { + if (err) { + return done(err); + } + + res.body.should.be.empty(); + + done(); + }); + }); + }); + }); + }); +});