Safe post_count for Tags & Users

refs #6009, #5614

- Use the new isPublicContext method to detect whether to add extra clauses to the count
- Add count to users
This commit is contained in:
Hannah Wolfe 2015-11-16 17:09:27 +00:00
parent 4a25a017c3
commit 770f45245c
5 changed files with 149 additions and 18 deletions

View File

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

View File

@ -9,10 +9,33 @@ module.exports = function (Bookshelf) {
tags: {
posts: function addPostCountToTags(model) {
model.query('columns', 'tags.*', function (qb) {
qb.count('posts_tags.post_id')
.from('posts_tags')
.whereRaw('tag_id = tags.id')
qb.count('posts.id')
.from('posts')
.leftOuterJoin('posts_tags', 'posts.id', 'posts_tags.post_id')
.whereRaw('posts_tags.tag_id = tags.id')
.as('post_count');
if (model.isPublicContext()) {
// @TODO use the filter behavior for posts
qb.andWhere('posts.page', '=', false);
qb.andWhere('posts.status', '=', 'published');
}
});
}
},
users: {
posts: function addPostCountToTags(model) {
model.query('columns', 'users.*', function (qb) {
qb.count('posts.id')
.from('posts')
.whereRaw('posts.author_id = users.id')
.as('post_count');
if (model.isPublicContext()) {
// @TODO use the filter behavior for posts
qb.andWhere('posts.page', '=', false);
qb.andWhere('posts.status', '=', 'published');
}
});
}
}

View File

@ -310,26 +310,98 @@ describe('Filter Param Spec', function () {
});
});
describe.skip('Count capabilities', function () {
it('can fetch `posts.count` for tags (published only)', function (done) {
// This could be posts.count & posts.all.count?
done();
describe('Count capabilities', function () {
it('can fetch `post_count` for tags (public data only)', function (done) {
TagAPI.browse({include: 'post_count'}).then(function (result) {
// 1. Result should have the correct base structure
should.exist(result);
result.should.have.property('tags');
result.should.have.property('meta');
// 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);
// Each tag should have the correct count
_.find(result.tags, function (tag) {
return tag.name === 'Getting Started';
}).post_count.should.eql(4);
_.find(result.tags, function (tag) {
return tag.name === 'photo';
}).post_count.should.eql(4);
_.find(result.tags, function (tag) {
return tag.name === 'Video';
}).post_count.should.eql(5);
_.find(result.tags, function (tag) {
return tag.name === 'Audio';
}).post_count.should.eql(6);
_.find(result.tags, function (tag) {
return tag.name === 'No Posts';
}).post_count.should.eql(0);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
result.meta.pagination.should.be.an.Object.with.properties(['page', 'limit', 'pages', 'total', 'next', 'prev']);
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);
should.equal(result.meta.pagination.next, null);
should.equal(result.meta.pagination.prev, null);
done();
}).catch(done);
});
it('can fetch `posts.all.count` for tags (all posts)', function (done) {
it.skip('can fetch `posts.count` 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) {
// This could be posts.count & posts.all.count?
UserAPI.browse({include: 'post_count'}).then(function (result) {
// 1. Result should have the correct base structure
should.exist(result);
result.should.have.property('users');
result.should.have.property('meta');
// 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);
// Each user should have the correct count
_.find(result.users, function (user) {
return user.slug === 'leslie';
}).post_count.should.eql(15);
_.find(result.users, function (user) {
return user.slug === 'pat-smith';
}).post_count.should.eql(3);
// 3. The meta object should contain the right details
result.meta.should.have.property('pagination');
result.meta.pagination.should.be.an.Object.with.properties(['page', 'limit', 'pages', 'total', 'next', 'prev']);
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);
should.equal(result.meta.pagination.next, null);
should.equal(result.meta.pagination.prev, null);
done();
}).catch(done);
});
it.skip('can fetch `posts.all.count` for users (all posts)', function (done) {
done();
});
it('can fetch `posts.all.count` for users (all posts)', function (done) {
done();
});
it('can fetch `tags.count` for posts', function (done) {
it.skip('can fetch `tags.count` for posts', function (done) {
done();
});
});

View File

@ -20,7 +20,9 @@ describe('Users API', function () {
// Keep the DB clean
before(testUtils.teardown);
beforeEach(testUtils.setup('users:roles', 'users', 'user:token', 'perms:user', 'perms:role', 'perms:setting', 'perms:init'));
beforeEach(testUtils.setup(
'users:roles', 'users', 'user:token', 'perms:user', 'perms:role', 'perms:setting', 'perms:init', 'posts'
));
afterEach(testUtils.teardown);
function checkForErrorType(type, done) {
@ -131,7 +133,6 @@ describe('Users API', function () {
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', 'roles');
testUtils.API.checkResponse(response.users[1], 'user', 'roles');
testUtils.API.checkResponse(response.users[2], 'user', 'roles');
@ -185,6 +186,41 @@ describe('Users API', function () {
.then(done)
.catch(done);
});
it('can browse with include post_count', function (done) {
UserAPI.browse(_.extend({}, testUtils.context.admin, {include: 'post_count'})).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');
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.meta.pagination.should.have.property('page', 1);
response.meta.pagination.should.have.property('limit', 15);
response.meta.pagination.should.have.property('pages', 1);
response.meta.pagination.should.have.property('total', 7);
response.meta.pagination.should.have.property('next', null);
response.meta.pagination.should.have.property('prev', null);
done();
}).catch(done);
});
});
describe('Read', function () {

View File

@ -241,7 +241,7 @@ data.posts = [
featured: false,
page: 1,
author_id: 1,
tags: []
tags: [1, 2, 3, 4]
}
];