From 3dc27f505c1790897c7c220460e2a4e61ac7ace8 Mon Sep 17 00:00:00 2001 From: Naz Date: Wed, 19 Jul 2023 22:11:28 +0800 Subject: [PATCH] Wrapped collection's creation in transaction refs https://github.com/TryGhost/Arch/issues/16 - This is needed to avoid stale data/race conditions when processing collection post updates --- ghost/collections/src/CollectionsService.ts | 47 +++++++++++-------- .../server/services/collections/service.js | 6 ++- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/ghost/collections/src/CollectionsService.ts b/ghost/collections/src/CollectionsService.ts index 70d66a3116..0f84c22d8a 100644 --- a/ghost/collections/src/CollectionsService.ts +++ b/ghost/collections/src/CollectionsService.ts @@ -22,7 +22,7 @@ const messages = { }; interface SlugService { - generate(desired: string): Promise; + generate(desired: string, options: {transaction: any}): Promise; } type CollectionsServiceDeps = { @@ -190,30 +190,37 @@ export class CollectionsService { } async createCollection(data: CollectionInputDTO): Promise { - const slug = await this.slugService.generate(data.slug || data.title); - const collection = await Collection.create({ - title: data.title, - slug: slug, - description: data.description, - type: data.type, - filter: data.filter, - featureImage: data.feature_image, - deletable: data.deletable - }); - - if (collection.type === 'automatic' && collection.filter) { - const posts = await this.postsRepository.getAll({ - filter: collection.filter + return await this.collectionsRepository.createTransaction(async (transaction) => { + const slug = await this.slugService.generate(data.slug || data.title, { + transaction: transaction + }); + const collection = await Collection.create({ + title: data.title, + slug: slug, + description: data.description, + type: data.type, + filter: data.filter, + featureImage: data.feature_image, + deletable: data.deletable }); - for (const post of posts) { - collection.addPost(post); + if (collection.type === 'automatic' && collection.filter) { + const posts = await this.postsRepository.getAll({ + filter: collection.filter, + transaction: transaction + }); + + for (const post of posts) { + collection.addPost(post); + } } - } - await this.collectionsRepository.save(collection); + await this.collectionsRepository.save(collection, { + transaction: transaction + }); - return this.toDTO(collection); + return this.toDTO(collection); + }); } async addPostToCollection(collectionId: string, post: CollectionPostListItemDTO): Promise { diff --git a/ghost/core/core/server/services/collections/service.js b/ghost/core/core/server/services/collections/service.js index 5abb70b349..3db55e549e 100644 --- a/ghost/core/core/server/services/collections/service.js +++ b/ghost/core/core/server/services/collections/service.js @@ -19,8 +19,10 @@ class CollectionsServiceWrapper { postsRepository: postsRepository, DomainEvents: DomainEvents, slugService: { - async generate(input) { - return models.Collection.generateSlug(models.Collection, input, {}); + async generate(input, options) { + return models.Collection.generateSlug(models.Collection, input, { + transacting: options.transaction + }); } } });