Fix time.Duration parsing for int param (#678)

This fixes an issue where if the param of a time.Durtion type is specified
as an integer, denoting nanosecond precision, instead of time duration
string the validation would panic.

the fixes ensures it falls back to the previous expected behaviour.
pull/668/merge v10.4.1
Dean Karn 4 years ago committed by GitHub
parent d6b17fd90b
commit 456221b630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 244
      baked_in.go
  3. 7
      util.go
  4. 45
      validator_test.go

@ -1,7 +1,7 @@
Package validator Package validator
================ ================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.3.0-green.svg) ![Project status](https://img.shields.io/badge/version-10.4.1-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator) [![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)

@ -67,125 +67,125 @@ var (
// you can add, remove or even replace items to suite your needs, // you can add, remove or even replace items to suite your needs,
// or even disregard and use your own map if so desired. // or even disregard and use your own map if so desired.
bakedInValidators = map[string]Func{ bakedInValidators = map[string]Func{
"required": hasValue, "required": hasValue,
"required_if": requiredIf, "required_if": requiredIf,
"required_unless": requiredUnless, "required_unless": requiredUnless,
"required_with": requiredWith, "required_with": requiredWith,
"required_with_all": requiredWithAll, "required_with_all": requiredWithAll,
"required_without": requiredWithout, "required_without": requiredWithout,
"required_without_all": requiredWithoutAll, "required_without_all": requiredWithoutAll,
"excluded_with": excludedWith, "excluded_with": excludedWith,
"excluded_with_all": excludedWithAll, "excluded_with_all": excludedWithAll,
"excluded_without": excludedWithout, "excluded_without": excludedWithout,
"excluded_without_all": excludedWithoutAll, "excluded_without_all": excludedWithoutAll,
"isdefault": isDefault, "isdefault": isDefault,
"len": hasLengthOf, "len": hasLengthOf,
"min": hasMinOf, "min": hasMinOf,
"max": hasMaxOf, "max": hasMaxOf,
"eq": isEq, "eq": isEq,
"ne": isNe, "ne": isNe,
"lt": isLt, "lt": isLt,
"lte": isLte, "lte": isLte,
"gt": isGt, "gt": isGt,
"gte": isGte, "gte": isGte,
"eqfield": isEqField, "eqfield": isEqField,
"eqcsfield": isEqCrossStructField, "eqcsfield": isEqCrossStructField,
"necsfield": isNeCrossStructField, "necsfield": isNeCrossStructField,
"gtcsfield": isGtCrossStructField, "gtcsfield": isGtCrossStructField,
"gtecsfield": isGteCrossStructField, "gtecsfield": isGteCrossStructField,
"ltcsfield": isLtCrossStructField, "ltcsfield": isLtCrossStructField,
"ltecsfield": isLteCrossStructField, "ltecsfield": isLteCrossStructField,
"nefield": isNeField, "nefield": isNeField,
"gtefield": isGteField, "gtefield": isGteField,
"gtfield": isGtField, "gtfield": isGtField,
"ltefield": isLteField, "ltefield": isLteField,
"ltfield": isLtField, "ltfield": isLtField,
"fieldcontains": fieldContains, "fieldcontains": fieldContains,
"fieldexcludes": fieldExcludes, "fieldexcludes": fieldExcludes,
"alpha": isAlpha, "alpha": isAlpha,
"alphanum": isAlphanum, "alphanum": isAlphanum,
"alphaunicode": isAlphaUnicode, "alphaunicode": isAlphaUnicode,
"alphanumunicode": isAlphanumUnicode, "alphanumunicode": isAlphanumUnicode,
"numeric": isNumeric, "numeric": isNumeric,
"number": isNumber, "number": isNumber,
"hexadecimal": isHexadecimal, "hexadecimal": isHexadecimal,
"hexcolor": isHEXColor, "hexcolor": isHEXColor,
"rgb": isRGB, "rgb": isRGB,
"rgba": isRGBA, "rgba": isRGBA,
"hsl": isHSL, "hsl": isHSL,
"hsla": isHSLA, "hsla": isHSLA,
"e164": isE164, "e164": isE164,
"email": isEmail, "email": isEmail,
"url": isURL, "url": isURL,
"uri": isURI, "uri": isURI,
"urn_rfc2141": isUrnRFC2141, // RFC 2141 "urn_rfc2141": isUrnRFC2141, // RFC 2141
"file": isFile, "file": isFile,
"base64": isBase64, "base64": isBase64,
"base64url": isBase64URL, "base64url": isBase64URL,
"contains": contains, "contains": contains,
"containsany": containsAny, "containsany": containsAny,
"containsrune": containsRune, "containsrune": containsRune,
"excludes": excludes, "excludes": excludes,
"excludesall": excludesAll, "excludesall": excludesAll,
"excludesrune": excludesRune, "excludesrune": excludesRune,
"startswith": startsWith, "startswith": startsWith,
"endswith": endsWith, "endswith": endsWith,
"startsnotwith": startsNotWith, "startsnotwith": startsNotWith,
"endsnotwith": endsNotWith, "endsnotwith": endsNotWith,
"isbn": isISBN, "isbn": isISBN,
"isbn10": isISBN10, "isbn10": isISBN10,
"isbn13": isISBN13, "isbn13": isISBN13,
"eth_addr": isEthereumAddress, "eth_addr": isEthereumAddress,
"btc_addr": isBitcoinAddress, "btc_addr": isBitcoinAddress,
"btc_addr_bech32": isBitcoinBech32Address, "btc_addr_bech32": isBitcoinBech32Address,
"uuid": isUUID, "uuid": isUUID,
"uuid3": isUUID3, "uuid3": isUUID3,
"uuid4": isUUID4, "uuid4": isUUID4,
"uuid5": isUUID5, "uuid5": isUUID5,
"uuid_rfc4122": isUUIDRFC4122, "uuid_rfc4122": isUUIDRFC4122,
"uuid3_rfc4122": isUUID3RFC4122, "uuid3_rfc4122": isUUID3RFC4122,
"uuid4_rfc4122": isUUID4RFC4122, "uuid4_rfc4122": isUUID4RFC4122,
"uuid5_rfc4122": isUUID5RFC4122, "uuid5_rfc4122": isUUID5RFC4122,
"ascii": isASCII, "ascii": isASCII,
"printascii": isPrintableASCII, "printascii": isPrintableASCII,
"multibyte": hasMultiByteCharacter, "multibyte": hasMultiByteCharacter,
"datauri": isDataURI, "datauri": isDataURI,
"latitude": isLatitude, "latitude": isLatitude,
"longitude": isLongitude, "longitude": isLongitude,
"ssn": isSSN, "ssn": isSSN,
"ipv4": isIPv4, "ipv4": isIPv4,
"ipv6": isIPv6, "ipv6": isIPv6,
"ip": isIP, "ip": isIP,
"cidrv4": isCIDRv4, "cidrv4": isCIDRv4,
"cidrv6": isCIDRv6, "cidrv6": isCIDRv6,
"cidr": isCIDR, "cidr": isCIDR,
"tcp4_addr": isTCP4AddrResolvable, "tcp4_addr": isTCP4AddrResolvable,
"tcp6_addr": isTCP6AddrResolvable, "tcp6_addr": isTCP6AddrResolvable,
"tcp_addr": isTCPAddrResolvable, "tcp_addr": isTCPAddrResolvable,
"udp4_addr": isUDP4AddrResolvable, "udp4_addr": isUDP4AddrResolvable,
"udp6_addr": isUDP6AddrResolvable, "udp6_addr": isUDP6AddrResolvable,
"udp_addr": isUDPAddrResolvable, "udp_addr": isUDPAddrResolvable,
"ip4_addr": isIP4AddrResolvable, "ip4_addr": isIP4AddrResolvable,
"ip6_addr": isIP6AddrResolvable, "ip6_addr": isIP6AddrResolvable,
"ip_addr": isIPAddrResolvable, "ip_addr": isIPAddrResolvable,
"unix_addr": isUnixAddrResolvable, "unix_addr": isUnixAddrResolvable,
"mac": isMAC, "mac": isMAC,
"hostname": isHostnameRFC952, // RFC 952 "hostname": isHostnameRFC952, // RFC 952
"hostname_rfc1123": isHostnameRFC1123, // RFC 1123 "hostname_rfc1123": isHostnameRFC1123, // RFC 1123
"fqdn": isFQDN, "fqdn": isFQDN,
"unique": isUnique, "unique": isUnique,
"oneof": isOneOf, "oneof": isOneOf,
"html": isHTML, "html": isHTML,
"html_encoded": isHTMLEncoded, "html_encoded": isHTMLEncoded,
"url_encoded": isURLEncoded, "url_encoded": isURLEncoded,
"dir": isDir, "dir": isDir,
"json": isJSON, "json": isJSON,
"hostname_port": isHostnamePort, "hostname_port": isHostnamePort,
"lowercase": isLowercase, "lowercase": isLowercase,
"uppercase": isUppercase, "uppercase": isUppercase,
"datetime": isDatetime, "datetime": isDatetime,
"timezone": isTimeZone, "timezone": isTimeZone,
"iso3166_1_alpha2": isIso3166Alpha2, "iso3166_1_alpha2": isIso3166Alpha2,
"iso3166_1_alpha3": isIso3166Alpha3, "iso3166_1_alpha3": isIso3166Alpha3,
"iso3166_1_alpha_numeric": isIso3166AlphaNumeric, "iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
} }
@ -2250,11 +2250,7 @@ func isTimeZone(fl FieldLevel) bool {
} }
_, err := time.LoadLocation(field.String()) _, err := time.LoadLocation(field.String())
if err != nil { return err == nil
return false
}
return true
} }
panic(fmt.Sprintf("Bad field type %T", field.Interface())) panic(fmt.Sprintf("Bad field type %T", field.Interface()))

@ -223,7 +223,6 @@ BEGIN:
// asInt returns the parameter as a int64 // asInt returns the parameter as a int64
// or panics if it can't convert // or panics if it can't convert
func asInt(param string) int64 { func asInt(param string) int64 {
i, err := strconv.ParseInt(param, 0, 64) i, err := strconv.ParseInt(param, 0, 64)
panicIf(err) panicIf(err)
@ -234,8 +233,10 @@ func asInt(param string) int64 {
// or panics on error. // or panics on error.
func asIntFromTimeDuration(param string) int64 { func asIntFromTimeDuration(param string) int64 {
d, err := time.ParseDuration(param) d, err := time.ParseDuration(param)
panicIf(err) if err != nil {
// attempt parsing as an an integer assuming nanosecond precision
return asInt(param)
}
return int64(d) return int64(d)
} }

@ -9475,7 +9475,7 @@ func TestUniqueValidationStructPtrSlice(t *testing.T) {
} }
} }
} }
PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C") PanicMatches(t, func() { _ = validate.Var(testStructs, "unique=C") }, "Bad field name C")
} }
func TestHTMLValidation(t *testing.T) { func TestHTMLValidation(t *testing.T) {
@ -10990,3 +10990,46 @@ func TestTimeZoneValidation(t *testing.T) {
_ = validate.Var(2, "timezone") _ = validate.Var(2, "timezone")
}, "Bad field type int") }, "Bad field type int")
} }
func TestDurationType(t *testing.T) {
tests := []struct {
name string
s interface{} // struct
success bool
}{
{
name: "valid duration string pass",
s: struct {
Value time.Duration `validate:"gte=500ns"`
}{
Value: time.Second,
},
success: true,
},
{
name: "valid duration int pass",
s: struct {
Value time.Duration `validate:"gte=500"`
}{
Value: time.Second,
},
success: true,
},
}
validate := New()
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
errs := validate.Struct(tc.s)
if tc.success {
Equal(t, errs, nil)
return
}
NotEqual(t, errs, nil)
})
}
}

Loading…
Cancel
Save