2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

Limited integrations triggering version mismatch emails

refs https://github.com/TryGhost/Toolbox/issues/500
refs https://ghost.notion.site/Data-Types-e5dc54dd0078443f9afd6b2abda443c4

- There current notification logic for incompatible integrations did not take into account the source of the trigger, which might have been causing emails to instance owners that did not ever set up custom integration - so they had nothing to fix.
- The "internal" and "core" integrations are maintained/controlled by the Ghost team, so there should never be a notification going out to the instance owner about possible incompatibility in the code they do not control.
- Along with changed updated the unit test threshold in the packages that were touched to 100%. As that's the standard for all new packages.
This commit is contained in:
Naz 2023-01-30 17:57:02 +08:00
parent c11e79765e
commit 478eb6ead6
No known key found for this signature in database
6 changed files with 144 additions and 12 deletions

View file

@ -41,7 +41,18 @@ class APIVersionCompatibilityService {
*/
async handleMismatch({acceptVersion, contentVersion, apiKeyValue, apiKeyType, requestURL, userAgent = ''}) {
if (!await this.versionNotificationsDataService.fetchNotification(acceptVersion)) {
const integrationName = await this.versionNotificationsDataService.getIntegrationName(apiKeyValue, apiKeyType);
const {
name: integrationName,
type: integrationType
} = await this.versionNotificationsDataService.getIntegration(apiKeyValue, apiKeyType);
// @NOTE: "internal" or "core" integrations (https://ghost.notion.site/Data-Types-e5dc54dd0078443f9afd6b2abda443c4)
// are maintained by Ghost team, so there is no sense notifying the instance owner about it's incompatibility.
// The other two integration types: "builtin" and "custom", is when we want to notify about incompatibility.
if (['internal', 'core'].includes(integrationType)) {
return;
}
const trimmedUseAgent = userAgent.split('/')[0];
const emails = await this.versionNotificationsDataService.getNotificationEmails();

View file

@ -7,7 +7,7 @@
"main": "index.js",
"scripts": {
"dev": "echo \"Implement me!\"",
"test:unit": "NODE_ENV=testing c8 --all --check-coverage --reporter text --reporter cobertura mocha './test/**/*.test.js'",
"test:unit": "NODE_ENV=testing c8 --all --check-coverage --100 --reporter text --reporter cobertura mocha './test/**/*.test.js'",
"test": "yarn test:unit",
"lint:code": "eslint *.js lib/ --ext .js --cache",
"lint": "yarn lint:code && yarn lint:test",

View file

@ -40,7 +40,10 @@ describe('APIVersionCompatibilityService', function () {
.resolves({
relations: {
integration: {
get: () => 'Elaborate Fox'
toJSON: () => ({
name: 'Elaborate Fox',
type: 'custom'
})
}
}
})
@ -181,6 +184,114 @@ describe('APIVersionCompatibilityService', function () {
assert.equal(sendEmail.calledTwice, false);
});
it('Does NOT send an email to the instance owner when "internal" or "core" integration triggered version mismatch', async function () {
const sendEmail = sinon.spy();
const findOneStub = sinon.stub();
findOneStub
.withArgs({
id: 'secret_core'
}, {
withRelated: ['integration']
})
.resolves({
relations: {
integration: {
toJSON: () => ({
name: 'Core Integration',
type: 'core'
})
}
}
});
findOneStub
.withArgs({
id: 'secrete_internal'
}, {
withRelated: ['integration']
})
.resolves({
relations: {
integration: {
toJSON: () => ({
name: 'Internal Integration',
type: 'internal'
})
}
}
});
findOneStub
.withArgs({
secret: 'custom_key_id'
}, {
withRelated: ['integration']
})
.resolves({
relations: {
integration: {
toJSON: () => ({
name: 'Custom Integration',
type: 'custom'
})
}
}
});
ApiKeyModel = {
findOne: findOneStub
};
settingsService = {
read: sinon.stub().resolves({
version_notifications: {
value: JSON.stringify([])
}
}),
edit: sinon.stub().resolves()
};
const compatibilityService = new APIVersionCompatibilityService({
sendEmail,
ApiKeyModel,
UserModel,
settingsService,
getSiteUrl,
getSiteTitle
});
await compatibilityService.handleMismatch({
acceptVersion: 'v4.5',
contentVersion: 'v5.1',
userAgent: 'GhostAdminSDK/2.4.0',
requestURL: '',
apiKeyValue: 'secret_core',
apiKeyType: 'admin'
});
assert.equal(sendEmail.called, false);
await compatibilityService.handleMismatch({
acceptVersion: 'v4.5',
contentVersion: 'v5.1',
userAgent: 'GhostAdminSDK/2.4.0',
requestURL: 'does not matter',
apiKeyValue: 'secrete_internal',
apiKeyType: 'admin'
});
assert.equal(sendEmail.called, false);
await compatibilityService.handleMismatch({
acceptVersion: 'v4.5',
contentVersion: 'v5.1',
userAgent: 'GhostContentSDK/2.4.0',
requestURL: 'https://amazeballsghostsite.com/ghost/api/admin/posts/dew023d9203se4',
apiKeyValue: 'custom_key_id',
apiKeyType: 'content'
});
assert.equal(sendEmail.called, true);
});
it('Does send multiple emails to the instance owners when previously unhandled accept-version header mismatch is detected', async function () {
const sendEmail = sinon.spy();
UserModel = {

View file

@ -53,12 +53,13 @@ class VersionNotificationsDataService {
}
/**
* This method is for internal use only.
*
* @param {String} key - api key identification value, it's "secret" in case of Content API key and "id" for Admin API
* @param {String} type - one of "content" or "admin" values
* @returns
* @returns {Promise<Object>} Integration JSON object
*/
async getIntegrationName(key, type) {
async getIntegration(key, type) {
let queryOptions = null;
if (type === 'content') {
@ -69,7 +70,7 @@ class VersionNotificationsDataService {
const apiKey = await this.ApiKeyModel.findOne(queryOptions, {withRelated: ['integration']});
return apiKey.relations.integration.get('name');
return apiKey.relations.integration.toJSON();
}
}

View file

@ -7,7 +7,7 @@
"main": "index.js",
"scripts": {
"dev": "echo \"Implement me!\"",
"test:unit": "NODE_ENV=testing c8 --all --check-coverage --reporter text --reporter cobertura mocha './test/**/*.test.js'",
"test:unit": "NODE_ENV=testing c8 --all --check-coverage --100 --reporter text --reporter cobertura mocha './test/**/*.test.js'",
"test": "yarn test:unit",
"lint:code": "eslint *.js lib/ --ext .js --cache",
"lint": "yarn lint:code && yarn lint:test",

View file

@ -123,7 +123,7 @@ describe('Version Notification Data Service', function () {
});
});
describe('getIntegrationName', function () {
describe('getIntegration', function () {
it('Queries for Content API key when such is provided', async function () {
const ApiKeyModel = {
findOne: sinon
@ -136,7 +136,10 @@ describe('Version Notification Data Service', function () {
.resolves({
relations: {
integration: {
get: () => 'live fast die young'
toJSON: () => ({
name: 'live fast die young',
type: 'custom'
})
}
}
})
@ -148,9 +151,10 @@ describe('Version Notification Data Service', function () {
settingsService: {}
});
const integrationName = await versionNotificationsDataService.getIntegrationName('super_secret', 'content');
const {name: integrationName, type: integrationType} = await versionNotificationsDataService.getIntegration('super_secret', 'content');
assert.equal(integrationName, 'live fast die young');
assert.equal(integrationType, 'custom');
});
it('Queries for Admin API key when such is provided', async function () {
@ -164,8 +168,12 @@ describe('Version Notification Data Service', function () {
})
.resolves({
relations: {
integration: {
get: () => 'Tri Hita Karana'
toJSON: () => ({
name: 'Tri Hita Karana',
type: 'core'
})
}
}
})
@ -177,9 +185,10 @@ describe('Version Notification Data Service', function () {
settingsService: {}
});
const integrationName = await versionNotificationsDataService.getIntegrationName('key_id', 'admin');
const {name: integrationName, type: integrationType} = await versionNotificationsDataService.getIntegration('key_id', 'admin');
assert.equal(integrationName, 'Tri Hita Karana');
assert.equal(integrationType, 'core');
});
});
});