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

Added /emails/:id/retry/ endpoint for retrying failed emails (#11410)

We want to allow admin users to trigger a retry of failed emails without having to go through the unpublish/republish dance.

- fixed resource identifier in email permissions migration so email permissions are added correctly
- added new email permissions migration so that beta releases can be upgraded without rollback (will be a no-op for any non-beta upgrades)
- added `/emails/:id/retry/` canary Admin API endpoint
  - follows same URL pattern as theme activation
  - only triggers mega service retry endpoint if the email has a `'failed'` status
This commit is contained in:
Kevin Ansfield 2019-11-22 14:20:32 +00:00 committed by GitHub
parent d7d5d9a13d
commit 6a057fad99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 99 additions and 5 deletions

View file

@ -1,5 +1,6 @@
const models = require('../../models');
const common = require('../../lib/common');
const megaService = require('../../services/mega');
module.exports = {
docName: 'emails',
@ -22,12 +23,38 @@ module.exports = {
.then((model) => {
if (!model) {
throw new common.errors.NotFoundError({
message: common.i18n.t('errors.api.email.emailNotFound')
message: common.i18n.t('errors.models.email.emailNotFound')
});
}
return model.toJSON(frame.options);
});
}
},
retry: {
data: [
'id'
],
permissions: true,
query(frame) {
return models.Email.findOne(frame.data, frame.options)
.then(async (model) => {
if (!model) {
throw new common.errors.NotFoundError({
message: common.i18n.t('errors.models.email.emailNotFound')
});
}
if (model.get('status') !== 'failed') {
throw new common.errors.IncorrectUsageError({
message: common.i18n.t('errors.models.email.retryNotAllowed')
});
}
const result = await megaService.mega.retryFailedEmail(model);
return result.toJSON(frame.options);
});
}
}
};

View file

@ -3,5 +3,9 @@ module.exports = {
frame.response = {
emails: [email]
};
},
get retry() {
return this.read;
}
};

View file

@ -3,7 +3,7 @@ const utils = require('../../../schema/fixtures/utils');
const permissions = require('../../../../services/permissions');
const logging = require('../../../../lib/common/logging');
const resources = ['emails'];
const resources = ['email'];
const _private = {};
_private.getPermissions = function getPermissions(resource) {

View file

@ -0,0 +1,51 @@
const _ = require('lodash');
const utils = require('../../../schema/fixtures/utils');
const permissions = require('../../../../services/permissions');
const logging = require('../../../../lib/common/logging');
const resources = ['email'];
const _private = {};
_private.getPermissions = function getPermissions(resource) {
return utils.findModelFixtures('Permission', {object_type: resource});
};
_private.printResult = function printResult(result, message) {
if (result.done === result.expected) {
logging.info(message);
} else {
logging.warn(`(${result.done}/${result.expected}) ${message}`);
}
};
module.exports.config = {
transaction: true
};
module.exports.up = (options) => {
const localOptions = _.merge({
context: {internal: true}
}, options);
return Promise.map(resources, (resource) => {
const modelToAdd = _private.getPermissions(resource);
return utils.addFixturesForModel(modelToAdd, localOptions)
.then(result => _private.printResult(result, `Adding permissions fixtures for ${resource}`))
.then(() => permissions.init(localOptions));
});
};
module.exports.down = (options) => {
const localOptions = _.merge({
context: {internal: true}
}, options);
return Promise.map(resources, (resource) => {
const modelToRemove = _private.getPermissions(resource);
// permission model automatically cleans up permissions_roles on .destroy()
return utils.removeFixturesForModel(modelToRemove, localOptions)
.then(result => _private.printResult(result, `Removing permissions fixtures for ${resource}s`));
});
};

View file

@ -374,9 +374,19 @@
"object_type": "email_preview"
},
{
"name": "Email",
"name": "Browse emails",
"action_type": "browse",
"object_type": "email"
},
{
"name": "Read emails",
"action_type": "read",
"object_type": "email"
},
{
"name": "Retry emails",
"action_type": "retry",
"object_type": "email"
}
]
},

View file

@ -256,7 +256,8 @@
"apiKeyNotFound": "API Key not found"
},
"email": {
"emailNotFound": "Email not found."
"emailNotFound": "Email not found.",
"retryNotAllowed": "Only failed emails can be retried"
},
"base": {
"index": {

View file

@ -221,6 +221,7 @@ module.exports = function apiRoutes() {
// ## Emails
router.get('/emails/:id', mw.authAdminApi, http(apiCanary.emails.read));
router.put('/emails/:id/retry', mw.authAdminApi, http(apiCanary.emails.retry));
return router;
};

View file

@ -20,7 +20,7 @@ var should = require('should'),
describe('DB version integrity', function () {
// Only these variables should need updating
const currentSchemaHash = '773f8f6cd4267f50aec6af8c8b1edbd2';
const currentFixturesHash = 'b1787330f042f3954d73c43aa8bfa915';
const currentFixturesHash = '1a0f96fa1d8b976d663eb06719be031c';
// If this test is failing, then it is likely a change has been made that requires a DB version bump,
// and the values above will need updating as confirmation