template

package
v1.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 18 Imported by: 1

Documentation

Overview

Package template defines the notification template entity with i18n support.

Index

Constants

View Source
const (
	SeverityError   = "error"
	SeverityWarning = "warning"

	KindParse      = "parse"
	KindExec       = "exec"
	KindEscape     = "escape"
	KindMissing    = "missing"
	KindUndeclared = "undeclared"
	KindUnprovided = "unprovided"
)

Diagnostic severities and kinds.

View Source
const (
	CategoryAuth          = "auth"
	CategoryTransactional = "transactional"
	CategoryMarketing     = "marketing"
	CategorySystem        = "system"
)

Category constants for template organization.

View Source
const MaxBuiltFieldBytes = 4 * MaxRenderedFieldBytes

MaxBuiltFieldBytes is the most the string-building functions (print, printf, println, html, js, urlquery and the string helpers) may build in total while one field renders. Capping each result alone isn't enough: a template can keep many results alive in variables without writing them.

These limits cover output and the strings templates build, not the data a render is given. html/template's contextual escapers (jsvalescaper, attrescaper, urlnormalizer, cssvaluefilter and the rest) build their escaped copy of a data value before the capped write refuses it, so a 50 MB value in <script>var v={{.b}}</script> can allocate gigabytes. What bounds that today is outside this package: templates.render caps the whole request at maxRenderBytes (256 KiB, extension/contract), and Send's data comes from application code. A new caller that hands Preview or RenderVersion untrusted data must cap that data itself.

View Source
const MaxRenderedFieldBytes = 1 << 20

MaxRenderedFieldBytes is the most one rendered field (subject, HTML, text or title) may produce. A loop such as {{range 100000000000}} would otherwise grow the output until the process runs out of memory.

Variables

View Source
var (
	ErrNoVersionForLocale      = errors.New("herald: no template version for locale")
	ErrTemplateRenderFailed    = errors.New("herald: template rendering failed")
	ErrMissingRequiredVariable = errors.New("herald: missing required template variable")
	ErrRenderedTooLarge        = errors.New("herald: rendered template is over the size limit")
)

Renderer errors.

Functions

func Explain

func Explain(tmpl *Template, locale string) ([]Step, *Version)

Explain returns every lookup Resolve makes for locale, in order, and the version that answered (nil when none did). Only active versions count, and the configured default locale is never consulted: a locale with no exact, language or "" version fails.

func Resolve

func Resolve(tmpl *Template, locale string) (*Version, Match)

Resolve returns the version that answers locale and how it matched, or (nil, MatchNone).

Types

type Content

type Content struct {
	Subject string `json:"subject"`
	HTML    string `json:"html"`
	Text    string `json:"text"`
	Title   string `json:"title"`
}

Content is the editable text of one version: a saved version or an unsaved editor buffer.

type Diagnostic

type Diagnostic struct {
	Field    string `json:"field"`
	Line     int    `json:"line"`
	Column   int    `json:"column"`
	Severity string `json:"severity"`
	Kind     string `json:"kind"`
	Message  string `json:"message"`
}

Diagnostic is one problem found while previewing a template. Positions are 1-based; a zero Line or Column means Go did not report one.

type FieldOutput

type FieldOutput struct {
	Field    string `json:"field"`
	Output   string `json:"output"`
	Rendered bool   `json:"rendered"`
}

FieldOutput is one field's rendered text. Rendered is false when the field was empty or failed.

type Match

type Match string

Match says how a version was chosen for a requested locale.

const (
	MatchExact    Match = "exact"    // an active version for the locale itself
	MatchLanguage Match = "language" // "fr" answering "fr-CA"
	MatchDefault  Match = "default"  // the active version with locale ""
	MatchNone     Match = "none"
)

The ways Resolve can answer a locale, in the order it tries them.

type PreviewResult

type PreviewResult struct {
	Fields      []FieldOutput `json:"fields"`
	Diagnostics []Diagnostic  `json:"diagnostics"`
}

PreviewResult is every field's output and every problem found.

type RenderedContent

type RenderedContent struct {
	Subject string
	HTML    string
	Text    string
	Title   string
}

RenderedContent holds the fully rendered template output.

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer processes Go templates with data to produce rendered notification content.

func NewRenderer

func NewRenderer() *Renderer

NewRenderer creates a new template renderer with default helper functions.

func (*Renderer) FuncNames

func (r *Renderer) FuncNames() []string

FuncNames lists the helper functions templates can call, sorted.

func (*Renderer) Preview

func (r *Renderer) Preview(c Content, vars []Variable, data map[string]any) *PreviewResult

Preview renders every field of c independently against data, with variable defaults applied, and reports every problem as a Diagnostic. It never returns an error and never stops at the first failure, so a broken HTML body doesn't hide a broken subject. Nothing is sent.

func (*Renderer) Render

func (r *Renderer) Render(tmpl *Template, locale string, data map[string]any) (*RenderedContent, error)

Render renders the version that answers locale (see Resolve) with data, after filling declared defaults. It stops at the first field that fails, which is what Send wants; Preview is the forgiving variant.

func (*Renderer) RenderVersion

func (r *Renderer) RenderVersion(version *Version, vars []Variable, data map[string]any) (*RenderedContent, error)

RenderVersion renders one version, active or not.

type Step

type Step struct {
	Try       string `json:"try"`
	Match     Match  `json:"match"`
	Found     bool   `json:"found"`
	VersionID string `json:"version_id,omitempty"`
}

Step is one lookup Resolve made: the locale it tried, which rule that was, and whether an active version answered.

type Store

type Store interface {
	// Template CRUD
	CreateTemplate(ctx context.Context, t *Template) error
	GetTemplate(ctx context.Context, templateID id.TemplateID) (*Template, error)
	GetTemplateBySlug(ctx context.Context, appID string, slug string, channel string) (*Template, error)
	UpdateTemplate(ctx context.Context, t *Template) error
	DeleteTemplate(ctx context.Context, templateID id.TemplateID) error
	ListTemplates(ctx context.Context, appID string) ([]*Template, error)
	ListTemplatesByChannel(ctx context.Context, appID string, channel string) ([]*Template, error)

	// Version CRUD
	CreateVersion(ctx context.Context, v *Version) error
	GetVersion(ctx context.Context, versionID id.TemplateVersionID) (*Version, error)
	UpdateVersion(ctx context.Context, v *Version) error
	DeleteVersion(ctx context.Context, versionID id.TemplateVersionID) error
	ListVersions(ctx context.Context, templateID id.TemplateID) ([]*Version, error)
}

Store defines persistence operations for templates and their versions.

type Template

type Template struct {
	ID        id.TemplateID `json:"id"`
	AppID     string        `json:"app_id"`
	Slug      string        `json:"slug"`
	Name      string        `json:"name"`
	Channel   string        `json:"channel"`
	Category  string        `json:"category"`
	Variables []Variable    `json:"variables,omitempty"`
	Versions  []Version     `json:"versions,omitempty"`
	IsSystem  bool          `json:"is_system"`
	Enabled   bool          `json:"enabled"`
	CreatedAt time.Time     `json:"created_at"`
	UpdatedAt time.Time     `json:"updated_at"`
}

Template represents a notification template with locale-specific versions.

func DefaultTemplates

func DefaultTemplates(appID string) []*Template

DefaultTemplates returns the default notification templates to seed per app.

type Variable

type Variable struct {
	Name        string `json:"name"`
	Type        string `json:"type"`
	Required    bool   `json:"required"`
	Default     string `json:"default,omitempty"`
	Description string `json:"description,omitempty"`
}

Variable describes an expected template variable.

type Version

type Version struct {
	ID         id.TemplateVersionID `json:"id"`
	TemplateID id.TemplateID        `json:"template_id"`
	Locale     string               `json:"locale"`
	Subject    string               `json:"subject,omitempty"`
	HTML       string               `json:"html,omitempty"`
	Text       string               `json:"text,omitempty"`
	Title      string               `json:"title,omitempty"`
	Active     bool                 `json:"active"`
	CreatedAt  time.Time            `json:"created_at"`
	UpdatedAt  time.Time            `json:"updated_at"`
}

Version represents a locale-specific content version of a template.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL