Skip to content

Commit

Permalink
Add support for validating against uuid values that are structs which…
Browse files Browse the repository at this point in the history
… implement the Stringer interface. (#1189)

## Fixes Or Enhances
This adds the ability to validate UUIDs that have an underlying struct
value but also implement the Stringer interface. This should cover most
UUID implementations (google's uuid, etc).

Implements: #900, specifically
#900 (comment).

**Make sure that you've checked the boxes below before you submit PR:**
- [x] Tests exist or have been written that cover this particular
change.

@go-playground/validator-maintainers
  • Loading branch information
JoshGlazebrook authored Nov 4, 2023
1 parent adda84d commit 4c1bd61
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 9 deletions.
18 changes: 9 additions & 9 deletions baked_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,47 +508,47 @@ func isASCII(fl FieldLevel) bool {

// isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
func isUUID5(fl FieldLevel) bool {
return uUID5Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID5Regex, fl)
}

// isUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
func isUUID4(fl FieldLevel) bool {
return uUID4Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID4Regex, fl)
}

// isUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
func isUUID3(fl FieldLevel) bool {
return uUID3Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID3Regex, fl)
}

// isUUID is the validation function for validating if the field's value is a valid UUID of any version.
func isUUID(fl FieldLevel) bool {
return uUIDRegex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUIDRegex, fl)
}

// isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
func isUUID5RFC4122(fl FieldLevel) bool {
return uUID5RFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID5RFC4122Regex, fl)
}

// isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
func isUUID4RFC4122(fl FieldLevel) bool {
return uUID4RFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID4RFC4122Regex, fl)
}

// isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
func isUUID3RFC4122(fl FieldLevel) bool {
return uUID3RFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUID3RFC4122Regex, fl)
}

// isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
func isUUIDRFC4122(fl FieldLevel) bool {
return uUIDRFC4122Regex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uUIDRFC4122Regex, fl)
}

// isULID is the validation function for validating if the field's value is a valid ULID.
func isULID(fl FieldLevel) bool {
return uLIDRegex.MatchString(fl.Field().String())
return fieldMatchesRegexByStringerValOrString(uLIDRegex, fl)
}

// isMD4 is the validation function for validating if the field's value is a valid MD4.
Expand Down
11 changes: 11 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package validator

import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -292,3 +294,12 @@ func panicIf(err error) {
panic(err.Error())
}
}

// Checks if field value matches regex. If fl.Field can be cast to Stringer, it uses the Stringer interfaces
// String() return value. Otherwise, it uses fl.Field's String() value.
func fieldMatchesRegexByStringerValOrString(regex *regexp.Regexp, fl FieldLevel) bool {
if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
return regex.MatchString(stringer.String())
}
return regex.MatchString(fl.Field().String())
}
29 changes: 29 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4105,6 +4105,16 @@ func TestUUID3Validation(t *testing.T) {
}
}

type uuidTestType struct {
val string
}

func (u uuidTestType) String() string {
return u.val
}

var _ fmt.Stringer = uuidTestType{}

func TestUUIDValidation(t *testing.T) {
tests := []struct {
param string
Expand Down Expand Up @@ -4141,6 +4151,25 @@ func TestUUIDValidation(t *testing.T) {
}
}
}

// Test UUID validation on uuid structs type that implements Stringer interface.
structWithValidUUID := struct {
UUID uuidTestType `validate:"uuid"`
}{
UUID: uuidTestType{val: "a987fbc9-4bed-3078-cf07-9141ba07c9f3"},
}
structWithInvalidUUID := struct {
UUID uuidTestType `validate:"uuid"`
}{
UUID: uuidTestType{val: "934859"},
}

if err := validate.Struct(structWithValidUUID); err != nil {
t.Fatalf("UUID failed Error: %s", err)
}
if err := validate.Struct(structWithInvalidUUID); err == nil {
t.Fatal("UUID failed Error expected but received nil")
}
}

func TestUUID5RFC4122Validation(t *testing.T) {
Expand Down

0 comments on commit 4c1bd61

Please sign in to comment.