mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Added collections handling for PostsBulkFeaturedEvent/PostsBulkUnfeaturedEvent
refs https://github.com/TryGhost/Arch/issues/16 - When posts produce PostsBulkFeaturedEvent/PostsBulkUnfeaturedEvent the collections having a featured filter should update the posts belonging to them.
This commit is contained in:
parent
8046c33194
commit
8635f4efeb
3 changed files with 91 additions and 7 deletions
|
@ -3,6 +3,8 @@ import tpl from '@tryghost/tpl';
|
||||||
import {Knex} from "knex";
|
import {Knex} from "knex";
|
||||||
import {
|
import {
|
||||||
PostsBulkUnpublishedEvent,
|
PostsBulkUnpublishedEvent,
|
||||||
|
PostsBulkFeaturedEvent,
|
||||||
|
PostsBulkUnfeaturedEvent
|
||||||
} from "@tryghost/post-events";
|
} from "@tryghost/post-events";
|
||||||
import {Collection} from './Collection';
|
import {Collection} from './Collection';
|
||||||
import {CollectionRepository} from './CollectionRepository';
|
import {CollectionRepository} from './CollectionRepository';
|
||||||
|
@ -192,6 +194,16 @@ export class CollectionsService {
|
||||||
await this.updateUnpublishedPosts(event.data);
|
await this.updateUnpublishedPosts(event.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.DomainEvents.subscribe(PostsBulkFeaturedEvent, async (event: PostsBulkFeaturedEvent) => {
|
||||||
|
logging.info(`PostsBulkFeaturedEvent received, updating collection posts ${event.data}`);
|
||||||
|
await this.updateFeaturedPosts(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.DomainEvents.subscribe(PostsBulkUnfeaturedEvent, async (event: PostsBulkUnfeaturedEvent) => {
|
||||||
|
logging.info(`PostsBulkUnfeaturedEvent received, updating collection posts ${event.data}`);
|
||||||
|
await this.updateFeaturedPosts(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
this.DomainEvents.subscribe(TagDeletedEvent, async (event: TagDeletedEvent) => {
|
this.DomainEvents.subscribe(TagDeletedEvent, async (event: TagDeletedEvent) => {
|
||||||
logging.info(`TagDeletedEvent received for ${event.data.id}, updating all collections`);
|
logging.info(`TagDeletedEvent received for ${event.data.id}, updating all collections`);
|
||||||
await this.updateAllAutomaticCollections();
|
await this.updateAllAutomaticCollections();
|
||||||
|
@ -318,7 +330,7 @@ export class CollectionsService {
|
||||||
async updatePostInMatchingCollections(postEdit: PostEditedEvent['data']) {
|
async updatePostInMatchingCollections(postEdit: PostEditedEvent['data']) {
|
||||||
return await this.collectionsRepository.createTransaction(async (transaction) => {
|
return await this.collectionsRepository.createTransaction(async (transaction) => {
|
||||||
const collections = await this.collectionsRepository.getAll({
|
const collections = await this.collectionsRepository.getAll({
|
||||||
filter: 'type:automatic',
|
filter: 'type:automatic+slug:-latest',
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -361,6 +373,24 @@ export class CollectionsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateFeaturedPosts(postIds: string[]) {
|
||||||
|
return await this.collectionsRepository.createTransaction(async (transaction) => {
|
||||||
|
let collections = await this.collectionsRepository.getAll({
|
||||||
|
filter: 'type:automatic+slug:-latest',
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
// only process collections that have a filter that includes featured
|
||||||
|
collections = collections.filter((collection) => collection.filter?.includes('featured'));
|
||||||
|
|
||||||
|
if (!collections.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updatePostsInCollections(postIds, collections, transaction);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async updatePostsInCollections(postIds: string[], collections: Collection[], transaction: Knex.Transaction) {
|
async updatePostsInCollections(postIds: string[], collections: Collection[], transaction: Knex.Transaction) {
|
||||||
const posts = await this.postsRepository.getBulk(postIds, transaction);
|
const posts = await this.postsRepository.getBulk(postIds, transaction);
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,15 @@ import {
|
||||||
} from '../src/index';
|
} from '../src/index';
|
||||||
import {
|
import {
|
||||||
PostsBulkDestroyedEvent,
|
PostsBulkDestroyedEvent,
|
||||||
PostsBulkUnpublishedEvent
|
PostsBulkUnpublishedEvent,
|
||||||
|
PostsBulkFeaturedEvent,
|
||||||
|
PostsBulkUnfeaturedEvent
|
||||||
} from '@tryghost/post-events';
|
} from '@tryghost/post-events';
|
||||||
import {PostsRepositoryInMemory} from './fixtures/PostsRepositoryInMemory';
|
import {PostsRepositoryInMemory} from './fixtures/PostsRepositoryInMemory';
|
||||||
import {posts as postFixtures} from './fixtures/posts';
|
import {posts as postFixtures} from './fixtures/posts';
|
||||||
import {CollectionPost} from '../src/CollectionPost';
|
import {CollectionPost} from '../src/CollectionPost';
|
||||||
|
|
||||||
const initPostsRepository = (posts: any): PostsRepositoryInMemory => {
|
const initPostsRepository = async (posts: any): Promise<PostsRepositoryInMemory> => {
|
||||||
const postsRepository = new PostsRepositoryInMemory();
|
const postsRepository = new PostsRepositoryInMemory();
|
||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
|
@ -29,7 +31,8 @@ const initPostsRepository = (posts: any): PostsRepositoryInMemory => {
|
||||||
tags: post.tags,
|
tags: post.tags,
|
||||||
deleted: false
|
deleted: false
|
||||||
};
|
};
|
||||||
postsRepository.save(collectionPost as CollectionPost & {deleted: false});
|
|
||||||
|
await postsRepository.save(collectionPost as CollectionPost & {deleted: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
return postsRepository;
|
return postsRepository;
|
||||||
|
@ -41,7 +44,7 @@ describe('CollectionsService', function () {
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
const collectionsRepository = new CollectionsRepositoryInMemory();
|
const collectionsRepository = new CollectionsRepositoryInMemory();
|
||||||
postsRepository = initPostsRepository(postFixtures);
|
postsRepository = await initPostsRepository(postFixtures);
|
||||||
|
|
||||||
collectionsService = new CollectionsService({
|
collectionsService = new CollectionsService({
|
||||||
collectionsRepository,
|
collectionsRepository,
|
||||||
|
@ -318,7 +321,7 @@ describe('CollectionsService', function () {
|
||||||
|
|
||||||
it('Updates all automatic collections when a tag is deleted', async function () {
|
it('Updates all automatic collections when a tag is deleted', async function () {
|
||||||
const collectionsRepository = new CollectionsRepositoryInMemory();
|
const collectionsRepository = new CollectionsRepositoryInMemory();
|
||||||
postsRepository = initPostsRepository([
|
postsRepository = await initPostsRepository([
|
||||||
{
|
{
|
||||||
id: 'post-1',
|
id: 'post-1',
|
||||||
url: 'http://localhost:2368/post-1/',
|
url: 'http://localhost:2368/post-1/',
|
||||||
|
@ -442,7 +445,7 @@ describe('CollectionsService', function () {
|
||||||
|
|
||||||
collectionsService.subscribeToEvents();
|
collectionsService.subscribeToEvents();
|
||||||
|
|
||||||
postsRepository.save(Object.assign(postFixtures[2], {
|
await postsRepository.save(Object.assign(postFixtures[2], {
|
||||||
published_at: null
|
published_at: null
|
||||||
}));
|
}));
|
||||||
const postsBulkUnpublishedEvent = PostsBulkUnpublishedEvent.create([
|
const postsBulkUnpublishedEvent = PostsBulkUnpublishedEvent.create([
|
||||||
|
@ -459,6 +462,56 @@ describe('CollectionsService', function () {
|
||||||
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2, 'There should be no change to the manual collection');
|
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2, 'There should be no change to the manual collection');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Updates collections with publish filter when PostsBulkFeaturedEvent/PostsBulkUnfeaturedEvent events are produced', 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 featuredPost = await postsRepository.getById(postFixtures[0].id);
|
||||||
|
if (featuredPost) {
|
||||||
|
featuredPost.featured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await postsRepository.save(featuredPost as CollectionPost & {deleted: false});
|
||||||
|
|
||||||
|
const postsBulkFeaturedEvent = PostsBulkFeaturedEvent.create([
|
||||||
|
postFixtures[0].id
|
||||||
|
]);
|
||||||
|
|
||||||
|
DomainEvents.dispatch(postsBulkFeaturedEvent);
|
||||||
|
await DomainEvents.allSettled();
|
||||||
|
|
||||||
|
assert.equal((await collectionsService.getById(automaticFeaturedCollection.id))?.posts.length, 3, 'There should be one extra post in the featured filter collection');
|
||||||
|
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 1, 'There should be one less posts in the non-featured filter collection');
|
||||||
|
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2, 'There should be no change to the manual collection');
|
||||||
|
|
||||||
|
const unFeaturedPost2 = await postsRepository.getById(postFixtures[2].id);
|
||||||
|
if (unFeaturedPost2) {
|
||||||
|
unFeaturedPost2.featured = false;
|
||||||
|
}
|
||||||
|
await postsRepository.save(unFeaturedPost2 as CollectionPost & {deleted: false});
|
||||||
|
|
||||||
|
const unFeaturedPost3 = await postsRepository.getById(postFixtures[3].id);
|
||||||
|
if (unFeaturedPost3) {
|
||||||
|
unFeaturedPost3.featured = false;
|
||||||
|
}
|
||||||
|
await postsRepository.save(unFeaturedPost3 as CollectionPost & {deleted: false});
|
||||||
|
|
||||||
|
const postsBulkUnfeaturedEvent = PostsBulkUnfeaturedEvent.create([
|
||||||
|
postFixtures[2].id,
|
||||||
|
postFixtures[3].id
|
||||||
|
]);
|
||||||
|
|
||||||
|
DomainEvents.dispatch(postsBulkUnfeaturedEvent);
|
||||||
|
await DomainEvents.allSettled();
|
||||||
|
|
||||||
|
assert.equal((await collectionsService.getById(automaticFeaturedCollection.id))?.posts.length, 1, 'There should be two less posts in the featured filter collection');
|
||||||
|
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 3, 'There should be two extra posts in the non-featured filter collection');
|
||||||
|
assert.equal((await collectionsService.getById(manualCollection.id))?.posts.length, 2, 'There should be no change to the manual collection');
|
||||||
|
});
|
||||||
|
|
||||||
it('Updates only index collection when a non-featured post is added', async function () {
|
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(automaticFeaturedCollection.id))?.posts?.length, 2);
|
||||||
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 2);
|
assert.equal((await collectionsService.getById(automaticNonFeaturedCollection.id))?.posts.length, 2);
|
||||||
|
|
|
@ -6,6 +6,7 @@ describe('Posts Bulk API', function () {
|
||||||
let agent;
|
let agent;
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
|
mockManager.mockLabsEnabled('collections');
|
||||||
agent = await agentProvider.getAdminAPIAgent();
|
agent = await agentProvider.getAdminAPIAgent();
|
||||||
|
|
||||||
// Note that we generate lots of fixtures here to test the bulk deletion correctly
|
// Note that we generate lots of fixtures here to test the bulk deletion correctly
|
||||||
|
|
Loading…
Add table
Reference in a new issue