mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Updated Content API resource ordering to be same as slugs in filter
closes #11994 - Adds support for ordering based on slug filter that contains a slug-is-in filter. It is applied only to Content API's resources - post, page, tag, author. The order is applied in the same order in which slugs appear in the filter. - For, example providing following query parameter filter for any of the above resources: `?filter=slug:[kitchen-sink,bacon,chorizo]`, would filter them by these slugs and order in the same way defined in the filter - Can be used in handlebars templates in following way: `{{#get "tags" filter="slug:[slugs,of,the,tags,in,order]"}}` - The property conteining this new order is assigned to `autoOrder` instead of `rawOrder` intentionally. This explicit asstignment would allow distinguishing where the 'orderRaw' comes from the model or the API layer. Apart from adding necessary context this separation makes it easier to refactor separately model layer and API specific ordering in the future - This commit also fixes default filtering for `author` resource in Content API. The serializer was never used before as it was missing from `serializers/index.js` module.
This commit is contained in:
parent
fe962345af
commit
d6267340a1
11 changed files with 129 additions and 11 deletions
|
@ -1,8 +1,13 @@
|
|||
const debug = require('ghost-ignition').debug('api:canary:utils:serializers:input:authors');
|
||||
const slugFilterOrder = require('./utils/slug-filter-order');
|
||||
const utils = require('../../index');
|
||||
|
||||
function setDefaultOrder(frame) {
|
||||
if (!frame.options.order) {
|
||||
if (!frame.options.order && frame.options.filter) {
|
||||
frame.options.autoOrder = slugFilterOrder('users', frame.options.filter);
|
||||
}
|
||||
|
||||
if (!frame.options.order && !frame.options.autoOrder) {
|
||||
frame.options.order = 'name asc';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ module.exports = {
|
|||
return require('./users');
|
||||
},
|
||||
|
||||
get authors() {
|
||||
return require('./authors');
|
||||
},
|
||||
|
||||
get tags() {
|
||||
return require('./tags');
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ const debug = require('ghost-ignition').debug('api:canary:utils:serializers:inpu
|
|||
const mapNQLKeyValues = require('@nexes/nql').utils.mapKeyValues;
|
||||
const mobiledoc = require('../../../../../lib/mobiledoc');
|
||||
const url = require('./utils/url');
|
||||
const slugFilterOrder = require('./utils/slug-filter-order');
|
||||
const localUtils = require('../../index');
|
||||
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
|
||||
|
||||
|
@ -48,7 +49,11 @@ function setDefaultOrder(frame) {
|
|||
includesOrderedRelations = _.intersection(orderedRelations, frame.options.withRelated).length > 0;
|
||||
}
|
||||
|
||||
if (!frame.options.order && !includesOrderedRelations) {
|
||||
if (!frame.options.order && !includesOrderedRelations && frame.options.filter) {
|
||||
frame.options.autoOrder = slugFilterOrder('posts', frame.options.filter);
|
||||
}
|
||||
|
||||
if (!frame.options.order && !frame.options.autoOrder && !includesOrderedRelations) {
|
||||
frame.options.order = 'title asc';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ const _ = require('lodash');
|
|||
const debug = require('ghost-ignition').debug('api:canary:utils:serializers:input:posts');
|
||||
const mapNQLKeyValues = require('@nexes/nql').utils.mapKeyValues;
|
||||
const url = require('./utils/url');
|
||||
const slugFilterOrder = require('./utils/slug-filter-order');
|
||||
const localUtils = require('../../index');
|
||||
const mobiledoc = require('../../../../../lib/mobiledoc');
|
||||
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
|
||||
|
@ -48,7 +49,11 @@ function setDefaultOrder(frame) {
|
|||
includesOrderedRelations = _.intersection(orderedRelations, frame.options.withRelated).length > 0;
|
||||
}
|
||||
|
||||
if (!frame.options.order && !includesOrderedRelations) {
|
||||
if (!frame.options.order && !includesOrderedRelations && frame.options.filter) {
|
||||
frame.options.autoOrder = slugFilterOrder('posts', frame.options.filter);
|
||||
}
|
||||
|
||||
if (!frame.options.order && !frame.options.autoOrder && !includesOrderedRelations) {
|
||||
frame.options.order = 'published_at desc';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
const debug = require('ghost-ignition').debug('api:canary:utils:serializers:input:tags');
|
||||
const url = require('./utils/url');
|
||||
const slugFilterOrder = require('./utils/slug-filter-order');
|
||||
const utils = require('../../index');
|
||||
|
||||
function setDefaultOrder(frame) {
|
||||
if (!frame.options.order) {
|
||||
frame.options.order = 'name asc';
|
||||
let defaultOrder = 'name asc';
|
||||
|
||||
if (!frame.options.order && frame.options.filter) {
|
||||
frame.options.autoOrder = slugFilterOrder('tags', frame.options.filter);
|
||||
}
|
||||
|
||||
if (!frame.options.order && !frame.options.autoOrder) {
|
||||
frame.options.order = defaultOrder;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
const slugFilterOrder = (table, filter) => {
|
||||
let orderMatch = filter.match(/slug:\s?\[(.*)\]/);
|
||||
|
||||
if (orderMatch) {
|
||||
let orderSlugs = orderMatch[1].split(',');
|
||||
let order = 'CASE ';
|
||||
|
||||
orderSlugs.forEach((slug, index) => {
|
||||
order += `WHEN \`${table}\`.\`slug\` = '${slug}' THEN ${index} `;
|
||||
});
|
||||
|
||||
order += 'END ASC';
|
||||
|
||||
return order;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = slugFilterOrder;
|
|
@ -696,7 +696,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
case 'findAll':
|
||||
return baseOptions.concat(extraOptions, ['filter', 'columns', 'mongoTransformer']);
|
||||
case 'findPage':
|
||||
return baseOptions.concat(extraOptions, ['filter', 'order', 'page', 'limit', 'columns', 'mongoTransformer']);
|
||||
return baseOptions.concat(extraOptions, ['filter', 'order', 'autoOrder', 'page', 'limit', 'columns', 'mongoTransformer']);
|
||||
default:
|
||||
return baseOptions.concat(extraOptions);
|
||||
}
|
||||
|
@ -907,6 +907,8 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
|
||||
if (options.order) {
|
||||
options.order = this.parseOrderOption(options.order, options.withRelated);
|
||||
} else if (options.autoOrder) {
|
||||
options.orderRaw = options.autoOrder;
|
||||
} else if (this.orderDefaultRaw) {
|
||||
options.orderRaw = this.orderDefaultRaw(options);
|
||||
} else if (this.orderDefaultOptions) {
|
||||
|
|
|
@ -38,4 +38,19 @@ describe('Authors Content API', function () {
|
|||
localUtils.API.checkResponse(res.body.authors[0], 'author', null, null, ['id', 'name']);
|
||||
});
|
||||
});
|
||||
|
||||
it('browse authors with slug filter, should order in slug order', function () {
|
||||
return request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}&filter=slug:[joe-bloggs,ghost,slimer-mcectoplasm]`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.authors.should.be.an.Array().with.lengthOf(3);
|
||||
jsonResponse.authors[0].slug.should.equal('joe-bloggs');
|
||||
jsonResponse.authors[1].slug.should.equal('ghost');
|
||||
jsonResponse.authors[2].slug.should.equal('slimer-mcectoplasm');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,8 @@ const ghost = testUtils.startGhost;
|
|||
let request;
|
||||
|
||||
describe('api/canary/content/pages', function () {
|
||||
const key = localUtils.getValidKey();
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function () {
|
||||
|
@ -24,7 +26,6 @@ describe('api/canary/content/pages', function () {
|
|||
});
|
||||
|
||||
it('Can browse pages with page:false', function () {
|
||||
const key = localUtils.getValidKey();
|
||||
return request.get(localUtils.API.getApiQuery(`pages/?key=${key}&filter=page:false`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
|
@ -43,9 +44,20 @@ describe('api/canary/content/pages', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('can\'t read post', function () {
|
||||
const key = localUtils.getValidKey();
|
||||
it('browse pages with slug filter, should order in slug order', function () {
|
||||
return request.get(localUtils.API.getApiQuery(`pages/?key=${key}&filter=slug:[static-page-test]`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.pages.should.be.an.Array().with.lengthOf(1);
|
||||
jsonResponse.pages[0].slug.should.equal('static-page-test');
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t read post', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`pages/${testUtils.DataGenerator.Content.posts[0].id}/?key=${key}`))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
|
|
|
@ -179,6 +179,36 @@ describe('api/canary/content/posts', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('browse posts with slug filter, should order in slug order', function () {
|
||||
return request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=slug:[themes,ghostly-kitchen-sink,the-editor]`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.posts.should.be.an.Array().with.lengthOf(3);
|
||||
jsonResponse.posts[0].slug.should.equal('themes');
|
||||
jsonResponse.posts[1].slug.should.equal('ghostly-kitchen-sink');
|
||||
jsonResponse.posts[2].slug.should.equal('the-editor');
|
||||
});
|
||||
});
|
||||
|
||||
it('browse posts with slug filter should order taking order parameter into account', function () {
|
||||
return request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&order=slug%20DESC&filter=slug:[themes,ghostly-kitchen-sink,the-editor]`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.posts.should.be.an.Array().with.lengthOf(3);
|
||||
jsonResponse.posts[0].slug.should.equal('themes');
|
||||
jsonResponse.posts[1].slug.should.equal('the-editor');
|
||||
jsonResponse.posts[2].slug.should.equal('ghostly-kitchen-sink');
|
||||
});
|
||||
});
|
||||
|
||||
it('ensure origin header on redirect is not getting lost', function (done) {
|
||||
// NOTE: force a redirect to the admin url
|
||||
configUtils.set('admin:url', 'http://localhost:9999');
|
||||
|
|
|
@ -9,6 +9,8 @@ const ghost = testUtils.startGhost;
|
|||
let request;
|
||||
|
||||
describe('api/canary/content/tags', function () {
|
||||
const validKey = localUtils.getValidKey();
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function () {
|
||||
|
@ -23,8 +25,6 @@ describe('api/canary/content/tags', function () {
|
|||
configUtils.restore();
|
||||
});
|
||||
|
||||
const validKey = localUtils.getValidKey();
|
||||
|
||||
it('can read tags with fields', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`tags/${testUtils.DataGenerator.Content.tags[0].id}/?key=${validKey}&fields=name,slug`))
|
||||
|
@ -36,4 +36,19 @@ describe('api/canary/content/tags', function () {
|
|||
localUtils.API.checkResponse(res.body.tags[0], 'tag', null, null, ['id', 'name', 'slug']);
|
||||
});
|
||||
});
|
||||
|
||||
it('browse tags with slug filter, should order in slug order', function () {
|
||||
return request.get(localUtils.API.getApiQuery(`tags/?key=${validKey}&filter=slug:[kitchen-sink,bacon,chorizo]`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.tags.should.be.an.Array().with.lengthOf(3);
|
||||
jsonResponse.tags[0].slug.should.equal('kitchen-sink');
|
||||
jsonResponse.tags[1].slug.should.equal('bacon');
|
||||
jsonResponse.tags[2].slug.should.equal('chorizo');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue