diff --git a/ghost/api-version-compatibility-service/lib/api-version-compatibility-service.js b/ghost/api-version-compatibility-service/lib/api-version-compatibility-service.js index 220c8f046b..e524d4a85c 100644 --- a/ghost/api-version-compatibility-service/lib/api-version-compatibility-service.js +++ b/ghost/api-version-compatibility-service/lib/api-version-compatibility-service.js @@ -7,16 +7,18 @@ class APIVersionCompatibilityService { * * @param {Object} options * @param {Object} options.UserModel - ghost user model + * @param {Object} options.ApiKeyModel - ghost api key model * @param {Object} options.settingsService - ghost settings service * @param {(Object: {subject: String, to: String, text: String, html: String}) => Promise} options.sendEmail - email sending function * @param {Function} options.getSiteUrl * @param {Function} options.getSiteTitle */ - constructor({UserModel, settingsService, sendEmail, getSiteUrl, getSiteTitle}) { + constructor({UserModel, ApiKeyModel, settingsService, sendEmail, getSiteUrl, getSiteTitle}) { this.sendEmail = sendEmail; this.versionNotificationsDataService = new VersionNotificationsDataService({ UserModel, + ApiKeyModel, settingsService }); @@ -32,11 +34,14 @@ class APIVersionCompatibilityService { * @param {Object} options * @param {string} options.acceptVersion - client's accept-version header value * @param {string} options.contentVersion - server's content-version header value + * @param {string} options.apiKeyValue - key value (secret for Content API and kid for Admin API) used to access the API + * @param {string} options.apiKeyType - key type used to access the API * @param {string} options.requestURL - url that was requested and failed compatibility test * @param {string} [options.userAgent] - client's user-agent header value */ - async handleMismatch({acceptVersion, contentVersion, requestURL, userAgent = ''}) { + async handleMismatch({acceptVersion, contentVersion, apiKeyValue, apiKeyType, requestURL, userAgent = ''}) { if (!await this.versionNotificationsDataService.fetchNotification(acceptVersion)) { + const integrationName = await this.versionNotificationsDataService.getIntegrationName(apiKeyValue, apiKeyType); const trimmedUseAgent = userAgent.split('/')[0]; const emails = await this.versionNotificationsDataService.getNotificationEmails(); @@ -54,7 +59,7 @@ class APIVersionCompatibilityService { data: { acceptVersion, contentVersion, - clientName: trimmedUseAgent, + clientName: integrationName, recipientEmail: email, requestURL: requestURL } diff --git a/ghost/api-version-compatibility-service/test/api-version-compatibility-service.test.js b/ghost/api-version-compatibility-service/test/api-version-compatibility-service.test.js index 25c0293556..f80857cbe5 100644 --- a/ghost/api-version-compatibility-service/test/api-version-compatibility-service.test.js +++ b/ghost/api-version-compatibility-service/test/api-version-compatibility-service.test.js @@ -6,6 +6,7 @@ describe('APIVersionCompatibilityService', function () { const getSiteUrl = () => 'https://amazeballsghostsite.com'; const getSiteTitle = () => 'Tahini and chickpeas'; let UserModel; + let ApiKeyModel; let settingsService; beforeEach(function () { @@ -27,6 +28,24 @@ describe('APIVersionCompatibilityService', function () { }] }) }; + + ApiKeyModel = { + findOne: sinon + .stub() + .withArgs({ + secret: 'super_secret' + }, { + withRelated: ['integration'] + }) + .resolves({ + relations: { + integration: { + get: () => 'Elaborate Fox' + } + } + }) + }; + settingsService = { read: sinon.stub().resolves({ version_notifications: { @@ -48,6 +67,7 @@ describe('APIVersionCompatibilityService', function () { const sendEmail = sinon.spy(); const compatibilityService = new APIVersionCompatibilityService({ UserModel, + ApiKeyModel, settingsService, sendEmail, getSiteUrl, @@ -58,7 +78,9 @@ describe('APIVersionCompatibilityService', function () { acceptVersion: 'v4.5', contentVersion: 'v5.1', userAgent: 'Elaborate Fox', - requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4' + requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4', + apiKeyValue: 'secret', + apiKeyType: 'content' }); assert.equal(sendEmail.called, true); @@ -109,6 +131,7 @@ describe('APIVersionCompatibilityService', function () { const compatibilityService = new APIVersionCompatibilityService({ sendEmail, + ApiKeyModel, UserModel, settingsService, getSiteUrl, @@ -119,7 +142,9 @@ describe('APIVersionCompatibilityService', function () { acceptVersion: 'v4.5', contentVersion: 'v5.1', userAgent: 'Elaborate Fox', - requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4' + requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4', + apiKeyValue: 'secret', + apiKeyType: 'content' }); assert.equal(sendEmail.called, true); @@ -147,7 +172,9 @@ describe('APIVersionCompatibilityService', function () { acceptVersion: 'v4.5', contentVersion: 'v5.1', userAgent: 'Elaborate Fox', - requestURL: 'does not matter' + requestURL: 'does not matter', + apiKeyValue: 'secret', + apiKeyType: 'content' }); assert.equal(sendEmail.calledOnce, true); @@ -211,6 +238,7 @@ describe('APIVersionCompatibilityService', function () { const compatibilityService = new APIVersionCompatibilityService({ sendEmail, UserModel, + ApiKeyModel, settingsService, getSiteUrl, getSiteTitle @@ -220,7 +248,9 @@ describe('APIVersionCompatibilityService', function () { acceptVersion: 'v4.5', contentVersion: 'v5.1', userAgent: 'Elaborate Fox', - requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4' + requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4', + apiKeyValue: 'secret', + apiKeyType: 'content' }); assert.equal(sendEmail.calledTwice, true); @@ -269,7 +299,9 @@ describe('APIVersionCompatibilityService', function () { acceptVersion: 'v4.8', contentVersion: 'v5.1', userAgent: 'Elaborate Fox', - requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4' + requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4', + apiKeyValue: 'secret', + apiKeyType: 'content' }); assert.equal(sendEmail.callCount, 4); @@ -287,52 +319,13 @@ describe('APIVersionCompatibilityService', function () { assert.match(sendEmail.args[2][0].text, /https:\/\/amazeballsghostsite.com\/ghost\/api\/admin\/posts\/dew023d9203se4/); }); - it('Trims down the name of the integration when a lot of meta information is present in user-agent header', async function (){ - const sendEmail = sinon.spy(); - - const compatibilityService = new APIVersionCompatibilityService({ - sendEmail, - UserModel, - settingsService, - getSiteUrl, - getSiteTitle - }); - - await compatibilityService.handleMismatch({ - acceptVersion: 'v4.5', - contentVersion: 'v5.1', - userAgent: 'Fancy Pants/2.3 GhostAdminSDK/2.4.0', - requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4' - }); - - assert.equal(sendEmail.called, true); - assert.equal(sendEmail.args[0][0].to, 'simon@example.com'); - assert.equal(sendEmail.args[0][0].subject, `Attention required: Your Fancy Pants integration has failed`); - - assert.match(sendEmail.args[0][0].html, /Ghost has noticed that your Fancy Pants<\/strong> is no longer working as expected\./); - assert.match(sendEmail.args[0][0].html, /Fancy Pants integration expected Ghost version:<\/strong>  v4.5/); - assert.match(sendEmail.args[0][0].html, /Current Ghost version:<\/strong>  v5.1/); - assert.match(sendEmail.args[0][0].html, /Failed request URL:<\/strong>  https:\/\/amazeballsghostsite.com\/ghost\/api\/admin\/posts\/dew023d9203se4/); - - assert.match(sendEmail.args[0][0].html, /This email was sent from