Merge pull request #81 from jwetzell/feat/richer-templating-data

use a struct to pass multiple pieces of data into templating context
This commit is contained in:
Joel Wetzell
2026-03-04 12:40:37 -06:00
committed by GitHub
12 changed files with 82 additions and 46 deletions

View File

@@ -26,8 +26,10 @@ type FreeDCreate struct {
func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) { func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var idBuffer bytes.Buffer var idBuffer bytes.Buffer
err := fc.Id.Execute(&idBuffer, payload) err := fc.Id.Execute(&idBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -42,7 +44,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var panBuffer bytes.Buffer var panBuffer bytes.Buffer
err = fc.Pan.Execute(&panBuffer, payload) err = fc.Pan.Execute(&panBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -57,7 +59,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var tiltBuffer bytes.Buffer var tiltBuffer bytes.Buffer
err = fc.Tilt.Execute(&tiltBuffer, payload) err = fc.Tilt.Execute(&tiltBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -72,7 +74,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var rollBuffer bytes.Buffer var rollBuffer bytes.Buffer
err = fc.Tilt.Execute(&rollBuffer, payload) err = fc.Tilt.Execute(&rollBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -87,7 +89,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var posXBuffer bytes.Buffer var posXBuffer bytes.Buffer
err = fc.PosX.Execute(&posXBuffer, payload) err = fc.PosX.Execute(&posXBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -102,7 +104,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var posYBuffer bytes.Buffer var posYBuffer bytes.Buffer
err = fc.PosY.Execute(&posYBuffer, payload) err = fc.PosY.Execute(&posYBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -117,7 +119,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var posZBuffer bytes.Buffer var posZBuffer bytes.Buffer
err = fc.PosZ.Execute(&posZBuffer, payload) err = fc.PosZ.Execute(&posZBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -132,7 +134,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var zoomBuffer bytes.Buffer var zoomBuffer bytes.Buffer
err = fc.Zoom.Execute(&zoomBuffer, payload) err = fc.Zoom.Execute(&zoomBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -147,7 +149,7 @@ func (fc *FreeDCreate) Process(ctx context.Context, payload any) (any, error) {
} }
var focusBuffer bytes.Buffer var focusBuffer bytes.Buffer
err = fc.Focus.Execute(&focusBuffer, payload) err = fc.Focus.Execute(&focusBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -18,8 +18,10 @@ type HTTPRequestCreate struct {
func (hrc *HTTPRequestCreate) Process(ctx context.Context, payload any) (any, error) { func (hrc *HTTPRequestCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var urlBuffer bytes.Buffer var urlBuffer bytes.Buffer
err := hrc.URL.Execute(&urlBuffer, payload) err := hrc.URL.Execute(&urlBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -21,8 +21,10 @@ type HTTPResponse struct {
} }
func (hre *HTTPResponseCreate) Process(ctx context.Context, payload any) (any, error) { func (hre *HTTPResponseCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var bodyBuffer bytes.Buffer var bodyBuffer bytes.Buffer
err := hre.BodyTmpl.Execute(&bodyBuffer, payload) err := hre.BodyTmpl.Execute(&bodyBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -65,9 +65,10 @@ func newMidiNoteOnCreate(config config.ProcessorConfig) (Processor, error) {
} }
return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) { return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var channelBuffer bytes.Buffer var channelBuffer bytes.Buffer
err := channelTemplate.Execute(&channelBuffer, payload) err := channelTemplate.Execute(&channelBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -76,7 +77,7 @@ func newMidiNoteOnCreate(config config.ProcessorConfig) (Processor, error) {
channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8) channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8)
var noteBuffer bytes.Buffer var noteBuffer bytes.Buffer
err = noteTemplate.Execute(&noteBuffer, payload) err = noteTemplate.Execute(&noteBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -85,7 +86,7 @@ func newMidiNoteOnCreate(config config.ProcessorConfig) (Processor, error) {
noteValue, err := strconv.ParseUint(noteBuffer.String(), 10, 8) noteValue, err := strconv.ParseUint(noteBuffer.String(), 10, 8)
var velocityBuffer bytes.Buffer var velocityBuffer bytes.Buffer
err = velocityTemplate.Execute(&velocityBuffer, payload) err = velocityTemplate.Execute(&velocityBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -136,8 +137,10 @@ func newMidiNoteOffCreate(config config.ProcessorConfig) (Processor, error) {
return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) { return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var channelBuffer bytes.Buffer var channelBuffer bytes.Buffer
err := channelTemplate.Execute(&channelBuffer, payload) err := channelTemplate.Execute(&channelBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -146,7 +149,7 @@ func newMidiNoteOffCreate(config config.ProcessorConfig) (Processor, error) {
channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8) channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8)
var noteBuffer bytes.Buffer var noteBuffer bytes.Buffer
err = noteTemplate.Execute(&noteBuffer, payload) err = noteTemplate.Execute(&noteBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -155,7 +158,7 @@ func newMidiNoteOffCreate(config config.ProcessorConfig) (Processor, error) {
noteValue, err := strconv.ParseUint(noteBuffer.String(), 10, 8) noteValue, err := strconv.ParseUint(noteBuffer.String(), 10, 8)
var velocityBuffer bytes.Buffer var velocityBuffer bytes.Buffer
err = velocityTemplate.Execute(&velocityBuffer, payload) err = velocityTemplate.Execute(&velocityBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -207,8 +210,10 @@ func newMidiControlChangeCreate(config config.ProcessorConfig) (Processor, error
return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) { return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var channelBuffer bytes.Buffer var channelBuffer bytes.Buffer
err := channelTemplate.Execute(&channelBuffer, payload) err := channelTemplate.Execute(&channelBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -217,7 +222,7 @@ func newMidiControlChangeCreate(config config.ProcessorConfig) (Processor, error
channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8) channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8)
var controlBuffer bytes.Buffer var controlBuffer bytes.Buffer
err = controlTemplate.Execute(&controlBuffer, payload) err = controlTemplate.Execute(&controlBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -226,7 +231,7 @@ func newMidiControlChangeCreate(config config.ProcessorConfig) (Processor, error
controlValue, err := strconv.ParseUint(controlBuffer.String(), 10, 8) controlValue, err := strconv.ParseUint(controlBuffer.String(), 10, 8)
var valueBuffer bytes.Buffer var valueBuffer bytes.Buffer
err = valueTemplate.Execute(&valueBuffer, payload) err = valueTemplate.Execute(&valueBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -266,9 +271,10 @@ func newMidiProgramChangeCreate(config config.ProcessorConfig) (Processor, error
} }
return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) { return &MIDIMessageCreate{config: config, ProcessFunc: func(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var channelBuffer bytes.Buffer var channelBuffer bytes.Buffer
err := channelTemplate.Execute(&channelBuffer, payload) err := channelTemplate.Execute(&channelBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -277,7 +283,7 @@ func newMidiProgramChangeCreate(config config.ProcessorConfig) (Processor, error
channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8) channelValue, err := strconv.ParseUint(channelBuffer.String(), 10, 8)
var programBuffer bytes.Buffer var programBuffer bytes.Buffer
err = programTemplate.Execute(&programBuffer, payload) err = programTemplate.Execute(&programBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -21,9 +21,10 @@ type NATSMessageCreate struct {
} }
func (nmc *NATSMessageCreate) Process(ctx context.Context, payload any) (any, error) { func (nmc *NATSMessageCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var payloadBuffer bytes.Buffer var payloadBuffer bytes.Buffer
err := nmc.Payload.Execute(&payloadBuffer, payload) err := nmc.Payload.Execute(&payloadBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -32,7 +33,7 @@ func (nmc *NATSMessageCreate) Process(ctx context.Context, payload any) (any, er
payloadString := payloadBuffer.String() payloadString := payloadBuffer.String()
var subjectBuffer bytes.Buffer var subjectBuffer bytes.Buffer
err = nmc.Subject.Execute(&subjectBuffer, payload) err = nmc.Subject.Execute(&subjectBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -22,8 +22,10 @@ type OSCMessageCreate struct {
func (omc *OSCMessageCreate) Process(ctx context.Context, payload any) (any, error) { func (omc *OSCMessageCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var addressBuffer bytes.Buffer var addressBuffer bytes.Buffer
err := omc.Address.Execute(&addressBuffer, payload) err := omc.Address.Execute(&addressBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -47,7 +49,7 @@ func (omc *OSCMessageCreate) Process(ctx context.Context, payload any) (any, err
for argIndex, argTemplate := range omc.Args { for argIndex, argTemplate := range omc.Args {
var argBuffer bytes.Buffer var argBuffer bytes.Buffer
err := argTemplate.Execute(&argBuffer, payload) err := argTemplate.Execute(&argBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
) )
@@ -45,3 +46,17 @@ func GetAnyAs[T any](p any) (T, bool) {
typed, ok := p.(T) typed, ok := p.(T)
return typed, ok return typed, ok
} }
type TemplateData struct {
Payload any
Modules any
}
func GetTemplateData(ctx context.Context, payload any) TemplateData {
templateData := TemplateData{Payload: payload}
modules := ctx.Value(common.ModulesContextKey)
if modules != nil {
templateData.Modules = modules
}
return templateData
}

View File

@@ -24,8 +24,10 @@ type SipAudioFileResponse struct {
func (scc *SipResponseAudioCreate) Process(ctx context.Context, payload any) (any, error) { func (scc *SipResponseAudioCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var audioFileBuffer bytes.Buffer var audioFileBuffer bytes.Buffer
err := scc.AudioFile.Execute(&audioFileBuffer, payload) err := scc.AudioFile.Execute(&audioFileBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -27,8 +27,10 @@ type SipDTMFResponse struct {
func (scc *SipResponseDTMFCreate) Process(ctx context.Context, payload any) (any, error) { func (scc *SipResponseDTMFCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var digitsBuffer bytes.Buffer var digitsBuffer bytes.Buffer
err := scc.Digits.Execute(&digitsBuffer, payload) err := scc.Digits.Execute(&digitsBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -15,8 +15,10 @@ type StringCreate struct {
} }
func (sc *StringCreate) Process(ctx context.Context, payload any) (any, error) { func (sc *StringCreate) Process(ctx context.Context, payload any) (any, error) {
templateData := GetTemplateData(ctx, payload)
var templateBuffer bytes.Buffer var templateBuffer bytes.Buffer
err := sc.Template.Execute(&templateBuffer, payload) err := sc.Template.Execute(&templateBuffer, templateData)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -49,7 +49,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and no args", name: "address with template and no args",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
}, },
payload: map[string]any{"Value": "value"}, payload: map[string]any{"Value": "value"},
expected: &osc.OSCMessage{Address: "/test/value"}, expected: &osc.OSCMessage{Address: "/test/value"},
@@ -57,7 +57,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and string arg", name: "address with template and string arg",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
"args": []interface{}{"arg1"}, "args": []interface{}{"arg1"},
"types": "s", "types": "s",
}, },
@@ -67,7 +67,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and mixed args", name: "address with template and mixed args",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
"args": []interface{}{"arg1", "42", "3.14"}, "args": []interface{}{"arg1", "42", "3.14"},
"types": "sif", "types": "sif",
}, },
@@ -84,7 +84,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and int64 arg", name: "address with template and int64 arg",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
"args": []interface{}{"42"}, "args": []interface{}{"42"},
"types": "h", "types": "h",
}, },
@@ -94,7 +94,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and double arg", name: "address with template and double arg",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
"args": []interface{}{"42"}, "args": []interface{}{"42"},
"types": "d", "types": "d",
}, },
@@ -104,7 +104,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and true arg", name: "address with template and true arg",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
"args": []interface{}{""}, "args": []interface{}{""},
"types": "T", "types": "T",
}, },
@@ -114,7 +114,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and false arg", name: "address with template and false arg",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
"args": []interface{}{""}, "args": []interface{}{""},
"types": "F", "types": "F",
}, },
@@ -124,7 +124,7 @@ func TestGoodOSCMessageCreate(t *testing.T) {
{ {
name: "address with template and nil arg", name: "address with template and nil arg",
params: map[string]any{ params: map[string]any{
"address": "/test/{{.Value}}", "address": "/test/{{.Payload.Value}}",
"args": []interface{}{""}, "args": []interface{}{""},
"types": "N", "types": "N",
}, },
@@ -286,7 +286,7 @@ func TestBadOSCMessageCreate(t *testing.T) {
"address": "/test/{{.missing}}", "address": "/test/{{.missing}}",
}, },
payload: "test", payload: "test",
errorString: "template: address:1:8: executing \"address\" at <.missing>: can't evaluate field missing in type string", errorString: "template: address:1:8: executing \"address\" at <.missing>: can't evaluate field missing in type processor.TemplateData",
}, },
{ {
name: "address doesn't start with slash", name: "address doesn't start with slash",
@@ -304,7 +304,7 @@ func TestBadOSCMessageCreate(t *testing.T) {
"types": "s", "types": "s",
}, },
payload: "test", payload: "test",
errorString: "template: arg:1:2: executing \"arg\" at <.missing>: can't evaluate field missing in type string", errorString: "template: arg:1:2: executing \"arg\" at <.missing>: can't evaluate field missing in type processor.TemplateData",
}, },
} }

View File

@@ -16,7 +16,7 @@ func TestStringCreateFromRegistry(t *testing.T) {
processorInstance, err := registration.New(config.ProcessorConfig{ processorInstance, err := registration.New(config.ProcessorConfig{
Type: "string.create", Type: "string.create",
Params: map[string]any{ Params: map[string]any{
"template": "{{.}}", "template": "{{.Payload}}",
}, },
}) })
if err != nil { if err != nil {
@@ -50,31 +50,31 @@ func TestGoodStringCreate(t *testing.T) {
}{ }{
{ {
name: "string payload", name: "string payload",
params: map[string]any{"template": "{{.}}"}, params: map[string]any{"template": "{{.Payload}}"},
payload: "hello", payload: "hello",
expected: "hello", expected: "hello",
}, },
{ {
name: "number payload", name: "number payload",
params: map[string]any{"template": "{{.}}"}, params: map[string]any{"template": "{{.Payload}}"},
payload: 4, payload: 4,
expected: "4", expected: "4",
}, },
{ {
name: "boolean payload", name: "boolean payload",
params: map[string]any{"template": "{{.}}"}, params: map[string]any{"template": "{{.Payload}}"},
payload: true, payload: true,
expected: "true", expected: "true",
}, },
{ {
name: "struct payload - field", name: "struct payload - field",
params: map[string]any{"template": "{{.Data}}"}, params: map[string]any{"template": "{{.Payload.Data}}"},
payload: TestStruct{Data: "test"}, payload: TestStruct{Data: "test"},
expected: "test", expected: "test",
}, },
{ {
name: "struct payload - method", name: "struct payload - method",
params: map[string]any{"template": "{{.GetData}}"}, params: map[string]any{"template": "{{.Payload.GetData}}"},
payload: TestStruct{Data: "test"}, payload: TestStruct{Data: "test"},
expected: "test", expected: "test",
}, },
@@ -148,7 +148,7 @@ func TestBadStringCreate(t *testing.T) {
params: map[string]any{ params: map[string]any{
"template": "{{.Invalid}}", "template": "{{.Invalid}}",
}, },
errorString: "template: template:1:2: executing \"template\" at <.Invalid>: can't evaluate field Invalid in type string", errorString: "template: template:1:2: executing \"template\" at <.Invalid>: can't evaluate field Invalid in type processor.TemplateData",
}, },
} }