Consolidate Distributor template parsing

This commit is contained in:
2026-08-13 03:55:03 +00:00
parent fb891fad07
commit 965f16d7a4
2 changed files with 60 additions and 51 deletions

View File

@@ -1725,43 +1725,59 @@ func TestDistributorBatchTemplateRendering(t *testing.T) {
} }
} }
func TestDistributorBatchTemplateRejectsUnknownAndMalformedVariables(t *testing.T) { func TestDistributorTemplateRejectsUnknownVariables(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
template string template string
render func(string) error
}{ }{
{name: "Unknown", template: "{report_id}"}, {
{name: "Unclosed", template: "{batch"}, name: "SingleReport",
{name: "Unopened", template: "batch}"}, template: "{unknown}",
{name: "Empty", template: "{}"}, render: func(template string) error {
_, err := RenderDistributorBundleID(template, DistributorTemplateValues{})
return err
},
},
{
name: "Batch",
template: "{report_id}",
render: func(template string) error {
_, err := RenderDistributorBatchBundleID(template, DistributorBatchTemplateValues{})
return err
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
_, err := RenderDistributorBatchBundleID(tt.template, DistributorBatchTemplateValues{}) err := tt.render(tt.template)
if err == nil { if err == nil {
t.Fatal("RenderDistributorBatchBundleID() error = nil, want error") t.Fatal("rendering error = nil, want error")
} }
}) })
} }
} }
func TestDistributorTemplateRejectsUnknownAndMalformedVariables(t *testing.T) { func TestDistributorTemplateParserRejectsMalformedVariables(t *testing.T) {
const name = "notify.distributor.bundle_id_template"
tests := []struct { tests := []struct {
name string name string
template string template string
wantErr string
}{ }{
{name: "Unknown", template: "{unknown}"}, {name: "Unclosed", template: "{location_id", wantErr: name + " contains an unclosed template variable"},
{name: "Unclosed", template: "{location_id"}, {name: "Unopened", template: "location_id}", wantErr: name + " contains an unopened template variable"},
{name: "Unopened", template: "location_id}"}, {name: "Empty", template: "{}", wantErr: name + " contains an empty template variable"},
{name: "Empty", template: "{}"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
_, err := RenderDistributorBundleID(tt.template, DistributorTemplateValues{}) _, err := renderDistributorTemplate(name, tt.template, func(variable string) (string, bool) {
if err == nil { return "value", variable == "location_id"
t.Fatal("RenderDistributorBundleID() error = nil, want error") })
if err == nil || err.Error() != tt.wantErr {
t.Fatalf("error = %v, want %q", err, tt.wantErr)
} }
}) })
} }

View File

@@ -78,7 +78,7 @@ var distributorBatchIdempotencyTemplateVariables = map[string]struct{}{
var distributorBatchPipelineTemplateVariables = distributorBatchTemplateVariables var distributorBatchPipelineTemplateVariables = distributorBatchTemplateVariables
func RenderDistributorBundleID(template string, values DistributorTemplateValues) (string, error) { func RenderDistributorBundleID(template string, values DistributorTemplateValues) (string, error) {
rendered, err := renderDistributorTemplate("notify.distributor.bundle_id_template", template, values, distributorTemplateVariables) rendered, err := renderDistributorTemplate("notify.distributor.bundle_id_template", template, newDistributorTemplateResolver(values, distributorTemplateVariables))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -89,7 +89,7 @@ func RenderDistributorBundleID(template string, values DistributorTemplateValues
} }
func RenderDistributorPipelineID(template string, values DistributorTemplateValues) (string, error) { func RenderDistributorPipelineID(template string, values DistributorTemplateValues) (string, error) {
rendered, err := renderDistributorTemplate("notify.distributor.pipeline_id_template", template, values, distributorPipelineTemplateVariables) rendered, err := renderDistributorTemplate("notify.distributor.pipeline_id_template", template, newDistributorTemplateResolver(values, distributorPipelineTemplateVariables))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -100,7 +100,7 @@ func RenderDistributorPipelineID(template string, values DistributorTemplateValu
} }
func RenderDistributorIdempotencyKey(template string, values DistributorTemplateValues) (string, error) { func RenderDistributorIdempotencyKey(template string, values DistributorTemplateValues) (string, error) {
rendered, err := renderDistributorTemplate("notify.distributor.idempotency_key_template", template, values, distributorIdempotencyTemplateVariables) rendered, err := renderDistributorTemplate("notify.distributor.idempotency_key_template", template, newDistributorTemplateResolver(values, distributorIdempotencyTemplateVariables))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -111,7 +111,7 @@ func RenderDistributorIdempotencyKey(template string, values DistributorTemplate
} }
func RenderDistributorBatchBundleID(template string, values DistributorBatchTemplateValues) (string, error) { func RenderDistributorBatchBundleID(template string, values DistributorBatchTemplateValues) (string, error) {
rendered, err := renderDistributorBatchTemplate("notify.distributor.batch.bundle_id_template", template, values, distributorBatchTemplateVariables) rendered, err := renderDistributorTemplate("notify.distributor.batch.bundle_id_template", template, newDistributorBatchTemplateResolver(values, distributorBatchTemplateVariables))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -122,7 +122,7 @@ func RenderDistributorBatchBundleID(template string, values DistributorBatchTemp
} }
func RenderDistributorBatchPipelineID(template string, values DistributorBatchTemplateValues) (string, error) { func RenderDistributorBatchPipelineID(template string, values DistributorBatchTemplateValues) (string, error) {
rendered, err := renderDistributorBatchTemplate("notify.distributor.batch.pipeline_id_template", template, values, distributorBatchPipelineTemplateVariables) rendered, err := renderDistributorTemplate("notify.distributor.batch.pipeline_id_template", template, newDistributorBatchTemplateResolver(values, distributorBatchPipelineTemplateVariables))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -133,7 +133,7 @@ func RenderDistributorBatchPipelineID(template string, values DistributorBatchTe
} }
func RenderDistributorBatchIdempotencyKey(template string, values DistributorBatchTemplateValues) (string, error) { func RenderDistributorBatchIdempotencyKey(template string, values DistributorBatchTemplateValues) (string, error) {
rendered, err := renderDistributorBatchTemplate("notify.distributor.batch.idempotency_key_template", template, values, distributorBatchIdempotencyTemplateVariables) rendered, err := renderDistributorTemplate("notify.distributor.batch.idempotency_key_template", template, newDistributorBatchTemplateResolver(values, distributorBatchIdempotencyTemplateVariables))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -151,7 +151,7 @@ func RenderDistributorReportPaths(name string, templates []string, values Distri
seen := make(map[string]struct{}, len(templates)) seen := make(map[string]struct{}, len(templates))
for i, template := range templates { for i, template := range templates {
itemName := fmt.Sprintf("%s[%d]", name, i) itemName := fmt.Sprintf("%s[%d]", name, i)
rendered, err := renderDistributorTemplate(itemName, template, values, distributorTemplateVariables) rendered, err := renderDistributorTemplate(itemName, template, newDistributorTemplateResolver(values, distributorTemplateVariables))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -168,16 +168,18 @@ func RenderDistributorReportPaths(name string, templates []string, values Distri
} }
func validateDistributorTemplate(name, template string, allowed map[string]struct{}) error { func validateDistributorTemplate(name, template string, allowed map[string]struct{}) error {
_, err := renderDistributorTemplate(name, template, DistributorTemplateValues{}, allowed) _, err := renderDistributorTemplate(name, template, newDistributorTemplateResolver(DistributorTemplateValues{}, allowed))
return err return err
} }
func validateDistributorBatchTemplate(name, template string, allowed map[string]struct{}) error { func validateDistributorBatchTemplate(name, template string, allowed map[string]struct{}) error {
_, err := renderDistributorBatchTemplate(name, template, DistributorBatchTemplateValues{}, allowed) _, err := renderDistributorTemplate(name, template, newDistributorBatchTemplateResolver(DistributorBatchTemplateValues{}, allowed))
return err return err
} }
func renderDistributorTemplate(name, template string, values DistributorTemplateValues, allowed map[string]struct{}) (string, error) { type distributorTemplateResolver func(string) (string, bool)
func renderDistributorTemplate(name, template string, resolve distributorTemplateResolver) (string, error) {
var rendered strings.Builder var rendered strings.Builder
for i := 0; i < len(template); { for i := 0; i < len(template); {
switch template[i] { switch template[i] {
@@ -190,10 +192,11 @@ func renderDistributorTemplate(name, template string, values DistributorTemplate
if variable == "" { if variable == "" {
return "", fmt.Errorf("%s contains an empty template variable", name) return "", fmt.Errorf("%s contains an empty template variable", name)
} }
if _, ok := allowed[variable]; !ok { value, ok := resolve(variable)
if !ok {
return "", fmt.Errorf("%s contains unknown template variable %q", name, variable) return "", fmt.Errorf("%s contains unknown template variable %q", name, variable)
} }
rendered.WriteString(distributorTemplateValue(variable, values)) rendered.WriteString(value)
i += end + 2 i += end + 2
case '}': case '}':
return "", fmt.Errorf("%s contains an unopened template variable", name) return "", fmt.Errorf("%s contains an unopened template variable", name)
@@ -205,32 +208,22 @@ func renderDistributorTemplate(name, template string, values DistributorTemplate
return rendered.String(), nil return rendered.String(), nil
} }
func renderDistributorBatchTemplate(name, template string, values DistributorBatchTemplateValues, allowed map[string]struct{}) (string, error) { func newDistributorTemplateResolver(values DistributorTemplateValues, allowed map[string]struct{}) distributorTemplateResolver {
var rendered strings.Builder return func(variable string) (string, bool) {
for i := 0; i < len(template); { if _, ok := allowed[variable]; !ok {
switch template[i] { return "", false
case '{':
end := strings.IndexByte(template[i+1:], '}')
if end < 0 {
return "", fmt.Errorf("%s contains an unclosed template variable", name)
}
variable := template[i+1 : i+1+end]
if variable == "" {
return "", fmt.Errorf("%s contains an empty template variable", name)
}
if _, ok := allowed[variable]; !ok {
return "", fmt.Errorf("%s contains unknown template variable %q", name, variable)
}
rendered.WriteString(distributorBatchTemplateValue(variable, values))
i += end + 2
case '}':
return "", fmt.Errorf("%s contains an unopened template variable", name)
default:
rendered.WriteByte(template[i])
i++
} }
return distributorTemplateValue(variable, values), true
}
}
func newDistributorBatchTemplateResolver(values DistributorBatchTemplateValues, allowed map[string]struct{}) distributorTemplateResolver {
return func(variable string) (string, bool) {
if _, ok := allowed[variable]; !ok {
return "", false
}
return distributorBatchTemplateValue(variable, values), true
} }
return rendered.String(), nil
} }
func distributorTemplateValue(variable string, values DistributorTemplateValues) string { func distributorTemplateValue(variable string, values DistributorTemplateValues) string {