Files
hatchet/internal/datautils/render.go
2024-01-04 08:28:49 -05:00

68 lines
1.7 KiB
Go

package datautils
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"text/template"
)
// RenderTemplateFields recursively processes the input map, rendering any string fields using the data map.
func RenderTemplateFields(data map[string]interface{}, input map[string]interface{}) (map[string]interface{}, error) {
output := map[string]interface{}{}
for key, val := range input {
switch v := val.(type) {
case string:
tmpl, err := template.New(key).Parse(v)
if err != nil {
return nil, fmt.Errorf("error creating template for key %s: %v", key, err)
}
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, data)
if err != nil {
return nil, fmt.Errorf("error executing template for key %s: %v", key, err)
}
res := tpl.String()
// if the string can be unmarshalled into a map[string]interface{}, do so
resMap := map[string]interface{}{}
if err := json.Unmarshal(tpl.Bytes(), &resMap); err == nil {
output[key] = resMap
// if the key is "object", the entire input is replaced with the rendered value
if key == "object" {
// note we do not recursively render the new input, as it may contain untrusted data.
return resMap, nil
}
} else {
output[key] = res
}
case map[string]interface{}:
// if we hit a nested map[string]interface{}, render those recursively
recOut, err := RenderTemplateFields(data, v)
if err != nil {
return nil, err
}
output[key] = recOut
default:
if reflect.TypeOf(v).Kind() == reflect.Map {
// If it's a map but not map[string]interface{}, return an error
return nil, fmt.Errorf("encountered a map that is not map[string]interface{}: %s", key)
}
// otherwise, just copy the value over
output[key] = v
}
}
return output, nil
}