From de318249e28f512f102fdfbe6887be2a9a7c7abe Mon Sep 17 00:00:00 2001 From: Michael Schuett Date: Tue, 15 Jul 2014 23:11:54 -0500 Subject: [PATCH 1/4] Support for min, max, regex, digit, digit_between, email, required, in. --- binding/binding.go | 55 ----- binding/validate.go | 280 +++++++++++++++++++++++++ binding/validate_test.go | 440 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 720 insertions(+), 55 deletions(-) create mode 100644 binding/validate.go create mode 100644 binding/validate_test.go diff --git a/binding/binding.go b/binding/binding.go index bb6cbde2..46383fc6 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -3,11 +3,9 @@ package binding import ( "encoding/json" "encoding/xml" - "errors" "net/http" "reflect" "strconv" - "strings" ) type ( @@ -150,56 +148,3 @@ func ensureNotPointer(obj interface{}) { panic("Pointers are not accepted as binding models") } } - -func Validate(obj interface{}) error { - typ := reflect.TypeOf(obj) - val := reflect.ValueOf(obj) - - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } - - switch typ.Kind() { - case reflect.Struct: - for i := 0; i < typ.NumField(); i++ { - field := typ.Field(i) - - // Allow ignored fields in the struct - if field.Tag.Get("form") == "-" { - continue - } - - fieldValue := val.Field(i).Interface() - zero := reflect.Zero(field.Type).Interface() - - if strings.Index(field.Tag.Get("binding"), "required") > -1 { - fieldType := field.Type.Kind() - if fieldType == reflect.Struct { - err := Validate(fieldValue) - if err != nil { - return err - } - } else if reflect.DeepEqual(zero, fieldValue) { - return errors.New("Required " + field.Name) - } else if fieldType == reflect.Slice && field.Type.Elem().Kind() == reflect.Struct { - err := Validate(fieldValue) - if err != nil { - return err - } - } - } - } - case reflect.Slice: - for i := 0; i < val.Len(); i++ { - fieldValue := val.Index(i).Interface() - err := Validate(fieldValue) - if err != nil { - return err - } - } - default: - return nil - } - return nil -} diff --git a/binding/validate.go b/binding/validate.go new file mode 100644 index 00000000..5afbeb2f --- /dev/null +++ b/binding/validate.go @@ -0,0 +1,280 @@ +package binding + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "strconv" + "strings" +) + +func Validate(obj interface{}) error { + + typ := reflect.TypeOf(obj) + value := reflect.ValueOf(obj) + + // Check to ensure we are getting a valid + // pointer for manipulation. + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + value = value.Elem() + } + + for i := 0; i < typ.NumField(); i++ { + + field := typ.Field(i) + fieldValue := value.Field(i).Interface() + zero := reflect.Zero(field.Type).Interface() + + // Validate nested and embedded structs (if pointer, only do so if not nil) + if field.Type.Kind() == reflect.Struct || + (field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) { + if err := Validate(fieldValue); err != nil { + return err + } + } + + if field.Tag.Get("validate") != "" || field.Tag.Get("binding") != "" { + // Break validate field into array + array := strings.Split(field.Tag.Get("validate"), "|") + + // Legacy Support for binding. + if array[0] == "" { + array = strings.Split(field.Tag.Get("binding"), "|") + } + + // Do the hard work of checking all assertions + for setting := range array { + + match := array[setting] + + switch { + case "required" == match: + if err := required(field, fieldValue, zero); err != nil { + return err + } + case "email" == match: + fmt.Println("email...") + if !reflect.DeepEqual(zero, fieldValue) { + if err := email(fieldValue); err != nil { + return err + } + } + case strings.Contains(match, "digit:"): + if !reflect.DeepEqual(zero, fieldValue) { + if err := digit(match, fieldValue); err != nil { + return err + } + } + case strings.Contains(match, "digits_between:"): + if !reflect.DeepEqual(zero, fieldValue) { + if err := digits_between(match, fieldValue); err != nil { + return err + } + } + case strings.Contains(match, "min:"): + if !reflect.DeepEqual(zero, fieldValue) { + if err := min(match, fieldValue); err != nil { + return err + } + } + case strings.Contains(match, "max:"): + if !reflect.DeepEqual(zero, fieldValue) { + if err := max(match, fieldValue); err != nil { + return err + } + } + case strings.Contains(match, "in:"): + if !reflect.DeepEqual(zero, fieldValue) { + if err := in(match, fieldValue); err != nil { + return err + } + } + case strings.Contains(match, "regex:"): + if !reflect.DeepEqual(zero, fieldValue) { + if err := regex(match, fieldValue); err != nil { + return err + } + } + default: + // Temp logging to check for errors + errors.New(array[setting] + " is not a valid validation type.") + } + } + } + } + + return nil +} + +// Check that the following function features +// the required field. May need to check for +// more special cases like since passing in null +// is the same as 0 for int type checking. +func required(field reflect.StructField, value, zero interface{}) error { + + if reflect.DeepEqual(zero, value) { + if _, ok := value.(int); !ok { + return errors.New("The required field " + field.Name + " was not submitted.") + } + } + + return nil +} + +// Check that the passed in field is a valid email +func email(value interface{}) error { + // Email Regex Checker + var emailRegex string = `^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$` + + if data, ok := value.(string); ok { + if match, _ := regexp.Match(emailRegex, []byte(data)); match { + return nil + } else { + return errors.New("A valid email address was not entered.") + } + } else { + return errors.New("Email was not able to convert the passed in data to a []byte.") + } +} + +// Check that the passed in field is a valid email +// Need to improve error logging for this method +// Currently only supports strings, ints +func in(field string, value interface{}) error { + + if data, ok := value.(string); ok { + + valid := strings.Split(field[3:], ",") + + for option := range valid { + if valid[option] == data { + return nil + } + } + + return errors.New("In did not match any of the expected values.") + + } else if data, ok := value.(int); ok { + // This will run with passed in data is an int + valid := strings.Split(field[3:], ",") + + for option := range valid { + // Check for convertion to valid int + if valint, err := strconv.ParseInt(valid[option], 0, 64); err == nil { + if valint == int64(data) { + return nil + } + } + } + + return errors.New("In did not match any of the expected values.") + + } else { + return errors.New("in, was not able to convert the data passed in to a string.") + } + +} + +// Check that the passed in field is exactly X digits +func digit(field string, value interface{}) error { + + if data, ok := value.(int); ok { + // Unpack number of digits it should be. + digit := field[6:] + + if digits, check := strconv.ParseInt(digit, 0, 64); check == nil { + + if int64(len(strconv.FormatInt(int64(data), 10))) == digits { + return nil + } else { + return errors.New("The data you passed in was not the right number of digits.") + } + + } else { + return errors.New("Digit must check for a number.") + } + } + + return errors.New("The number passed into digit was not an int.") +} + +func digits_between(field string, value interface{}) error { + + if data, ok := value.(int); ok { + + digit := strings.Split(field[15:], ",") + + if digitSmall, ok := strconv.ParseInt(digit[0], 0, 64); ok == nil { + + if digitLarge, okk := strconv.ParseInt(digit[1], 0, 64); okk == nil { + + num := int64(len(strconv.FormatInt(int64(data), 10))) + + if num >= digitSmall && num <= digitLarge { + return nil + } else { + return errors.New("The data you passed in was not the right number of digits.") + } + } + } + } + + return errors.New("The value passed into digits_between could not be converted to an int.") +} + +func min(field string, value interface{}) error { + + if data, ok := value.(int); ok { + + min := field[4:] + + if minNum, ok := strconv.ParseInt(min, 0, 64); ok == nil { + + if int64(data) >= minNum { + return nil + } else { + return errors.New("The data you passed in was smaller then the allowed minimum.") + } + + } + } + + return errors.New("The value passed into min could not be converted to an int.") +} + +func max(field string, value interface{}) error { + + if data, ok := value.(int); ok { + + max := field[4:] + + if maxNum, ok := strconv.ParseInt(max, 0, 64); ok == nil { + if int64(data) <= maxNum { + return nil + } else { + return errors.New("The data you passed in was larger than the maximum.") + } + + } + } + + return errors.New("The value passed into max could not be converted to an int.") +} + +func regex(field string, value interface{}) error { + // Email Regex Checker + + reg := field[6:] + + if data, ok := value.(string); ok { + if match, err := regexp.Match(reg, []byte(data)); err == nil && match { + return nil + } else { + return errors.New("Your regex did not match or was not valid.") + } + } else { + return errors.New("Regex was not able to convert the passed in data to a string.") + } +} diff --git a/binding/validate_test.go b/binding/validate_test.go new file mode 100644 index 00000000..4812569f --- /dev/null +++ b/binding/validate_test.go @@ -0,0 +1,440 @@ +package binding + +import ( + "io" + "io/ioutil" + "net/http" + "strings" + "testing" +) + +// Make string into io.ReadCloser +// this is just for convinience +func jsonFactory(s string) io.ReadCloser { + return ioutil.NopCloser(strings.NewReader(s)) +} + +// Ensure all required fields are matching +func TestRequired(t *testing.T) { + + // // Test if STRING required is valid + var testString struct { + Test string `json:"something" validate:"required" ` + } + + testJSON := jsonFactory(`{"something": "hello"}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testString); err != nil { + t.Error(err) + } + + var testString2 struct { + Test string `json:"something" validate:"required" ` + } + + testJSON = jsonFactory(`{}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testString2); err == nil { + t.Error("Required string, empty JSON object should return error but did not.") + } + + // Test if INT require is valid + var testInt struct { + Test int `json:"something" validate:"required" ` + } + + testJSON = jsonFactory(`{"something": 2}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testInt); err != nil { + t.Error(err) + } + + // Test if BOOL required is valid + var testBool struct { + Test bool `json:"something" validate:"required" ` + } + + testJSON = jsonFactory(`{"something": true}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testBool); err != nil { + t.Error(err) + } + + var testBool2 struct { + Test string `json:"something" validate:"required" ` + } + + testJSON = jsonFactory(`{}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testBool2); err == nil { + t.Error("Required bool, empty JSON object should return error but did not.") + } + + // Test if ARRAY required is valid + var testArray struct { + Test []string `json:"something" validate:"required" ` + } + + testJSON = jsonFactory(`{"something": ["test", "data"]}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testArray); err != nil { + t.Error(err) + } + + // Test is OBJECT required is valid + type testObjectTP struct { + Name string `json:"name" validate:"required" ` + } + + var testObject struct { + Test testObjectTP `json:"something" validate:"required" ` + } + + testJSON = jsonFactory(`{"something": {"name": "test"}}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testObject); err != nil { + t.Error(err) + } + + type testObjectTP2 struct { + Name string `json:"name" validate:"required" ` + } +} + +func TestEmail(t *testing.T) { + + var testValEmail struct { + Test string `json:"email" validate:"email" ` + } + + testJSON := jsonFactory(`{"email": "michaeljs@gmail.com"}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValEmail); err != nil { + t.Error(err) + } + + var testValEmail2 struct { + Test string `json:"email" validate:"email" ` + } + + testJSON = jsonFactory(`{"email": "michaeljs@gail.edu"}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValEmail2); err != nil { + t.Error(err) + } + + var testValEmail3 struct { + Test string `json:"email" validate:"email" ` + } + + testJSON = jsonFactory(`{"email": "michaeljs.edu"}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValEmail3); err == nil { + t.Error("Email test failed, michaeljs.edu passed as a valid email.") + } + + // This should not return an error since email is not required. + var testValEmail4 struct { + Test string `json:"email" validate:"email" ` + } + + testJSON = jsonFactory(`{"jeff": "really"}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValEmail4); err != nil { + t.Error(err) + } + +} + +// Ensure In is matching properly +// Supporting string and int currently +func TestIn(t *testing.T) { + + var testValIn struct { + Test string `json:"special" validate:"in:admin,user,other" ` + } + + testJSON := jsonFactory(`{"special": "admin"}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValIn); err != nil { + t.Error(err) + } + + var testValIn2 struct { + Test int `json:"special" validate:"in:1,3,2" ` + } + + testJSON = jsonFactory(`{"special": 3}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValIn2); err != nil { + t.Error(err) + } + + var testValIn3 struct { + Test int `json:"special" validate:"in:1,3,2" ` + } + + testJSON = jsonFactory(`{"special": 6}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValIn3); err == nil { + t.Error("6 is not in validate in call, err should not have been nil.") + } + + var testValIn4 struct { + Test2 string `json:"what" validate:in:this,that` + Test int `json:"special" validate:"in:1,3,2" ` + } + + testJSON = jsonFactory(`{"special": 3,"what": "this"}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValIn4); err != nil { + t.Error(err) + } + + var testValIn5 struct { + Test2 string `json:"what" validate:in:this,that` + Test int `json:"special" validate:"in:1,3,2" ` + } + + testJSON = jsonFactory(`{"special": 3}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValIn5); err != nil { + t.Error(err) + } + + var testValIn6 struct { + Test2 string `json:"what" validate:"in:this,that"` + Test3 string `json:"what1" validate:"in:this,then"` + Test4 string `json:"what2" validate:"in:this,that"` + Test5 string `json:"what3" validate:"in:this,that"` + Test int `json:"special" validate:"in:1,3,2"` + } + + testJSON = jsonFactory(`{"sa": 34, "what":"this", "what1":"then", "what2":"this"}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValIn6); err != nil { + t.Error(err) + } +} + +// Check if the entered JSON is a data matching the one in a string. +func TestDigit(t *testing.T) { + + var testValDigit struct { + Test int `json:"digit" validate:"digit:5" ` + } + + testJSON := jsonFactory(`{"digit": 12345}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit); err != nil { + t.Error(err) + } + + var testValDigit2 struct { + Test int `json:"digit" validate:"digit:5" ` + } + + testJSON = jsonFactory(`{"digit": 123456}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit2); err == nil { + t.Error("Error should have been thrown, digits should be 5 but was 6.") + } + + var testValDigit3 struct { + Test int `json:"digit" validate:"digit:12" ` + } + + testJSON = jsonFactory(`{"digit": 111111111111}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit3); err != nil { + t.Error(err) + } + +} + +// Check if the entered JSON is a data matching the one in a string. +func TestDigitBetween(t *testing.T) { + + var testValDigit struct { + Test int `json:"digit" validate:"digits_between:5,7" ` + } + + testJSON := jsonFactory(`{"digit": 123456}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit); err != nil { + t.Error(err) + } + + var testValDigit2 struct { + Test int `json:"digit" validate:"digits_between:0,10" ` + } + + testJSON = jsonFactory(`{"digit": 1234564}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit2); err != nil { + t.Error("Error should have been thrown, digit was not between 0 and 10.") + } + + var testValDigit3 struct { + Test int `json:"digit" validate:"digits_between:0,1" ` + } + + testJSON = jsonFactory(`{"digit": 1234564}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit3); err == nil { + t.Error(err) + } + +} + +// Check if the entered JSON is a data matching the one in a string. +func TestMin(t *testing.T) { + + var testValMin struct { + Test int `json:"digit" validate:"min:23" ` + } + + testJSON := jsonFactory(`{"digit": 24}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValMin); err != nil { + t.Error(err) + } + + var testValMin2 struct { + Test int `json:"digit" validate:"min:20" ` + } + + testJSON = jsonFactory(`{"digit": 19}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValMin2); err == nil { + t.Error("Min was 20 digit of 19 should not have validated properly.") + } + + var testValMin3 struct { + Test int `json:"digit" validate:"min:20" ` + } + + testJSON = jsonFactory(`{"jeff":"greg"}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValMin3); err != nil { + t.Error("Nothing was entered but min was not required. No error should be thrown.") + } +} + +func TestMax(t *testing.T) { + + var testValMin struct { + Test int `json:"digit" validate:"max:23" ` + } + + testJSON := jsonFactory(`{"digit": 23}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValMin); err != nil { + t.Error(err) + } + + var testValMin2 struct { + Test int `json:"digit" validate:"max:20" ` + } + + testJSON = jsonFactory(`{"digit": 21}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValMin2); err == nil { + t.Error("Max was 20 digit of 21 should not have validated properly.") + } + + var testValMin3 struct { + Test int `json:"digit" validate:"max:20" ` + } + + testJSON = jsonFactory(`{"jeff":"greg"}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValMin3); err != nil { + t.Error("Nothing was entered but max was not required. No error should be thrown.") + } +} + +func TestRegex(t *testing.T) { + + var testValDigit struct { + Test int `json:"digit" validate:"regex:\d+" ` + } + + testJSON := jsonFactory(`{"digit": 23}`) + + req, _ := http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit); err != nil { + t.Error(err) + } + + var testValDigit2 struct { + Test int `json:"digit" validate:"regex:\d+" ` + } + + testJSON = jsonFactory(`{"digit": 2dsa3}`) + + req, _ = http.NewRequest("POST", "/", testJSON) + + if err := JSON.Bind(req, &testValDigit2); err == nil { + t.Error("\\d+ regex should not match the string 2dsa3.") + } +} From 038a2efa80244f7a75d4268214f90118265fc67f Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 15 Jul 2014 23:31:31 -0500 Subject: [PATCH 2/4] Build test files on commit. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 98b43464..318aa273 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,6 @@ language: go go: - 1.3 - tip + +script: + - go test github.com/gin-gonic/gin/binding From 797bf049ec7fcad0be34a10b857520346f8bc8b4 Mon Sep 17 00:00:00 2001 From: Michael Schuett Date: Thu, 17 Jul 2014 00:47:24 -0500 Subject: [PATCH 3/4] Clean up, removed some redundant code. Used regex call to check email. Improved testing suite. --- binding/validate.go | 178 ++++++++---------------------- binding/validate_test.go | 232 ++++++++++----------------------------- 2 files changed, 108 insertions(+), 302 deletions(-) diff --git a/binding/validate.go b/binding/validate.go index 5afbeb2f..3346e362 100644 --- a/binding/validate.go +++ b/binding/validate.go @@ -2,7 +2,6 @@ package binding import ( "errors" - "fmt" "reflect" "regexp" "strconv" @@ -55,51 +54,27 @@ func Validate(obj interface{}) error { return err } case "email" == match: - fmt.Println("email...") - if !reflect.DeepEqual(zero, fieldValue) { - if err := email(fieldValue); err != nil { - return err - } + if err := regex(`regex:^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$`, fieldValue); err != nil { + return err } - case strings.Contains(match, "digit:"): - if !reflect.DeepEqual(zero, fieldValue) { - if err := digit(match, fieldValue); err != nil { - return err - } + case strings.HasPrefix(match, "min:"): + if err := min(match, fieldValue); err != nil { + return err } - case strings.Contains(match, "digits_between:"): - if !reflect.DeepEqual(zero, fieldValue) { - if err := digits_between(match, fieldValue); err != nil { - return err - } + case strings.HasPrefix(match, "max:"): + if err := max(match, fieldValue); err != nil { + return err } - case strings.Contains(match, "min:"): - if !reflect.DeepEqual(zero, fieldValue) { - if err := min(match, fieldValue); err != nil { - return err - } + case strings.HasPrefix(match, "in:"): + if err := in(match, fieldValue); err != nil { + return err } - case strings.Contains(match, "max:"): - if !reflect.DeepEqual(zero, fieldValue) { - if err := max(match, fieldValue); err != nil { - return err - } - } - case strings.Contains(match, "in:"): - if !reflect.DeepEqual(zero, fieldValue) { - if err := in(match, fieldValue); err != nil { - return err - } - } - case strings.Contains(match, "regex:"): - if !reflect.DeepEqual(zero, fieldValue) { - if err := regex(match, fieldValue); err != nil { - return err - } + case strings.HasPrefix(match, "regex:"): + if err := regex(match, fieldValue); err != nil { + return err } default: - // Temp logging to check for errors - errors.New(array[setting] + " is not a valid validation type.") + panic("The field " + match + " is not a valid validation check.") } } } @@ -123,28 +98,15 @@ func required(field reflect.StructField, value, zero interface{}) error { return nil } -// Check that the passed in field is a valid email -func email(value interface{}) error { - // Email Regex Checker - var emailRegex string = `^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$` - - if data, ok := value.(string); ok { - if match, _ := regexp.Match(emailRegex, []byte(data)); match { - return nil - } else { - return errors.New("A valid email address was not entered.") - } - } else { - return errors.New("Email was not able to convert the passed in data to a []byte.") - } -} - // Check that the passed in field is a valid email // Need to improve error logging for this method // Currently only supports strings, ints func in(field string, value interface{}) error { if data, ok := value.(string); ok { + if len(data) == 0 { + return nil + } valid := strings.Split(field[3:], ",") @@ -154,81 +116,18 @@ func in(field string, value interface{}) error { } } - return errors.New("In did not match any of the expected values.") - - } else if data, ok := value.(int); ok { - // This will run with passed in data is an int - valid := strings.Split(field[3:], ",") - - for option := range valid { - // Check for convertion to valid int - if valint, err := strconv.ParseInt(valid[option], 0, 64); err == nil { - if valint == int64(data) { - return nil - } - } - } - - return errors.New("In did not match any of the expected values.") - } else { - return errors.New("in, was not able to convert the data passed in to a string.") + return errors.New("The value passed in for IN could not be converted to a string.") } -} - -// Check that the passed in field is exactly X digits -func digit(field string, value interface{}) error { - - if data, ok := value.(int); ok { - // Unpack number of digits it should be. - digit := field[6:] - - if digits, check := strconv.ParseInt(digit, 0, 64); check == nil { - - if int64(len(strconv.FormatInt(int64(data), 10))) == digits { - return nil - } else { - return errors.New("The data you passed in was not the right number of digits.") - } - - } else { - return errors.New("Digit must check for a number.") - } - } - - return errors.New("The number passed into digit was not an int.") -} - -func digits_between(field string, value interface{}) error { - - if data, ok := value.(int); ok { - - digit := strings.Split(field[15:], ",") - - if digitSmall, ok := strconv.ParseInt(digit[0], 0, 64); ok == nil { - - if digitLarge, okk := strconv.ParseInt(digit[1], 0, 64); okk == nil { - - num := int64(len(strconv.FormatInt(int64(data), 10))) - - if num >= digitSmall && num <= digitLarge { - return nil - } else { - return errors.New("The data you passed in was not the right number of digits.") - } - } - } - } - - return errors.New("The value passed into digits_between could not be converted to an int.") + return errors.New("In did not match any of the expected values.") } func min(field string, value interface{}) error { if data, ok := value.(int); ok { - min := field[4:] + min := field[strings.Index(field, ":")+1:] if minNum, ok := strconv.ParseInt(min, 0, 64); ok == nil { @@ -241,14 +140,14 @@ func min(field string, value interface{}) error { } } - return errors.New("The value passed into min could not be converted to an int.") + return errors.New("The value passed in for MIN could not be converted to an int.") } func max(field string, value interface{}) error { if data, ok := value.(int); ok { - max := field[4:] + max := field[strings.Index(field, ":")+1:] if maxNum, ok := strconv.ParseInt(max, 0, 64); ok == nil { if int64(data) <= maxNum { @@ -260,21 +159,38 @@ func max(field string, value interface{}) error { } } - return errors.New("The value passed into max could not be converted to an int.") + return errors.New("The value passed in for MAX could not be converted to an int.") } +// Regex handles the general regex call and also handles +// the regex email. func regex(field string, value interface{}) error { - // Email Regex Checker - reg := field[6:] + reg := field[strings.Index(field, ":")+1:] if data, ok := value.(string); ok { - if match, err := regexp.Match(reg, []byte(data)); err == nil && match { + if len(data) == 0 { return nil - } else { - return errors.New("Your regex did not match or was not valid.") + } else if err := match_regex(reg, []byte(data)); err != nil { + return err + } + } else if data, ok := value.(int); ok { + if err := match_regex(reg, []byte(strconv.Itoa(data))); err != nil { + return err } } else { - return errors.New("Regex was not able to convert the passed in data to a string.") + return errors.New("The value passed in for REGEX could not be converted to a string or int.") + } + + return nil +} + +// Helper function for regex. +func match_regex(reg string, data []byte) error { + + if match, err := regexp.Match(reg, []byte(data)); err == nil && match { + return nil + } else { + return errors.New("Your regex did not match or was not valid.") } } diff --git a/binding/validate_test.go b/binding/validate_test.go index 4812569f..2db301c5 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -1,6 +1,7 @@ package binding import ( + "fmt" "io" "io/ioutil" "net/http" @@ -22,9 +23,7 @@ func TestRequired(t *testing.T) { Test string `json:"something" validate:"required" ` } - testJSON := jsonFactory(`{"something": "hello"}`) - - req, _ := http.NewRequest("POST", "/", testJSON) + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"something": "hello"}`)) if err := JSON.Bind(req, &testString); err != nil { t.Error(err) @@ -34,9 +33,7 @@ func TestRequired(t *testing.T) { Test string `json:"something" validate:"required" ` } - testJSON = jsonFactory(`{}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{}`)) if err := JSON.Bind(req, &testString2); err == nil { t.Error("Required string, empty JSON object should return error but did not.") @@ -47,9 +44,7 @@ func TestRequired(t *testing.T) { Test int `json:"something" validate:"required" ` } - testJSON = jsonFactory(`{"something": 2}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"something": 2}`)) if err := JSON.Bind(req, &testInt); err != nil { t.Error(err) @@ -60,9 +55,7 @@ func TestRequired(t *testing.T) { Test bool `json:"something" validate:"required" ` } - testJSON = jsonFactory(`{"something": true}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"something": true}`)) if err := JSON.Bind(req, &testBool); err != nil { t.Error(err) @@ -72,9 +65,7 @@ func TestRequired(t *testing.T) { Test string `json:"something" validate:"required" ` } - testJSON = jsonFactory(`{}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{}`)) if err := JSON.Bind(req, &testBool2); err == nil { t.Error("Required bool, empty JSON object should return error but did not.") @@ -85,9 +76,7 @@ func TestRequired(t *testing.T) { Test []string `json:"something" validate:"required" ` } - testJSON = jsonFactory(`{"something": ["test", "data"]}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"something": ["test", "data"]}`)) if err := JSON.Bind(req, &testArray); err != nil { t.Error(err) @@ -102,17 +91,11 @@ func TestRequired(t *testing.T) { Test testObjectTP `json:"something" validate:"required" ` } - testJSON = jsonFactory(`{"something": {"name": "test"}}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"something": {"name": "test"}}`)) if err := JSON.Bind(req, &testObject); err != nil { t.Error(err) } - - type testObjectTP2 struct { - Name string `json:"name" validate:"required" ` - } } func TestEmail(t *testing.T) { @@ -121,9 +104,7 @@ func TestEmail(t *testing.T) { Test string `json:"email" validate:"email" ` } - testJSON := jsonFactory(`{"email": "michaeljs@gmail.com"}`) - - req, _ := http.NewRequest("POST", "/", testJSON) + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"email": "michaeljs@gmail.com"}`)) if err := JSON.Bind(req, &testValEmail); err != nil { t.Error(err) @@ -133,9 +114,7 @@ func TestEmail(t *testing.T) { Test string `json:"email" validate:"email" ` } - testJSON = jsonFactory(`{"email": "michaeljs@gail.edu"}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"email": "michaeljs@gail.edu"}`)) if err := JSON.Bind(req, &testValEmail2); err != nil { t.Error(err) @@ -145,9 +124,7 @@ func TestEmail(t *testing.T) { Test string `json:"email" validate:"email" ` } - testJSON = jsonFactory(`{"email": "michaeljs.edu"}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"email": "michaeljs.edu"}`)) if err := JSON.Bind(req, &testValEmail3); err == nil { t.Error("Email test failed, michaeljs.edu passed as a valid email.") @@ -158,9 +135,7 @@ func TestEmail(t *testing.T) { Test string `json:"email" validate:"email" ` } - testJSON = jsonFactory(`{"jeff": "really"}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"jeff": "really"}`)) if err := JSON.Bind(req, &testValEmail4); err != nil { t.Error(err) @@ -176,21 +151,17 @@ func TestIn(t *testing.T) { Test string `json:"special" validate:"in:admin,user,other" ` } - testJSON := jsonFactory(`{"special": "admin"}`) - - req, _ := http.NewRequest("POST", "/", testJSON) + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"special": "admin"}`)) if err := JSON.Bind(req, &testValIn); err != nil { t.Error(err) } var testValIn2 struct { - Test int `json:"special" validate:"in:1,3,2" ` + Test string `json:"special" validate:"in:1,3,2" ` } - testJSON = jsonFactory(`{"special": 3}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": "3"}`)) if err := JSON.Bind(req, &testValIn2); err != nil { t.Error(err) @@ -200,9 +171,7 @@ func TestIn(t *testing.T) { Test int `json:"special" validate:"in:1,3,2" ` } - testJSON = jsonFactory(`{"special": 6}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": 6}`)) if err := JSON.Bind(req, &testValIn3); err == nil { t.Error("6 is not in validate in call, err should not have been nil.") @@ -210,12 +179,10 @@ func TestIn(t *testing.T) { var testValIn4 struct { Test2 string `json:"what" validate:in:this,that` - Test int `json:"special" validate:"in:1,3,2" ` + Test string `json:"special" validate:"in:1,3,2" ` } - testJSON = jsonFactory(`{"special": 3,"what": "this"}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": "3","what": "this"}`)) if err := JSON.Bind(req, &testValIn4); err != nil { t.Error(err) @@ -223,12 +190,10 @@ func TestIn(t *testing.T) { var testValIn5 struct { Test2 string `json:"what" validate:in:this,that` - Test int `json:"special" validate:"in:1,3,2" ` + Test string `json:"special" validate:"in:1,3,2" ` } - testJSON = jsonFactory(`{"special": 3}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": "3"}`)) if err := JSON.Bind(req, &testValIn5); err != nil { t.Error(err) @@ -239,100 +204,17 @@ func TestIn(t *testing.T) { Test3 string `json:"what1" validate:"in:this,then"` Test4 string `json:"what2" validate:"in:this,that"` Test5 string `json:"what3" validate:"in:this,that"` - Test int `json:"special" validate:"in:1,3,2"` + Test string `json:"special" validate:"in:1,3,2"` } - testJSON = jsonFactory(`{"sa": 34, "what":"this", "what1":"then", "what2":"this"}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"sa": 34, "what":"this", "what1":"then", "what2":"this"}`)) + fmt.Println("error here:") if err := JSON.Bind(req, &testValIn6); err != nil { t.Error(err) } } -// Check if the entered JSON is a data matching the one in a string. -func TestDigit(t *testing.T) { - - var testValDigit struct { - Test int `json:"digit" validate:"digit:5" ` - } - - testJSON := jsonFactory(`{"digit": 12345}`) - - req, _ := http.NewRequest("POST", "/", testJSON) - - if err := JSON.Bind(req, &testValDigit); err != nil { - t.Error(err) - } - - var testValDigit2 struct { - Test int `json:"digit" validate:"digit:5" ` - } - - testJSON = jsonFactory(`{"digit": 123456}`) - - req, _ = http.NewRequest("POST", "/", testJSON) - - if err := JSON.Bind(req, &testValDigit2); err == nil { - t.Error("Error should have been thrown, digits should be 5 but was 6.") - } - - var testValDigit3 struct { - Test int `json:"digit" validate:"digit:12" ` - } - - testJSON = jsonFactory(`{"digit": 111111111111}`) - - req, _ = http.NewRequest("POST", "/", testJSON) - - if err := JSON.Bind(req, &testValDigit3); err != nil { - t.Error(err) - } - -} - -// Check if the entered JSON is a data matching the one in a string. -func TestDigitBetween(t *testing.T) { - - var testValDigit struct { - Test int `json:"digit" validate:"digits_between:5,7" ` - } - - testJSON := jsonFactory(`{"digit": 123456}`) - - req, _ := http.NewRequest("POST", "/", testJSON) - - if err := JSON.Bind(req, &testValDigit); err != nil { - t.Error(err) - } - - var testValDigit2 struct { - Test int `json:"digit" validate:"digits_between:0,10" ` - } - - testJSON = jsonFactory(`{"digit": 1234564}`) - - req, _ = http.NewRequest("POST", "/", testJSON) - - if err := JSON.Bind(req, &testValDigit2); err != nil { - t.Error("Error should have been thrown, digit was not between 0 and 10.") - } - - var testValDigit3 struct { - Test int `json:"digit" validate:"digits_between:0,1" ` - } - - testJSON = jsonFactory(`{"digit": 1234564}`) - - req, _ = http.NewRequest("POST", "/", testJSON) - - if err := JSON.Bind(req, &testValDigit3); err == nil { - t.Error(err) - } - -} - // Check if the entered JSON is a data matching the one in a string. func TestMin(t *testing.T) { @@ -340,9 +222,7 @@ func TestMin(t *testing.T) { Test int `json:"digit" validate:"min:23" ` } - testJSON := jsonFactory(`{"digit": 24}`) - - req, _ := http.NewRequest("POST", "/", testJSON) + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 24}`)) if err := JSON.Bind(req, &testValMin); err != nil { t.Error(err) @@ -352,25 +232,21 @@ func TestMin(t *testing.T) { Test int `json:"digit" validate:"min:20" ` } - testJSON = jsonFactory(`{"digit": 19}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": 19}`)) if err := JSON.Bind(req, &testValMin2); err == nil { t.Error("Min was 20 digit of 19 should not have validated properly.") } - var testValMin3 struct { - Test int `json:"digit" validate:"min:20" ` - } + // var testValMin3 struct { + // Test int `json:"digit" validate:"min:20" ` + // } - testJSON = jsonFactory(`{"jeff":"greg"}`) + // req, _ = http.NewRequest("POST", "/", jsonFactory(`{"jeff":"greg"}`)) - req, _ = http.NewRequest("POST", "/", testJSON) - - if err := JSON.Bind(req, &testValMin3); err != nil { - t.Error("Nothing was entered but min was not required. No error should be thrown.") - } + // if err := JSON.Bind(req, &testValMin3); err != nil { + // t.Error("Nothing was entered but min was not required. No error should be thrown.") + // } } func TestMax(t *testing.T) { @@ -379,9 +255,7 @@ func TestMax(t *testing.T) { Test int `json:"digit" validate:"max:23" ` } - testJSON := jsonFactory(`{"digit": 23}`) - - req, _ := http.NewRequest("POST", "/", testJSON) + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 23}`)) if err := JSON.Bind(req, &testValMin); err != nil { t.Error(err) @@ -391,9 +265,7 @@ func TestMax(t *testing.T) { Test int `json:"digit" validate:"max:20" ` } - testJSON = jsonFactory(`{"digit": 21}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": 21}`)) if err := JSON.Bind(req, &testValMin2); err == nil { t.Error("Max was 20 digit of 21 should not have validated properly.") @@ -403,9 +275,7 @@ func TestMax(t *testing.T) { Test int `json:"digit" validate:"max:20" ` } - testJSON = jsonFactory(`{"jeff":"greg"}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"jeff":"greg"}`)) if err := JSON.Bind(req, &testValMin3); err != nil { t.Error("Nothing was entered but max was not required. No error should be thrown.") @@ -418,9 +288,7 @@ func TestRegex(t *testing.T) { Test int `json:"digit" validate:"regex:\d+" ` } - testJSON := jsonFactory(`{"digit": 23}`) - - req, _ := http.NewRequest("POST", "/", testJSON) + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 23}`)) if err := JSON.Bind(req, &testValDigit); err != nil { t.Error(err) @@ -430,11 +298,33 @@ func TestRegex(t *testing.T) { Test int `json:"digit" validate:"regex:\d+" ` } - testJSON = jsonFactory(`{"digit": 2dsa3}`) - - req, _ = http.NewRequest("POST", "/", testJSON) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": 2dsa3}`)) if err := JSON.Bind(req, &testValDigit2); err == nil { t.Error("\\d+ regex should not match the string 2dsa3.") } } + +func TestMultiple(t *testing.T) { + + var testValMulti struct { + Test int `json:"digit" validate:"regex:\\d+|required|max:23" ` + } + + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 23}`)) + + if err := JSON.Bind(req, &testValMulti); err != nil { + t.Error(err) + } + + var testValMulti2 struct { + Test string `json:"digit" validate:"email|required|regex:\\d+" ` + } + + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": "m@g.com"}`)) + + if err := JSON.Bind(req, &testValMulti2); err == nil { + t.Error("Should have returned error but did not.") + } + +} From 66b55b8f04117325a9c42f0e3bbb2b98c04f5173 Mon Sep 17 00:00:00 2001 From: Michael Schuett Date: Sun, 20 Jul 2014 23:52:57 -0500 Subject: [PATCH 4/4] Switched to using pointers to allow for optional validation. --- binding/validate.go | 109 +++++++++++++++++++++--- binding/validate_test.go | 180 ++++++++++++++++++++++++++++++--------- 2 files changed, 237 insertions(+), 52 deletions(-) diff --git a/binding/validate.go b/binding/validate.go index 3346e362..48dcf87d 100644 --- a/binding/validate.go +++ b/binding/validate.go @@ -20,6 +20,12 @@ func Validate(obj interface{}) error { value = value.Elem() } + // Kill process if obj did not pass in a scruct. + // This happens when a pointer passed in. + if value.Kind() != reflect.Struct { + return nil + } + for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) @@ -48,6 +54,11 @@ func Validate(obj interface{}) error { match := array[setting] + //Check that value was passed in and is not required. + if match != "required" && null(fieldValue) == true { + return nil + } + switch { case "required" == match: if err := required(field, fieldValue, zero); err != nil { @@ -73,6 +84,14 @@ func Validate(obj interface{}) error { if err := regex(match, fieldValue); err != nil { return err } + case strings.HasPrefix(match, "length:"): + if err := length(match, fieldValue); err != nil { + return err + } + case strings.HasPrefix(match, "length_between:"): + if err := length_between(match, fieldValue); err != nil { + return err + } default: panic("The field " + match + " is not a valid validation check.") } @@ -83,6 +102,15 @@ func Validate(obj interface{}) error { return nil } +// Ensure that the value being passed in is not of type nil. +func null(value interface{}) bool { + if reflect.ValueOf(value).IsNil() { + return true + } + + return false +} + // Check that the following function features // the required field. May need to check for // more special cases like since passing in null @@ -103,15 +131,15 @@ func required(field reflect.StructField, value, zero interface{}) error { // Currently only supports strings, ints func in(field string, value interface{}) error { - if data, ok := value.(string); ok { - if len(data) == 0 { + if data, ok := value.(*string); ok { + if len(*data) == 0 { return nil } valid := strings.Split(field[3:], ",") for option := range valid { - if valid[option] == data { + if valid[option] == *data { return nil } } @@ -125,13 +153,13 @@ func in(field string, value interface{}) error { func min(field string, value interface{}) error { - if data, ok := value.(int); ok { + if data, ok := value.(*int); ok { min := field[strings.Index(field, ":")+1:] if minNum, ok := strconv.ParseInt(min, 0, 64); ok == nil { - if int64(data) >= minNum { + if int64(*data) >= minNum { return nil } else { return errors.New("The data you passed in was smaller then the allowed minimum.") @@ -145,12 +173,12 @@ func min(field string, value interface{}) error { func max(field string, value interface{}) error { - if data, ok := value.(int); ok { + if data, ok := value.(*int); ok { max := field[strings.Index(field, ":")+1:] if maxNum, ok := strconv.ParseInt(max, 0, 64); ok == nil { - if int64(data) <= maxNum { + if int64(*data) <= maxNum { return nil } else { return errors.New("The data you passed in was larger than the maximum.") @@ -168,14 +196,14 @@ func regex(field string, value interface{}) error { reg := field[strings.Index(field, ":")+1:] - if data, ok := value.(string); ok { - if len(data) == 0 { + if data, ok := value.(*string); ok { + if len(*data) == 0 { return nil - } else if err := match_regex(reg, []byte(data)); err != nil { + } else if err := match_regex(reg, []byte(*data)); err != nil { return err } - } else if data, ok := value.(int); ok { - if err := match_regex(reg, []byte(strconv.Itoa(data))); err != nil { + } else if data, ok := value.(*int); ok { + if err := match_regex(reg, []byte(strconv.Itoa(*data))); err != nil { return err } } else { @@ -194,3 +222,60 @@ func match_regex(reg string, data []byte) error { return errors.New("Your regex did not match or was not valid.") } } + +// Check passed in json length string is exact value passed in. +// Also checks if passed in values is between two different ones. +func length(field string, value interface{}) error { + + length := field[strings.Index(field, ":")+1:] + + if data, ok := value.(*string); ok { + if intdata, intok := strconv.Atoi(length); intok == nil { + if len(*data) == intdata { + return nil + } else { + return errors.New("The data passed in was not equal to the expected length.") + } + } else { + return errors.New("The value passed in for LENGTH could not be converted to an int.") + } + } else { + return errors.New("The value passed in for LENGTH could not be converted to a string.") + } +} + +// Check if the strings length is between high,low. +func length_between(field string, value interface{}) error { + + length := field[strings.Index(field, ":")+1:] + vals := strings.Split(length, ",") + + if len(vals) == 2 { + + if data, ok := value.(*string); ok { + + if lowerbound, lowok := strconv.Atoi(vals[0]); lowok == nil { + + if upperbound, upok := strconv.Atoi(vals[1]); upok == nil { + + if lowerbound <= len(*data) && upperbound >= len(*data) { + return nil + } else { + return errors.New("The value passed in for LENGTH BETWEEN was not in bounds.") + } + + } else { + return errors.New("The value passed in for LENGTH BETWEEN could not be converted to an int.") + } + + } else { + return errors.New("The value passed in for LENGTH BETWEEN could not be converted to an int.") + } + + } else { + return errors.New("The value passed in for LENGTH BETWEEN could not be converted to a string.") + } + } else { + return errors.New("LENGTH BETWEEN requires exactly two paramaters.") + } +} diff --git a/binding/validate_test.go b/binding/validate_test.go index 2db301c5..565ab9b1 100644 --- a/binding/validate_test.go +++ b/binding/validate_test.go @@ -1,7 +1,6 @@ package binding import ( - "fmt" "io" "io/ioutil" "net/http" @@ -20,7 +19,7 @@ func TestRequired(t *testing.T) { // // Test if STRING required is valid var testString struct { - Test string `json:"something" validate:"required" ` + Test *string `json:"something" validate:"required" ` } req, _ := http.NewRequest("POST", "/", jsonFactory(`{"something": "hello"}`)) @@ -30,7 +29,7 @@ func TestRequired(t *testing.T) { } var testString2 struct { - Test string `json:"something" validate:"required" ` + Test *string `json:"something" validate:"required" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{}`)) @@ -41,7 +40,7 @@ func TestRequired(t *testing.T) { // Test if INT require is valid var testInt struct { - Test int `json:"something" validate:"required" ` + Test *int `json:"something" validate:"required" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"something": 2}`)) @@ -52,7 +51,7 @@ func TestRequired(t *testing.T) { // Test if BOOL required is valid var testBool struct { - Test bool `json:"something" validate:"required" ` + Test *bool `json:"something" validate:"required" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"something": true}`)) @@ -62,7 +61,7 @@ func TestRequired(t *testing.T) { } var testBool2 struct { - Test string `json:"something" validate:"required" ` + Test *string `json:"something" validate:"required" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{}`)) @@ -73,7 +72,7 @@ func TestRequired(t *testing.T) { // Test if ARRAY required is valid var testArray struct { - Test []string `json:"something" validate:"required" ` + Test *[]string `json:"something" validate:"required" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"something": ["test", "data"]}`)) @@ -101,7 +100,7 @@ func TestRequired(t *testing.T) { func TestEmail(t *testing.T) { var testValEmail struct { - Test string `json:"email" validate:"email" ` + Test *string `json:"email" validate:"email" ` } req, _ := http.NewRequest("POST", "/", jsonFactory(`{"email": "michaeljs@gmail.com"}`)) @@ -111,7 +110,7 @@ func TestEmail(t *testing.T) { } var testValEmail2 struct { - Test string `json:"email" validate:"email" ` + Test *string `json:"email" validate:"email" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"email": "michaeljs@gail.edu"}`)) @@ -121,7 +120,7 @@ func TestEmail(t *testing.T) { } var testValEmail3 struct { - Test string `json:"email" validate:"email" ` + Test *string `json:"email" validate:"email" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"email": "michaeljs.edu"}`)) @@ -132,7 +131,7 @@ func TestEmail(t *testing.T) { // This should not return an error since email is not required. var testValEmail4 struct { - Test string `json:"email" validate:"email" ` + Test *string `json:"email" validate:"email" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"jeff": "really"}`)) @@ -148,7 +147,7 @@ func TestEmail(t *testing.T) { func TestIn(t *testing.T) { var testValIn struct { - Test string `json:"special" validate:"in:admin,user,other" ` + Test *string `json:"special" validate:"in:admin,user,other" ` } req, _ := http.NewRequest("POST", "/", jsonFactory(`{"special": "admin"}`)) @@ -158,7 +157,7 @@ func TestIn(t *testing.T) { } var testValIn2 struct { - Test string `json:"special" validate:"in:1,3,2" ` + Test *string `json:"special" validate:"in:1,3,2" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": "3"}`)) @@ -168,7 +167,7 @@ func TestIn(t *testing.T) { } var testValIn3 struct { - Test int `json:"special" validate:"in:1,3,2" ` + Test *int `json:"special" validate:"in:1,3,2" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": 6}`)) @@ -178,8 +177,8 @@ func TestIn(t *testing.T) { } var testValIn4 struct { - Test2 string `json:"what" validate:in:this,that` - Test string `json:"special" validate:"in:1,3,2" ` + Test2 *string `json:"what" validate:in:this,that` + Test *string `json:"special" validate:"in:1,3,2" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": "3","what": "this"}`)) @@ -189,8 +188,8 @@ func TestIn(t *testing.T) { } var testValIn5 struct { - Test2 string `json:"what" validate:in:this,that` - Test string `json:"special" validate:"in:1,3,2" ` + Test2 *string `json:"what" validate:in:this,that` + Test *string `json:"special" validate:"in:1,3,2" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"special": "3"}`)) @@ -200,16 +199,15 @@ func TestIn(t *testing.T) { } var testValIn6 struct { - Test2 string `json:"what" validate:"in:this,that"` - Test3 string `json:"what1" validate:"in:this,then"` - Test4 string `json:"what2" validate:"in:this,that"` - Test5 string `json:"what3" validate:"in:this,that"` - Test string `json:"special" validate:"in:1,3,2"` + Test2 *string `json:"what" validate:"in:this,that"` + Test3 *string `json:"what1" validate:"in:this,then"` + Test4 *string `json:"what2" validate:"in:this,that"` + Test5 *string `json:"what3" validate:"in:this,that"` + Test *string `json:"special" validate:"in:1,3,2"` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"sa": 34, "what":"this", "what1":"then", "what2":"this"}`)) - fmt.Println("error here:") if err := JSON.Bind(req, &testValIn6); err != nil { t.Error(err) } @@ -219,7 +217,7 @@ func TestIn(t *testing.T) { func TestMin(t *testing.T) { var testValMin struct { - Test int `json:"digit" validate:"min:23" ` + Test *int `json:"digit" validate:"min:23" ` } req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 24}`)) @@ -229,7 +227,7 @@ func TestMin(t *testing.T) { } var testValMin2 struct { - Test int `json:"digit" validate:"min:20" ` + Test *int `json:"digit" validate:"min:20" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": 19}`)) @@ -238,21 +236,21 @@ func TestMin(t *testing.T) { t.Error("Min was 20 digit of 19 should not have validated properly.") } - // var testValMin3 struct { - // Test int `json:"digit" validate:"min:20" ` - // } + var testValMin3 struct { + Test *int `json:"digit" validate:"min:20" ` + } - // req, _ = http.NewRequest("POST", "/", jsonFactory(`{"jeff":"greg"}`)) + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"jeff":"greg"}`)) - // if err := JSON.Bind(req, &testValMin3); err != nil { - // t.Error("Nothing was entered but min was not required. No error should be thrown.") - // } + if err := JSON.Bind(req, &testValMin3); err != nil { + t.Error("Nothing was entered but min was not required. No error should be thrown.") + } } func TestMax(t *testing.T) { var testValMin struct { - Test int `json:"digit" validate:"max:23" ` + Test *int `json:"digit" validate:"max:23" ` } req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 23}`)) @@ -262,7 +260,7 @@ func TestMax(t *testing.T) { } var testValMin2 struct { - Test int `json:"digit" validate:"max:20" ` + Test *int `json:"digit" validate:"max:20" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": 21}`)) @@ -272,7 +270,7 @@ func TestMax(t *testing.T) { } var testValMin3 struct { - Test int `json:"digit" validate:"max:20" ` + Test *int `json:"digit" validate:"max:20" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"jeff":"greg"}`)) @@ -285,7 +283,7 @@ func TestMax(t *testing.T) { func TestRegex(t *testing.T) { var testValDigit struct { - Test int `json:"digit" validate:"regex:\d+" ` + Test *int `json:"digit" validate:"regex:\\d+" ` } req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 23}`)) @@ -295,7 +293,7 @@ func TestRegex(t *testing.T) { } var testValDigit2 struct { - Test int `json:"digit" validate:"regex:\d+" ` + Test *int `json:"digit" validate:"regex:\\d+" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": 2dsa3}`)) @@ -308,7 +306,7 @@ func TestRegex(t *testing.T) { func TestMultiple(t *testing.T) { var testValMulti struct { - Test int `json:"digit" validate:"regex:\\d+|required|max:23" ` + Test *int `json:"digit" validate:"regex:\\d+|required|max:23" ` } req, _ := http.NewRequest("POST", "/", jsonFactory(`{"digit": 23}`)) @@ -318,7 +316,7 @@ func TestMultiple(t *testing.T) { } var testValMulti2 struct { - Test string `json:"digit" validate:"email|required|regex:\\d+" ` + Test *string `json:"digit" validate:"email|required|regex:\\d+" ` } req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": "m@g.com"}`)) @@ -328,3 +326,105 @@ func TestMultiple(t *testing.T) { } } + +func TestPointers(t *testing.T) { + + var testValMulti struct { + Test *string `json:"digit" validate:"in:3,4,5" ` + } + + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"invalid": "23"}`)) + + if err := JSON.Bind(req, &testValMulti); err != nil { + t.Error(err) + } + + var testValMulti2 struct { + Test *string `json:"digit" validate:"in:3,4,5" ` + } + + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": "23"}`)) + + if err := JSON.Bind(req, &testValMulti2); err == nil { + t.Error("Value was passed in but did not match in:3,4,5 error should have been returned.") + } + + var testValMulti3 struct { + Test *string `json:"digit" validate:"in:3,4,5" ` + } + + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": "4"}`)) + + if err := JSON.Bind(req, &testValMulti3); err != nil { + t.Error(err) + } + +} + +func TestLength(t *testing.T) { + + var testValLength struct { + Test *string `json:"username" validate:"length:5" ` + } + + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"username": "aaaaa"}`)) + + if err := JSON.Bind(req, &testValLength); err != nil { + t.Error(err) + } + + var testValLength2 struct { + Test *string `json:"username" validate:"length:4" ` + } + + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"username": "aaa"}`)) + + if err := JSON.Bind(req, &testValLength2); err == nil { + t.Error("Value was passed in but did not match length of 4 error should have been returned.") + } + + var testValLength3 struct { + Test *string `json:"username" validate:"length:23" ` + } + + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": "4"}`)) + + if err := JSON.Bind(req, &testValLength3); err != nil { + t.Error(err) + } + +} + +func TestLengthBetween(t *testing.T) { + + var testValLength struct { + Test *string `json:"username" validate:"length_between:5,10" ` + } + + req, _ := http.NewRequest("POST", "/", jsonFactory(`{"username": "aaaaaa"}`)) + + if err := JSON.Bind(req, &testValLength); err != nil { + t.Error(err) + } + + var testValLength2 struct { + Test *string `json:"username" validate:"length_between:4,5" ` + } + + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"username": "aaa"}`)) + + if err := JSON.Bind(req, &testValLength2); err == nil { + t.Error("Value was passed in but was not inbetween 4,5 should have returned error.") + } + + var testValLength3 struct { + Test *string `json:"username" validate:"length_between:2,3" ` + } + + req, _ = http.NewRequest("POST", "/", jsonFactory(`{"digit": "4"}`)) + + if err := JSON.Bind(req, &testValLength3); err != nil { + t.Error(err) + } + +}