Supported ?include=count.posts for Collections API

Unfortuantely our framework is bookshelf centric so we have to refer to the
`withRelated` property rather than a more generic `include` property.

The collection entity already contains the list of post ids, so we can just
return the length of that array.
This commit is contained in:
Fabien "egg" O'Carroll 2023-07-25 16:30:40 +02:00 committed by Fabien 'egg' O'Carroll
parent 431f0ed842
commit 3d0825ea2f
4 changed files with 200 additions and 2 deletions

View File

@ -17,8 +17,16 @@ module.exports = {
'limit',
'order',
'page',
'filter'
'filter',
'include'
],
validation: {
options: {
include: {
values: ['count.posts']
}
}
},
permissions: true,
query(frame) {
return collectionsService.api.getAll(frame.options);
@ -29,10 +37,20 @@ module.exports = {
headers: {
cacheInvalidate: false
},
options: [
'include'
],
data: [
'id',
'slug'
],
validation: {
options: {
include: {
values: ['count.posts']
}
}
},
permissions: true,
async query(frame) {
let model;

View File

@ -4,7 +4,7 @@
*
* @returns {SerializedCollection}
*/
const mapper = (collection) => {
const mapper = (collection, frame) => {
let json;
if (collection.toJSON) {
json = collection.toJSON();
@ -24,6 +24,12 @@ const mapper = (collection) => {
updated_at: (json.updated_at || json.updatedAt).toISOString().replace(/\d{3}Z$/, '000Z')
};
if (frame?.options?.withRelated?.includes('count.posts')) {
serialized.count = {
posts: json.posts.length
};
}
return serialized;
};

View File

@ -1172,6 +1172,64 @@ Object {
}
`;
exports[`Collections API Browse Can browse Collections and include the posts count 1: [body] 1`] = `
Object {
"collections": Array [
Object {
"count": Object {
"posts": 13,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"description": "All posts",
"feature_image": null,
"filter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"slug": "latest",
"title": "Latest",
"type": "automatic",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
Object {
"count": Object {
"posts": 2,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"description": "Featured posts",
"feature_image": null,
"filter": "featured:true",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"slug": "featured",
"title": "Featured",
"type": "automatic",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
"meta": Object {
"pagination": Object {
"limit": 2,
"next": null,
"page": 1,
"pages": 1,
"prev": null,
"total": 2,
},
},
}
`;
exports[`Collections API Browse Can browse Collections and include the posts count 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "619",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Version, Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Collections API Can add a Collection 1: [body] 1`] = `
Object {
"collections": Array [
@ -1378,6 +1436,74 @@ Object {
}
`;
exports[`Collections API Can read a Collection by id and slug and include the post counts 1: [body] 1`] = `
Object {
"collections": Array [
Object {
"count": Object {
"posts": 2,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"description": "Featured posts",
"feature_image": null,
"filter": "featured:true",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"slug": "featured",
"title": "Featured",
"type": "automatic",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
}
`;
exports[`Collections API Can read a Collection by id and slug and include the post counts 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "284",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Version, Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Collections API Can read a Collection by id and slug and include the post counts 3: [body] 1`] = `
Object {
"collections": Array [
Object {
"count": Object {
"posts": 2,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"description": "Featured posts",
"feature_image": null,
"filter": "featured:true",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"slug": "featured",
"title": "Featured",
"type": "automatic",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
},
],
}
`;
exports[`Collections API Can read a Collection by id and slug and include the post counts 4: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "284",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Version, Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Collections API Cannot delete a built in collection 1: [body] 1`] = `
Object {
"errors": Array [

View File

@ -102,6 +102,22 @@ describe('Collections API', function () {
]
});
});
it('Can browse Collections and include the posts count', async function () {
await agent
.get('/collections/?include=count.posts')
.expectStatus(200)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
collections: [
{...matchCollection, count: {posts: 13}},
{...matchCollection, count: {posts: 2}}
]
});
});
});
it('Can read a Collection by id and slug', async function () {
@ -158,6 +174,38 @@ describe('Collections API', function () {
.expectStatus(204);
});
it('Can read a Collection by id and slug and include the post counts', async function () {
const {body: {collections: [collection]}} = await agent.get(`/collections/slug/featured/?include=count.posts`)
.expectStatus(200)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
collections: [{
...matchCollection,
count: {
posts: 2
}
}]
});
await agent.get(`/collections/${collection.id}/?include=count.posts`)
.expectStatus(200)
.matchHeaderSnapshot({
'content-version': anyContentVersion,
etag: anyEtag
})
.matchBodySnapshot({
collections: [{
...matchCollection,
count: {
posts: 2
}
}]
});
});
describe('Edit', function () {
let collectionToEdit;