Added posts_meta.feature_image_{alt,caption} columns (#13030)

refs https://github.com/TryGhost/Team/issues/770

We want post feature image functionality to better match what's available inside the editor, to do that we'll need somewhere to store alt and caption meta data. `posts_meta` chosen because even though we want to make this generic for other tables in the future those tables also have a `feature_image` (or closely related) field.

- updated schema with new columns
- added migration to create columns
- cleaned new columns from API output
  - not output on v2/v3
  - conditionally output on v4/canary output based on labs flag
- bumped `@tryghost/admin-api-schema` to allow new columns through in canary API requests
  - silently clean properties from input when labs flag is disabled
  - updated acceptance tests so they fail if `admin-api-schema` is not letting the new fields through
This commit is contained in:
Kevin Ansfield 2021-06-10 20:35:56 +01:00 committed by GitHub
parent b53de12e12
commit 1bc57b584a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 88 additions and 27 deletions

View File

@ -6,6 +6,7 @@ const url = require('./utils/url');
const slugFilterOrder = require('./utils/slug-filter-order');
const localUtils = require('../../index');
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
const labs = require('../../../../../services/labs');
const replacePageWithType = mapNQLKeyValues({
key: {
@ -102,6 +103,13 @@ const forceStatusFilter = (frame) => {
}
};
const cleanLabsProperties = (frame) => {
if (!labs.isSet('featureImageMeta') && frame.data.pages[0]) {
delete frame.data.pages[0].feature_image_alt;
delete frame.data.pages[0].feature_image_caption;
}
};
module.exports = {
browse(apiConfig, frame) {
debug('browse');
@ -180,6 +188,7 @@ module.exports = {
});
}
cleanLabsProperties(frame);
handlePostsMeta(frame);
defaultFormat(frame);
defaultRelations(frame);
@ -189,7 +198,6 @@ module.exports = {
debug('edit');
this.add(...arguments, {add: false});
handlePostsMeta(frame);
forceStatusFilter(frame);
forcePageFilter(frame);
},

View File

@ -6,6 +6,7 @@ const slugFilterOrder = require('./utils/slug-filter-order');
const localUtils = require('../../index');
const mobiledoc = require('../../../../../lib/mobiledoc');
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
const labs = require('../../../../../services/labs');
const replacePageWithType = mapNQLKeyValues({
key: {
@ -111,6 +112,13 @@ const transformLegacyEmailRecipientFilters = (frame) => {
}
};
const cleanLabsProperties = (frame) => {
if (!labs.isSet('featureImageMeta') && frame.data.posts[0]) {
delete frame.data.posts[0].feature_image_alt;
delete frame.data.posts[0].feature_image_caption;
}
};
module.exports = {
browse(apiConfig, frame) {
debug('browse');
@ -205,6 +213,7 @@ module.exports = {
});
}
cleanLabsProperties(frame);
transformLegacyEmailRecipientFilters(frame);
handlePostsMeta(frame);
defaultFormat(frame);
@ -215,8 +224,6 @@ module.exports = {
debug('edit');
this.add(apiConfig, frame, {add: false});
transformLegacyEmailRecipientFilters(frame);
handlePostsMeta(frame);
forceStatusFilter(frame);
forcePageFilter(frame);
},

View File

@ -1,5 +1,6 @@
const _ = require('lodash');
const localUtils = require('../../../index');
const labs = require('../../../../../../services/labs');
const tag = (attrs, frame) => {
if (localUtils.isContentAPI(frame)) {
@ -124,6 +125,11 @@ const post = (attrs, frame) => {
delete attrs.author;
delete attrs.type;
if (!labs.isSet('featureImageMeta')) {
delete attrs.feature_image_alt;
delete attrs.feature_image_caption;
}
return attrs;
};

View File

@ -136,6 +136,8 @@ const post = (attrs, frame) => {
delete attrs.send_email_when_published;
delete attrs.email_recipient_filter;
delete attrs.email_subject;
delete attrs.feature_image_alt;
delete attrs.feature_image_caption;
return attrs;
};

View File

@ -123,6 +123,8 @@ const post = (attrs, frame) => {
delete attrs.locale;
delete attrs.author;
delete attrs.type;
delete attrs.feature_image_alt;
delete attrs.feature_image_caption;
return attrs;
};

View File

@ -0,0 +1,7 @@
const {createAddColumnMigration} = require('../../utils');
module.exports = createAddColumnMigration('posts_meta', 'feature_image_alt', {
type: 'string',
maxlength: 191,
nullable: true
});

View File

@ -0,0 +1,7 @@
const {createAddColumnMigration} = require('../../utils');
module.exports = createAddColumnMigration('posts_meta', 'feature_image_caption', {
type: 'text',
maxlength: 65535,
nullable: true
});

View File

@ -72,7 +72,9 @@ module.exports = {
meta_title: {type: 'string', maxlength: 2000, nullable: true, validations: {isLength: {max: 300}}},
meta_description: {type: 'string', maxlength: 2000, nullable: true, validations: {isLength: {max: 500}}},
email_subject: {type: 'string', maxlength: 300, nullable: true},
frontmatter: {type: 'text', maxlength: 65535, nullable: true}
frontmatter: {type: 'text', maxlength: 65535, nullable: true},
feature_image_alt: {type: 'string', maxlength: 191, nullable: true, validations: {isLength: {max: 125}}},
feature_image_caption: {type: 'text', maxlength: 65535, nullable: true}
},
users: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},

View File

@ -15,7 +15,8 @@ const BETA_FEATURES = [
];
const ALPHA_FEATURES = [
'multipleProducts'
'multipleProducts',
'featureImageMeta'
];
module.exports.WRITABLE_KEYS_ALLOWLIST = [...BETA_FEATURES, ...ALPHA_FEATURES];

View File

@ -43,7 +43,7 @@
"@nexes/nql": "0.5.2",
"@sentry/node": "6.6.0",
"@tryghost/adapter-manager": "0.2.12",
"@tryghost/admin-api-schema": "2.2.2",
"@tryghost/admin-api-schema": "2.3.0",
"@tryghost/bootstrap-socket": "0.2.8",
"@tryghost/constants": "0.1.7",
"@tryghost/email-analytics-provider-mailgun": "1.0.0",

View File

@ -41,7 +41,9 @@ describe('Pages API', function () {
const page = {
title: 'My Page',
page: false,
status: 'published'
status: 'published',
feature_image_alt: 'Testing feature image alt',
feature_image_caption: 'Testing <b>feature image caption</b>'
};
const res = await request.post(localUtils.API.getApiQuery('pages/'))
@ -63,9 +65,14 @@ describe('Pages API', function () {
id: res.body.pages[0].id
}, testUtils.context.internal);
model.get('title').should.eql(page.title);
model.get('status').should.eql(page.status);
model.get('type').should.eql('page');
const modelJson = model.toJSON();
modelJson.title.should.eql(page.title);
modelJson.status.should.eql(page.status);
modelJson.type.should.eql('page');
modelJson.posts_meta.feature_image_alt.should.eql(page.feature_image_alt);
modelJson.posts_meta.feature_image_caption.should.eql(page.feature_image_caption);
});
it('Can update a page', async function () {

View File

@ -204,6 +204,8 @@ describe('Posts API', function () {
const post = {
title: 'My post',
status: 'draft',
feature_image_alt: 'Testing feature image alt',
feature_image_caption: 'Testing <b>feature image caption</b>',
published_at: '2016-05-30T07:00:00.000Z',
mobiledoc: testUtils.DataGenerator.markdownToMobiledoc('my post'),
created_at: moment().subtract(2, 'days').toDate(),
@ -232,13 +234,18 @@ describe('Posts API', function () {
status: 'draft'
}, testUtils.context.internal);
model.get('title').should.eql(post.title);
model.get('status').should.eql(post.status);
model.get('published_at').toISOString().should.eql('2016-05-30T07:00:00.000Z');
model.get('created_at').toISOString().should.not.eql(post.created_at.toISOString());
model.get('updated_at').toISOString().should.not.eql(post.updated_at.toISOString());
model.get('updated_by').should.not.eql(post.updated_by);
model.get('created_by').should.not.eql(post.created_by);
const modelJson = model.toJSON();
modelJson.title.should.eql(post.title);
modelJson.status.should.eql(post.status);
modelJson.published_at.toISOString().should.eql('2016-05-30T07:00:00.000Z');
modelJson.created_at.toISOString().should.not.eql(post.created_at.toISOString());
modelJson.updated_at.toISOString().should.not.eql(post.updated_at.toISOString());
modelJson.updated_by.should.not.eql(post.updated_by);
modelJson.created_by.should.not.eql(post.created_by);
modelJson.posts_meta.feature_image_alt.should.eql(post.feature_image_alt);
modelJson.posts_meta.feature_image_caption.should.eql(post.feature_image_caption);
});
it('Can update draft', async function () {

View File

@ -39,6 +39,8 @@ const expectedProperties = {
'mobiledoc',
'comment_id',
'feature_image',
'feature_image_alt',
'feature_image_caption',
'featured',
'status',
'visibility',
@ -78,6 +80,8 @@ const expectedProperties = {
'mobiledoc',
'comment_id',
'feature_image',
'feature_image_alt',
'feature_image_caption',
'featured',
'status',
'visibility',

View File

@ -19,6 +19,8 @@ const expectedProperties = {
'html',
'comment_id',
'feature_image',
'feature_image_alt',
'feature_image_caption',
'featured',
'visibility',
'email_recipient_filter',

View File

@ -34,6 +34,8 @@ const expectedProperties = {
'mobiledoc',
'comment_id',
'feature_image',
'feature_image_alt',
'feature_image_caption',
'featured',
'status',
'visibility',

View File

@ -264,11 +264,6 @@ describe('api/canary/content/posts', function () {
let paidPost;
let membersPostWithPaywallCard;
before(function () {
// NOTE: ideally this would be set through Admin API request not a stub
sinon.stub(labs, 'isSet').withArgs('members').returns(true);
});
before (function () {
publicPost = testUtils.DataGenerator.forKnex.createPost({
slug: 'free-to-see',

View File

@ -19,6 +19,8 @@ const expectedProperties = {
'html',
'comment_id',
'feature_image',
'feature_image_alt',
'feature_image_caption',
'featured',
'visibility',
'email_recipient_filter',

View File

@ -32,7 +32,7 @@ const defaultSettings = require('../../../../core/server/data/schema/default-set
*/
describe('DB version integrity', function () {
// Only these variables should need updating
const currentSchemaHash = '2099a4ba6b9462c5a0aa36a434139e8b';
const currentSchemaHash = 'ef13a18aee78222086ca864c16bde696';
const currentFixturesHash = '8671672598d2a62e53418c4b91aa79a3';
const currentSettingsHash = 'fec0d2f71557a9bd2ff5ff8b423e11be';
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';

View File

@ -586,10 +586,10 @@
dependencies:
"@tryghost/errors" "^0.2.11"
"@tryghost/admin-api-schema@2.2.2":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@tryghost/admin-api-schema/-/admin-api-schema-2.2.2.tgz#d02c811f10bee5c3f62d3349ed220afd318f43cc"
integrity sha512-H2L8DkGloUT+1i8/0qdNYEeZNCrocFIqa8kE89EfjcyxTPkMsI6whpdI1CFQNHxuz9TazbC2M2TeTzCgALrQLg==
"@tryghost/admin-api-schema@2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@tryghost/admin-api-schema/-/admin-api-schema-2.3.0.tgz#3ec7dd98576e26166ff4c8082e6cd7542c3322d0"
integrity sha512-f69N5TE4Wzff0xr13Pa+HyfqCVktiIg+5L3we2TWYUO8XSrZcLKo5ionj/ybMHUY1M3DMVL2yVFjOfRWnV2TpA==
dependencies:
"@tryghost/errors" "^0.2.10"
bluebird "^3.5.3"