From bc5f5d3731d775d0e2836551e925d555e3a5d488 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sat, 4 Jul 2026 23:38:45 +0000 Subject: [PATCH] Share validator mode handling --- internal/validate/standard_validator.go | 127 ++++++++---------------- 1 file changed, 44 insertions(+), 83 deletions(-) diff --git a/internal/validate/standard_validator.go b/internal/validate/standard_validator.go index 8ad6e2a..d08a9ed 100644 --- a/internal/validate/standard_validator.go +++ b/internal/validate/standard_validator.go @@ -34,6 +34,16 @@ func NewFSValidator(fsys fs.FS, root string) Validator { } func (v *StandardValidator) Validate(ctx context.Context, artifact *domain.Artifact, contract domain.OutputContract) (domain.ValidationResult, error) { + return validateArtifact(ctx, artifact, contract, v.validateJSONSchema) +} + +func (v *FSValidator) Validate(ctx context.Context, artifact *domain.Artifact, contract domain.OutputContract) (domain.ValidationResult, error) { + return validateArtifact(ctx, artifact, contract, v.validateJSONSchema) +} + +type schemaValidatorFunc func(instance any, schemaPath string) ([]string, error) + +func validateArtifact(ctx context.Context, artifact *domain.Artifact, contract domain.OutputContract, validateSchema schemaValidatorFunc) (domain.ValidationResult, error) { select { case <-ctx.Done(): return domain.ValidationResult{}, ctx.Err() @@ -85,21 +95,14 @@ func (v *StandardValidator) Validate(ctx context.Context, artifact *domain.Artif return res, nil } - schemaPath, err := v.resolveSchemaPath(contract.SchemaPath) + validationErrors, err := validateSchema(instance, contract.SchemaPath) if err != nil { return domain.ValidationResult{}, err } - - compiler := jsonschema.NewCompiler() - schema, err := compiler.Compile(schemaPath) - if err != nil { - return domain.ValidationResult{}, fmt.Errorf("failed to compile JSON schema %q: %w", schemaPath, err) - } - - if err := schema.Validate(instance); err != nil { + if len(validationErrors) > 0 { res.Status = domain.ValidationFailed res.IsValid = false - res.Errors = []string{fmt.Sprintf("json schema validation failed: %v", err)} + res.Errors = validationErrors return res, nil } @@ -111,86 +114,44 @@ func (v *StandardValidator) Validate(ctx context.Context, artifact *domain.Artif } } -func (v *FSValidator) Validate(ctx context.Context, artifact *domain.Artifact, contract domain.OutputContract) (domain.ValidationResult, error) { - select { - case <-ctx.Done(): - return domain.ValidationResult{}, ctx.Err() - default: +func (v *StandardValidator) validateJSONSchema(instance any, schemaPath string) ([]string, error) { + resolvedSchemaPath, err := v.resolveSchemaPath(schemaPath) + if err != nil { + return nil, err } - res := domain.ValidationResult{ - Mode: contract.ValidationMode, - SchemaPath: contract.SchemaPath, - RepairAttempts: contract.RepairAttempts, + compiler := jsonschema.NewCompiler() + schema, err := compiler.Compile(resolvedSchemaPath) + if err != nil { + return nil, fmt.Errorf("failed to compile JSON schema %q: %w", resolvedSchemaPath, err) } - if artifact == nil { - return domain.ValidationResult{}, errors.New("artifact is required for validation") + if err := schema.Validate(instance); err != nil { + return []string{fmt.Sprintf("json schema validation failed: %v", err)}, nil + } + return nil, nil +} + +func (v *FSValidator) validateJSONSchema(instance any, schemaPath string) ([]string, error) { + schemaName, schemaDoc, err := v.loadSchemaDocument(schemaPath) + if err != nil { + return nil, err } - switch contract.ValidationMode { - case domain.ValidationNone: - res.Status = domain.ValidationSkipped - res.IsValid = true - return res, nil - case domain.ValidationBasic: - if strings.TrimSpace(string(artifact.Body)) == "" { - res.Status = domain.ValidationFailed - res.IsValid = false - res.Errors = []string{"output is empty"} - return res, nil - } - res.Status = domain.ValidationPassed - res.IsValid = true - return res, nil - case domain.ValidationJSON: - _, jsonErr := parseJSON(artifact.Body) - if jsonErr != nil { - res.Status = domain.ValidationFailed - res.IsValid = false - res.Errors = []string{fmt.Sprintf("invalid JSON: %v", jsonErr)} - return res, nil - } - res.Status = domain.ValidationPassed - res.IsValid = true - return res, nil - case domain.ValidationJSONSchema: - instance, jsonErr := parseJSON(artifact.Body) - if jsonErr != nil { - res.Status = domain.ValidationFailed - res.IsValid = false - res.Errors = []string{fmt.Sprintf("invalid JSON: %v", jsonErr)} - return res, nil - } - - schemaName, schemaDoc, err := v.loadSchemaDocument(contract.SchemaPath) - if err != nil { - return domain.ValidationResult{}, err - } - - resourceURL := fsSchemaResourceURL(schemaName) - compiler := jsonschema.NewCompiler() - if err := compiler.AddResource(resourceURL, schemaDoc); err != nil { - return domain.ValidationResult{}, fmt.Errorf("failed to register JSON schema %q: %w", schemaName, err) - } - schema, err := compiler.Compile(resourceURL) - if err != nil { - return domain.ValidationResult{}, fmt.Errorf("failed to compile JSON schema %q: %w", schemaName, err) - } - - if err := schema.Validate(instance); err != nil { - res.Status = domain.ValidationFailed - res.IsValid = false - res.Errors = []string{fmt.Sprintf("json schema validation failed: %v", err)} - return res, nil - } - - res.Status = domain.ValidationPassed - res.IsValid = true - return res, nil - default: - return domain.ValidationResult{}, fmt.Errorf("unsupported validation mode: %q", contract.ValidationMode) + resourceURL := fsSchemaResourceURL(schemaName) + compiler := jsonschema.NewCompiler() + if err := compiler.AddResource(resourceURL, schemaDoc); err != nil { + return nil, fmt.Errorf("failed to register JSON schema %q: %w", schemaName, err) } + schema, err := compiler.Compile(resourceURL) + if err != nil { + return nil, fmt.Errorf("failed to compile JSON schema %q: %w", schemaName, err) + } + + if err := schema.Validate(instance); err != nil { + return []string{fmt.Sprintf("json schema validation failed: %v", err)}, nil + } + return nil, nil } func parseJSON(body []byte) (any, error) {