feat: add template.FromGlobsWithAdditionalFuncs (#3174)
* refactor: add Options to the template.FromGlob function to allow customizing the Template Signed-off-by: Martin Chodur <m.chodur@seznam.cz>
This commit is contained in:
parent
d9c847e38b
commit
26cbd6bd86
|
@ -84,7 +84,7 @@ func CheckConfig(args []string) error {
|
||||||
fmt.Printf(" - %d receivers\n", len(cfg.Receivers))
|
fmt.Printf(" - %d receivers\n", len(cfg.Receivers))
|
||||||
fmt.Printf(" - %d templates\n", len(cfg.Templates))
|
fmt.Printf(" - %d templates\n", len(cfg.Templates))
|
||||||
if len(cfg.Templates) > 0 {
|
if len(cfg.Templates) > 0 {
|
||||||
_, err = template.FromGlobs(cfg.Templates...)
|
_, err = template.FromGlobs(cfg.Templates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf(" FAILED: %s\n", err)
|
fmt.Printf(" FAILED: %s\n", err)
|
||||||
failed++
|
failed++
|
||||||
|
|
|
@ -107,7 +107,7 @@ func configureTemplateRenderCmd(cc *kingpin.CmdClause) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *templateRenderCmd) render(ctx context.Context, _ *kingpin.ParseContext) error {
|
func (c *templateRenderCmd) render(ctx context.Context, _ *kingpin.ParseContext) error {
|
||||||
tmpl, err := template.FromGlobs(c.templateFilesGlobs...)
|
tmpl, err := template.FromGlobs(c.templateFilesGlobs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -419,7 +419,7 @@ func run() int {
|
||||||
configLogger,
|
configLogger,
|
||||||
)
|
)
|
||||||
configCoordinator.Subscribe(func(conf *config.Config) error {
|
configCoordinator.Subscribe(func(conf *config.Config) error {
|
||||||
tmpl, err = template.FromGlobs(conf.Templates...)
|
tmpl, err = template.FromGlobs(conf.Templates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to parse templates")
|
return errors.Wrap(err, "failed to parse templates")
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ func notifyEmailWithContext(ctx context.Context, cfg *config.EmailConfig, server
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.FromGlobs()
|
tmpl, err := template.FromGlobs([]string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ func DefaultRetryCodes() []int {
|
||||||
|
|
||||||
// CreateTmpl returns a ready-to-use template.
|
// CreateTmpl returns a ready-to-use template.
|
||||||
func CreateTmpl(t *testing.T) *template.Template {
|
func CreateTmpl(t *testing.T) *template.Template {
|
||||||
tmpl, err := template.FromGlobs()
|
tmpl, err := template.FromGlobs([]string{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tmpl.ExternalURL, _ = url.Parse("http://am")
|
tmpl.ExternalURL, _ = url.Parse("http://am")
|
||||||
return tmpl
|
return tmpl
|
||||||
|
|
|
@ -42,16 +42,24 @@ type Template struct {
|
||||||
ExternalURL *url.URL
|
ExternalURL *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Option is generic modifier of the text and html templates used by a Template.
|
||||||
|
type Option func(text *tmpltext.Template, html *tmplhtml.Template)
|
||||||
|
|
||||||
// FromGlobs calls ParseGlob on all path globs provided and returns the
|
// FromGlobs calls ParseGlob on all path globs provided and returns the
|
||||||
// resulting Template.
|
// resulting Template. Options allows customization of the text and html templates in given order.
|
||||||
func FromGlobs(paths ...string) (*Template, error) {
|
// The DefaultFuncs have precedence over any added custom functions.
|
||||||
|
func FromGlobs(paths []string, options ...Option) (*Template, error) {
|
||||||
t := &Template{
|
t := &Template{
|
||||||
text: tmpltext.New("").Option("missingkey=zero"),
|
text: tmpltext.New("").Option("missingkey=zero"),
|
||||||
html: tmplhtml.New("").Option("missingkey=zero"),
|
html: tmplhtml.New("").Option("missingkey=zero"),
|
||||||
}
|
}
|
||||||
|
|
||||||
t.text = t.text.Funcs(tmpltext.FuncMap(DefaultFuncs))
|
for _, o := range options {
|
||||||
t.html = t.html.Funcs(tmplhtml.FuncMap(DefaultFuncs))
|
o(t.text, t.html)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.text.Funcs(tmpltext.FuncMap(DefaultFuncs))
|
||||||
|
t.html.Funcs(tmplhtml.FuncMap(DefaultFuncs))
|
||||||
|
|
||||||
defaultTemplates := []string{"default.tmpl", "email.tmpl"}
|
defaultTemplates := []string{"default.tmpl", "email.tmpl"}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
package template
|
package template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
tmplhtml "html/template"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
tmpltext "text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
|
@ -277,7 +279,7 @@ func TestData(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTemplateExpansion(t *testing.T) {
|
func TestTemplateExpansion(t *testing.T) {
|
||||||
tmpl, err := FromGlobs()
|
tmpl, err := FromGlobs([]string{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
|
@ -387,3 +389,67 @@ func TestTemplateExpansion(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTemplateExpansionWithOptions(t *testing.T) {
|
||||||
|
testOptionWithAdditionalFuncs := func(funcs FuncMap) Option {
|
||||||
|
return func(text *tmpltext.Template, html *tmplhtml.Template) {
|
||||||
|
text.Funcs(tmpltext.FuncMap(funcs))
|
||||||
|
html.Funcs(tmplhtml.FuncMap(funcs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
options []Option
|
||||||
|
title string
|
||||||
|
in string
|
||||||
|
data interface{}
|
||||||
|
html bool
|
||||||
|
|
||||||
|
exp string
|
||||||
|
fail bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
title: "Test custom function",
|
||||||
|
options: []Option{testOptionWithAdditionalFuncs(FuncMap{"printFoo": func() string { return "foo" }})},
|
||||||
|
in: `{{ printFoo }}`,
|
||||||
|
exp: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test Default function with additional function added",
|
||||||
|
options: []Option{testOptionWithAdditionalFuncs(FuncMap{"printFoo": func() string { return "foo" }})},
|
||||||
|
in: `{{ toUpper "test" }}`,
|
||||||
|
exp: "TEST",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test custom function is overridden by the DefaultFuncs",
|
||||||
|
options: []Option{testOptionWithAdditionalFuncs(FuncMap{"toUpper": func(s string) string { return "foo" }})},
|
||||||
|
in: `{{ toUpper "test" }}`,
|
||||||
|
exp: "TEST",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test later Option overrides the previous",
|
||||||
|
options: []Option{
|
||||||
|
testOptionWithAdditionalFuncs(FuncMap{"printFoo": func() string { return "foo" }}),
|
||||||
|
testOptionWithAdditionalFuncs(FuncMap{"printFoo": func() string { return "bar" }}),
|
||||||
|
},
|
||||||
|
in: `{{ printFoo }}`,
|
||||||
|
exp: "bar",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.title, func(t *testing.T) {
|
||||||
|
tmpl, err := FromGlobs([]string{}, tc.options...)
|
||||||
|
require.NoError(t, err)
|
||||||
|
f := tmpl.ExecuteTextString
|
||||||
|
if tc.html {
|
||||||
|
f = tmpl.ExecuteHTMLString
|
||||||
|
}
|
||||||
|
got, err := f(tc.in, tc.data)
|
||||||
|
if tc.fail {
|
||||||
|
require.NotNil(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.exp, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue