feat(backend): send release confirmation emails#6905
feat(backend): send release confirmation emails#6905theosanderson-agent wants to merge 11 commits into
Conversation
|
This PR may be related to: #4317 |
|
Claude finished @theosanderson's task in 2m 52s —— View job Review complete ✅
Overall this is a well-structured, cleanly-tested feature (good use of the existing 1. Send-then-record is not atomic — possible duplicate emails on transient DB failure
2. A single malformed/blank group contact email permanently blocks that group's batch
Minor notes
Nothing above is blocking — the core design (batching, task locking, feature flag defaulting to off, backfill seeding to avoid a historical backlog) is sound and the tests cover the main happy/failure paths well. Items 1 and 2 are the ones I'd most want a decision on before enabling this in production. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac359f73fa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| helper.setFrom(from) | ||
| helper.setTo(recipientEmail) | ||
| if (!recipientEmail.equals(ccEmail, ignoreCase = true)) { | ||
| helper.setCc(ccEmail) |
There was a problem hiding this comment.
Do not let an invalid group CC block confirmations
This makes the group contact address a required SMTP recipient for every confirmation. If a group has an invalid or stale contactEmail (the backend stores it as an unconstrained string), setCc/SMTP delivery can fail or partially send, and the surrounding catch leaves the whole approver/group batch pending, so the valid approver either never gets the confirmation or gets duplicate retries. Validate/skip the CC independently or avoid letting CC failures block recording the To delivery.
Useful? React with 👍 / 👎.
…ging sent ones Replace the sent_notifications table with a pending_release_notifications queue that batches release confirmation emails per group, validates configured from/reply-to addresses, and summarizes accessions per organism. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop organism, approver, and group_id from pending_release_notifications; they are immutable per accession-version and already stored on sequence_entries, so read them back via a join keyed on (accession, version). The FK's ON DELETE CASCADE guarantees the join always resolves. Simplifies enqueue to take only the released accession-versions and replaces the covering index with one on enqueued_at. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The queue now derives group_id via a join, so approval no longer needs to group released accession-versions by group before enqueueing. Revert the release update to the flat accession-version list it used before the notification feature. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The task already sorts organisms and sorts+caps each organism's accessions when building the notification content, so drop the redundant re-sort, re-cap, and duplicate MAX_ACCESSIONS constant in the email body renderer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Classify each released accession-version as a new submission (version 1),
a revision (version > 1), or a revocation (is_revocation), reading
is_revocation back from sequence_entries via the existing join. The email
now labels each revision/revocation line and adds a per-kind breakdown
("This included 2 new, 1 revised.") when anything is not a plain new
submission; the all-new case stays unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fold approver and groupId into ReleaseNotificationContent instead of threading them as separate arguments through sendReleaseConfirmation, populateMessage, and buildBody. Extract the two duplicated single-address parsers into one shared parseSingleInternetAddress helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
This adds batched release confirmation emails for sequence approvals. Once enabled, the backend checks for newly released sequence versions every minute, groups them by approver and group, emails the approver, and copies the group's contact address. Each message lists the released accession versions grouped by organism and links back to the group's released-sequences pages.
Delivery bookkeeping lives in a new
sent_notificationstable rather than onsequence_entries. This avoids firing the sequence table's update-tracker trigger when an email is recorded. One row is stored for each sequence version included in a successfully accepted SMTP message, including the To/CC address snapshots and shared message ID. Existing releases are seeded as handled by the migration, preventing an historical email backlog during rollout.The scheduled worker uses the existing database-backed task lock so only one backend replica processes a batch. Failed recipient lookups or SMTP sends are isolated per approver/group and leave the affected sequences pending for a later retry. Group CC is omitted when it duplicates the approver address.
The backend now includes Spring Mail support and reuses the Helm chart's existing SMTP settings and password secret. Release confirmations are feature-flagged with
releaseConfirmationEmails.enabledand remain disabled by default. The default interval is 60 seconds.Validation
./gradlew test --tests 'org.loculus.backend.service.notification.*' --console=plain./gradlew ktlintCheck --console=plainhelm lint kubernetes/loculus -f kubernetes/loculus/values.yamlreleaseConfirmationEmails.enabled=true, confirming all backend SMTP arguments and the password secret reference.The full backend suite was also run. It completed 550 tests with 29 failures, all in existing file-upload tests after the test MinIO returned
507 Insufficient Storageor invalid/missing upload results. The host filesystem was at 100% usage with 1.1 GB free. The focused notification tests, including the PostgreSQL migration/persistence test, pass. That integration test also verifies notification inserts do not add atable_update_trackerentry.Rollout
Configure
auth.smtp, ensure the existingsmtp-passwordsecret is populated, and setreleaseConfirmationEmails.enabled=true. Deploying with the default values performs the schema migration but does not send email.🚀 Preview: https://agent-release-confirmatio.loculus.org