feat: public api package for other qbits #13

Merged
KofTwentyTwo merged 3 commits from feature/public-api into develop 2026-10-14 14:44:04 +00:00
KofTwentyTwo commented 2026-10-14 14:02:49 +00:00 (Migrated from github.com)

Fixes the coupling tracked in QRun-IO/qbit-marketing#13: consumers import qbit-email internals, which guardrail G1 forbids.

  • com.kingsrook.qbits.email.api (the objectstorage.api precedent): EmailApi.isInstalled, send(EmailApiRequest) → EmailApiResult (QUEUED/DUPLICATE/SUPPRESSED/REFUSED + reasons), ensureTemplate(EmailApiTemplate), getMessage(id) and findMessagesByIdempotencyKeyPrefix (ids, status, category, keys, template, linked record and dates only; never addresses, subjects or bodies), listSenderAddresses, defaultProviderName, readiness(qInstance, category) (plain reasons, no secret values).
  • Own DTOs; a reflection test asserts no public or protected member of the api package exposes an internal qbit-email type.
  • README "Sending from another qbit" and DESIGN 2.5: route 1 is core-only (SendMessageAction, provider qbitEmail), route 2 is this package; internal packages are not API.
  • Companion PRs: QRun-IO/qbit-bom#12 (default G1 allow-list for this package), an additive core messaging PR (rich SendMessageInput/Output so send-only consumers need no qbit-email dependency), then the consumer switches.

Also folds #12's EmailEffects into the same surface: EmailApi.sendOnce(request, effectKey) (EmailApiEffectOutcome, EmailApiEffectResult), getMessageByIdempotencyKey, getMessageByRfcMessageId, rfcMessageId on EmailApiMessage. EmailEffects stays as an internal, deprecated delegate; docs/DURABLE-JOBS.md points at EmailApi.

Tests: 287, all green; API coverage 99.2%, overall 95.1%; Checkstyle clean.

🤖 Generated with Claude Code

Fixes the coupling tracked in QRun-IO/qbit-marketing#13: consumers import qbit-email internals, which guardrail G1 forbids. - **`com.kingsrook.qbits.email.api`** (the objectstorage.api precedent): `EmailApi.isInstalled`, `send(EmailApiRequest)` → `EmailApiResult` (QUEUED/DUPLICATE/SUPPRESSED/REFUSED + reasons), `ensureTemplate(EmailApiTemplate)`, `getMessage(id)` and `findMessagesByIdempotencyKeyPrefix` (ids, status, category, keys, template, linked record and dates only; never addresses, subjects or bodies), `listSenderAddresses`, `defaultProviderName`, `readiness(qInstance, category)` (plain reasons, no secret values). - Own DTOs; a reflection test asserts no public or protected member of the api package exposes an internal qbit-email type. - README "Sending from another qbit" and DESIGN 2.5: route 1 is core-only (`SendMessageAction`, provider `qbitEmail`), route 2 is this package; internal packages are not API. - Companion PRs: QRun-IO/qbit-bom#12 (default G1 allow-list for this package), an additive core messaging PR (rich SendMessageInput/Output so send-only consumers need no qbit-email dependency), then the consumer switches. Also folds #12's EmailEffects into the same surface: `EmailApi.sendOnce(request, effectKey)` (EmailApiEffectOutcome, EmailApiEffectResult), `getMessageByIdempotencyKey`, `getMessageByRfcMessageId`, `rfcMessageId` on EmailApiMessage. EmailEffects stays as an internal, deprecated delegate; docs/DURABLE-JOBS.md points at EmailApi. Tests: 287, all green; API coverage 99.2%, overall 95.1%; Checkstyle clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.