Rename post_count to count.posts

refs  #6009

- This is a straight rename, no functionality is added
- The dot syntax requires pre/post processing to convert the name
- This PR also includes several updates to the tests, as they weren't being run as part of Travis!
This commit is contained in:
Hannah Wolfe 2015-11-20 12:04:49 +00:00
parent 5df3cd5cfd
commit 2aa16514a3
9 changed files with 106 additions and 72 deletions

View File

@ -227,8 +227,8 @@ var _ = require('lodash'),
// #### All Integration tests
integration: {
src: [
'core/test/integration/**/model*_spec.js',
'core/test/integration/**/api*_spec.js',
'core/test/integration/**/*_spec.js',
'core/test/integration/**/*_spec.js',
'core/test/integration/*_spec.js'
]
},

View File

@ -8,7 +8,7 @@ var Promise = require('bluebird'),
pipeline = require('../utils/pipeline'),
docName = 'tags',
allowedIncludes = ['post_count'],
allowedIncludes = ['count.posts'],
tags;
/**

View File

@ -14,7 +14,7 @@ var Promise = require('bluebird'),
docName = 'users',
// TODO: implement created_by, updated_by
allowedIncludes = ['post_count', 'permissions', 'roles', 'roles.permissions'],
allowedIncludes = ['count.posts', 'permissions', 'roles', 'roles.permissions'],
users,
sendInviteEmail;

View File

@ -19,7 +19,8 @@ var _ = require('lodash'),
validation = require('../../data/validation'),
plugins = require('../plugins'),
ghostBookshelf;
ghostBookshelf,
proto;
// ### ghostBookshelf
// Initializes a new Bookshelf instance called ghostBookshelf, for reference elsewhere in Ghost.
@ -40,6 +41,9 @@ ghostBookshelf.plugin(plugins.includeCount);
// Load the Ghost pagination plugin, which gives us the `fetchPage` method on Models
ghostBookshelf.plugin(plugins.pagination);
// Cache an instance of the base model prototype
proto = ghostBookshelf.Model.prototype;
// ## ghostBookshelf.Model
// The Base Model which other Ghost objects will inherit from,
// including some convenience functions as static properties on the model.
@ -173,7 +177,8 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
}
});
return attrs;
// @TODO upgrade bookshelf & knex and use serialize & toJSON to do this in a neater way (see #6103)
return proto.finalize.call(this, attrs);
},
sanitize: function sanitize(attr) {

View File

@ -13,7 +13,7 @@ module.exports = function (Bookshelf) {
.from('posts')
.leftOuterJoin('posts_tags', 'posts.id', 'posts_tags.post_id')
.whereRaw('posts_tags.tag_id = tags.id')
.as('post_count');
.as('count__posts');
if (model.isPublicContext()) {
// @TODO use the filter behavior for posts
@ -29,7 +29,7 @@ module.exports = function (Bookshelf) {
qb.count('posts.id')
.from('posts')
.whereRaw('posts.author_id = users.id')
.as('post_count');
.as('count__posts');
if (model.isPublicContext()) {
// @TODO use the filter behavior for posts
@ -49,10 +49,9 @@ module.exports = function (Bookshelf) {
var tableName = _.result(this, 'tableName');
if (options.include && options.include.indexOf('post_count') > -1) {
if (options.include && options.include.indexOf('count.posts') > -1) {
// remove post_count from withRelated and include
options.withRelated = _.pull([].concat(options.withRelated), 'post_count');
options.include = _.pull([].concat(options.include), 'post_count');
options.withRelated = _.pull([].concat(options.withRelated), 'count.posts');
// Call the query builder
countQueryBuilder[tableName].posts(this);
@ -77,6 +76,20 @@ module.exports = function (Bookshelf) {
// Call parent fetchAll
return modelProto.fetchAll.apply(this, arguments);
},
finalize: function (attrs) {
var countRegex = /^(count)(__)(.*)$/;
_.forOwn(attrs, function (value, key) {
var match = key.match(countRegex);
if (match) {
attrs[match[1]] = attrs[match[1]] || {};
attrs[match[1]][match[3]] = value;
delete attrs[key];
}
});
return attrs;
}
});

View File

@ -5,9 +5,9 @@ var testUtils = require('../../utils'),
_ = require('lodash'),
// Stuff we are testing
PostAPI = require('../../../server/api/posts'),
TagAPI = require('../../../server/api/tags'),
UserAPI = require('../../../server/api/users');
PostAPI = require('../../../server/api/posts'),
TagAPI = require('../../../server/api/tags'),
UserAPI = require('../../../server/api/users');
describe('Filter Param Spec', function () {
// Initialise the DB just once, the tests are fetch-only
@ -16,6 +16,8 @@ describe('Filter Param Spec', function () {
after(testUtils.teardown);
should.exist(PostAPI);
should.exist(TagAPI);
should.exist(UserAPI);
describe('Advanced Use Cases', function () {
describe('1. Posts - filter: "tags: [photo, video] + id: -4", limit: "3", include: "tags"', function () {
@ -106,9 +108,10 @@ describe('Filter Param Spec', function () {
});
});
describe.skip('3. Tags - filter="post.count:>=1" order="posts.count DESC" limit="all"', function () {
describe.skip('3. Tags - filter="count.posts:>=1" order="count.posts DESC" limit="all"', function () {
// @TODO add support for counts/aggregates in order & filter params
it('Will fetch all tags, ordered by post count, where the post count is at least 1.', function (done) {
TagAPI.browse({filter: 'post.count:>=1', order: 'posts.count DESC', limit: 'all', include: 'posts.count'}).then(function (result) {
TagAPI.browse({filter: 'count.posts:>=1', order: 'count.posts DESC', limit: 'all', include: 'count.posts'}).then(function (result) {
// 1. Result should have the correct base structure
should.exist(result);
result.should.have.property('tags');
@ -181,9 +184,10 @@ describe('Filter Param Spec', function () {
});
});
describe('5. Users - filter="posts.tags:photo" order="posts.count DESC" limit="3"', function () {
describe.skip('5. Users - filter="posts.tags:photo" order="count.posts DESC" limit="3"', function () {
// @TODO: add support for joining through posts and tags for users
it('Will fetch the 3 most prolific users who write posts with the tag `photo` ordered by most posts.', function (done) {
UserAPI.browse({filter: 'posts.tags:special', order: 'posts.count DESC', limit: 3}).then(function (result) {
UserAPI.browse({filter: 'posts.tags:special', order: 'count.posts DESC', limit: 3}).then(function (result) {
var ids;
// 1. Result should have the correct base structure
should.exist(result);
@ -284,7 +288,11 @@ describe('Filter Param Spec', function () {
result.tags.should.be.an.Array.with.lengthOf(3);
ids = _.pluck(result.tags, 'id');
ids.should.eql([4, 3, 2]);
ids.should.containEql(4);
ids.should.containEql(3);
ids.should.containEql(2);
// @TODO standardise how alphabetical ordering is done across DBs (see #6104)
// ids.should.eql([4, 2, 3]);
should.exist(result.tags[0].image);
should.exist(result.tags[1].image);
@ -311,8 +319,8 @@ describe('Filter Param Spec', function () {
});
describe('Count capabilities', function () {
it('can fetch `post_count` for tags (public data only)', function (done) {
TagAPI.browse({include: 'post_count'}).then(function (result) {
it('can fetch `count.posts` for tags (public data only)', function (done) {
TagAPI.browse({include: 'count.posts'}).then(function (result) {
// 1. Result should have the correct base structure
should.exist(result);
result.should.have.property('tags');
@ -320,28 +328,32 @@ describe('Filter Param Spec', function () {
// 2. The data part of the response should be correct
// We should have 5 matching items
result.tags.should.be.an.Array.with.lengthOf(5);
result.tags.should.be.an.Array.with.lengthOf(6);
// Each tag should have the correct count
_.find(result.tags, function (tag) {
return tag.name === 'Getting Started';
}).post_count.should.eql(4);
}).count.posts.should.eql(4);
_.find(result.tags, function (tag) {
return tag.name === 'photo';
}).post_count.should.eql(4);
}).count.posts.should.eql(4);
_.find(result.tags, function (tag) {
return tag.name === 'Video';
}).post_count.should.eql(5);
}).count.posts.should.eql(5);
_.find(result.tags, function (tag) {
return tag.name === 'Audio';
}).post_count.should.eql(6);
}).count.posts.should.eql(6);
_.find(result.tags, function (tag) {
return tag.name === 'No Posts';
}).post_count.should.eql(0);
}).count.posts.should.eql(0);
_.find(result.tags, function (tag) {
return tag.name === 'Special';
}).count.posts.should.eql(3);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -349,7 +361,7 @@ describe('Filter Param Spec', function () {
result.meta.pagination.page.should.eql(1);
result.meta.pagination.limit.should.eql(15);
result.meta.pagination.pages.should.eql(1);
result.meta.pagination.total.should.eql(5);
result.meta.pagination.total.should.eql(6);
should.equal(result.meta.pagination.next, null);
should.equal(result.meta.pagination.prev, null);
@ -357,14 +369,14 @@ describe('Filter Param Spec', function () {
}).catch(done);
});
it.skip('can fetch `posts.count` for tags (all data)', function (done) {
it.skip('can fetch `count.posts` for tags (all data)', function (done) {
// This is tested elsewhere for now using user context
// No way to override it for public requests
done();
});
it('can fetch `posts.count` for users (published only)', function (done) {
UserAPI.browse({include: 'post_count'}).then(function (result) {
it('can fetch `count.posts` for users (published only)', function (done) {
UserAPI.browse({include: 'count.posts'}).then(function (result) {
// 1. Result should have the correct base structure
should.exist(result);
result.should.have.property('users');
@ -372,16 +384,20 @@ describe('Filter Param Spec', function () {
// 2. The data part of the response should be correct
// We should have 5 matching items
result.users.should.be.an.Array.with.lengthOf(2);
result.users.should.be.an.Array.with.lengthOf(3);
// Each user should have the correct count
_.find(result.users, function (user) {
return user.slug === 'leslie';
}).post_count.should.eql(15);
}).count.posts.should.eql(15);
_.find(result.users, function (user) {
return user.slug === 'pat-smith';
}).post_count.should.eql(3);
}).count.posts.should.eql(3);
_.find(result.users, function (user) {
return user.slug === 'camhowe';
}).count.posts.should.eql(0);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
@ -389,7 +405,7 @@ describe('Filter Param Spec', function () {
result.meta.pagination.page.should.eql(1);
result.meta.pagination.limit.should.eql(15);
result.meta.pagination.pages.should.eql(1);
result.meta.pagination.total.should.eql(2);
result.meta.pagination.total.should.eql(3);
should.equal(result.meta.pagination.next, null);
should.equal(result.meta.pagination.prev, null);

View File

@ -228,16 +228,16 @@ describe('Tags API', function () {
}).catch(done);
});
it('can browse with include post_count', function (done) {
TagAPI.browse({context: {user: 1}, include: 'post_count'}).then(function (results) {
it('can browse with include count.posts', function (done) {
TagAPI.browse({context: {user: 1}, include: 'count.posts'}).then(function (results) {
should.exist(results);
should.exist(results.tags);
results.tags.should.have.lengthOf(15);
testUtils.API.checkResponse(results.tags[0], 'tag', 'post_count');
should.exist(results.tags[0].post_count);
testUtils.API.checkResponse(results.tags[0], 'tag', 'count');
should.exist(results.tags[0].count.posts);
results.tags[0].post_count.should.eql(2);
results.tags[1].post_count.should.eql(2);
results.tags[0].count.posts.should.eql(2);
results.tags[1].count.posts.should.eql(2);
results.meta.pagination.should.have.property('page', 1);
results.meta.pagination.should.have.property('limit', 15);
results.meta.pagination.should.have.property('pages', 4);
@ -249,13 +249,13 @@ describe('Tags API', function () {
}).catch(done);
});
it('can browse page 4 with include post_count', function (done) {
TagAPI.browse({context: {user: 1}, include: 'post_count', page: 4}).then(function (results) {
it('can browse page 4 with include count.posts', function (done) {
TagAPI.browse({context: {user: 1}, include: 'count.posts', page: 4}).then(function (results) {
should.exist(results);
should.exist(results.tags);
results.tags.should.have.lengthOf(10);
testUtils.API.checkResponse(results.tags[0], 'tag', 'post_count');
should.exist(results.tags[0].post_count);
testUtils.API.checkResponse(results.tags[0], 'tag', 'count');
should.exist(results.tags[0].count.posts);
results.meta.pagination.should.have.property('page', 4);
results.meta.pagination.should.have.property('limit', 15);
@ -320,15 +320,15 @@ describe('Tags API', function () {
return _.filter(tags, {id: 1})[0];
}
it('returns post_count with include post_count', function (done) {
TagAPI.read({context: {user: 1}, include: 'post_count', slug: 'kitchen-sink'}).then(function (results) {
it('returns count.posts with include count.posts', function (done) {
TagAPI.read({context: {user: 1}, include: 'count.posts', slug: 'kitchen-sink'}).then(function (results) {
should.exist(results);
should.exist(results.tags);
results.tags.length.should.be.above(0);
testUtils.API.checkResponse(results.tags[0], 'tag', 'post_count');
should.exist(results.tags[0].post_count);
results.tags[0].post_count.should.equal(2);
testUtils.API.checkResponse(results.tags[0], 'tag', 'count');
should.exist(results.tags[0].count.posts);
results.tags[0].count.posts.should.equal(2);
done();
}).catch(done);

View File

@ -187,29 +187,29 @@ describe('Users API', function () {
.catch(done);
});
it('can browse with include post_count', function (done) {
UserAPI.browse(_.extend({}, testUtils.context.admin, {include: 'post_count'})).then(function (response) {
it('can browse with include count.posts', function (done) {
UserAPI.browse(_.extend({}, testUtils.context.admin, {include: 'count.posts'})).then(function (response) {
should.exist(response);
testUtils.API.checkResponse(response, 'users');
should.exist(response.users);
response.users.should.have.length(7);
response.users.should.have.length(7);
testUtils.API.checkResponse(response.users[0], 'user', 'post_count');
testUtils.API.checkResponse(response.users[1], 'user', 'post_count');
testUtils.API.checkResponse(response.users[2], 'user', 'post_count');
testUtils.API.checkResponse(response.users[3], 'user', 'post_count');
testUtils.API.checkResponse(response.users[4], 'user', 'post_count');
testUtils.API.checkResponse(response.users[5], 'user', 'post_count');
testUtils.API.checkResponse(response.users[6], 'user', 'post_count');
testUtils.API.checkResponse(response.users[0], 'user', 'count');
testUtils.API.checkResponse(response.users[1], 'user', 'count');
testUtils.API.checkResponse(response.users[2], 'user', 'count');
testUtils.API.checkResponse(response.users[3], 'user', 'count');
testUtils.API.checkResponse(response.users[4], 'user', 'count');
testUtils.API.checkResponse(response.users[5], 'user', 'count');
testUtils.API.checkResponse(response.users[6], 'user', 'count');
response.users[0].post_count.should.eql(0);
response.users[1].post_count.should.eql(0);
response.users[2].post_count.should.eql(0);
response.users[3].post_count.should.eql(7);
response.users[4].post_count.should.eql(0);
response.users[5].post_count.should.eql(0);
response.users[6].post_count.should.eql(0);
response.users[0].count.posts.should.eql(0);
response.users[1].count.posts.should.eql(0);
response.users[2].count.posts.should.eql(0);
response.users[3].count.posts.should.eql(7);
response.users[4].count.posts.should.eql(0);
response.users[5].count.posts.should.eql(0);
response.users[6].count.posts.should.eql(0);
response.meta.pagination.should.have.property('page', 1);
response.meta.pagination.should.have.property('limit', 15);

View File

@ -49,11 +49,11 @@ describe('Tag Model', function () {
}).catch(done);
});
it('returns post_count if include post_count', function (done) {
it('returns count.posts if include count.posts', function (done) {
testUtils.fixtures.insertPosts().then(function () {
TagModel.findOne({slug: 'kitchen-sink'}, {include: 'post_count'}).then(function (tag) {
TagModel.findOne({slug: 'kitchen-sink'}, {include: 'count.posts'}).then(function (tag) {
should.exist(tag);
tag.get('post_count').should.equal(2);
tag.toJSON().count.posts.should.equal(2);
done();
}).catch(done);
@ -78,13 +78,13 @@ describe('Tag Model', function () {
}).catch(done);
});
it('with include post_count', function (done) {
TagModel.findPage({limit: 'all', include: 'post_count'}).then(function (results) {
it('with include count.posts', function (done) {
TagModel.findPage({limit: 'all', include: 'count.posts'}).then(function (results) {
results.meta.pagination.page.should.equal(1);
results.meta.pagination.limit.should.equal('all');
results.meta.pagination.pages.should.equal(1);
results.tags.length.should.equal(5);
should.exist(results.tags[0].post_count);
should.exist(results.tags[0].count.posts);
done();
}).catch(done);