Skip to content

[INTS26] Set up Mailer/Send Bulk Rejection Emails - #77

Open
OwenChen07 wants to merge 1 commit into
mainfrom
INTS26-setup-mailer
Open

[INTS26] Set up Mailer/Send Bulk Rejection Emails#77
OwenChen07 wants to merge 1 commit into
mainfrom
INTS26-setup-mailer

Conversation

@OwenChen07

Copy link
Copy Markdown
Contributor

Notion ticket link

Set up Mailer/Send Bulk Rejection Emails

Implementation description

  • Kept the boilerplate Nodemailer + Gmail OAuth2 transport (emailService.ts) rather than switching providers, and extended it with sendBulkEmail, which sends in batches of 10 and never rejects for individual failures — it returns { sent, failed } so one bad address can't abort a whole rejection run.
  • Added a file-based HTML template system (emails/templates/*.html + emails/index.ts) using Handlebars. Each email type is one HTML file with {{placeholders}} plus one typed builder function (e.g. buildRejectionEmail), so future devs can add new templates without touching the sending logic.
  • Wired bulk rejection emails into the existing bulkUpdateApplicantRecordsStatus mutation: after the status update transaction commits, any records set to REJECTED get a templated rejection email fetched with their Applicant data. This is fire-and-forget — email failures are logged but never roll back the already-committed status change, and the mutation doesn't wait on send completion.
  • Documented Gmail OAuth2 credential setup (including the invalid_grant/Testing-mode-7-day-expiry gotcha) and "how to add a new email template" in docs/mailer.md.

Steps to test

  1. Run the new unit tests: cd backend/typescript && npx jest emails emailService.test applicantRecordService.test (6 tests covering template rendering, bulk-send partial failure handling, and the rejection hook's message-building/error-resilience).
  2. Preview the template with sample data using this command:
    """
    npx ts-node -e 'import { buildRejectionEmail } from "./emails";
    const { html } = buildRejectionEmail({ firstName: "Owen", position: "Developer", term: "Fall 2026" });
    require("fs").writeFileSync("/tmp/rejection-preview.html", html);'
    open /tmp/rejection-preview.html
    """
  3. Real send test using this command*:
    *The credentials in .env have expired, so I can send you the new credentials I created on Discord if you want.
    """
    docker exec -i recruitment_platform_ts_backend npx ts-node -e '
    import EmailService from "./services/implementations/emailService";
    import nodemailerConfig from "./nodemailer.config";
    import { buildRejectionEmail } from "./emails";
    const svc = new EmailService(nodemailerConfig, "UW Blueprint Recruitment");
    const { subject, html } = buildRejectionEmail({ firstName: "Owen", position: "Developer", term: "Fall 2026" });
    svc.sendEmail("owenchenyp@gmail.com", subject, html).then(() => console.log("sent!")).catch(console.error);'
    """

What should reviewers focus on?

  • The credentials in .env have expired, so someone will need to create new ones. There are instructions in recruitment-platform/docs/mailer.md.

Checklist

  • My PR name is descriptive and in imperative tense
  • My commit messages are descriptive and in imperative tense. My commits are atomic and trivial commits are squashed or fixup'd into non-trivial commits
  • I have run the appropriate linter(s)
  • I have requested a review from the PL, as well as other devs who have background knowledge on this PR or who will be building on top of this PR

@OwenChen07 OwenChen07 changed the title initial work [INTS26] Set up Mailer/Send Bulk Rejection Emails Aug 5, 2026
@mxc-maggiechen
mxc-maggiechen marked this pull request as ready for review August 5, 2026 01:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant