Centralize publish transform policy validation
This commit is contained in:
@@ -18,7 +18,7 @@ Supported actions are publish new, replace older destination, skip same source,
|
||||
|
||||
The current implementation publishes source files and Markdown-to-HTML sidecar outputs. Remote backend execution is not implemented.
|
||||
|
||||
The package uses `internal/state` for destination comparison and `internal/storage` for IO. It does not parse CLI flags or load config files.
|
||||
The package uses `internal/state` for destination comparison, `internal/storage` for IO, and the shared `internal/config` publish/transform policy helper for request validation. It does not parse CLI flags or load config files.
|
||||
|
||||
## Safety
|
||||
|
||||
|
||||
@@ -57,8 +57,7 @@ func Validate(cfg Config) error {
|
||||
}
|
||||
|
||||
errs = validateBackend(errs, destinationContext, destination.Backend, destination.Path, destination.URI, destination.Endpoint, destination.Bucket)
|
||||
errs = validatePublishPolicy(errs, destinationContext+".publish", destination.Publish)
|
||||
errs = validateTransform(errs, destinationContext+".transform", destination.Publish, destination.Transform)
|
||||
errs = validatePublishTransformPolicy(errs, destinationContext, destination.Publish, destination.Transform)
|
||||
errs = validateTransferPolicy(errs, destinationContext+".transfer", destination.Transfer)
|
||||
}
|
||||
}
|
||||
@@ -104,36 +103,40 @@ func validateValidationPolicy(errs ValidationErrors, context string, policy Vali
|
||||
return errs
|
||||
}
|
||||
|
||||
func validatePublishPolicy(errs ValidationErrors, context string, policy *PublishPolicy) ValidationErrors {
|
||||
func validatePublishTransformPolicy(errs ValidationErrors, context string, policy *PublishPolicy, transform Transform) ValidationErrors {
|
||||
if policy == nil {
|
||||
errs = append(errs, context+" is required")
|
||||
errs = append(errs, context+".publish is required")
|
||||
return errs
|
||||
}
|
||||
if !policy.Source && !policy.HTML {
|
||||
errs = append(errs, context+" must enable source or html")
|
||||
if err := ValidatePublishTransformPolicy(*policy, transform); err != nil {
|
||||
errs = append(errs, context+"."+err.Error())
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func validateTransform(errs ValidationErrors, context string, publish *PublishPolicy, transform Transform) ValidationErrors {
|
||||
publishesHTML := publish != nil && publish.HTML
|
||||
if transform.MarkdownToHTML == nil {
|
||||
if publishesHTML {
|
||||
errs = append(errs, context+".markdown_to_html is required when publish.html is true")
|
||||
}
|
||||
return errs
|
||||
func ValidatePublishTransformPolicy(publish PublishPolicy, transform Transform) error {
|
||||
if !publish.Source && !publish.HTML {
|
||||
return fmt.Errorf("publish must enable source or html")
|
||||
}
|
||||
|
||||
if publishesHTML && !transform.MarkdownToHTML.Enabled {
|
||||
errs = append(errs, context+".markdown_to_html.enabled must be true when publish.html is true")
|
||||
if publish.HTML && transform.MarkdownToHTML == nil {
|
||||
return fmt.Errorf("transform.markdown_to_html is required when publish.html is true")
|
||||
}
|
||||
if transform.MarkdownToHTML == nil {
|
||||
return nil
|
||||
}
|
||||
if publish.HTML && !transform.MarkdownToHTML.Enabled {
|
||||
return fmt.Errorf("transform.markdown_to_html.enabled must be true when publish.html is true")
|
||||
}
|
||||
if publish.HTML && transform.MarkdownToHTML.Mode != TransformModeSidecar {
|
||||
return fmt.Errorf("transform.markdown_to_html.mode must be %s", TransformModeSidecar)
|
||||
}
|
||||
if transform.MarkdownToHTML.Enabled && transform.MarkdownToHTML.Mode != TransformModeSidecar {
|
||||
errs = append(errs, context+".markdown_to_html.mode must be "+TransformModeSidecar)
|
||||
return fmt.Errorf("transform.markdown_to_html.mode must be %s", TransformModeSidecar)
|
||||
}
|
||||
if !transform.MarkdownToHTML.Enabled && transform.MarkdownToHTML.Mode != "" && transform.MarkdownToHTML.Mode != TransformModeSidecar {
|
||||
errs = append(errs, context+".markdown_to_html.mode must be "+TransformModeSidecar)
|
||||
return fmt.Errorf("transform.markdown_to_html.mode must be %s", TransformModeSidecar)
|
||||
}
|
||||
return errs
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateTransferPolicy(errs ValidationErrors, context string, policy TransferPolicy) ValidationErrors {
|
||||
|
||||
141
internal/config/validate_test.go
Normal file
141
internal/config/validate_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidatePublishTransformPolicy(t *testing.T) {
|
||||
tests := publishTransformPolicyCases()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidatePublishTransformPolicy(tt.publish, tt.transform)
|
||||
if tt.wantErr && err == nil {
|
||||
t.Fatal("ValidatePublishTransformPolicy() error = nil, want error")
|
||||
}
|
||||
if !tt.wantErr && err != nil {
|
||||
t.Fatalf("ValidatePublishTransformPolicy() error = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateChecksPublishTransformPolicy(t *testing.T) {
|
||||
tests := publishTransformPolicyCases()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := Config{Pipelines: []Pipeline{{
|
||||
ID: "reports",
|
||||
Source: Backend{
|
||||
Backend: BackendLocal,
|
||||
Path: "/source",
|
||||
},
|
||||
Destinations: []Destination{{
|
||||
ID: "archive",
|
||||
Backend: BackendLocal,
|
||||
Path: "/destination",
|
||||
Publish: &tt.publish,
|
||||
Transform: tt.transform,
|
||||
}},
|
||||
}}}
|
||||
ApplyDefaults(&cfg)
|
||||
err := Validate(cfg)
|
||||
if tt.wantErr && err == nil {
|
||||
t.Fatal("Validate() error = nil, want error")
|
||||
}
|
||||
if !tt.wantErr && err != nil {
|
||||
t.Fatalf("Validate() error = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type publishTransformPolicyCase struct {
|
||||
name string
|
||||
publish PublishPolicy
|
||||
transform Transform
|
||||
wantErr bool
|
||||
}
|
||||
|
||||
func publishTransformPolicyCases() []publishTransformPolicyCase {
|
||||
return []publishTransformPolicyCase{
|
||||
{
|
||||
name: "source only allowed",
|
||||
publish: PublishPolicy{Source: true},
|
||||
},
|
||||
{
|
||||
name: "html only sidecar allowed",
|
||||
publish: PublishPolicy{HTML: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: TransformModeSidecar,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "source and html sidecar allowed",
|
||||
publish: PublishPolicy{Source: true, HTML: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: TransformModeSidecar,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "no outputs rejected",
|
||||
publish: PublishPolicy{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "html without transform rejected",
|
||||
publish: PublishPolicy{HTML: true},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "html with disabled transform rejected",
|
||||
publish: PublishPolicy{HTML: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: false,
|
||||
Mode: TransformModeSidecar,
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "html with wrong mode rejected",
|
||||
publish: PublishPolicy{HTML: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: "inline",
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "enabled markdown wrong mode rejected",
|
||||
publish: PublishPolicy{Source: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: "inline",
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "disabled markdown empty mode allowed",
|
||||
publish: PublishPolicy{Source: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: false,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "disabled markdown sidecar mode allowed",
|
||||
publish: PublishPolicy{Source: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: false,
|
||||
Mode: TransformModeSidecar,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "disabled markdown wrong mode rejected",
|
||||
publish: PublishPolicy{Source: true},
|
||||
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
|
||||
Enabled: false,
|
||||
Mode: "inline",
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -99,3 +99,112 @@ func TestBuildRejectsHTMLWithoutTransform(t *testing.T) {
|
||||
t.Fatal("Build() error = nil, want missing transform error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRequestChecksPublishTransformPolicy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
publish config.PublishPolicy
|
||||
transform config.Transform
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "source only allowed",
|
||||
publish: config.PublishPolicy{Source: true},
|
||||
},
|
||||
{
|
||||
name: "html only sidecar allowed",
|
||||
publish: config.PublishPolicy{HTML: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: config.TransformModeSidecar,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "source and html sidecar allowed",
|
||||
publish: config.PublishPolicy{Source: true, HTML: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: config.TransformModeSidecar,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "no outputs rejected",
|
||||
publish: config.PublishPolicy{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "html without transform rejected",
|
||||
publish: config.PublishPolicy{HTML: true},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "html with disabled transform rejected",
|
||||
publish: config.PublishPolicy{HTML: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: false,
|
||||
Mode: config.TransformModeSidecar,
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "html with wrong mode rejected",
|
||||
publish: config.PublishPolicy{HTML: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: "inline",
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "enabled markdown wrong mode rejected",
|
||||
publish: config.PublishPolicy{Source: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: true,
|
||||
Mode: "inline",
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "disabled markdown empty mode allowed",
|
||||
publish: config.PublishPolicy{Source: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: false,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "disabled markdown sidecar mode allowed",
|
||||
publish: config.PublishPolicy{Source: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: false,
|
||||
Mode: config.TransformModeSidecar,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "disabled markdown wrong mode rejected",
|
||||
publish: config.PublishPolicy{Source: true},
|
||||
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
||||
Enabled: false,
|
||||
Mode: "inline",
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateRequest(Request{
|
||||
PipelineID: "reports",
|
||||
DestinationID: "archive",
|
||||
SourceBackend: fake.New(),
|
||||
DestinationBackend: fake.New(),
|
||||
Publish: tt.publish,
|
||||
Transform: tt.transform,
|
||||
})
|
||||
if tt.wantErr && err == nil {
|
||||
t.Fatal("validateRequest() error = nil, want error")
|
||||
}
|
||||
if !tt.wantErr && err != nil {
|
||||
t.Fatalf("validateRequest() error = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,13 +102,8 @@ func validateRequest(req Request) error {
|
||||
if req.DestinationBackend == nil {
|
||||
return fmt.Errorf("destination backend is required")
|
||||
}
|
||||
if !req.Publish.Source && !req.Publish.HTML {
|
||||
return fmt.Errorf("publish source or html must be enabled")
|
||||
}
|
||||
if req.Publish.HTML {
|
||||
if req.Transform.MarkdownToHTML == nil || !req.Transform.MarkdownToHTML.Enabled || req.Transform.MarkdownToHTML.Mode != config.TransformModeSidecar {
|
||||
return fmt.Errorf("publish html requires markdown_to_html transform enabled with sidecar mode")
|
||||
}
|
||||
if err := config.ValidatePublishTransformPolicy(req.Publish, req.Transform); err != nil {
|
||||
return fmt.Errorf("publish/transform policy: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user