Use go templates/sprig for messages (#2527)

This commit is contained in:
Markus Thierolf 2022-02-26 13:08:32 +01:00 • committed by GitHub
parent 2f06a88ead
commit 45a5bdf7d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 12 deletions

View file

@ -64,7 +64,7 @@ type typedConfig struct {
}
type messagingConfig struct {
Events map[string]push.EventTemplate
Events map[string]push.EventTemplateConfig
Services []typedConfig
}

View file

@ -138,7 +138,10 @@ func configureEEBus(conf map[string]interface{}) error {
// setup messaging
func configureMessengers(conf messagingConfig, cache *util.Cache) chan push.Event {
notificationChan := make(chan push.Event, 1)
notificationHub := push.NewHub(conf.Events, cache)
notificationHub, err := push.NewHub(conf.Events, cache)
if err != nil {
log.FATAL.Fatalf("failed configuring push services: %v", err)
}
for _, service := range conf.Services {
impl, err := push.NewMessengerFromConfig(service.Type, service.Other)

View file

@ -12,11 +12,6 @@ type Sender interface {
Send(title, msg string)
}
// EventTemplate is the push message template for an event
type EventTemplate struct {
Title, Msg string
}
var log = util.NewLogger("push")
// NewMessengerFromConfig creates a new messenger

View file

@ -1,8 +1,11 @@
package push
import (
"strings"
"text/template"
"time"
"github.com/Masterminds/sprig/v3"
"github.com/evcc-io/evcc/util"
)
@ -12,6 +15,16 @@ type Event struct {
Event string
}
// EventTemplateConfig is the push message configuration for an event
type EventTemplateConfig struct {
Title, Msg string
}
// EventTemplate is the push message template for an event
type EventTemplate struct {
Title, Msg *template.Template
}
// Hub subscribes to event notifications and sends them to client devices
type Hub struct {
definitions map[string]EventTemplate
@ -20,12 +33,32 @@ type Hub struct {
}
// NewHub creates push hub with definitions and receiver
func NewHub(definitions map[string]EventTemplate, cache *util.Cache) *Hub {
func NewHub(cc map[string]EventTemplateConfig, cache *util.Cache) (*Hub, error) {
definitions := make(map[string]EventTemplate)
// instantiate all event templates
for k, v := range cc {
var def EventTemplate
var err error
def.Title, err = template.New("out").Funcs(template.FuncMap(sprig.FuncMap())).Parse(v.Title)
if err == nil {
def.Msg, err = template.New("out").Funcs(template.FuncMap(sprig.FuncMap())).Parse(v.Msg)
}
if err != nil {
return nil, err
}
definitions[k] = def
}
h := &Hub{
definitions: definitions,
cache: cache,
}
return h
return h, nil
}
// Add adds a sender to the list of senders
@ -34,7 +67,7 @@ func (h *Hub) Add(sender Sender) {
}
// apply applies the event template to the content to produce the actual message
func (h *Hub) apply(ev Event, template string) (string, error) {
func (h *Hub) apply(ev Event, tmpl *template.Template) (string, error) {
attr := make(map[string]interface{})
// let cache catch up, refs reverted https://github.com/evcc-io/evcc/pull/445
@ -47,7 +80,13 @@ func (h *Hub) apply(ev Event, template string) (string, error) {
}
}
return util.ReplaceFormatted(template, attr)
// apply data attributes to template using sprig functions
applied := new(strings.Builder)
if err := tmpl.Execute(applied, attr); err != nil {
return "", err
}
return util.ReplaceFormatted(applied.String(), attr)
}
// Run is the Hub's main publishing loop

View file

@ -1,10 +1,14 @@
package util
import (
"bytes"
"fmt"
"html/template"
"regexp"
"strings"
"time"
"github.com/Masterminds/sprig/v3"
)
var re = regexp.MustCompile(`\${(\w+)(:([a-zA-Z0-9%.]+))?}`)
@ -46,6 +50,20 @@ func FormatValue(format string, val interface{}) string {
// ReplaceFormatted replaces all occurrences of ${key} with formatted val from the kv map
func ReplaceFormatted(s string, kv map[string]interface{}) (string, error) {
// Enhanced golang template logic
tpl, err := template.New("base").Funcs(sprig.FuncMap()).Parse(s)
if err != nil {
return s, err
}
var rs bytes.Buffer
err = tpl.Execute(&rs, kv)
if err != nil {
return s, err
}
s = rs.String()
// Regex logic for backward compatibility
wanted := make([]string, 0)
for m := re.FindStringSubmatch(s); m != nil; m = re.FindStringSubmatch(s) {
@ -65,7 +83,6 @@ func ReplaceFormatted(s string, kv map[string]interface{}) (string, error) {
}
// return missing keys
var err error
if len(wanted) > 0 {
got := make([]string, 0)
for k := range kv {

View file

@ -33,6 +33,7 @@ func TestReplace(t *testing.T) {
v interface{}
fmt, expected string
}{
// regex tests
{"foo", true, "${foo}", "true"},
{"foo", "1", "abc${foo}${foo}", "abc11"},
{"foo", math.Pi, "${foo:%.2f}", "3.14"},