Skip to content

feat: add external template rendering with configurable branding#18

Merged
juliankoehn merged 11 commits intomainfrom
feat/external-templates
Mar 29, 2026
Merged

feat: add external template rendering with configurable branding#18
juliankoehn merged 11 commits intomainfrom
feat/external-templates

Conversation

@juliankoehn
Copy link
Copy Markdown
Member

Summary

  • Add two-phase API for rendering externally-provided email templates (e.g., from database) with configurable branding
  • RenderTemplate() renders Go template strings into a branded HTML email shell (pure function, no driver needed)
  • SendRendered() and SendFromTemplate() send rendered emails via the configured driver
  • Auto-generates plain text fallback from HTML body when no TextTemplate is provided
  • All branding (colors, logo, font, footer) configurable with sensible Kopexa defaults
  • Existing hardcoded templates and Send* methods are completely unchanged — this is a purely additive extension

New Public API

Symbol Description
ExternalTemplate Holds template strings (subject, preheader, body, text, defaults)
Branding Configures email shell appearance (colors, logo, font, footer)
RenderedEmail Result of rendering — subject, HTML, and plain text
RenderTemplate() Package-level function: renders template + branding into RenderedEmail
SendRendered() Method on Comms: sends a pre-rendered email
SendFromTemplate() Method on Comms: convenience render + send in one call

Test plan

  • 134 tests passing (up from 59), 0 lint issues
  • Branding default resolution: nil, partial, full custom
  • Template validation: both empty → error, body-only, text-only, both set
  • Data merging: defaults + data precedence, Branding key immutable
  • Template rendering: variable substitution, Sprig functions, XSS escaping, invalid syntax errors
  • HTML shell: DOCTYPE, branding colors, logo vs. text, preheader, footer
  • Full pipeline: end-to-end render, branding in body templates, fallback behavior
  • Send methods: message construction, validation, driver error propagation
  • HTML-to-text fallback: tag stripping, entity decoding, whitespace normalization
  • GoDoc examples: 3 testable examples
  • Race detector: all tests pass with -race

Design spec for extending comms with externally-provided templates
and configurable branding. Two-phase API (RenderTemplate + SendRendered),
Go html/template shell with table layout, TDD plan in 8 phases.
10-task TDD implementation plan for external template rendering
with branding support. Tests-first approach, bite-sized steps,
complete code in every step.
Introduces the Branding struct and resolveBranding helper as the
foundational building block for external template rendering with
per-tenant branding support.
Introduces emailShellTemplate (shell.go) with table-based layout for
broad email client compatibility, and renderShell/shellData in
render_external.go to wrap pre-rendered body HTML with a branded
header, preheader, and footer. 11 TDD tests added.
Implements the RenderTemplate function that orchestrates validation,
branding resolution, data merging, and subject/body/text rendering into
a ready-to-send RenderedEmail. Partial failures (one of body or text) are
tolerated; only errors when both renderers fail.
Implements SendRendered for sending pre-rendered emails and SendFromTemplate
as a convenience wrapper combining RenderTemplate + SendRendered in one call.
When no TextTemplate is provided but BodyTemplate renders successfully,
automatically generate a plain text version by stripping HTML tags,
converting block elements to newlines, and decoding HTML entities.
Explicit TextTemplate always takes precedence over the fallback.
@juliankoehn juliankoehn merged commit 17714e4 into main Mar 29, 2026
7 of 8 checks passed
@juliankoehn juliankoehn deleted the feat/external-templates branch March 29, 2026 13:27
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