template-monorepo/internal/model/notification/template/render.go

109 lines
2.7 KiB
Go
Raw Normal View History

package template
import (
"bytes"
"fmt"
htmltemplate "html/template"
"strings"
texttemplate "text/template"
"gateway/internal/model/notification/domain/enum"
domtpl "gateway/internal/model/notification/domain/template"
)
// Renderer renders notification templates from a registry.
type Renderer struct {
registry domtpl.Registry
fallbacks []string
}
// NewRenderer builds a Renderer. Implements domain/template.Renderer.
func NewRenderer(registry domtpl.Registry, fallbacks ...string) *Renderer {
if registry == nil {
registry = DefaultRegistry()
}
return &Renderer{
registry: registry,
fallbacks: fallbacks,
}
}
// Render produces subject/body/sms text for the given kind and locale.
func (r *Renderer) Render(kind enum.NotifyKind, locale string, data map[string]any) (*domtpl.Rendered, error) {
spec, err := r.registry.Lookup(kind, locale, r.fallbacks...)
if err != nil {
return nil, err
}
if err := validateRequiredVars(spec.RequiredVars, data); err != nil {
return nil, err
}
subjectTpl, bodyTpl, smsTpl, err := resolveSpec(spec)
if err != nil {
return nil, err
}
out := &domtpl.Rendered{}
if subjectTpl != "" {
subject, err := executeTextTemplate("subject", subjectTpl, data)
if err != nil {
return nil, fmt.Errorf("template: render email subject: %w", err)
}
out.Subject = subject
}
if bodyTpl != "" {
body, err := executeHTMLTemplate("body", bodyTpl, data)
if err != nil {
return nil, fmt.Errorf("template: render email body: %w", err)
}
out.Body = body
}
if smsTpl != "" {
sms, err := executeTextTemplate("sms", smsTpl, data)
if err != nil {
return nil, fmt.Errorf("template: render sms: %w", err)
}
out.SMSText = sms
}
if out.Subject == "" && out.Body == "" && out.SMSText == "" {
return nil, fmt.Errorf("template: kind %q has no renderable fields for locale %q", kind, locale)
}
return out, nil
}
func validateRequiredVars(required []string, data map[string]any) error {
for _, key := range required {
if _, ok := data[key]; !ok {
return fmt.Errorf("template: missing required variable %q", key)
}
}
return nil
}
func executeTextTemplate(name, tpl string, data map[string]any) (string, error) {
t, err := texttemplate.New(name).Parse(tpl)
if err != nil {
return "", err
}
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
return "", err
}
return strings.TrimSpace(buf.String()), nil
}
func executeHTMLTemplate(name, tpl string, data map[string]any) (string, error) {
t, err := htmltemplate.New(name).Parse(tpl)
if err != nil {
return "", err
}
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
return "", err
}
return strings.TrimSpace(buf.String()), nil
}