mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
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:
parent
4cfbee15df
commit
f5b2cdc3cb
5 changed files with 69 additions and 15 deletions
|
@ -2,11 +2,13 @@ const { callbackify } = require('util')
|
||||||
const Settings = require('@overleaf/settings')
|
const Settings = require('@overleaf/settings')
|
||||||
const EmailBuilder = require('./EmailBuilder')
|
const EmailBuilder = require('./EmailBuilder')
|
||||||
const EmailSender = require('./EmailSender')
|
const EmailSender = require('./EmailSender')
|
||||||
|
const Queues = require('../../infrastructure/Queues')
|
||||||
|
|
||||||
const EMAIL_SETTINGS = Settings.email || {}
|
const EMAIL_SETTINGS = Settings.email || {}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sendEmail: callbackify(sendEmail),
|
sendEmail: callbackify(sendEmail),
|
||||||
|
sendDeferredEmail,
|
||||||
promises: {
|
promises: {
|
||||||
sendEmail,
|
sendEmail,
|
||||||
},
|
},
|
||||||
|
@ -22,3 +24,11 @@ async function sendEmail(emailType, opts) {
|
||||||
opts.subject = email.subject
|
opts.subject = email.subject
|
||||||
await EmailSender.promises.sendEmail(opts)
|
await EmailSender.promises.sendEmail(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendDeferredEmail(emailType, opts, delay) {
|
||||||
|
Queues.createScheduledJob(
|
||||||
|
'deferred-emails',
|
||||||
|
{ data: { emailType, opts } },
|
||||||
|
delay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -199,20 +199,9 @@ function cancelSubscription(user, callback) {
|
||||||
first_name: user.first_name,
|
first_name: user.first_name,
|
||||||
}
|
}
|
||||||
const ONE_HOUR_IN_MS = 1000 * 60 * 60
|
const ONE_HOUR_IN_MS = 1000 * 60 * 60
|
||||||
setTimeout(
|
EmailHandler.sendDeferredEmail(
|
||||||
() =>
|
'canceledSubscription',
|
||||||
EmailHandler.sendEmail(
|
emailOpts,
|
||||||
'canceledSubscription',
|
|
||||||
emailOpts,
|
|
||||||
err => {
|
|
||||||
if (err) {
|
|
||||||
logger.warn(
|
|
||||||
{ err },
|
|
||||||
'failed to send confirmation email for subscription cancellation'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
ONE_HOUR_IN_MS
|
ONE_HOUR_IN_MS
|
||||||
)
|
)
|
||||||
callback()
|
callback()
|
||||||
|
|
|
@ -7,6 +7,9 @@ const {
|
||||||
addOptionalCleanupHandlerBeforeStoppingTraffic,
|
addOptionalCleanupHandlerBeforeStoppingTraffic,
|
||||||
addRequiredCleanupHandlerBeforeDrainingConnections,
|
addRequiredCleanupHandlerBeforeDrainingConnections,
|
||||||
} = require('./GracefulShutdown')
|
} = require('./GracefulShutdown')
|
||||||
|
const EmailHandler = require('../Features/Email/EmailHandler')
|
||||||
|
const logger = require('@overleaf/logger')
|
||||||
|
const OError = require('@overleaf/o-error')
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
if (!Features.hasFeature('saas')) {
|
if (!Features.hasFeature('saas')) {
|
||||||
|
@ -47,6 +50,19 @@ function start() {
|
||||||
await FeaturesUpdater.promises.refreshFeatures(userId, reason)
|
await FeaturesUpdater.promises.refreshFeatures(userId, reason)
|
||||||
})
|
})
|
||||||
registerCleanup(refreshFeaturesQueue)
|
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) {
|
function registerCleanup(queue) {
|
||||||
|
|
|
@ -20,11 +20,15 @@ describe('EmailHandler', function () {
|
||||||
sendEmail: sinon.stub().resolves(),
|
sendEmail: sinon.stub().resolves(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
this.Queues = {
|
||||||
|
createScheduledJob: sinon.stub(),
|
||||||
|
}
|
||||||
this.EmailHandler = SandboxedModule.require(MODULE_PATH, {
|
this.EmailHandler = SandboxedModule.require(MODULE_PATH, {
|
||||||
requires: {
|
requires: {
|
||||||
'./EmailBuilder': this.EmailBuilder,
|
'./EmailBuilder': this.EmailBuilder,
|
||||||
'./EmailSender': this.EmailSender,
|
'./EmailSender': this.EmailSender,
|
||||||
'@overleaf/settings': this.Settings,
|
'@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
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -110,7 +110,10 @@ describe('SubscriptionHandler', function () {
|
||||||
|
|
||||||
this.LimitationsManager = { userHasV2Subscription: sinon.stub() }
|
this.LimitationsManager = { userHasV2Subscription: sinon.stub() }
|
||||||
|
|
||||||
this.EmailHandler = { sendEmail: sinon.stub() }
|
this.EmailHandler = {
|
||||||
|
sendEmail: sinon.stub(),
|
||||||
|
sendDeferredEmail: sinon.stub(),
|
||||||
|
}
|
||||||
|
|
||||||
this.AnalyticsManager = { recordEventForUser: sinon.stub() }
|
this.AnalyticsManager = { recordEventForUser: sinon.stub() }
|
||||||
|
|
||||||
|
@ -405,6 +408,15 @@ describe('SubscriptionHandler', function () {
|
||||||
.calledWith(this.subscription.recurlySubscription_id)
|
.calledWith(this.subscription.recurlySubscription_id)
|
||||||
.should.equal(true)
|
.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
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue