-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidation.go
More file actions
138 lines (116 loc) · 3.43 KB
/
validation.go
File metadata and controls
138 lines (116 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package gorkflow
import (
"encoding/json"
"fmt"
"reflect"
"github.com/go-playground/validator/v10"
)
// validationConfig holds internal validation settings
type validationConfig struct {
validateInput bool
validateOutput bool
validator *validator.Validate
}
// defaultValidator is the shared validator instance used by all steps
var defaultValidator = validator.New()
// defaultValidationConfig is used when validation is enabled
var defaultValidationConfig = &validationConfig{
validateInput: true,
validateOutput: true,
validator: defaultValidator,
}
// validateStruct validates a struct using the validator
func (vc *validationConfig) validateStruct(v interface{}) error {
if vc == nil || vc.validator == nil {
return nil
}
// Only validate structs
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return nil
}
if err := vc.validator.Struct(v); err != nil {
if validationErrors, ok := err.(validator.ValidationErrors); ok {
return newValidationError(validationErrors)
}
return err
}
return nil
}
// validateInputData unmarshals and validates input data
func validateInputData[T any](data []byte, config *validationConfig) (T, error) {
var input T
// Unmarshal
if err := json.Unmarshal(data, &input); err != nil {
return input, fmt.Errorf("failed to unmarshal input: %w", err)
}
// Validate if enabled
if config != nil && config.validateInput {
if err := config.validateStruct(input); err != nil {
return input, fmt.Errorf("input validation failed: %w", err)
}
}
return input, nil
}
// validateOutputData validates and marshals output data
func validateOutputData[T any](output T, config *validationConfig) ([]byte, error) {
// Validate if enabled
if config != nil && config.validateOutput {
if err := config.validateStruct(output); err != nil {
return nil, fmt.Errorf("output validation failed: %w", err)
}
}
// Marshal
outputBytes, err := json.Marshal(output)
if err != nil {
return nil, fmt.Errorf("failed to marshal output: %w", err)
}
return outputBytes, nil
}
// validationError wraps validator.ValidationErrors with formatted output
type validationError struct {
errors validator.ValidationErrors
}
func newValidationError(errs validator.ValidationErrors) *validationError {
return &validationError{errors: errs}
}
func (e *validationError) Error() string {
if len(e.errors) == 0 {
return "validation failed"
}
// Format validation errors in a readable way
msg := "validation failed:\n"
for _, err := range e.errors {
msg += fmt.Sprintf(" - field '%s' failed on '%s' tag", err.Field(), err.Tag())
if err.Param() != "" {
msg += fmt.Sprintf(" (param: %s)", err.Param())
}
msg += fmt.Sprintf(": got value '%v'\n", err.Value())
}
return msg
}
func (e *validationError) Unwrap() error {
return e.errors
}
// WithCustomValidator sets a custom validator instance for a step
func WithCustomValidator(v *validator.Validate) StepOption {
return stepOptionFunc(func(s interface{}) {
if step, ok := s.(interface{ SetCustomValidator(*validator.Validate) }); ok {
step.SetCustomValidator(v)
}
})
}
// WithoutValidation disables validation for a step (validation is enabled by default)
func WithoutValidation() StepOption {
return stepOptionFunc(func(s interface{}) {
type validationDisabler interface {
DisableValidation()
}
if step, ok := s.(validationDisabler); ok {
step.DisableValidation()
}
})
}