From 6ab29c14e45d9747cae137705f2756f2f8750143 Mon Sep 17 00:00:00 2001 From: Simon Backx Date: Tue, 19 Apr 2022 12:47:03 +0200 Subject: [PATCH] Fixed OfferCreatedEvent being dispatched before the transaction has been committed (#391) refs https://github.com/TryGhost/Ghost/pull/14488 **Bug:** When creating a new offer, sometimes no discount object is created in Stripe (only in MySQL). This issue causes tests to fail in https://github.com/TryGhost/Ghost/pull/14488 **Cause:** When creating an offer with the OffersAPI, we create a new transaction to save the offer and check for uniqueness. Inside that transaction, we dispatch an OfferCreatedEvent event before the transaction is committed to the database. When listening for this event in the `members-payments` package, we create the discount object in Stripe and save the stripe_coupon_id to the newly created offer. But because (in most cases) the offer doesn't yet exist in the database, that listener fails without warning and exits early. **Solution:** Creating a discount in Stripe should only happen after we succesfully committed the offer to the database. If we don't wait for the commit, the offer often (by chance) won't exist in the database, so the coupon won't be created inside Stripe. The solution is to wait for the transaction to succeed/be committed before dispatching the event. --- ghost/offers/lib/application/OfferRepository.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ghost/offers/lib/application/OfferRepository.js b/ghost/offers/lib/application/OfferRepository.js index 5dfe364406..2585d2d332 100644 --- a/ghost/offers/lib/application/OfferRepository.js +++ b/ghost/offers/lib/application/OfferRepository.js @@ -212,9 +212,18 @@ class OfferRepository { } if (offer.isNew) { - const event = OfferCreatedEvent.create({offer}); await this.OfferModel.add(data, options); - DomainEvents.dispatch(event); + const event = OfferCreatedEvent.create({offer}); + + if (options.transacting) { + // Only dispatch the event after the transaction has finished + // Because else the offer won't be committed to the database yet + options.transacting.executionPromise.then(() => { + DomainEvents.dispatch(event); + }); + } else { + DomainEvents.dispatch(event); + } } else { await this.OfferModel.edit(data, {...options, id: data.id}); }