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 045d470c9e..220c8f046b 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 @@ -1,22 +1,24 @@ const path = require('path'); +const VersionNotificationsDataService = require('@tryghost/version-notifications-data-service'); const EmailContentGenerator = require('@tryghost/email-content-generator'); class APIVersionCompatibilityService { /** * * @param {Object} options + * @param {Object} options.UserModel - ghost user 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 {() => Promise} options.fetchEmailsToNotify - email address to receive notifications - * @param {(acceptVersion: String) => Promise} options.fetchHandled - retrives already handled compatibility notifications - * @param {(acceptVersion: String) => Promise} options.saveHandled - persists already handled compatibility notifications * @param {Function} options.getSiteUrl * @param {Function} options.getSiteTitle */ - constructor({sendEmail, fetchEmailsToNotify, fetchHandled, saveHandled, getSiteUrl, getSiteTitle}) { + constructor({UserModel, settingsService, sendEmail, getSiteUrl, getSiteTitle}) { this.sendEmail = sendEmail; - this.fetchEmailsToNotify = fetchEmailsToNotify; - this.fetchHandled = fetchHandled; - this.saveHandled = saveHandled; + + this.versionNotificationsDataService = new VersionNotificationsDataService({ + UserModel, + settingsService + }); this.emailContentGenerator = new EmailContentGenerator({ getSiteUrl, @@ -34,9 +36,9 @@ class APIVersionCompatibilityService { * @param {string} [options.userAgent] - client's user-agent header value */ async handleMismatch({acceptVersion, contentVersion, requestURL, userAgent = ''}) { - if (!await this.fetchHandled(acceptVersion)) { + if (!await this.versionNotificationsDataService.fetchNotification(acceptVersion)) { const trimmedUseAgent = userAgent.split('/')[0]; - const emails = await this.fetchEmailsToNotify(); + const emails = await this.versionNotificationsDataService.getNotificationEmails(); for (const email of emails) { const template = (trimmedUseAgent === 'Zapier') @@ -66,7 +68,7 @@ class APIVersionCompatibilityService { }); } - await this.saveHandled(acceptVersion); + await this.versionNotificationsDataService.saveNotification(acceptVersion); } } } 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 41f17d47a1..25c0293556 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 @@ -5,6 +5,40 @@ const APIVersionCompatibilityService = require('../index'); describe('APIVersionCompatibilityService', function () { const getSiteUrl = () => 'https://amazeballsghostsite.com'; const getSiteTitle = () => 'Tahini and chickpeas'; + let UserModel; + let settingsService; + + beforeEach(function () { + UserModel = { + findAll: sinon + .stub() + .withArgs({ + withRelated: ['roles'], + filter: 'status:active' + }, { + internal: true + }) + .resolves({ + toJSON: () => [{ + email: 'simon@example.com', + roles: [{ + name: 'Administrator' + }] + }] + }) + }; + settingsService = { + read: sinon.stub().resolves({ + version_notifications: { + value: JSON.stringify([ + 'v3.4', + 'v4.1' + ]) + } + }), + edit: sinon.stub().resolves() + }; + }); afterEach(function () { sinon.reset(); @@ -12,14 +46,10 @@ describe('APIVersionCompatibilityService', function () { it('Sends an email to the instance owners when fresh accept-version header mismatch detected', async function () { const sendEmail = sinon.spy(); - const fetchHandled = sinon.spy(); - const saveHandled = sinon.spy(); - const compatibilityService = new APIVersionCompatibilityService({ + UserModel, + settingsService, sendEmail, - fetchEmailsToNotify: async () => ['test_env@example.com'], - fetchHandled, - saveHandled, getSiteUrl, getSiteTitle }); @@ -32,7 +62,7 @@ describe('APIVersionCompatibilityService', function () { }); assert.equal(sendEmail.called, true); - assert.equal(sendEmail.args[0][0].to, 'test_env@example.com'); + assert.equal(sendEmail.args[0][0].to, 'simon@example.com'); assert.equal(sendEmail.args[0][0].subject, `Attention required: Your Elaborate Fox integration has failed`); assert.match(sendEmail.args[0][0].html, /Ghost has noticed that your Elaborate Fox<\/strong> is no longer working as expected\./); @@ -41,7 +71,7 @@ describe('APIVersionCompatibilityService', function () { 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 ['test_env@example.com'], - fetchHandled, - saveHandled, + UserModel, + settingsService, getSiteUrl, getSiteTitle }); @@ -78,7 +123,7 @@ describe('APIVersionCompatibilityService', function () { }); assert.equal(sendEmail.called, true); - assert.equal(sendEmail.args[0][0].to, 'test_env@example.com'); + assert.equal(sendEmail.args[0][0].to, 'simon@example.com'); assert.equal(sendEmail.args[0][0].subject, `Attention required: Your Elaborate Fox integration has failed`); assert.match(sendEmail.args[0][0].html, /Ghost has noticed that your Elaborate Fox<\/strong> is no longer working as expected\./); @@ -87,7 +132,7 @@ describe('APIVersionCompatibilityService', function () { 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 [{ + email: 'simon@example.com', + roles: [{ + name: 'Administrator' + }] + }, { + email: 'sam@example.com', + roles: [{ + name: 'Owner' + }] + }] + }) + }; + settingsService = { + read: sinon.stub() + .onCall(0).resolves({ + version_notifications: { + value: JSON.stringify([]) + } + }) + .onCall(1).resolves({ + version_notifications: { + value: JSON.stringify([]) + } + }) + .onCall(2).resolves({ + version_notifications: { + value: JSON.stringify([ + 'v4.5' + ]) + } + }) + .onCall(3).resolves({ + version_notifications: { + value: JSON.stringify([ + 'v4.5' + ]) + } + }), + edit: sinon.stub().resolves() + }; const compatibilityService = new APIVersionCompatibilityService({ sendEmail, - fetchEmailsToNotify: async () => ['test_env@example.com', 'test_env2@example.com'], - fetchHandled, - saveHandled, + UserModel, + settingsService, getSiteUrl, getSiteTitle }); @@ -133,7 +224,7 @@ describe('APIVersionCompatibilityService', function () { }); assert.equal(sendEmail.calledTwice, true); - assert.equal(sendEmail.args[0][0].to, 'test_env@example.com'); + assert.equal(sendEmail.args[0][0].to, 'simon@example.com'); assert.equal(sendEmail.args[0][0].subject, `Attention required: Your Elaborate Fox integration has failed`); assert.match(sendEmail.args[0][0].html, /Ghost has noticed that your Elaborate Fox<\/strong> is no longer working as expected\./); @@ -142,7 +233,7 @@ describe('APIVersionCompatibilityService', function () { 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 Elaborate Fox<\/strong> is no longer working as expected\./); @@ -163,7 +254,7 @@ describe('APIVersionCompatibilityService', function () { assert.match(sendEmail.args[1][0].html, /Failed request URL:<\/strong>  https:\/\/amazeballsghostsite.com\/ghost\/api\/admin\/posts\/dew023d9203se4/); assert.match(sendEmail.args[1][0].html, /This email was sent from Elaborate Fox<\/strong> is no longer working as expected\./); assert.match(sendEmail.args[2][0].html, /Elaborate Fox integration expected Ghost version:<\/strong>  v4.8/); @@ -198,14 +289,11 @@ describe('APIVersionCompatibilityService', function () { 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 fetchHandled = sinon.spy(); - const saveHandled = sinon.spy(); const compatibilityService = new APIVersionCompatibilityService({ sendEmail, - fetchEmailsToNotify: async () => ['test_env@example.com'], - fetchHandled, - saveHandled, + UserModel, + settingsService, getSiteUrl, getSiteTitle }); @@ -218,7 +306,7 @@ describe('APIVersionCompatibilityService', function () { }); assert.equal(sendEmail.called, true); - assert.equal(sendEmail.args[0][0].to, 'test_env@example.com'); + 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\./); @@ -227,7 +315,7 @@ describe('APIVersionCompatibilityService', function () { 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 ['test_env@example.com'], - fetchHandled, - saveHandled, + UserModel, + settingsService, getSiteUrl, getSiteTitle }); @@ -261,19 +346,19 @@ describe('APIVersionCompatibilityService', function () { }); assert.equal(sendEmail.called, true); - assert.equal(sendEmail.args[0][0].to, 'test_env@example.com'); + assert.equal(sendEmail.args[0][0].to, 'simon@example.com'); assert.equal(sendEmail.args[0][0].subject, `Attention required: One of your Zaps has failed`); assert.match(sendEmail.args[0][0].html, /Ghost has noticed that one of the Zaps in your Zapier integration has stopped working<\/span>\./); assert.match(sendEmail.args[0][0].html, /To get this resolved as quickly as possible, please log in to your Zapier account to view any failing Zaps and recreate them using the most recent Ghost-supported versions. Zap errors can be found here<\/a> in your “Zap history”\./); assert.match(sendEmail.args[0][0].html, /This email was sent from