2016-02-09 05:23:11 +01:00
|
|
|
package corehandlers
|
|
|
|
|
2016-07-31 19:39:16 +02:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/request"
|
|
|
|
)
|
2016-02-09 05:23:11 +01:00
|
|
|
|
|
|
|
// ValidateParametersHandler is a request handler to validate the input parameters.
|
|
|
|
// Validating parameters only has meaning if done prior to the request being sent.
|
|
|
|
var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) {
|
2016-07-31 19:39:16 +02:00
|
|
|
if r.ParamsFilled() {
|
|
|
|
v := validator{errors: []string{}}
|
|
|
|
v.validateAny(reflect.ValueOf(r.Params), "")
|
|
|
|
|
|
|
|
if count := len(v.errors); count > 0 {
|
|
|
|
format := "%d validation errors:\n- %s"
|
|
|
|
msg := fmt.Sprintf(format, count, strings.Join(v.errors, "\n- "))
|
|
|
|
r.Error = awserr.New("InvalidParameter", msg, nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
|
|
|
|
// A validator validates values. Collects validations errors which occurs.
|
|
|
|
type validator struct {
|
|
|
|
errors []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// There's no validation to be done on the contents of []byte values. Prepare
|
|
|
|
// to check validateAny arguments against that type so we can quickly skip
|
|
|
|
// them.
|
|
|
|
var byteSliceType = reflect.TypeOf([]byte(nil))
|
|
|
|
|
|
|
|
// validateAny will validate any struct, slice or map type. All validations
|
|
|
|
// are also performed recursively for nested types.
|
|
|
|
func (v *validator) validateAny(value reflect.Value, path string) {
|
|
|
|
value = reflect.Indirect(value)
|
|
|
|
if !value.IsValid() {
|
2016-02-09 05:23:11 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-07-31 19:39:16 +02:00
|
|
|
switch value.Kind() {
|
|
|
|
case reflect.Struct:
|
|
|
|
v.validateStruct(value, path)
|
|
|
|
case reflect.Slice:
|
|
|
|
if value.Type() == byteSliceType {
|
|
|
|
// We don't need to validate the contents of []byte.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for i := 0; i < value.Len(); i++ {
|
|
|
|
v.validateAny(value.Index(i), path+fmt.Sprintf("[%d]", i))
|
|
|
|
}
|
|
|
|
case reflect.Map:
|
|
|
|
for _, n := range value.MapKeys() {
|
|
|
|
v.validateAny(value.MapIndex(n), path+fmt.Sprintf("[%q]", n.String()))
|
2016-02-09 05:23:11 +01:00
|
|
|
}
|
|
|
|
}
|
2016-07-31 19:39:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// validateStruct will validate the struct value's fields. If the structure has
|
|
|
|
// nested types those types will be validated also.
|
|
|
|
func (v *validator) validateStruct(value reflect.Value, path string) {
|
|
|
|
prefix := "."
|
|
|
|
if path == "" {
|
|
|
|
prefix = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < value.Type().NumField(); i++ {
|
|
|
|
f := value.Type().Field(i)
|
|
|
|
if strings.ToLower(f.Name[0:1]) == f.Name[0:1] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fvalue := value.FieldByName(f.Name)
|
|
|
|
|
|
|
|
err := validateField(f, fvalue, validateFieldRequired, validateFieldMin)
|
|
|
|
if err != nil {
|
|
|
|
v.errors = append(v.errors, fmt.Sprintf("%s: %s", err.Error(), path+prefix+f.Name))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
v.validateAny(fvalue, path+prefix+f.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type validatorFunc func(f reflect.StructField, fvalue reflect.Value) error
|
|
|
|
|
|
|
|
func validateField(f reflect.StructField, fvalue reflect.Value, funcs ...validatorFunc) error {
|
|
|
|
for _, fn := range funcs {
|
|
|
|
if err := fn(f, fvalue); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validates that a field has a valid value provided for required fields.
|
|
|
|
func validateFieldRequired(f reflect.StructField, fvalue reflect.Value) error {
|
|
|
|
if f.Tag.Get("required") == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch fvalue.Kind() {
|
|
|
|
case reflect.Ptr, reflect.Slice, reflect.Map:
|
|
|
|
if fvalue.IsNil() {
|
|
|
|
return fmt.Errorf("missing required parameter")
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if !fvalue.IsValid() {
|
|
|
|
return fmt.Errorf("missing required parameter")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validates that if a value is provided for a field, that value must be at
|
|
|
|
// least a minimum length.
|
|
|
|
func validateFieldMin(f reflect.StructField, fvalue reflect.Value) error {
|
|
|
|
minStr := f.Tag.Get("min")
|
|
|
|
if minStr == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
min, _ := strconv.ParseInt(minStr, 10, 64)
|
|
|
|
|
|
|
|
kind := fvalue.Kind()
|
|
|
|
if kind == reflect.Ptr {
|
|
|
|
if fvalue.IsNil() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
fvalue = fvalue.Elem()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch fvalue.Kind() {
|
|
|
|
case reflect.String:
|
|
|
|
if int64(fvalue.Len()) < min {
|
|
|
|
return fmt.Errorf("field too short, minimum length %d", min)
|
|
|
|
}
|
|
|
|
case reflect.Slice, reflect.Map:
|
|
|
|
if fvalue.IsNil() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if int64(fvalue.Len()) < min {
|
|
|
|
return fmt.Errorf("field too short, minimum length %d", min)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO min can also apply to number minimum value.
|
|
|
|
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|