Merge pull request #9319 from overleaf/mj-deferred-recurly-email

[web] Use bull queues for deferred cancellation email

GitOrigin-RevId: a104f9940badcffc15f1f237a1cefd5dd912f4e0
This commit is contained in:
Mathias Jakobsen 2022-08-23 10:16:45 +01:00 committed by Copybot
parent 4cfbee15df
commit f5b2cdc3cb
5 changed files with 69 additions and 15 deletions

View file

@ -2,11 +2,13 @@ const { callbackify } = require('util')
const Settings = require('@overleaf/settings')
const EmailBuilder = require('./EmailBuilder')
const EmailSender = require('./EmailSender')
const Queues = require('../../infrastructure/Queues')
const EMAIL_SETTINGS = Settings.email || {}
module.exports = {
sendEmail: callbackify(sendEmail),
sendDeferredEmail,
promises: {
sendEmail,
},
@ -22,3 +24,11 @@ async function sendEmail(emailType, opts) {
opts.subject = email.subject
await EmailSender.promises.sendEmail(opts)
}
function sendDeferredEmail(emailType, opts, delay) {
Queues.createScheduledJob(
'deferred-emails',
{ data: { emailType, opts } },
delay
)
}

View file

@ -199,20 +199,9 @@ function cancelSubscription(user, callback) {
first_name: user.first_name,
}
const ONE_HOUR_IN_MS = 1000 * 60 * 60
setTimeout(
() =>
EmailHandler.sendEmail(
'canceledSubscription',
emailOpts,
err => {
if (err) {
logger.warn(
{ err },
'failed to send confirmation email for subscription cancellation'
)
}
}
),
EmailHandler.sendDeferredEmail(
'canceledSubscription',
emailOpts,
ONE_HOUR_IN_MS
)
callback()

View file

@ -7,6 +7,9 @@ const {
addOptionalCleanupHandlerBeforeStoppingTraffic,
addRequiredCleanupHandlerBeforeDrainingConnections,
} = require('./GracefulShutdown')
const EmailHandler = require('../Features/Email/EmailHandler')
const logger = require('@overleaf/logger')
const OError = require('@overleaf/o-error')
function start() {
if (!Features.hasFeature('saas')) {
@ -47,6 +50,19 @@ function start() {
await FeaturesUpdater.promises.refreshFeatures(userId, reason)
})
registerCleanup(refreshFeaturesQueue)
const deferredEmailsQueue = Queues.getQueue('deferred-emails')
deferredEmailsQueue.process(async job => {
const { emailType, opts } = job.data
try {
await EmailHandler.promises.sendEmail(emailType, opts)
} catch (e) {
const error = OError.tag(e, 'failed to send deferred email')
logger.warn(error)
throw error
}
})
registerCleanup(deferredEmailsQueue)
}
function registerCleanup(queue) {

View file

@ -20,11 +20,15 @@ describe('EmailHandler', function () {
sendEmail: sinon.stub().resolves(),
},
}
this.Queues = {
createScheduledJob: sinon.stub(),
}
this.EmailHandler = SandboxedModule.require(MODULE_PATH, {
requires: {
'./EmailBuilder': this.EmailBuilder,
'./EmailSender': this.EmailSender,
'@overleaf/settings': this.Settings,
'../../infrastructure/Queues': this.Queues,
},
})
})
@ -92,4 +96,27 @@ describe('EmailHandler', function () {
})
})
})
describe('send deferred email', function () {
beforeEach(function () {
this.opts = {
to: 'bob@bob.com',
first_name: 'hello bob',
}
this.emailType = 'canceledSubscription'
this.ONE_HOUR_IN_MS = 1000 * 60 * 60
this.EmailHandler.sendDeferredEmail(
this.emailType,
this.opts,
this.ONE_HOUR_IN_MS
)
})
it('should add a email job to the queue', function () {
expect(this.Queues.createScheduledJob).to.have.been.calledWith(
'deferred-emails',
{ data: { emailType: this.emailType, opts: this.opts } },
this.ONE_HOUR_IN_MS
)
})
})
})

View file

@ -110,7 +110,10 @@ describe('SubscriptionHandler', function () {
this.LimitationsManager = { userHasV2Subscription: sinon.stub() }
this.EmailHandler = { sendEmail: sinon.stub() }
this.EmailHandler = {
sendEmail: sinon.stub(),
sendDeferredEmail: sinon.stub(),
}
this.AnalyticsManager = { recordEventForUser: sinon.stub() }
@ -405,6 +408,15 @@ describe('SubscriptionHandler', function () {
.calledWith(this.subscription.recurlySubscription_id)
.should.equal(true)
})
it('should send the email after 1 hour', function () {
const ONE_HOUR_IN_MS = 1000 * 60 * 60
expect(this.EmailHandler.sendDeferredEmail).to.have.been.calledWith(
'canceledSubscription',
{ to: this.user.email, first_name: this.user.first_name },
ONE_HOUR_IN_MS
)
})
})
})