Added 'post.added' event handling in Collections

refs https://github.com/TryGhost/Team/issues/3169

- This piece of logic handles the 'post.added' model event mapping to Collection's PostAddedEvent domain event and logic related to updating collections when the new post is added.
This commit is contained in:
Naz 2023-06-23 13:56:06 +07:00 committed by naz
parent 42539b954f
commit 36783e456b
7 changed files with 71 additions and 15 deletions

View File

@ -31,6 +31,7 @@
"@tryghost/domain-events": "0.0.0",
"@tryghost/errors": "^1.2.25",
"@tryghost/in-memory-repository": "0.0.0",
"@tryghost/nql": "^0.11.0",
"@tryghost/tpl": "^0.1.25",
"bson-objectid": "^2.0.4"
},

View File

@ -1,5 +1,6 @@
import {ValidationError} from '@tryghost/errors';
import tpl from '@tryghost/tpl';
import nql = require('@tryghost/nql');
import ObjectID from 'bson-objectid';
@ -81,11 +82,15 @@ export class Collection {
* @param post {{id: string}} - The post to add to the collection
* @param index {number} - The index to insert the post at, use negative numbers to count from the end.
*/
addPost(post: {id: string}, index: number = -0) {
// if (this.type === 'automatic') {
// TODO: Test the post against the NQL filter stored in `this.filter`
// This will need the `post` param to include more data.
// }
addPost(post: CollectionPost, index: number = -0) {
if (this.type === 'automatic') {
const filterNql = nql(this.filter);
const matchesFilter = filterNql.queryJSON(post);
if (!matchesFilter) {
return false;
}
}
if (this.posts.includes(post.id)) {
this._posts = this.posts.filter(id => id !== post.id);
@ -96,6 +101,7 @@ export class Collection {
}
this.posts.splice(index, 0, post.id);
return true;
}
removePost(id: string) {
@ -172,6 +178,7 @@ export class Collection {
}
if (data.type === 'automatic' && !data.filter) {
// @NOTE: add filter validation here
throw new ValidationError({
message: tpl(messages.invalidFilterProvided.message),
context: tpl(messages.invalidFilterProvided.context)

View File

@ -200,7 +200,7 @@ export class CollectionsService {
collection.removeAllPosts();
for (const post of posts) {
collection.addPost(post);
await collection.addPost(post);
}
}
}
@ -222,10 +222,11 @@ export class CollectionsService {
for (const collection of collections) {
await collection.addPost(post);
// const added = await collection.addPost(post);
// if (added) {
await this.collectionsRepository.save(collection);
// }
const added = await collection.addPost(post);
if (added) {
await this.collectionsRepository.save(collection);
}
}
}

View File

@ -2,7 +2,6 @@ type PostData = {
id: string;
featured: boolean;
published_at: Date;
timestamp: Date;
};
export class PostAddedEvent {
@ -10,9 +9,9 @@ export class PostAddedEvent {
data: PostData;
timestamp: Date;
constructor(data: PostAddedEvent, timestamp: Date) {
constructor(data: PostData, timestamp: Date) {
this.id = data.id;
this.data = data.data;
this.data = data;
this.timestamp = timestamp;
}

View File

@ -1,3 +1,4 @@
declare module '@tryghost/errors';
declare module '@tryghost/tpl';
declare module '@tryghost/domain-events'
declare module '@tryghost/nql'

View File

@ -163,7 +163,8 @@ describe('Collection', function () {
it('Can add posts to different positions', async function () {
const collection = await Collection.create({
title: 'Testing adding posts'
title: 'Testing adding posts',
type: 'manual'
});
assert(collection.posts.length === 0);
@ -191,6 +192,32 @@ describe('Collection', function () {
assert(collection.posts[collection.posts.length - 2] === '3');
});
it('Adds a post to an automatic collection when it matches the filter', async function () {
const collection = await Collection.create({
title: 'Testing adding posts',
type: 'automatic',
filter: 'featured:true'
});
assert.equal(collection.posts.length, 0, 'Collection should have no posts');
const added = await collection.addPost({
id: '0',
featured: false
});
assert.equal(added, false);
assert.equal(collection.posts.length, 0, 'The non-featured post should not have been added');
const featuredAdded = await collection.addPost({
id: '1',
featured: true
});
assert.equal(featuredAdded, true);
assert.equal(collection.posts.length, 1, 'The featured post should have been added');
});
it('Removes a post by id', async function () {
const collection = await Collection.create({
title: 'Testing adding posts'

View File

@ -5,7 +5,8 @@ import {
CollectionsService,
CollectionsRepositoryInMemory,
CollectionResourceChangeEvent,
PostDeletedEvent
PostDeletedEvent,
PostAddedEvent
} from '../src/index';
import {PostsRepositoryInMemory} from './fixtures/PostsRepositoryInMemory';
import {posts} from './fixtures/posts';
@ -393,6 +394,25 @@ describe('CollectionsService', function () {
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 1);
});
it('Updates only index collection when a non-featured post is added', 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);
collectionsService.subscribeToEvents();
const postAddedEvent = PostAddedEvent.create({
id: 'non-featured-post',
featured: false
});
DomainEvents.dispatch(postAddedEvent);
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);
});
it('Updates automatic collections only when post is published', async function () {
const newPost = {
id: 'post-published',