Substituted UserAgent with Integration Name in emails
refs https://github.com/TryGhost/Toolbox/issues/292 - Providing user-defined Integration name instead of API client's UserAgent gives a lot more control to instance administrators identifying which integration is being used incorrectly. - It's best practice to create an Integration with a set of API keys per API client - which should be enough to identify an outdated one.
This commit is contained in:
parent
09594cb5e1
commit
8cc9fc4353
@ -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<any>} 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
|
||||
}
|
||||
|
@ -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 <strong style="font-weight: 600;">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 <a href="https:\/\/amazeballsghostsite.com"/);
|
||||
assert.match(sendEmail.args[0][0].html, /to <a href="mailto:simon@example.com"/);
|
||||
|
||||
assert.match(sendEmail.args[0][0].text, /Ghost has noticed that your Fancy Pants is no longer working as expected\./);
|
||||
assert.match(sendEmail.args[0][0].text, /Fancy Pants integration expected Ghost version:v4.5/);
|
||||
assert.match(sendEmail.args[0][0].text, /Current Ghost version:v5.1/);
|
||||
assert.match(sendEmail.args[0][0].text, /Failed request URL:/);
|
||||
assert.match(sendEmail.args[0][0].text, /https:\/\/amazeballsghostsite.com\/ghost\/api\/admin\/posts\/dew023d9203se4/);
|
||||
|
||||
assert.match(sendEmail.args[0][0].text, /This email was sent from https:\/\/amazeballsghostsite.com/);
|
||||
assert.match(sendEmail.args[0][0].text, /to simon@example.com/);
|
||||
});
|
||||
|
||||
it('Sends Zapier-specific email when userAgent is a Zapier client', async function (){
|
||||
const sendEmail = sinon.spy();
|
||||
|
||||
const compatibilityService = new APIVersionCompatibilityService({
|
||||
sendEmail,
|
||||
UserModel,
|
||||
ApiKeyModel,
|
||||
settingsService,
|
||||
getSiteUrl,
|
||||
getSiteTitle
|
||||
@ -342,7 +335,9 @@ describe('APIVersionCompatibilityService', function () {
|
||||
acceptVersion: 'v4.5',
|
||||
contentVersion: 'v5.1',
|
||||
userAgent: 'Zapier/4.20 GhostAdminSDK/2.4.0',
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user