Skip to content

R1.3 Template catalog: versions, preview, test send, typed context - #16

Merged
alexeygrigorev merged 1 commit into
mainfrom
issue-12-template-catalog
Sep 8, 2026
Merged

alexeygrigorev merged 1 commit into
mainfrom
issue-12-template-catalog

Conversation

@alexeygrigorev

Copy link
Copy Markdown
Member

Closes #12

Goal

Give transactional templates an editable draft plus immutable published versions, a markdown template format with typed required context, a render-preview endpoint, a controlled test send to an allowlisted staff address, and a management command that imports AI Shipping Labs-format markdown templates.

What changed

  • Models: EmailTemplate gains markdown_body and category; new write-once EmailTemplateVersion (email_template_versions, immutable via save/delete guards); TransactionalMessage records template_version. Migration 0026 included; makemigrations --check --dry-run is clean.
  • Send pipeline (mailing/services/transactional.py): sends accept optional template_version (default: latest published version, else the draft for never-published templates); rendering goes through a render source so markdown drafts and published snapshots render correctly in single sends, dry runs, and both recipient-list send paths; required context validates against the resolved source (draft or version) with one context.<name>: required error per missing key, before any contact, message, or queue work; the queue payload carries template_version when set.
  • API (mailing/views.py, mailing/urls.py, mailing/services/api.py): PUT template upsert now accepts markdown_body and category (and GET reports them plus latest_version); new endpoints POST .../publish (201, version N), GET .../versions, POST .../preview, POST .../test-send following the existing auth/error shapes.
  • Preview: renders draft or a named version with a context, returns subject/html/text plus missing_context; rendering is identical to a dry_run send for the same version and context.
  • Test send: reuses the durable transactional send pipeline, gated by TRANSACTIONAL_TEST_SEND_ALLOWLIST (empty disables the endpoint with 403; non-allowlisted addresses get 403); messages carry "test_send": true metadata.
  • Markdown rendering (mailing/services/transactional_markdown.py, mailing/templates/mailing/email_base.html): ports the AISL email pipeline (Django template substitution first, markdown with the donor extension set, shared HTML shell with header/footer, external links get target="_blank" rel="noopener", inline dash-run lists normalized).
  • import_templates --dir <path> --client <slug>: imports AISL-format markdown files as drafts (keyed by file stem), rerunnable, per-file errors reported and fail the run.
  • Settings: TRANSACTIONAL_TEST_SEND_ALLOWLIST, RELAY_EMAIL_BRAND_NAME, RELAY_EMAIL_SITE_BASE_URL. Deps: markdown>=3.6, python-frontmatter>=1.1.0 (plus transitive pyyaml); the uv.lock diff touches only these.
  • Docs: docs/api.md documents versions, preview, test send, template_version on send, and the frontmatter format; docs/data-model.md covers email_template_versions and the new fields.

Verification

  • uv run pytest (full suite):

    585 passed in 139.49s (0:02:19)
    

    (includes 23 new tests in mailing/tests/test_transactional_template_versions.py and mailing/tests/test_import_templates_command.py: publish immutability, version defaulting and explicit-version sends, preview parity with dry_run, required-context per-key failures before any work, unknown/invalid version handling, versions listing, test-send allowlist enforcement, markdown shell rendering, and import command behavior)

  • uv run ruff check .:

    All checks passed!
    
  • uv run python manage.py makemigrations --check --dry-run:

    No changes detected
    
  • Import of the AISL template directory against a disposable local SQLite database:

    Imported 50 templates from /home/alexey/git/ai-shipping-labs/email_app/email_templates: 50 created, 0 updated, 0 errors.
    drafts created: 50
    drafts with markdown body: 50
    
  • Preview of event_registration with the AISL preview context from email_app/services/preview_contexts.py (addresses sanitized for this log):

    event_registration preview: version=None published=False
    missing_context: []
    subject: You're registered: AI Shipping Workshop
    VERIFY OK
    

Not run here, needs:

  • Real SES delivery of a test-send and a versioned send (sandbox deploy with SQS_TRANSACTIONAL_EMAIL_QUEUE_URL configured).
  • Development-copy rehearsal and donor-equivalence sign-off for the imported AISL templates (covered by the later compatibility/adoption plan issues).

Notes

  • Preview and dry-run rendering share one render function with sends, so parity is structural, not best-effort.
  • Missing context keys render empty in previews (Django template semantics); missing_context in the preview response is the operator signal.
  • The operator template-detail page (catalog_context) still previews raw html_body/text_body, so markdown drafts show an empty preview there; the API preview endpoint covers markdown. Left out to keep this PR scoped to the issue; can follow up separately.
  • Local uv 0.10.11 rewrites uv.lock on plain uv sync/uv run (drops the [options] exclude-newer pins). The committed lock keeps the pins exactly as on main and only adds the three packages; the same rewrite would happen on main today, so nothing in this PR changes that behavior.
  • Backend-only change: screenshots are not applicable.

Transactional templates gain an editable draft plus immutable published
versions (publish endpoint, version list, per-send template_version with
latest-published default), a markdown template format with YAML
frontmatter rendered through the shared email shell, a render-preview
endpoint with preview parity and missing-context reporting, a test send
gated by TRANSACTIONAL_TEST_SEND_ALLOWLIST, and an import_templates
command for AISL-format markdown directories.

Closes #12
@alexeygrigorev
alexeygrigorev merged commit 234cd39 into main Sep 8, 2026
1 check passed
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.

R1.3 Template catalog: versions, preview, test send, typed context

1 participant