diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 289da85..3e9a7fc 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -1725,43 +1725,59 @@ func TestDistributorBatchTemplateRendering(t *testing.T) { } } -func TestDistributorBatchTemplateRejectsUnknownAndMalformedVariables(t *testing.T) { +func TestDistributorTemplateRejectsUnknownVariables(t *testing.T) { tests := []struct { name string template string + render func(string) error }{ - {name: "Unknown", template: "{report_id}"}, - {name: "Unclosed", template: "{batch"}, - {name: "Unopened", template: "batch}"}, - {name: "Empty", template: "{}"}, + { + name: "SingleReport", + template: "{unknown}", + 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 { t.Run(tt.name, func(t *testing.T) { - _, err := RenderDistributorBatchBundleID(tt.template, DistributorBatchTemplateValues{}) + err := tt.render(tt.template) 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 { name string template string + wantErr string }{ - {name: "Unknown", template: "{unknown}"}, - {name: "Unclosed", template: "{location_id"}, - {name: "Unopened", template: "location_id}"}, - {name: "Empty", template: "{}"}, + {name: "Unclosed", template: "{location_id", wantErr: name + " contains an unclosed template variable"}, + {name: "Unopened", template: "location_id}", wantErr: name + " contains an unopened template variable"}, + {name: "Empty", template: "{}", wantErr: name + " contains an empty template variable"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, err := RenderDistributorBundleID(tt.template, DistributorTemplateValues{}) - if err == nil { - t.Fatal("RenderDistributorBundleID() error = nil, want error") + _, err := renderDistributorTemplate(name, tt.template, func(variable string) (string, bool) { + return "value", variable == "location_id" + }) + if err == nil || err.Error() != tt.wantErr { + t.Fatalf("error = %v, want %q", err, tt.wantErr) } }) } diff --git a/internal/config/notify_templates.go b/internal/config/notify_templates.go index 0d627fb..f959512 100644 --- a/internal/config/notify_templates.go +++ b/internal/config/notify_templates.go @@ -78,7 +78,7 @@ var distributorBatchIdempotencyTemplateVariables = map[string]struct{}{ var distributorBatchPipelineTemplateVariables = distributorBatchTemplateVariables 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 { return "", err } @@ -89,7 +89,7 @@ func RenderDistributorBundleID(template string, values DistributorTemplateValues } 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 { return "", err } @@ -100,7 +100,7 @@ func RenderDistributorPipelineID(template string, values DistributorTemplateValu } 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 { return "", err } @@ -111,7 +111,7 @@ func RenderDistributorIdempotencyKey(template string, values DistributorTemplate } 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 { return "", err } @@ -122,7 +122,7 @@ func RenderDistributorBatchBundleID(template string, values DistributorBatchTemp } 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 { return "", err } @@ -133,7 +133,7 @@ func RenderDistributorBatchPipelineID(template string, values DistributorBatchTe } 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 { return "", err } @@ -151,7 +151,7 @@ func RenderDistributorReportPaths(name string, templates []string, values Distri seen := make(map[string]struct{}, len(templates)) for i, template := range templates { 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 { 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 { - _, err := renderDistributorTemplate(name, template, DistributorTemplateValues{}, allowed) + _, err := renderDistributorTemplate(name, template, newDistributorTemplateResolver(DistributorTemplateValues{}, allowed)) return err } 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 } -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 for i := 0; i < len(template); { switch template[i] { @@ -190,10 +192,11 @@ func renderDistributorTemplate(name, template string, values DistributorTemplate if variable == "" { 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) } - rendered.WriteString(distributorTemplateValue(variable, values)) + rendered.WriteString(value) i += end + 2 case '}': 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 } -func renderDistributorBatchTemplate(name, template string, values DistributorBatchTemplateValues, allowed map[string]struct{}) (string, error) { - var rendered strings.Builder - for i := 0; i < len(template); { - switch template[i] { - 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++ +func newDistributorTemplateResolver(values DistributorTemplateValues, allowed map[string]struct{}) distributorTemplateResolver { + return func(variable string) (string, bool) { + if _, ok := allowed[variable]; !ok { + return "", false } + 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 {