feat: EmailJobEffectTarget, so durable jobs can send email through the outbox #18

Merged
KofTwentyTwo merged 1 commits from feature/email-job-effect-target into develop 2026-10-14 21:43:58 +00:00
KofTwentyTwo commented 2026-10-14 21:33:45 +00:00 (Migrated from github.com)

This builds on QQQ 4.1.0-RC.7 and follows #12.

  • What it is: EmailJobEffectTarget implements core's EffectTarget (QRun-IO/qqq#1191, spec section 2.13).
  • Delivery: the email goes to qbit-email's own outbox via EmailEffects.send with the effect's idempotency key, so it lands exactly once per key, with the fixed Message-ID. The result is SENT with the message id, or FAILED when refused.
  • Identity: delivery runs as the job owner (JobSessions.forJob). If the owner can no longer send, the result is FAILED, never UNKNOWN.
  • Reconcile: looks the message up by key (findByIdempotencyKey).
  • Effect type: IDEMPOTENT_EXTERNAL.
  • Registration: EmailQBitProducer registers it as target email when durable jobs are enabled.
  • Payload keys: to, cc, bcc, subject, templateName, markdownBody, htmlBody, textBody, variables, category, providerName, brandName, locale, from, fromAddressId, replyTo, sendAfter, linkedTableName, linkedRecordId.
  • Docs: docs/DURABLE-JOBS.md shows how to declare and emit an email effect.

Testing: ./mvnw verify passes 301 tests (14 new), with checkstyle on main and test clean and coverage at 95.4% / 100%.

🤖 Generated with Claude Code

This builds on QQQ 4.1.0-RC.7 and follows #12. - **What it is:** `EmailJobEffectTarget` implements core's `EffectTarget` (QRun-IO/qqq#1191, spec section 2.13). - **Delivery:** the email goes to qbit-email's own outbox via `EmailEffects.send` with the effect's idempotency key, so it lands exactly once per key, with the fixed Message-ID. The result is SENT with the message id, or FAILED when refused. - **Identity:** delivery runs as the job owner (`JobSessions.forJob`). If the owner can no longer send, the result is FAILED, never UNKNOWN. - **Reconcile:** looks the message up by key (`findByIdempotencyKey`). - **Effect type:** IDEMPOTENT_EXTERNAL. - **Registration:** `EmailQBitProducer` registers it as target `email` when durable jobs are enabled. - **Payload keys:** to, cc, bcc, subject, templateName, markdownBody, htmlBody, textBody, variables, category, providerName, brandName, locale, from, fromAddressId, replyTo, sendAfter, linkedTableName, linkedRecordId. - **Docs:** `docs/DURABLE-JOBS.md` shows how to declare and emit an email effect. **Testing:** `./mvnw verify` passes 301 tests (14 new), with checkstyle on main and test clean and coverage at 95.4% / 100%. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.