mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Added handling for 'post.edited' Ghost model event
refs https://github.com/TryGhost/Team/issues/3169 - Adds optomized collection update handling for when post.edited model event is emitted.
This commit is contained in:
parent
a8e5cbcc3d
commit
5dd6159ac6
8 changed files with 140 additions and 7 deletions
|
@ -21,8 +21,8 @@
|
|||
"build"
|
||||
],
|
||||
"devDependencies": {
|
||||
"c8": "7.13.0",
|
||||
"@tryghost/domain-events": "0.0.0",
|
||||
"c8": "7.13.0",
|
||||
"mocha": "10.2.0",
|
||||
"sinon": "15.0.4",
|
||||
"ts-node": "10.9.1",
|
||||
|
@ -31,6 +31,7 @@
|
|||
"dependencies": {
|
||||
"@tryghost/errors": "^1.2.25",
|
||||
"@tryghost/in-memory-repository": "0.0.0",
|
||||
"@tryghost/logging": "^2.4.5",
|
||||
"@tryghost/nql": "^0.11.0",
|
||||
"@tryghost/tpl": "^0.1.25",
|
||||
"bson-objectid": "^2.0.4"
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import logging from '@tryghost/logging';
|
||||
import tpl from '@tryghost/tpl';
|
||||
import {Collection} from './Collection';
|
||||
import {CollectionResourceChangeEvent} from './CollectionResourceChangeEvent';
|
||||
import {CollectionRepository} from './CollectionRepository';
|
||||
import tpl from '@tryghost/tpl';
|
||||
import {MethodNotAllowedError, NotFoundError} from '@tryghost/errors';
|
||||
import {PostDeletedEvent} from './events/PostDeletedEvent';
|
||||
import {PostAddedEvent} from './events/PostAddedEvent';
|
||||
import {PostEditedEvent} from './events/PostEditedEvent';
|
||||
|
||||
const messages = {
|
||||
cannotDeleteBuiltInCollectionError: {
|
||||
|
@ -153,6 +155,10 @@ export class CollectionsService {
|
|||
this.DomainEvents.subscribe(PostAddedEvent, async (event: PostAddedEvent) => {
|
||||
await this.addPostToMatchingCollections(event.data);
|
||||
});
|
||||
|
||||
this.DomainEvents.subscribe(PostEditedEvent, async (event: PostEditedEvent) => {
|
||||
await this.updatePostInMatchingCollections(event.data);
|
||||
});
|
||||
}
|
||||
|
||||
async createCollection(data: CollectionInputDTO): Promise<CollectionDTO> {
|
||||
|
@ -228,7 +234,6 @@ export class CollectionsService {
|
|||
});
|
||||
|
||||
for (const collection of collections) {
|
||||
await collection.addPost(post);
|
||||
const added = await collection.addPost(post);
|
||||
|
||||
if (added) {
|
||||
|
@ -252,6 +257,31 @@ export class CollectionsService {
|
|||
}
|
||||
}
|
||||
|
||||
async updatePostInMatchingCollections(postEdit: PostEditedEvent['data']) {
|
||||
const collections = await this.collectionsRepository.getAll({
|
||||
filter: 'type:automatic'
|
||||
});
|
||||
|
||||
for (const collection of collections) {
|
||||
if (collection.includesPost(postEdit.id) && !collection.postMatchesFilter(postEdit.current)) {
|
||||
await collection.removePost(postEdit.id);
|
||||
await this.collectionsRepository.save(collection);
|
||||
|
||||
logging.info(`[Collections] Post ${postEdit.id} was updated and removed from collection ${collection.id} with filter ${collection.filter}`);
|
||||
} else if (!collection.includesPost(postEdit.id) && collection.postMatchesFilter(postEdit.current)) {
|
||||
const added = await collection.addPost(postEdit.current);
|
||||
|
||||
if (added) {
|
||||
await this.collectionsRepository.save(collection);
|
||||
}
|
||||
|
||||
logging.info(`[Collections] Post ${postEdit.id} was updated and added to collection ${collection.id} with filter ${collection.filter}`);
|
||||
} else {
|
||||
logging.info(`[Collections] Post ${postEdit.id} was updated but did not update any collections`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async edit(data: any): Promise<CollectionDTO | null> {
|
||||
const collection = await this.collectionsRepository.getById(data.id);
|
||||
|
||||
|
|
31
ghost/collections/src/events/PostEditedEvent.ts
Normal file
31
ghost/collections/src/events/PostEditedEvent.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
type PostEditData = {
|
||||
id: string;
|
||||
current: {
|
||||
id: string;
|
||||
title: string;
|
||||
featured: boolean;
|
||||
published_at: Date;
|
||||
},
|
||||
previous: {
|
||||
id: string;
|
||||
title: string;
|
||||
featured: boolean;
|
||||
published_at: Date;
|
||||
}
|
||||
};
|
||||
|
||||
export class PostEditedEvent {
|
||||
id: string;
|
||||
data: PostEditData;
|
||||
timestamp: Date;
|
||||
|
||||
constructor(data: any, timestamp: Date) {
|
||||
this.id = data.id;
|
||||
this.data = data;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
static create(data: any, timestamp = new Date()) {
|
||||
return new PostEditedEvent(data, timestamp);
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@ export * from './Collection';
|
|||
export * from './CollectionResourceChangeEvent';
|
||||
export * from './events/PostDeletedEvent';
|
||||
export * from './events/PostAddedEvent';
|
||||
export * from './events/PostEditedEvent';
|
||||
|
|
3
ghost/collections/src/libraries.d.ts
vendored
3
ghost/collections/src/libraries.d.ts
vendored
|
@ -1,4 +1,5 @@
|
|||
declare module '@tryghost/errors';
|
||||
declare module '@tryghost/tpl';
|
||||
declare module '@tryghost/domain-events'
|
||||
declare module '@tryghost/logging'
|
||||
declare module '@tryghost/nql'
|
||||
declare module '@tryghost/tpl';
|
||||
|
|
|
@ -266,7 +266,8 @@ describe('Collection', function () {
|
|||
it('Can match a post with a filter', async function () {
|
||||
const collection = await Collection.create({
|
||||
title: 'Testing filtering posts',
|
||||
type: 'automatic'
|
||||
type: 'automatic',
|
||||
filter: 'featured:true'
|
||||
});
|
||||
|
||||
const featuredPost = {
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
CollectionsRepositoryInMemory,
|
||||
CollectionResourceChangeEvent,
|
||||
PostDeletedEvent,
|
||||
PostAddedEvent
|
||||
PostAddedEvent,
|
||||
PostEditedEvent
|
||||
} from '../src/index';
|
||||
import {PostsRepositoryInMemory} from './fixtures/PostsRepositoryInMemory';
|
||||
import {posts} from './fixtures/posts';
|
||||
|
@ -417,6 +418,57 @@ describe('CollectionsService', function () {
|
|||
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 2);
|
||||
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2);
|
||||
});
|
||||
|
||||
it('Moves post form featured to non featured collection when the featured attribute is changed', async function () {
|
||||
collectionsService.subscribeToEvents();
|
||||
const newFeaturedPost = {
|
||||
id: 'post-featured',
|
||||
title: 'Post Featured',
|
||||
slug: 'post-featured',
|
||||
featured: false,
|
||||
published_at: new Date('2023-03-16T07:19:07.447Z'),
|
||||
deleted: false
|
||||
};
|
||||
await postsRepository.save(newFeaturedPost);
|
||||
const updateCollectionEvent = PostEditedEvent.create({
|
||||
id: newFeaturedPost.id,
|
||||
current: {
|
||||
id: newFeaturedPost.id,
|
||||
featured: false
|
||||
},
|
||||
previous: {
|
||||
id: newFeaturedPost.id,
|
||||
featured: true
|
||||
}
|
||||
});
|
||||
|
||||
DomainEvents.dispatch(updateCollectionEvent);
|
||||
await DomainEvents.allSettled();
|
||||
|
||||
assert.equal((await collectionsService.getById(automaticFeaturedCollection.id))?.posts?.length, 2);
|
||||
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 3);
|
||||
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2);
|
||||
|
||||
// change featured back to true
|
||||
const updateCollectionEventBackToFeatured = PostEditedEvent.create({
|
||||
id: newFeaturedPost.id,
|
||||
current: {
|
||||
id: newFeaturedPost.id,
|
||||
featured: true
|
||||
},
|
||||
previous: {
|
||||
id: newFeaturedPost.id,
|
||||
featured: false
|
||||
}
|
||||
});
|
||||
|
||||
DomainEvents.dispatch(updateCollectionEventBackToFeatured);
|
||||
await DomainEvents.allSettled();
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,8 @@ const DomainEvents = require('@tryghost/domain-events');
|
|||
const {
|
||||
CollectionResourceChangeEvent,
|
||||
PostDeletedEvent,
|
||||
PostAddedEvent
|
||||
PostAddedEvent,
|
||||
PostEditedEvent
|
||||
} = require('@tryghost/collections');
|
||||
|
||||
const domainEventDispatcher = (modelEventName, data) => {
|
||||
|
@ -18,8 +19,23 @@ const domainEventDispatcher = (modelEventName, data) => {
|
|||
event = PostAddedEvent.create({
|
||||
id: data.id,
|
||||
featured: data.featured,
|
||||
status: data.attributes.status,
|
||||
published_at: data.published_at
|
||||
});
|
||||
} if (modelEventName === 'post.edited') {
|
||||
event = PostEditedEvent.create({
|
||||
id: data.id,
|
||||
current: {
|
||||
title: data.attributes.title,
|
||||
status: data.attributes.status,
|
||||
featured: data.attributes.featured,
|
||||
published_at: data.attributes.published_at
|
||||
},
|
||||
// @NOTE: this will need to represent the previous state of the post
|
||||
// will be needed to optimize the query for the collection
|
||||
previous: {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
event = CollectionResourceChangeEvent.create(modelEventName, change);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue