Skip to content

Commit

Permalink
Prevented pages content api queries from returning mobiledoc or lexic…
Browse files Browse the repository at this point in the history
…al fields (#20454)

ref https://linear.app/tryghost/issue/CFR-43/
ref 9d9a421

We recently stopped `select *` from posts when making Content API
requests. This is now being applied to the pages endpoint to help
improve performance. These fields were already being stripped out in the
output serializer, and they will now no longer be returned from the db
at all, reducing the amount of data transferred.
  • Loading branch information
9larsons committed Jun 24, 2024
1 parent b924027 commit b10b81b
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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 postsSchema = require('../../../../../data/schema').tables.posts;
const clean = require('./utils/clean');
const lexical = require('../../../../../lib/lexical');
const sentry = require('../../../../../../shared/sentry');
Expand All @@ -24,6 +25,33 @@ function removeSourceFormats(frame) {
}
}

/**
* Selects all allowed columns for the given frame.
*
* This removes the lexical and mobiledoc columns from the query. This is a performance improvement as we never intend
* to expose those columns in the content API and they are very large datasets to be passing around and de/serializing.
*
* NOTE: This is only intended for the Content API. We need these fields within Admin API responses.
*
* @param {Object} frame - The frame object.
*/
function selectAllAllowedColumns(frame) {
if (!frame.options.columns && !frame.options.selectRaw) {
// Because we're returning columns directly from the schema we need to remove info columns like @@UNIQUE_CONSTRAINTS@@ and @@INDEXES@@
frame.options.selectRaw = _.keys(_.omit(postsSchema, ['lexical','mobiledoc','@@INDEXES@@','@@UNIQUE_CONSTRAINTS@@'])).join(',');
} else if (frame.options.columns) {
frame.options.columns = frame.options.columns.filter((column) => {
return !['mobiledoc', 'lexical'].includes(column);
});
} else if (frame.options.selectRaw) {
frame.options.selectRaw = frame.options.selectRaw.split(',').map((column) => {
return column.trim();
}).filter((column) => {
return !['mobiledoc', 'lexical'].includes(column);
}).join(',');
}
}

function defaultRelations(frame) {
if (frame.options.withRelated) {
return;
Expand Down Expand Up @@ -97,7 +125,10 @@ module.exports = {
forcePageFilter(frame);

if (localUtils.isContentAPI(frame)) {
removeSourceFormats(frame);
// CASE: the content api endpoint for posts should not return mobiledoc or lexical
removeSourceFormats(frame); // remove from the format field
selectAllAllowedColumns(frame); // remove from any specified column or selectRaw options

setDefaultOrder(frame);
forceVisibilityColumn(frame);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const should = require('should');
const sinon = require('sinon');
const serializers = require('../../../../../../../core/server/api/endpoints/utils/serializers');
const postsSchema = require('../../../../../../../core/server/data/schema').tables.posts;

const mobiledocLib = require('@tryghost/html-to-mobiledoc');

Expand Down Expand Up @@ -67,6 +68,65 @@ describe('Unit: endpoints/utils/serializers/input/pages', function () {
frame.options.formats.should.containEql('html');
frame.options.formats.should.containEql('plaintext');
});

describe('Content API', function () {
it('selects all columns from the posts schema but mobiledoc and lexical when no columns are specified', function () {
const apiConfig = {};
const frame = {
apiType: 'content',
options: {
context: {}
}
};

serializers.input.pages.browse(apiConfig, frame);
const columns = Object.keys(postsSchema);
const parsedSelectRaw = frame.options.selectRaw.split(',').map(column => column.trim());
parsedSelectRaw.should.eql(columns.filter(column => !['mobiledoc', 'lexical','@@UNIQUE_CONSTRAINTS@@','@@INDEXES@@'].includes(column)));
});

it('strips mobiledoc and lexical columns from a specified columns option', function () {
const apiConfig = {};
const frame = {
apiType: 'content',
options: {
context: {},
columns: ['id', 'mobiledoc', 'lexical', 'visibility']
}
};

serializers.input.pages.browse(apiConfig, frame);
frame.options.columns.should.eql(['id', 'visibility']);
});

it('forces visibility column if columns are specified', function () {
const apiConfig = {};
const frame = {
apiType: 'content',
options: {
context: {},
columns: ['id']
}
};

serializers.input.pages.browse(apiConfig, frame);
frame.options.columns.should.eql(['id', 'visibility']);
});

it('strips mobiledoc and lexical columns from a specified selectRaw option', function () {
const apiConfig = {};
const frame = {
apiType: 'content',
options: {
context: {},
selectRaw: 'id, mobiledoc, lexical'
}
};

serializers.input.posts.browse(apiConfig, frame);
frame.options.selectRaw.should.eql('id');
});
});
});

describe('read', function () {
Expand Down

0 comments on commit b10b81b

Please sign in to comment.