diff --git a/core/server/services/webhooks/trigger.js b/core/server/services/webhooks/trigger.js index 4dfbce1db4..d4ecb86559 100644 --- a/core/server/services/webhooks/trigger.js +++ b/core/server/services/webhooks/trigger.js @@ -1,12 +1,19 @@ const debug = require('@tryghost/debug')('services:webhooks:trigger'); const logging = require('@tryghost/logging'); -const request = require('@tryghost/request'); -const ghostVersion = require('@tryghost/version'); class WebhookTrigger { - constructor({models, payload}){ + /** + * + * @param {Object} options + * @param {Object} options.models - Ghost models + * @param {Function} options.payload - Function to generate payload + * @param {Object} [options.request] - HTTP request handling library + */ + constructor({models, payload, request}){ this.models = models; this.payload = payload; + + this.request = request ?? require('@tryghost/request'); } getAll(event) { @@ -72,7 +79,7 @@ class WebhookTrigger { debug(`${hooks.models.length} webhooks found for ${event}.`); - hooks.models.forEach(async (webhook) => { + for (const webhook of hooks.models) { const hookPayload = await this.payload(webhook.get('event'), model); const reqPayload = JSON.stringify(hookPayload); @@ -89,10 +96,10 @@ class WebhookTrigger { logging.info(`Triggering webhook for "${webhook.get('event')}" with url "${url}"`); - request(url, opts) + await this.request(url, opts) .then(response.onSuccess(webhook)) .catch(response.onError(webhook)); - }); + } } } diff --git a/test/unit/server/services/webhooks/trigger.test.js b/test/unit/server/services/webhooks/trigger.test.js index fbb022d67a..5174323894 100644 --- a/test/unit/server/services/webhooks/trigger.test.js +++ b/test/unit/server/services/webhooks/trigger.test.js @@ -14,7 +14,7 @@ describe('Webhook Service', function () { } }; - const payload = sinon.stub().resolves(null); + let payload = sinon.stub().resolves(null); afterEach(function () { sinon.restore(); @@ -36,5 +36,46 @@ describe('Webhook Service', function () { should.equal(models.Webhook.findAllByEvent.called, true); should.equal(payload.called, false); }); + + it('Does triggers payload handler and request when event when model has a registered hook', async function () { + const postModelStub = sinon.stub(); + const webhookModelStub = { + get: () => {} + }; + sinon.stub(webhookModelStub, 'get') + .withArgs('event').returns('post.added') + .withArgs('target_url').returns('http://example.com'); + + const requestStub = sinon.stub().resolves({}); + + sinon.stub(models.Webhook, 'findAllByEvent') + .withArgs('post.added', {context: {internal: true}}) + .resolves({models: [webhookModelStub]}); + + payload = sinon.stub().resolves({data: [1]}); + + const webhookTrigger = new WebhookTrigger({ + models, + payload, + request: requestStub + }); + + sinon.stub(webhookTrigger, 'onSuccess').callsFake(function () { + return Promise.resolve(); + }); + sinon.stub(webhookTrigger, 'onError').callsFake(function () { + return Promise.resolve(); + }); + await webhookTrigger.trigger('post.added', postModelStub); + + should.equal(models.Webhook.findAllByEvent.called, true); + should.equal(payload.called, true); + + should.equal(requestStub.called, true); + should.equal(requestStub.args[0][0], 'http://example.com'); + should.deepEqual(requestStub.args[0][1].body, '{"data":[1]}'); + should.equal(requestStub.args[0][1].headers['Content-Length'], 12); + should.equal(requestStub.args[0][1].headers['Content-Length'], 'application/json'); + }); }); });