package aws import ( "fmt" "reflect" "strings" "github.com/ks3sdklib/aws-sdk-go/internal/apierr" ) // ValidateParameters is a request handler to validate the input parameters. // Validating parameters only has meaning if done prior to the request being sent. func ValidateParameters(r *Request) { 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 = apierr.New("InvalidParameter", msg, nil) } } } // A validator validates values. Collects validations errors which occurs. type validator struct { errors []string } // 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() { return } switch value.Kind() { case reflect.Struct: v.validateStruct(value, path) case reflect.Slice: 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())) } } } // 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) notset := false notval := false if f.Tag.Get("required") != "" { switch fvalue.Kind() { case reflect.Ptr: if fvalue.IsNil() { notset = true } else { elem := fvalue.Elem() if elem.Kind() == reflect.String && elem.Len() == 0 { notval = true } } case reflect.Slice, reflect.Map: if fvalue.IsNil() { notset = true } default: if !fvalue.IsValid() { notset = true } } } if notset { msg := "missing required parameter: " + path + prefix + f.Name v.errors = append(v.errors, msg) } else if notval { msg := "input member " + path + prefix + f.Name + " must not be empty" v.errors = append(v.errors, msg) } else { v.validateAny(fvalue, path+prefix+f.Name) } } }