Skip to content

Commit 079f810

Browse files
authored
Merge pull request #19968 from mozilla/dont-fail-on-email-send-error
polish(auth): Only fail hard on email send for emails with codes
2 parents d5d8a1f + ba7344f commit 079f810

3 files changed

Lines changed: 54 additions & 22 deletions

File tree

libs/accounts/email-sender/src/email-sender.spec.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ describe('EmailSender', () => {
226226
new Error('Temporary error')
227227
);
228228

229-
await expect(emailSender.send(defaultMockEmail)).rejects.toThrow(
229+
await expect(emailSender.send(defaultMockEmail, true)).rejects.toThrow(
230230
'Temporary error'
231231
);
232232

@@ -260,7 +260,7 @@ describe('EmailSender', () => {
260260
new Error('Temporary error')
261261
);
262262

263-
await expect(emailSender.send(defaultMockEmail)).rejects.toThrow(
263+
await expect(emailSender.send(defaultMockEmail, true)).rejects.toThrow(
264264
'Temporary error'
265265
);
266266

@@ -294,7 +294,7 @@ describe('EmailSender', () => {
294294
const error = new Error('SMTP connection failed');
295295
mockTransport.sendMail.mockRejectedValueOnce(error);
296296

297-
await expect(emailSender.send(defaultMockEmail)).rejects.toThrow(
297+
await expect(emailSender.send(defaultMockEmail, true)).rejects.toThrow(
298298
'SMTP connection failed'
299299
);
300300

@@ -331,7 +331,7 @@ describe('EmailSender', () => {
331331
const appError = AppError.emailBouncedHard(100_000_000_000);
332332
mockTransport.sendMail.mockRejectedValueOnce(appError);
333333

334-
await expect(emailSender.send(defaultMockEmail)).rejects.toThrow();
334+
await expect(emailSender.send(defaultMockEmail, true)).rejects.toThrow();
335335

336336
expect(mockTransport.sendMail).toHaveBeenCalledTimes(1);
337337
expect(mockLogger.error).toHaveBeenCalledWith(
@@ -378,7 +378,7 @@ describe('EmailSender', () => {
378378

379379
mockTransport.sendMail.mockRejectedValueOnce(smtpError);
380380

381-
await expect(emailSender.send(emailWithCc)).rejects.toThrow();
381+
await expect(emailSender.send(emailWithCc, true)).rejects.toThrow();
382382

383383
expect(mockLogger.error).toHaveBeenCalledWith(
384384
'mailer.send.error',
@@ -621,7 +621,7 @@ describe('EmailSender', () => {
621621
mockLogger
622622
);
623623

624-
await expect(emailSender.send(defaultMockEmail)).rejects.toThrow(
624+
await expect(emailSender.send(defaultMockEmail, true)).rejects.toThrow(
625625
'Temporary SMTP error'
626626
);
627627

@@ -640,6 +640,28 @@ describe('EmailSender', () => {
640640
}
641641
);
642642
expect(mockStatsd.increment).toHaveBeenCalledTimes(4);
643+
644+
// Make sure issue is still sent to sentry
645+
expect(mockSentryCaptureException).toHaveBeenCalledWith(error);
646+
});
647+
648+
it('suppresses error with directive', async () => {
649+
const error = new Error('Temporary SMTP error');
650+
651+
mockTransport.sendMail.mockRejectedValue(error);
652+
653+
emailSender = new EmailSender(
654+
configWithRetry,
655+
mockBounces,
656+
mockStatsd,
657+
mockLogger
658+
);
659+
660+
// Shouldn't error...
661+
await emailSender.send(defaultMockEmail, false);
662+
663+
// But should capture
664+
expect(mockSentryCaptureException).toHaveBeenCalledWith(error);
643665
});
644666
});
645667
describe('calculateBackoffDelay', () => {

libs/accounts/email-sender/src/email-sender.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,17 +222,18 @@ export class EmailSender {
222222
/**
223223
* Send out an email.
224224
* @param email Email to send
225+
* @param
225226
* @returns
226227
*/
227-
async send(email: Email) {
228+
async send(email: Email, throwOnFailure = false) {
228229
// Always check for email bounces. Don't send if there bounce errors
229230
if (await this.hasBounceErrors(email)) {
230231
return {
231232
sent: false,
232233
message: 'Has bounce errors!',
233234
};
234235
}
235-
return await this.sendMail(email);
236+
return await this.sendMail(email, throwOnFailure);
236237
}
237238

238239
/**
@@ -274,6 +275,7 @@ export class EmailSender {
274275

275276
private async sendMail(
276277
email: Email,
278+
throwOnFailure: boolean,
277279
attempt = 0
278280
): Promise<{
279281
sent: boolean;
@@ -372,7 +374,7 @@ export class EmailSender {
372374

373375
// Wait for backoff period, then retry
374376
await new Promise((resolve) => setTimeout(resolve, backoffDelay));
375-
return this.sendMail(email, ++attempt);
377+
return this.sendMail(email, throwOnFailure, ++attempt);
376378
}
377379

378380
const smtpErrorDetails: Record<string, unknown> = {
@@ -415,7 +417,11 @@ export class EmailSender {
415417
}
416418

417419
// Throw error back to calling code.
418-
throw err;
420+
if (throwOnFailure) {
421+
throw err;
422+
}
423+
424+
return { sent: false };
419425
}
420426
}
421427
}

packages/fxa-auth-server/lib/senders/fxa-mailer.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export class FxaMailer extends FxaEmailRenderer {
156156
...opts,
157157
...links,
158158
});
159-
return this.sendEmail(opts, headers, rendered);
159+
return this.sendEmail(opts, headers, rendered, true);
160160
}
161161

162162
/**
@@ -189,7 +189,7 @@ export class FxaMailer extends FxaEmailRenderer {
189189
...opts,
190190
...links,
191191
});
192-
return this.sendEmail(opts, headers, rendered);
192+
return this.sendEmail(opts, headers, rendered, true);
193193
}
194194

195195
async sendPostVerifySecondaryEmail(
@@ -1030,7 +1030,7 @@ export class FxaMailer extends FxaEmailRenderer {
10301030
...opts,
10311031
...links,
10321032
});
1033-
return this.sendEmail(opts, headers, rendered);
1033+
return this.sendEmail(opts, headers, rendered, true);
10341034
}
10351035

10361036
async sendVerifyShortCodeEmail(
@@ -1067,7 +1067,7 @@ export class FxaMailer extends FxaEmailRenderer {
10671067
...opts,
10681068
...links,
10691069
});
1070-
return this.sendEmail(opts, headers, rendered);
1070+
return this.sendEmail(opts, headers, rendered, true);
10711071
}
10721072

10731073
async sendVerifyEmail(
@@ -1686,19 +1686,23 @@ export class FxaMailer extends FxaEmailRenderer {
16861686
private async sendEmail(
16871687
opts: { to: string; cc?: string[]; cmsRpFromName?: string },
16881688
headers: Record<string, string>,
1689-
rendered: RenderedTemplate
1689+
rendered: RenderedTemplate,
1690+
throwErrorOnSendFailure = true
16901691
) {
16911692
const { cmsRpFromName, to, cc } = opts;
16921693
const from = cmsRpFromName
16931694
? `${cmsRpFromName} <${this.mailerConfig.sender}>`
16941695
: this.mailerConfig.sender;
16951696

1696-
return this.emailSender.send({
1697-
to,
1698-
cc,
1699-
from,
1700-
headers,
1701-
...rendered,
1702-
});
1697+
return this.emailSender.send(
1698+
{
1699+
to,
1700+
cc,
1701+
from,
1702+
headers,
1703+
...rendered,
1704+
},
1705+
throwErrorOnSendFailure
1706+
);
17031707
}
17041708
}

0 commit comments

Comments
 (0)