moved sending mentions to job (#16234)

refs TryGhost/Team#2523
-implemented jobsService within mention sending service
-updated tests so we don't need to sleep
This commit is contained in:
Steve Larson 2023-02-08 16:29:12 -06:00 committed by GitHub
parent 0d7944861c
commit 6c97edec25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 22 deletions

View File

@ -88,7 +88,16 @@ module.exports = {
externalRequest,
getSiteUrl: () => urlUtils.urlFor('home', true),
getPostUrl: post => getPostUrl(post),
isEnabled: () => !settingsCache.get('is_private')
isEnabled: () => !settingsCache.get('is_private'),
jobService: {
async addJob(name, fn) {
jobsService.addJob({
name,
job: fn,
offloaded: false
});
}
}
});
sendingService.listen(events);
}

View File

@ -2,9 +2,9 @@ const {agentProvider, fixtureManager, mockManager} = require('../../utils/e2e-fr
const sinon = require('sinon');
const nock = require('nock');
const assert = require('assert');
const sleep = require('../../utils/sleep');
const markdownToMobiledoc = require('../../utils/fixtures/data-generator').markdownToMobiledoc;
const dnsPromises = require('dns').promises;
const jobsService = require('../../../core/server/services/jobs');
let agent;
let mentionUrl = new URL('https://www.otherghostsite.com/');
@ -19,9 +19,6 @@ const mentionsPost = {
mobiledoc: markdownToMobiledoc(mentionHtml)
};
// NOTE: we need to sleep after the API calls because there's a race condition between the nock'd http response and the mentions service
// TODO: move the mentions service to a jobbed process so we can await it
describe('Mentions Service', function () {
before(async function () {
agent = await agentProvider.getAdminAPIAgent();
@ -47,7 +44,7 @@ describe('Mentions Service', function () {
.reply(201);
});
afterEach(function () {
afterEach(async function () {
mockManager.restore();
nock.cleanAll();
});
@ -84,32 +81,36 @@ describe('Mentions Service', function () {
describe(`does send when we expect it to send`, function () {
it('Newly published post (post.published)', async function () {
let sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let publishedPost = {status: 'published', ...mentionsPost};
await agent
.post('posts/')
.body({posts: [publishedPost]})
.expectStatus(201);
await sleep(10);
await sendWebmentionsJob;
assert.equal(mentionMock.isDone(), true);
assert.equal(endpointMock.isDone(), true);
});
it('Edited published post (post.published.edited)', async function () {
let sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let publishedPost = {status: 'published', ...mentionsPost};
let res = await agent
.post('posts/')
.body({posts: [publishedPost]})
.expectStatus(201);
await sleep(10);
await sendWebmentionsJob;
// while not the point of the test, we should have real links/mentions to start with
assert.equal(mentionMock.isDone(), true);
assert.equal(endpointMock.isDone(), true);
// reset mocks for mention
sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let mentionMockTwo = nock(mentionUrl.href)
.get('/')
.reply(200, targetHtml, {'content-type': 'text/html'});
@ -127,26 +128,28 @@ describe('Mentions Service', function () {
.body({posts: [editedPost]})
.expectStatus(200);
await sleep(10);
await sendWebmentionsJob;
assert.equal(mentionMockTwo.isDone(), true);
assert.equal(endpointMockTwo.isDone(), true);
});
it('Unpublished post (post.unpublished)', async function () {
let sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let publishedPost = {status: 'published', ...mentionsPost};
let res = await agent
.post('posts/')
.body({posts: [publishedPost]})
.expectStatus(201);
await sleep(10);
await sendWebmentionsJob;
// while not the point of the test, we should have real links/mentions to start with
assert.equal(mentionMock.isDone(), true);
assert.equal(endpointMock.isDone(), true);
// reset mocks for mention
sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let mentionMockTwo = nock(mentionUrl.href)
.get('/')
.reply(200, targetHtml, {'content-type': 'text/html'});
@ -164,26 +167,28 @@ describe('Mentions Service', function () {
.body({posts: [unpublishedPost]})
.expectStatus(200);
await sleep(10);
await sendWebmentionsJob;
assert.equal(mentionMockTwo.isDone(), true);
assert.equal(endpointMockTwo.isDone(), true);
});
it('Sends for links that got removed from a post', async function () {
let sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let publishedPost = {status: 'published', ...mentionsPost};
let res = await agent
.post('posts/')
.body({posts: [publishedPost]})
.expectStatus(201);
await sleep(10);
await sendWebmentionsJob;
// while not the point of the test, we should have real links/mentions to start with
assert.equal(mentionMock.isDone(), true);
assert.equal(endpointMock.isDone(), true);
// reset mocks for mention
sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let mentionMockTwo = nock(mentionUrl.href)
.get('/')
.reply(200, targetHtml, {'content-type': 'text/html'});
@ -200,7 +205,7 @@ describe('Mentions Service', function () {
.body({posts: [editedPost]})
.expectStatus(200);
await sleep(10);
await sendWebmentionsJob;
assert.equal(mentionMockTwo.isDone(), true);
assert.equal(endpointMockTwo.isDone(), true);
@ -208,13 +213,14 @@ describe('Mentions Service', function () {
// there's no special handling for this atm, but could be down the road
it('New paid post', async function () {
let sendWebmentionsJob = jobsService.awaitCompletion('sendWebmentions');
let publishedPost = {status: 'published', visibility: 'paid', ...mentionsPost};
await agent
.post('posts/')
.body({posts: [publishedPost]})
.expectStatus(201);
await sleep(10);
await sendWebmentionsJob;
assert.equal(mentionMock.isDone(), true);
assert.equal(endpointMock.isDone(), true);

View File

@ -7,13 +7,15 @@ module.exports = class MentionSendingService {
#getSiteUrl;
#getPostUrl;
#isEnabled;
#jobService;
constructor({discoveryService, externalRequest, getSiteUrl, getPostUrl, isEnabled}) {
constructor({discoveryService, externalRequest, getSiteUrl, getPostUrl, isEnabled, jobService}) {
this.#discoveryService = discoveryService;
this.#externalRequest = externalRequest;
this.#getSiteUrl = getSiteUrl;
this.#getPostUrl = getPostUrl;
this.#isEnabled = isEnabled;
this.#jobService = jobService;
}
get siteUrl() {
@ -57,10 +59,12 @@ module.exports = class MentionSendingService {
// Post should be or should have been published
return;
}
await this.sendAll({
url: new URL(this.#getPostUrl(post)),
html: post.get('html'),
previousHtml: post.previous('status') === 'published' ? post.previous('html') : null
await this.#jobService.addJob('sendWebmentions', async () => {
await this.sendAll({
url: new URL(this.#getPostUrl(post)),
html: post.get('html'),
previousHtml: post.previous('status') === 'published' ? post.previous('html') : null
});
});
} catch (e) {
logging.error('Error in webmention sending service post update event handler:');

View File

@ -7,6 +7,13 @@ const sinon = require('sinon');
const logging = require('@tryghost/logging');
const {createModel} = require('./utils/index.js');
// mock up job service
let jobService = {
async addJob(name, fn) {
return fn();
}
};
describe('MentionSendingService', function () {
let errorLogStub;
@ -124,7 +131,8 @@ describe('MentionSendingService', function () {
it('Sends on publish', async function () {
const service = new MentionSendingService({
isEnabled: () => true,
getPostUrl: () => 'https://site.com/post/'
getPostUrl: () => 'https://site.com/post/',
jobService: jobService
});
const stub = sinon.stub(service, 'sendAll');
await service.sendForPost(createModel({
@ -145,7 +153,8 @@ describe('MentionSendingService', function () {
it('Sends on html change', async function () {
const service = new MentionSendingService({
isEnabled: () => true,
getPostUrl: () => 'https://site.com/post/'
getPostUrl: () => 'https://site.com/post/',
jobService: jobService
});
const stub = sinon.stub(service, 'sendAll');
await service.sendForPost(createModel({
@ -265,7 +274,8 @@ describe('MentionSendingService', function () {
getSiteUrl: () => new URL('https://site.com'),
discoveryService: {
getEndpoint: async () => new URL('https://example.org/webmentions-test')
}
},
jobService: jobService
});
await service.sendAll({url: new URL('https://site.com'),
html: `<a href="https://example.com">Example</a>`,