mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added automatic collection recalculation
closes https://github.com/TryGhost/Team/issues/3170 - When resources that are related to automatic collection filter are updated the posts in collections should be updated as well. - This change adds a super-basic way to track changes in post/tag/author resources and updated all automatic collections when any of those resources change. In the future we can optimize the update process to be more performant, but it's good enough for current needs
This commit is contained in:
parent
4d3c7d3177
commit
2390afc6f1
5 changed files with 82 additions and 10 deletions
|
@ -6,7 +6,8 @@ export class CollectionsRepositoryInMemory extends InMemoryRepository<string, Co
|
|||
return {
|
||||
title: entity.title,
|
||||
description: entity.description,
|
||||
feature_image: entity.featureImage
|
||||
feature_image: entity.featureImage,
|
||||
type: entity.type
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,6 +139,33 @@ export class CollectionsService {
|
|||
return this.toDTO(collection);
|
||||
}
|
||||
|
||||
async #updateAutomaticCollectionItems(collection: Collection, filter?:string) {
|
||||
const collectionFilter = filter || collection.filter;
|
||||
|
||||
if (collectionFilter) {
|
||||
const posts = await this.postsRepository.getAll({
|
||||
filter: collectionFilter
|
||||
});
|
||||
|
||||
collection.removeAllPosts();
|
||||
|
||||
for (const post of posts) {
|
||||
collection.addPost(post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateAutomaticCollections() {
|
||||
const collections = await this.collectionsRepository.getAll({
|
||||
filter: 'type:automatic'
|
||||
});
|
||||
|
||||
for (const collection of collections) {
|
||||
await this.#updateAutomaticCollectionItems(collection);
|
||||
await this.collectionsRepository.save(collection);
|
||||
}
|
||||
}
|
||||
|
||||
async edit(data: any): Promise<CollectionDTO | null> {
|
||||
const collection = await this.collectionsRepository.getById(data.id);
|
||||
|
||||
|
@ -153,15 +180,7 @@ export class CollectionsService {
|
|||
}
|
||||
|
||||
if ((collection.type === 'automatic' || data.type === 'automatic') && data.filter) {
|
||||
const posts = await this.postsRepository.getAll({
|
||||
filter: data.filter
|
||||
});
|
||||
|
||||
collection.removeAllPosts();
|
||||
|
||||
for (const post of posts) {
|
||||
collection.addPost(post);
|
||||
}
|
||||
await this.#updateAutomaticCollectionItems(collection, data.filter);
|
||||
}
|
||||
|
||||
const collectionData = this.fromDTO(data);
|
||||
|
|
|
@ -225,5 +225,30 @@ describe('CollectionsService', function () {
|
|||
assert.equal(updatedCollection?.posts.length, 1, 'Collection should have one post');
|
||||
assert.equal(updatedCollection?.posts[0].id, 'post-2', 'Collection should have the correct post');
|
||||
});
|
||||
|
||||
// @NOTE: add a more comprehensive test as this one is too basic
|
||||
it('Updates all automatic collections', async function () {
|
||||
let collection1 = await collectionsService.createCollection({
|
||||
title: 'Featured Collection 1',
|
||||
description: 'testing automatic collection',
|
||||
type: 'automatic',
|
||||
filter: 'featured:true'
|
||||
});
|
||||
|
||||
let collection2 = await collectionsService.createCollection({
|
||||
title: 'Featured Collection 2',
|
||||
description: 'testing automatic collection',
|
||||
type: 'automatic',
|
||||
filter: 'featured:true'
|
||||
});
|
||||
|
||||
assert.equal(collection1.posts.length, 2);
|
||||
assert.equal(collection2.posts.length, 2);
|
||||
|
||||
await collectionsService.updateAutomaticCollections();
|
||||
|
||||
assert.equal(collection1.posts.length, 2);
|
||||
assert.equal(collection2.posts.length, 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@ class CollectionsServiceWrapper {
|
|||
|
||||
constructor() {
|
||||
const models = require('../../models');
|
||||
const events = require('../../lib/common/events');
|
||||
const collectionsRepositoryInMemory = new CollectionsRepositoryInMemory();
|
||||
|
||||
const collectionsService = new CollectionsService({
|
||||
|
@ -22,6 +23,17 @@ class CollectionsServiceWrapper {
|
|||
}
|
||||
});
|
||||
|
||||
// @NOTE: these should be reworked to use the "Event" classes
|
||||
// instead of Bookshelf model events
|
||||
const updateEvents = require('./update-events');
|
||||
|
||||
// @NOTE: naive update implementation to keep things simple for the first version
|
||||
for (const event of updateEvents) {
|
||||
events.on(event, () => {
|
||||
collectionsService.updateAutomaticCollections();
|
||||
});
|
||||
}
|
||||
|
||||
this.api = {
|
||||
browse: collectionsService.getAll.bind(collectionsService),
|
||||
read: collectionsService.getById.bind(collectionsService),
|
||||
|
|
15
ghost/core/core/server/services/collections/update-events.js
Normal file
15
ghost/core/core/server/services/collections/update-events.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
module.exports = [
|
||||
'post.published',
|
||||
'post.published.edited',
|
||||
'post.unpublished',
|
||||
'tag.added',
|
||||
'tag.edited',
|
||||
'tag.attached',
|
||||
'tag.detached',
|
||||
'tag.deleted',
|
||||
'user.activated',
|
||||
'user.activated.edited',
|
||||
'user.attached',
|
||||
'user.detached',
|
||||
'user.deleted'
|
||||
];
|
Loading…
Add table
Reference in a new issue