Refactored webhook triggering to be asyc

refs https://github.com/TryGhost/Toolbox/issues/283

- In tests we need assurance that the triggering of webhooks has been finished before making assertions. Doing this was impossible with a previous fire-and-forget style of the request call.
- The change also adds an optional "request" parameter to be able to override the request library used internally - this is purely for testing purposes.
This commit is contained in:
Naz 2022-05-12 11:35:15 +08:00 committed by naz
parent 67dca08df8
commit 21c2c5579b
2 changed files with 55 additions and 7 deletions

View File

@ -1,12 +1,19 @@
const debug = require('@tryghost/debug')('services:webhooks:trigger'); const debug = require('@tryghost/debug')('services:webhooks:trigger');
const logging = require('@tryghost/logging'); const logging = require('@tryghost/logging');
const request = require('@tryghost/request');
const ghostVersion = require('@tryghost/version');
class WebhookTrigger { 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.models = models;
this.payload = payload; this.payload = payload;
this.request = request ?? require('@tryghost/request');
} }
getAll(event) { getAll(event) {
@ -72,7 +79,7 @@ class WebhookTrigger {
debug(`${hooks.models.length} webhooks found for ${event}.`); 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 hookPayload = await this.payload(webhook.get('event'), model);
const reqPayload = JSON.stringify(hookPayload); const reqPayload = JSON.stringify(hookPayload);
@ -89,10 +96,10 @@ class WebhookTrigger {
logging.info(`Triggering webhook for "${webhook.get('event')}" with url "${url}"`); logging.info(`Triggering webhook for "${webhook.get('event')}" with url "${url}"`);
request(url, opts) await this.request(url, opts)
.then(response.onSuccess(webhook)) .then(response.onSuccess(webhook))
.catch(response.onError(webhook)); .catch(response.onError(webhook));
}); }
} }
} }

View File

@ -14,7 +14,7 @@ describe('Webhook Service', function () {
} }
}; };
const payload = sinon.stub().resolves(null); let payload = sinon.stub().resolves(null);
afterEach(function () { afterEach(function () {
sinon.restore(); sinon.restore();
@ -36,5 +36,46 @@ describe('Webhook Service', function () {
should.equal(models.Webhook.findAllByEvent.called, true); should.equal(models.Webhook.findAllByEvent.called, true);
should.equal(payload.called, false); 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');
});
}); });
}); });