mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 22:11:09 +03:00
Optimized handling for post.deleted event in collections
refs https://github.com/TryGhost/Team/issues/3428 - I'm taking an approach of adding specialized support for each event one-by-one. - The post resource deletion event is the most straight forward and works same for both types of collections.
This commit is contained in:
parent
2c6f30b4b8
commit
49d831d971
@ -67,6 +67,10 @@ export class Collection {
|
||||
}
|
||||
}
|
||||
|
||||
includesPost(id: string) {
|
||||
return this.posts.includes(id);
|
||||
}
|
||||
|
||||
removeAllPosts() {
|
||||
this._posts = [];
|
||||
}
|
||||
|
@ -4,5 +4,5 @@ export interface CollectionRepository {
|
||||
save(collection: Collection): Promise<void>
|
||||
getById(id: string): Promise<Collection | null>
|
||||
getBySlug(slug: string): Promise<Collection | null>
|
||||
getAll(options: any): Promise<Collection[]>
|
||||
getAll(options?: any): Promise<Collection[]>
|
||||
}
|
||||
|
21
ghost/collections/src/CollectionResourceChangeEvent.ts
Normal file
21
ghost/collections/src/CollectionResourceChangeEvent.ts
Normal file
@ -0,0 +1,21 @@
|
||||
type CollectionResourceChangeEventData = {
|
||||
id: string;
|
||||
resource: 'post' | 'tag' | 'author';
|
||||
[any: string]: any;
|
||||
};
|
||||
|
||||
export class CollectionResourceChangeEvent {
|
||||
name: string;
|
||||
data: CollectionResourceChangeEventData;
|
||||
timestamp: Date;
|
||||
|
||||
constructor(name: string, data: CollectionResourceChangeEventData, timestamp: Date) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
static create(name: string, data: CollectionResourceChangeEventData, timestamp = new Date()) {
|
||||
return new CollectionResourceChangeEvent(name, data, timestamp);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import {Collection} from './Collection';
|
||||
import {CollectionResourceChangeEvent} from './CollectionResourceChangeEvent';
|
||||
import {CollectionRepository} from './CollectionRepository';
|
||||
import tpl from '@tryghost/tpl';
|
||||
import {MethodNotAllowedError, NotFoundError} from '@tryghost/errors';
|
||||
@ -187,14 +188,25 @@ export class CollectionsService {
|
||||
}
|
||||
}
|
||||
|
||||
async updateAutomaticCollections() {
|
||||
const collections = await this.collectionsRepository.getAll({
|
||||
filter: 'type:automatic'
|
||||
});
|
||||
async updateCollections(event: CollectionResourceChangeEvent) {
|
||||
if (event.name === 'post.deleted') {
|
||||
// NOTE: 'delete' works the same for both manual and automatic collections
|
||||
const collections = await this.collectionsRepository.getAll();
|
||||
|
||||
for (const collection of collections) {
|
||||
await this.updateAutomaticCollectionItems(collection);
|
||||
await this.collectionsRepository.save(collection);
|
||||
for (const collection of collections) {
|
||||
if (collection.includesPost(event.data.id)) {
|
||||
await collection.removePost(event.data.id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const collections = await this.collectionsRepository.getAll({
|
||||
filter: 'type:automatic'
|
||||
});
|
||||
|
||||
for (const collection of collections) {
|
||||
await this.updateAutomaticCollectionItems(collection);
|
||||
await this.collectionsRepository.save(collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './CollectionsService';
|
||||
export * from './CollectionsRepositoryInMemory';
|
||||
export * from './Collection';
|
||||
export * from './CollectionResourceChangeEvent';
|
||||
|
@ -1,5 +1,9 @@
|
||||
import assert from 'assert';
|
||||
import {CollectionsService, CollectionsRepositoryInMemory} from '../src/index';
|
||||
import {
|
||||
CollectionsService,
|
||||
CollectionsRepositoryInMemory,
|
||||
CollectionResourceChangeEvent
|
||||
} from '../src/index';
|
||||
import {PostsRepositoryInMemory} from './fixtures/PostsRepositoryInMemory';
|
||||
import {posts} from './fixtures/posts';
|
||||
|
||||
@ -23,10 +27,11 @@ const initPostsRepository = (): PostsRepositoryInMemory => {
|
||||
|
||||
describe('CollectionsService', function () {
|
||||
let collectionsService: CollectionsService;
|
||||
let postsRepository: PostsRepositoryInMemory;
|
||||
|
||||
beforeEach(async function () {
|
||||
const collectionsRepository = new CollectionsRepositoryInMemory();
|
||||
const postsRepository = initPostsRepository();
|
||||
postsRepository = initPostsRepository();
|
||||
|
||||
collectionsService = new CollectionsService({
|
||||
collectionsRepository,
|
||||
@ -311,29 +316,81 @@ describe('CollectionsService', function () {
|
||||
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'
|
||||
describe('updateCollections', function () {
|
||||
let automaticFeaturedCollection: any;
|
||||
let automaticNonFeaturedCollection: any;
|
||||
let manualCollection: any;
|
||||
|
||||
beforeEach(async function () {
|
||||
automaticFeaturedCollection = await collectionsService.createCollection({
|
||||
title: 'Featured Collection',
|
||||
description: 'testing automatic collection',
|
||||
type: 'automatic',
|
||||
filter: 'featured:true'
|
||||
});
|
||||
|
||||
automaticNonFeaturedCollection = await collectionsService.createCollection({
|
||||
title: 'Non-Featured Collection',
|
||||
description: 'testing automatic collection',
|
||||
type: 'automatic',
|
||||
filter: 'featured:false'
|
||||
});
|
||||
|
||||
manualCollection = await collectionsService.createCollection({
|
||||
title: 'Manual Collection',
|
||||
description: 'testing manual collection',
|
||||
type: 'manual'
|
||||
});
|
||||
|
||||
await collectionsService.addPostToCollection(manualCollection.id, posts[0]);
|
||||
await collectionsService.addPostToCollection(manualCollection.id, posts[1]);
|
||||
});
|
||||
|
||||
let collection2 = await collectionsService.createCollection({
|
||||
title: 'Featured Collection 2',
|
||||
description: 'testing automatic collection',
|
||||
type: 'automatic',
|
||||
filter: 'featured:true'
|
||||
afterEach(async function () {
|
||||
await collectionsService.destroy(automaticFeaturedCollection.id);
|
||||
await collectionsService.destroy(automaticNonFeaturedCollection.id);
|
||||
await collectionsService.destroy(manualCollection.id);
|
||||
});
|
||||
|
||||
assert.equal(collection1.posts.length, 2);
|
||||
assert.equal(collection2.posts.length, 2);
|
||||
it('Updates all collections when post is deleted', async function () {
|
||||
assert.equal((await collectionsService.getById(automaticFeaturedCollection.id))?.posts?.length, 2);
|
||||
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 2);
|
||||
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2);
|
||||
|
||||
await collectionsService.updateAutomaticCollections();
|
||||
const updateCollectionEvent = CollectionResourceChangeEvent.create('post.deleted', {
|
||||
id: posts[0].id,
|
||||
resource: 'post'
|
||||
});
|
||||
|
||||
assert.equal(collection1.posts.length, 2);
|
||||
assert.equal(collection2.posts.length, 2);
|
||||
await collectionsService.updateCollections(updateCollectionEvent);
|
||||
|
||||
assert.equal((await collectionsService.getById(automaticFeaturedCollection.id))?.posts?.length, 2);
|
||||
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 1);
|
||||
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 1);
|
||||
});
|
||||
|
||||
it('Updates automatic collections only when post is published', async function () {
|
||||
const newPost = {
|
||||
id: 'post-published',
|
||||
title: 'Post Published',
|
||||
slug: 'post-published',
|
||||
featured: true,
|
||||
published_at: new Date('2023-03-16T07:19:07.447Z'),
|
||||
deleted: false
|
||||
};
|
||||
await postsRepository.save(newPost);
|
||||
|
||||
const updateCollectionEvent = CollectionResourceChangeEvent.create('post.published', {
|
||||
id: newPost.id,
|
||||
resource: 'post'
|
||||
});
|
||||
|
||||
await collectionsService.updateCollections(updateCollectionEvent);
|
||||
|
||||
assert.equal((await collectionsService.getById(automaticFeaturedCollection.id))?.posts?.length, 3);
|
||||
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 2);
|
||||
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user