Skip to content

Commit 44d016c

Browse files
committed
Add support for giving back total count via header
1 parent 712756e commit 44d016c

5 files changed

Lines changed: 101 additions & 5 deletions

File tree

lib/action-util.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const internals = {};
99
*
1010
* @type {Object}
1111
*/
12-
module.exports = (request, options) => {
12+
module.exports = (request, options, h) => {
1313

1414
return {
1515

@@ -157,6 +157,15 @@ module.exports = (request, options) => {
157157
const userId = Hoek.reach(request.auth.credentials, options.userIdProperty);
158158

159159
return userId;
160+
},
161+
162+
replyWithRange: (rangeStart, rangeEnd, total, results) => {
163+
if (options.totalCountHeader === 'content-range') {
164+
return h.response(results).header('content-range', `${options.model} ${rangeStart}-${rangeStart + results.length}/${total[0]['count(*)']}`);
165+
}
166+
167+
return h.response(results).header(options.totalCountHeader, String(total[0]['count(*)']));
168+
160169
}
161170
};
162171
};

lib/actions/add.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = function addToCollection(route, options) {
3232
return Boom.notFound('No record found with the specified `id`.');
3333
}
3434

35-
//if a key was specified for the relation, we're linking an exsiting.
35+
//if a key was specified for the relation, we're linking an existing.
3636
if (keys.child.value) {
3737

3838
const updatedTokenId = await foundModel.$relatedQuery(options.associationAttr)

lib/actions/find.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ const Actions = require('../action-util');
2121

2222
module.exports = function findRecords(route, options) {
2323

24-
return async (request) => {
24+
return async (request, reply) => {
2525

26-
const actionUtil = Actions(request, options);
26+
const actionUtil = Actions(request, options, reply);
2727
// Look up the model
2828
const Model = actionUtil.parseModel();
2929
// Lookup for records that match the specified criteria. Are we just counting?
@@ -39,18 +39,30 @@ module.exports = function findRecords(route, options) {
3939
const rangeEnd = rangeStart ? rangeStart + limit : limit;
4040
const where = actionUtil.parseWhere();
4141

42-
const foundModelsQuery = Model.query().range(rangeStart, rangeEnd).limit(limit);
42+
const foundModelsQuery = Model.query();
4343

4444
if (where) {
4545
foundModelsQuery.where(where);
4646
}
4747

48+
let total;
49+
50+
if (options.includeTotalCount) {
51+
total = await foundModelsQuery.clone().count();
52+
}
53+
4854
if (sort) {
4955
foundModelsQuery.orderByRaw(sort);
5056
}
5157

58+
foundModelsQuery.range(rangeStart, rangeEnd).limit(limit);
59+
5260
const foundModels = await foundModelsQuery;
5361

62+
if (total) {
63+
return actionUtil.replyWithRange(rangeStart, rangeEnd, total, foundModels.results);
64+
}
65+
5466
return foundModels.results;
5567

5668
};

lib/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ internals.defaults = {
7272
userModel: 'Users', // since it's not in the url
7373
userIdProperty: 'id', // on auth credentials
7474
prefix: '',
75+
includeTotalCount: false,
76+
totalCountHeader: 'content-range',
7577
_private: {
7678
actAsUserModifiedPath: false,
7779
count: false

test/index.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,79 @@ describe('Tandy', () => {
955955
expect(result).to.be.an.array();
956956
expect(result.length).to.equal(4);
957957
});
958+
it('Fetches all users and includes total count', async () => {
959+
960+
const server = await getServer(getOptions({
961+
tandy: {
962+
includeTotalCount: true
963+
}
964+
}));
965+
server.registerModel([
966+
TestModels.Users,
967+
TestModels.Tokens
968+
]);
969+
970+
await server.initialize();
971+
const knex = server.knex();
972+
// const data = await knex.seed.run({ directory: 'test/seeds' });
973+
await knex.seed.run({ directory: 'test/seeds' });
974+
975+
server.route({
976+
method: 'GET',
977+
path: '/users',
978+
handler: { tandy: {} }
979+
});
980+
981+
const options = {
982+
method: 'GET',
983+
url: '/users'
984+
};
985+
986+
const response = await server.inject(options);
987+
const result = response.result;
988+
989+
expect(response.statusCode).to.equal(200);
990+
expect(result).to.be.an.array();
991+
expect(result.length).to.equal(4);
992+
expect(response.headers['content-range']).to.equal('users 0-4/4');
993+
});
994+
it('Fetches all users and includes total count, custom header name', async () => {
995+
996+
const server = await getServer(getOptions({
997+
tandy: {
998+
includeTotalCount: true,
999+
totalCountHeader: 'x-count'
1000+
}
1001+
}));
1002+
server.registerModel([
1003+
TestModels.Users,
1004+
TestModels.Tokens
1005+
]);
1006+
1007+
await server.initialize();
1008+
const knex = server.knex();
1009+
// const data = await knex.seed.run({ directory: 'test/seeds' });
1010+
await knex.seed.run({ directory: 'test/seeds' });
1011+
1012+
server.route({
1013+
method: 'GET',
1014+
path: '/users',
1015+
handler: { tandy: {} }
1016+
});
1017+
1018+
const options = {
1019+
method: 'GET',
1020+
url: '/users'
1021+
};
1022+
1023+
const response = await server.inject(options);
1024+
const result = response.result;
1025+
1026+
expect(response.statusCode).to.equal(200);
1027+
expect(result).to.be.an.array();
1028+
expect(result.length).to.equal(4);
1029+
expect(response.headers['x-count']).to.equal('4');
1030+
});
9581031
it('Fetches all users without actAsUser', async () => {
9591032

9601033
const config = getOptions({

0 commit comments

Comments
 (0)