0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

🐛 Fixed 404 when trying to update codeinjection_* settings in Admin API v2

refs #10560
This commit is contained in:
kirrg001 2019-03-04 22:59:13 +01:00
parent 449bae9a48
commit 97cf337907
8 changed files with 117 additions and 37 deletions

View file

@ -41,7 +41,7 @@ class Frame {
this.options.context = this.original.context;
if (this.original.body && Object.keys(this.original.body).length) {
this.data = this.original.body;
this.data = _.cloneDeep(this.original.body);
} else {
if (apiConfig.data) {
if (typeof apiConfig.data === 'function') {

View file

@ -46,15 +46,6 @@ module.exports = {
}
},
permissions: {
before(frame) {
let setting = settingsCache.get(frame.options.key, {resolve: false});
if (setting.type === 'core' && !(frame.options.context && frame.options.context.internal)) {
return Promise.reject(new common.errors.NoPermissionError({
message: common.i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
}));
}
},
identifier(frame) {
return frame.options.key;
}
@ -62,6 +53,21 @@ module.exports = {
query(frame) {
let setting = settingsCache.get(frame.options.key, {resolve: false});
if (!setting) {
return Promise.reject(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.settings.problemFindingSetting', {
key: frame.options.key
})
}));
}
// @TODO: handle in settings model permissible fn
if (setting.type === 'core' && !(frame.options.context && frame.options.context.internal)) {
return Promise.reject(new common.errors.NoPermissionError({
message: common.i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
}));
}
return {
[frame.options.key]: setting
};
@ -102,6 +108,29 @@ module.exports = {
return setting.key === 'type';
});
const errors = [];
_.each(frame.data.settings, (setting) => {
const settingFromCache = settingsCache.get(setting.key, {resolve: false});
if (!settingFromCache) {
errors.push(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.settings.problemFindingSetting', {
key: setting.key
})
}));
} else if (settingFromCache.type === 'core' && !(frame.options.context && frame.options.context.internal)) {
// @TODO: handle in settings model permissible fn
errors.push(new common.errors.NoPermissionError({
message: common.i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
}));
}
});
if (errors.length) {
return Promise.reject(errors[0]);
}
return models.Settings.edit(frame.data.settings, frame.options);
}
},

View file

@ -1,6 +1,16 @@
const _ = require('lodash');
module.exports = {
read(apiConfig, frame) {
if (frame.options.key === 'codeinjection_head') {
frame.options.key = 'ghost_head';
}
if (frame.options.key === 'codeinjection_foot') {
frame.options.key = 'ghost_foot';
}
},
edit(apiConfig, frame) {
// CASE: allow shorthand syntax where a single key and value are passed to edit instead of object and options
if (_.isString(frame.data)) {
@ -8,11 +18,19 @@ module.exports = {
}
// CASE: transform objects/arrays into string (we store stringified objects in the db)
// @TODO: This belongs into the model layer?
frame.data.settings.forEach((setting) => {
// @TODO: This belongs into the model layer?
if (_.isObject(setting.value)) {
setting.value = JSON.stringify(setting.value);
}
if (setting.key === 'codeinjection_head') {
setting.key = 'ghost_head';
}
if (setting.key === 'codeinjection_foot') {
setting.key = 'ghost_foot';
}
});
}
};

View file

@ -35,7 +35,7 @@ module.exports = {
}
frame.response = {
settings: mapper.mapSettings(filteredSettings),
settings: mapper.mapSettings(filteredSettings, frame),
meta: {}
};

View file

@ -18,12 +18,49 @@ module.exports.forPost = (frame, model, attrs) => {
};
// @NOTE: ghost_head & ghost_foot are deprecated, remove in Ghost 3.0
module.exports.forSettings = (attrs) => {
module.exports.forSettings = (attrs, frame) => {
const _ = require('lodash');
// @TODO: https://github.com/TryGhost/Ghost/issues/10106
// @NOTE: Admin & Content API return a different format, need to mappers
if (_.isArray(attrs)) {
// CASE: read single setting
if (frame.original.params && frame.original.params.key) {
if (frame.original.params.key === 'ghost_head') {
return;
}
if (frame.original.params.key === 'ghost_foot') {
return;
}
if (frame.original.params.key === 'codeinjection_head') {
attrs[0].key = 'codeinjection_head';
return;
}
if (frame.original.params.key === 'codeinjection_foot') {
attrs[0].key = 'codeinjection_foot';
return;
}
}
// CASE: edit
if (frame.original.body && frame.original.body.settings) {
frame.original.body.settings.forEach((setting, index) => {
if (setting.key === 'codeinjection_head') {
attrs[index].key = 'codeinjection_head';
}
if (setting.key === 'codeinjection_foot') {
attrs[index].key = 'codeinjection_foot';
}
});
return;
}
// CASE: browse all settings, add extra keys and keep deprecated
const ghostHead = _.cloneDeep(_.find(attrs, {key: 'ghost_head'}));
const ghostFoot = _.cloneDeep(_.find(attrs, {key: 'ghost_foot'}));

View file

@ -55,9 +55,9 @@ const mapPost = (model, frame) => {
return jsonModel;
};
const mapSettings = (attrs) => {
const mapSettings = (attrs, frame) => {
url.forSettings(attrs);
extraAttrs.forSettings(attrs);
extraAttrs.forSettings(attrs, frame);
return attrs;
};

View file

@ -1,20 +1,11 @@
const Promise = require('bluebird');
const _ = require('lodash');
const common = require('../../../../../lib/common');
const settingsCache = require('../../../../../services/settings/cache');
module.exports = {
read(apiConfig, frame) {
let setting = settingsCache.get(frame.options.key, {resolve: false});
if (!setting) {
return Promise.reject(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.settings.problemFindingSetting', {key: frame.options.key})
}));
}
// @NOTE: was removed (https://github.com/TryGhost/Ghost/commit/8bb7088ba026efd4a1c9cf7d6f1a5e9b4fa82575)
if (setting.key === 'permalinks') {
if (frame.options.key === 'permalinks') {
return Promise.reject(new common.errors.NotFoundError({
message: common.i18n.t('errors.errors.resourceNotFound')
}));
@ -25,13 +16,7 @@ module.exports = {
const errors = [];
_.each(frame.data.settings, (setting) => {
const settingFromCache = settingsCache.get(setting.key, {resolve: false});
if (!settingFromCache) {
errors.push(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.settings.problemFindingSetting', {key: setting.key})
}));
} else if (settingFromCache.key === 'active_theme') {
if (setting.key === 'active_theme') {
// @NOTE: active theme has to be changed via theme endpoints
errors.push(
new common.errors.BadRequestError({
@ -39,7 +24,7 @@ module.exports = {
help: common.i18n.t('errors.api.settings.activeThemeSetViaAPI.help')
})
);
} else if (settingFromCache.key === 'permalinks') {
} else if (setting.key === 'permalinks') {
// @NOTE: was removed (https://github.com/TryGhost/Ghost/commit/8bb7088ba026efd4a1c9cf7d6f1a5e9b4fa82575)
errors.push(new common.errors.NotFoundError({
message: common.i18n.t('errors.api.settings.problemFindingSetting', {key: setting.key})

View file

@ -60,7 +60,7 @@ describe('Settings API', function () {
});
it('Can read a setting', function (done) {
request.get(localUtils.API.getApiQuery('settings/title/'))
request.get(localUtils.API.getApiQuery('settings/ghost_head/'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
@ -71,13 +71,15 @@ describe('Settings API', function () {
}
should.not.exist(res.headers['x-cache-invalidate']);
var jsonResponse = res.body;
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.settings);
jsonResponse.settings.length.should.eql(1);
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'key', 'value', 'type', 'created_at', 'updated_at']);
jsonResponse.settings[0].key.should.eql('title');
jsonResponse.settings[0].key.should.eql('ghost_head');
testUtils.API.isISO8601(jsonResponse.settings[0].created_at).should.be.true();
done();
});
@ -102,7 +104,7 @@ describe('Settings API', function () {
value: changedValue
},
{
key: 'ghost_head',
key: 'codeinjection_head',
value: null
},
{
@ -133,10 +135,19 @@ describe('Settings API', function () {
const putBody = res.body;
res.headers['x-cache-invalidate'].should.eql('/*');
should.exist(putBody);
putBody.settings[0].key.should.eql('title');
putBody.settings[0].value.should.eql(JSON.stringify(changedValue));
putBody.settings[1].key.should.eql('codeinjection_head');
should.equal(putBody.settings[1].value, null);
putBody.settings[2].key.should.eql('navigation');
should.equal(putBody.settings[2].value, JSON.stringify({label: 'label1'}));
putBody.settings[3].key.should.eql('slack');
should.equal(putBody.settings[3].value, JSON.stringify({username: 'username'}));
localUtils.API.checkResponse(putBody, 'settings');
done();
});