From 734ed0b4144140498170c7ce88e7d03c582dca60 Mon Sep 17 00:00:00 2001
From: Steve Larson <9larsons@gmail.com>
Date: Thu, 6 Jun 2024 13:45:02 -0500
Subject: [PATCH] Added selectRaw to permitted options for posts model (#20340)
ref https://linear.app/tryghost/issue/CFR-29/
- this allows our content api requests to omit fields we do not use,
improving performance
---
.../utils/serializers/input/pages.js | 11 +----
ghost/core/core/server/models/post.js | 2 +-
ghost/core/test/e2e-api/content/posts.test.js | 42 +++++++++++++++++++
3 files changed, 45 insertions(+), 10 deletions(-)
diff --git a/ghost/core/core/server/api/endpoints/utils/serializers/input/pages.js b/ghost/core/core/server/api/endpoints/utils/serializers/input/pages.js
index 52ec3626b3..19c58aa306 100644
--- a/ghost/core/core/server/api/endpoints/utils/serializers/input/pages.js
+++ b/ghost/core/core/server/api/endpoints/utils/serializers/input/pages.js
@@ -37,18 +37,11 @@ function defaultRelations(frame) {
}
function setDefaultOrder(frame) {
- let includesOrderedRelations = false;
-
- if (frame.options.withRelated) {
- const orderedRelations = ['author', 'authors', 'tag', 'tags'];
- includesOrderedRelations = _.intersection(orderedRelations, frame.options.withRelated).length > 0;
- }
-
- if (!frame.options.order && !includesOrderedRelations && frame.options.filter) {
+ if (!frame.options.order && frame.options.filter) {
frame.options.autoOrder = slugFilterOrder('posts', frame.options.filter);
}
- if (!frame.options.order && !frame.options.autoOrder && !includesOrderedRelations) {
+ if (!frame.options.order && !frame.options.autoOrder) {
frame.options.order = 'title asc';
}
}
diff --git a/ghost/core/core/server/models/post.js b/ghost/core/core/server/models/post.js
index 19ad820765..c1313faa76 100644
--- a/ghost/core/core/server/models/post.js
+++ b/ghost/core/core/server/models/post.js
@@ -1260,7 +1260,7 @@ Post = ghostBookshelf.Model.extend({
// these are the only options that can be passed to Bookshelf / Knex.
const validOptions = {
findOne: ['columns', 'importing', 'withRelated', 'require', 'filter'],
- findPage: ['status'],
+ findPage: ['status','selectRaw'],
findAll: ['columns', 'filter'],
destroy: ['destroyAll', 'destroyBy'],
diff --git a/ghost/core/test/e2e-api/content/posts.test.js b/ghost/core/test/e2e-api/content/posts.test.js
index f5280b7a72..7eeba91d84 100644
--- a/ghost/core/test/e2e-api/content/posts.test.js
+++ b/ghost/core/test/e2e-api/content/posts.test.js
@@ -22,6 +22,28 @@ const postMatcherShallowIncludes = Object.assign(
}
);
+async function trackDb(fn, skip) {
+ const db = require('../../../core/server/data/db');
+ if (db?.knex?.client?.config?.client !== 'sqlite3') {
+ return skip();
+ }
+ /** @type {import('sqlite3').Database} */
+ const database = db.knex.client;
+
+ const queries = [];
+ function handler(/** @type {{sql: string}} */ query) {
+ queries.push(query);
+ }
+
+ database.on('query', handler);
+
+ await fn();
+
+ database.off('query', handler);
+
+ return queries;
+}
+
describe('Posts Content API', function () {
let agent;
@@ -420,4 +442,24 @@ describe('Posts Content API', function () {
.expectStatus(200);
assert(response.body.posts[0].html.includes('LinkTest'), 'Html not expected: ' + response.body.posts[0].html);
});
+
+ it('Does not select * by default', async function () {
+ let queries = await trackDb(() => agent.get('posts/?limit=all').expectStatus(200), this.skip.bind(this));
+ let postsRelatedQueries = queries.filter(q => q.sql.includes('`posts`'));
+ for (const query of postsRelatedQueries) {
+ assert(!query.sql.includes('*'), 'Query should not select *');
+ }
+
+ queries = await trackDb(() => agent.get('posts/?limit=3').expectStatus(200), this.skip.bind(this));
+ postsRelatedQueries = queries.filter(q => q.sql.includes('`posts`'));
+ for (const query of postsRelatedQueries) {
+ assert(!query.sql.includes('*'), 'Query should not select *');
+ }
+
+ queries = await trackDb(() => agent.get('posts/?include=tags,authors').expectStatus(200), this.skip.bind(this));
+ postsRelatedQueries = queries.filter(q => q.sql.includes('`posts`'));
+ for (const query of postsRelatedQueries) {
+ assert(!query.sql.includes('*'), 'Query should not select *');
+ }
+ });
});