Fixed webhooks not firing for internal integrations

ref https://linear.app/ghost/issue/AP-598

We want to make sure that webhooks for internal integrations are fired even
when the custom integrations limit has been hit. In order to test this we've
had to update the fixtureManager to allow passing the integration type when
inserting fixture webhooks into the db.
This commit is contained in:
Fabien O'Carroll 2024-11-20 17:54:30 +07:00
parent 263c493e2b
commit 3265e257b7
6 changed files with 77 additions and 8 deletions

View File

@ -27,9 +27,18 @@ class WebhookTrigger {
const overLimit = await this.limitService.checkWouldGoOverLimit('customIntegrations'); const overLimit = await this.limitService.checkWouldGoOverLimit('customIntegrations');
if (overLimit) { if (overLimit) {
logging.info(`Skipping all webhooks for event ${event}. The "customIntegrations" plan limit is enabled.`); logging.info(`Skipping all non-internal webhooks for event ${event}. The "customIntegrations" plan limit is enabled.`);
const result = await this.models
.Webhook
.findAllByEvent(event, {
context: {internal: true},
withRelated: ['integration']
});
return { return {
models: [] models: result?.models?.filter((model) => {
return model.related('integration')?.get('type') === 'internal';
}) || []
}; };
} }
} }

View File

@ -11,3 +11,15 @@ Object {
`; `;
exports[`site.* events site.changed event is triggered 2: [body] 1`] = `Object {}`; exports[`site.* events site.changed event is triggered 2: [body] 1`] = `Object {}`;
exports[`site.* events site.changed event is triggered, custom integrations are limited but we have an internal webhook 1: [headers] 1`] = `
Object {
"accept-encoding": "gzip, deflate, br",
"content-length": Any<Number>,
"content-type": "application/json",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"user-agent": StringMatching /Ghost\\\\/\\\\d\\+\\\\\\.\\\\d\\+\\\\\\.\\\\d\\+\\\\s\\\\\\(https:\\\\/\\\\/github\\.com\\\\/TryGhost\\\\/Ghost\\\\\\)/,
}
`;
exports[`site.* events site.changed event is triggered, custom integrations are limited but we have an internal webhook 2: [body] 1`] = `Object {}`;

View File

@ -87,4 +87,40 @@ describe('site.* events', function () {
throw new Error('The webhook should not have been sent.'); throw new Error('The webhook should not have been sent.');
} }
}); });
it('site.changed event is triggered, custom integrations are limited but we have an internal webhook', async function () {
const webhookURL = 'https://test-webhook-receiver.com/site-changed';
await webhookMockReceiver.mock(webhookURL);
await fixtureManager.insertWebhook({
event: 'site.changed',
url: webhookURL,
integrationType: 'internal'
});
mockManager.mockLimitService('customIntegrations', {
isLimited: true,
wouldGoOverLimit: true
});
await adminAPIAgent
.post('posts/')
.body({
posts: [{
title: 'webhookz',
status: 'published',
mobiledoc: fixtureManager.get('posts', 1).mobiledoc
}]
})
.expectStatus(201);
await webhookMockReceiver.receivedRequest();
webhookMockReceiver
.matchHeaderSnapshot({
'content-version': anyContentVersion,
'content-length': anyNumber,
'user-agent': anyGhostAgent
})
.matchBodySnapshot();
});
}); });

View File

@ -61,15 +61,19 @@ describe('Webhook Service', function () {
it('does not trigger payload handler when there are hooks registered for an event, but the custom integrations limit is active', async function () { it('does not trigger payload handler when there are hooks registered for an event, but the custom integrations limit is active', async function () {
const webhookModel = { const webhookModel = {
get: sinon.stub() get: sinon.stub(),
related: sinon.stub()
}; };
webhookModel.get webhookModel.get
.withArgs('event').returns(WEBHOOK_EVENT) .withArgs('event').returns(WEBHOOK_EVENT)
.withArgs('target_url').returns(WEBHOOK_TARGET_URL); .withArgs('target_url').returns(WEBHOOK_TARGET_URL);
webhookModel.related
.withArgs('integration').returns(null);
models.Webhook.findAllByEvent models.Webhook.findAllByEvent
.withArgs(WEBHOOK_EVENT, {context: {internal: true}}) .withArgs(WEBHOOK_EVENT, {context: {internal: true}, withRelated: ['integration']})
.resolves({models: [webhookModel]}); .resolves({models: [webhookModel]});
limitService.isLimited.withArgs('customIntegrations').returns(true); limitService.isLimited.withArgs('customIntegrations').returns(true);
@ -77,7 +81,7 @@ describe('Webhook Service', function () {
await webhookTrigger.trigger(WEBHOOK_EVENT); await webhookTrigger.trigger(WEBHOOK_EVENT);
assert.equal(models.Webhook.findAllByEvent.called, false); assert.equal(models.Webhook.findAllByEvent.called, true);
assert.equal(payload.called, false); assert.equal(payload.called, false);
assert.equal(request.called, false); assert.equal(request.called, false);
}); });

View File

@ -409,10 +409,12 @@ const getAgentsWithFrontend = async () => {
}; };
}; };
const insertWebhook = ({event, url}) => { const insertWebhook = ({event, url, integrationType = undefined}) => {
return fixtureUtils.fixtures.insertWebhook({ return fixtureUtils.fixtures.insertWebhook({
event: event, event: event,
target_url: url target_url: url
}, {
integrationType
}); });
}; };

View File

@ -426,10 +426,16 @@ const fixtures = {
})); }));
}, },
insertWebhook: function (attributes) { insertWebhook: function (attributes, options = {}) {
let integration = DataGenerator.forKnex.integrations[0];
if (options.integrationType) {
integration = DataGenerator.forKnex.integrations.find(
props => props.type === options.integrationType
);
}
const webhook = DataGenerator.forKnex.createWebhook( const webhook = DataGenerator.forKnex.createWebhook(
Object.assign(attributes, { Object.assign(attributes, {
integration_id: DataGenerator.forKnex.integrations[0].id integration_id: integration.id
}) })
); );