Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safe post_count for Tags & Users #6091

Merged
merged 1 commit into from
Nov 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/server/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
29 changes: 26 additions & 3 deletions core/server/models/plugins/include-count.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
});
}
}
Expand Down
90 changes: 81 additions & 9 deletions core/test/integration/api/advanced_browse_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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?
done();
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('can fetch `posts.all.count` for users (all posts)', function (done) {
it.skip('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();
});
});
Expand Down
40 changes: 38 additions & 2 deletions core/test/integration/api/api_users_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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 () {
Expand Down
2 changes: 1 addition & 1 deletion core/test/utils/fixtures/filter-param/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ data.posts = [
featured: false,
page: 1,
author_id: 1,
tags: []
tags: [1, 2, 3, 4]
}
];

Expand Down