Wired up verification to MentionsAPI

refs https://github.com/TryGhost/Team/issues/2550

Whilst this isn't ideal making multiple requests for the same site
it's a first step towards verification properties, and can be
refactored to improve performance later. With the current volume of
incoming Webmentions we've seen so far this shouldn't be a problem.
This commit is contained in:
Fabien "egg" O'Carroll 2023-02-17 18:53:06 +07:00 committed by Fabien 'egg' O'Carroll
parent 37cfb96d9f
commit e14d2e662b
4 changed files with 66 additions and 11 deletions

View File

@ -0,0 +1,20 @@
const logging = require('@tryghost/logging');
const oembedService = require('../oembed');
module.exports = class WebmentionRequest {
/**
* @param {URL} url
* @returns {Promise<{html: string}>}
*/
async fetch(url) {
try {
const data = await oembedService.fetchPageHtml(url.href);
return {
html: data.body
};
} catch (err) {
logging.warn(err);
return null;
}
}
};

View File

@ -1,5 +1,6 @@
const MentionController = require('./MentionController');
const WebmentionMetadata = require('./WebmentionMetadata');
const WebmentionRequest = require('./WebmentionRequest');
const {
MentionsAPI,
MentionSendingService,
@ -32,6 +33,7 @@ module.exports = {
DomainEvents
});
const webmentionMetadata = new WebmentionMetadata();
const webmentionRequest = new WebmentionRequest();
const discoveryService = new MentionDiscoveryService({externalRequest});
const resourceService = new ResourceService({
urlUtils,
@ -47,6 +49,7 @@ module.exports = {
const api = new MentionsAPI({
repository,
webmentionMetadata,
webmentionRequest,
resourceService,
routingService
});

View File

@ -72,6 +72,11 @@ const Mention = require('./Mention');
* @prop {(url: URL) => Promise<WebmentionMetadata>} fetch
*/
/**
* @typedef {object} IWebmentionRequest
* @prop {(url: URL) => Promise<{html: string}>} fetch
*/
module.exports = class MentionsAPI {
/** @type {IMentionRepository} */
#repository;
@ -81,6 +86,8 @@ module.exports = class MentionsAPI {
#routingService;
/** @type {IWebmentionMetadata} */
#webmentionMetadata;
/** @type {IWebmentionRequest} */
#webmentionRequest;
/**
* @param {object} deps
@ -88,12 +95,14 @@ module.exports = class MentionsAPI {
* @param {IResourceService} deps.resourceService
* @param {IRoutingService} deps.routingService
* @param {IWebmentionMetadata} deps.webmentionMetadata
* @param {IWebmentionRequest} deps.webmentionRequest
*/
constructor(deps) {
this.#repository = deps.repository;
this.#resourceService = deps.resourceService;
this.#routingService = deps.routingService;
this.#webmentionMetadata = deps.webmentionMetadata;
this.#webmentionRequest = deps.webmentionRequest;
}
/**
@ -187,8 +196,13 @@ module.exports = class MentionsAPI {
sourceFeaturedImage: metadata.image
});
}
await this.#repository.save(mention);
const responseBody = await this.#webmentionRequest.fetch(webmention.source);
if (responseBody?.html) {
mention.verify(responseBody.html);
}
await this.#repository.save(mention);
return mention;
}
};

View File

@ -31,6 +31,14 @@ const mockWebmentionMetadata = {
}
};
const mockWebmentionRequest = {
async fetch() {
return {
html: `<p>Some HTML and a <a href='http://target.com/'>mentioned url</a></p>`
};
}
};
function addMinutes(date, minutes) {
date.setMinutes(date.getMinutes() + minutes);
@ -48,7 +56,8 @@ describe('MentionsAPI', function () {
repository,
routingService: mockRoutingService,
resourceService: mockResourceService,
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
const mention = await api.processWebmention({
@ -73,7 +82,8 @@ describe('MentionsAPI', function () {
repository,
routingService: mockRoutingService,
resourceService: mockResourceService,
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
const mention = await api.processWebmention({
@ -97,7 +107,8 @@ describe('MentionsAPI', function () {
repository,
routingService: mockRoutingService,
resourceService: mockResourceService,
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
const mentionOne = await api.processWebmention({
@ -129,7 +140,8 @@ describe('MentionsAPI', function () {
repository,
routingService: mockRoutingService,
resourceService: mockResourceService,
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
const mentionOne = await api.processWebmention({
@ -165,7 +177,8 @@ describe('MentionsAPI', function () {
repository,
routingService: mockRoutingService,
resourceService: mockResourceService,
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
const mentionOne = await api.processWebmention({
@ -201,7 +214,8 @@ describe('MentionsAPI', function () {
repository,
routingService: mockRoutingService,
resourceService: mockResourceService,
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
const mentionOne = await api.processWebmention({
@ -238,7 +252,8 @@ describe('MentionsAPI', function () {
}
},
resourceService: mockResourceService,
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
let errored = false;
@ -268,7 +283,8 @@ describe('MentionsAPI', function () {
};
}
},
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
const mention = await api.processWebmention({
@ -301,7 +317,8 @@ describe('MentionsAPI', function () {
};
}
},
webmentionMetadata: mockWebmentionMetadata
webmentionMetadata: mockWebmentionMetadata,
webmentionRequest: mockWebmentionRequest
});
checkFirstMention: {
@ -352,7 +369,8 @@ describe('MentionsAPI', function () {
fetch: sinon.stub()
.onFirstCall().resolves(mockWebmentionMetadata.fetch())
.onSecondCall().rejects()
}
},
webmentionRequest: mockWebmentionRequest
});
checkFirstMention: {