Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
deankarn committed May 1, 2022
2 parents 6d23138 + f2d3ff7 commit 248410d
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 28 deletions.
13 changes: 8 additions & 5 deletions .github/workflows/workflow.yml
Expand Up @@ -8,7 +8,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.15.x, 1.16.x]
go-version: [1.17.x, 1.18.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -32,7 +32,7 @@ jobs:
run: go test -race -covermode=atomic -coverprofile="profile.cov" ./...

- name: Send Coverage
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.16.x'
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.18.x'
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: profile.cov
Expand All @@ -41,8 +41,11 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v3
with:
go-version: 1.16
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v3
with:
version: v1.41.1
version: v1.45.2
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -153,6 +153,7 @@ Baked-in Validations
| bcp47_language_tag | Language tag (BCP 47) |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
Expand Down Expand Up @@ -219,6 +220,8 @@ Baked-in Validations
| required_with_all | Required With All |
| required_without | Required Without |
| required_without_all | Required Without All |
| excluded_if | Excluded If |
| excluded_unless | Excluded Unless |
| excluded_with | Excluded With |
| excluded_with_all | Excluded With All |
| excluded_without | Excluded Without |
Expand Down
72 changes: 72 additions & 0 deletions baked_in.go
Expand Up @@ -75,6 +75,8 @@ var (
"required_with_all": requiredWithAll,
"required_without": requiredWithout,
"required_without_all": requiredWithoutAll,
"excluded_if": excludedIf,
"excluded_unless": excludedUnless,
"excluded_with": excludedWith,
"excluded_with_all": excludedWithAll,
"excluded_without": excludedWithout,
Expand Down Expand Up @@ -201,6 +203,7 @@ var (
"bic": isIsoBicFormat,
"semver": isSemverFormat,
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
"credit_card": isCreditCard,
}
)

Expand Down Expand Up @@ -1542,6 +1545,22 @@ func requiredIf(fl FieldLevel) bool {
return hasValue(fl)
}

// excludedIf is the validation function
// The field under validation must not be present or is empty only if all the other specified fields are equal to the value following with the specified field.
func excludedIf(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName()))
}

for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return false
}
}
return true
}

// requiredUnless is the validation function
// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
func requiredUnless(fl FieldLevel) bool {
Expand All @@ -1558,6 +1577,21 @@ func requiredUnless(fl FieldLevel) bool {
return hasValue(fl)
}

// excludedUnless is the validation function
// The field under validation must not be present or is empty unless all the other specified fields are equal to the value following with the specified field.
func excludedUnless(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName()))
}
for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return true
}
}
return !hasValue(fl)
}

// excludedWith is the validation function
// The field under validation must not be present or is empty if any of the other specified fields are present.
func excludedWith(fl FieldLevel) bool {
Expand Down Expand Up @@ -2436,3 +2470,41 @@ func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
val := fl.Field().String()
return dnsRegexRFC1035Label.MatchString(val)
}

// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
func isCreditCard(fl FieldLevel) bool {
val := fl.Field().String()
var creditCard bytes.Buffer
segments := strings.Split(val, " ")
for _, segment := range segments {
if len(segment) < 3 {
return false
}
creditCard.WriteString(segment)
}

ccDigits := strings.Split(creditCard.String(), "")
size := len(ccDigits)
if size < 12 || size > 19 {
return false
}

sum := 0
for i, digit := range ccDigits {
value, err := strconv.Atoi(digit)
if err != nil {
return false
}
if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
v := value * 2
if v >= 10 {
sum += 1 + (v % 10)
} else {
sum += v
}
} else {
sum += value
}
}
return (sum % 10) == 0
}
40 changes: 40 additions & 0 deletions doc.go
Expand Up @@ -349,6 +349,40 @@ Example:
// require the field if the Field1 and Field2 is not present:
Usage: required_without_all=Field1 Field2
Excluded If
The field under validation must not be present or not empty only if all
the other specified fields are equal to the value following the specified
field. For strings ensures value is not "". For slices, maps, pointers,
interfaces, channels and functions ensures the value is not nil.
Usage: excluded_if
Examples:
// exclude the field if the Field1 is equal to the parameter given:
Usage: excluded_if=Field1 foobar
// exclude the field if the Field1 and Field2 is equal to the value respectively:
Usage: excluded_if=Field1 foo Field2 bar
Excluded Unless
The field under validation must not be present or empty unless all
the other specified fields are equal to the value following the specified
field. For strings ensures value is not "". For slices, maps, pointers,
interfaces, channels and functions ensures the value is not nil.
Usage: excluded_unless
Examples:
// exclude the field unless the Field1 is equal to the parameter given:
Usage: excluded_unless=Field1 foobar
// exclude the field unless the Field1 and Field2 is equal to the value respectively:
Usage: excluded_unless=Field1 foo Field2 bar
Is Default
This validates that the value is the default value and is almost the
Expand Down Expand Up @@ -1283,6 +1317,12 @@ More information on https://semver.org/
Usage: semver
Credit Card
This validates that a string value contains a valid credit card number using Luhn algoritm.
Usage: credit_card
Alias Validators and Tags
NOTE: When returning an error, the tag returned in "FieldError" will be
Expand Down
12 changes: 6 additions & 6 deletions regexes.go
Expand Up @@ -37,12 +37,12 @@ const (
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
Expand Down
8 changes: 4 additions & 4 deletions translations/ja/ja.go
Expand Up @@ -136,7 +136,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return
}

if err = ut.Add("min-number", "{0}は{1}かより大きくなければなりません", false); err != nil {
if err = ut.Add("min-number", "{0}は{1}より大きくなければなりません", false); err != nil {
return
}

Expand Down Expand Up @@ -227,7 +227,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return
}

if err = ut.Add("max-number", "{0}は{1}かより小さくなければなりません", false); err != nil {
if err = ut.Add("max-number", "{0}は{1}より小さくなければなりません", false); err != nil {
return
}

Expand Down Expand Up @@ -525,7 +525,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return
}

if err = ut.Add("lte-number", "{0}は{1}かより小さくなければなりません", false); err != nil {
if err = ut.Add("lte-number", "{0}は{1}より小さくなければなりません", false); err != nil {
return
}

Expand Down Expand Up @@ -765,7 +765,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
return
}

if err = ut.Add("gte-number", "{0}は{1}かより大きくなければなりません", false); err != nil {
if err = ut.Add("gte-number", "{0}は{1}より大きくなければなりません", false); err != nil {
return
}

Expand Down
8 changes: 4 additions & 4 deletions translations/ja/ja_test.go
Expand Up @@ -453,7 +453,7 @@ func TestTranslations(t *testing.T) {
},
{
ns: "Test.GteNumber",
expected: "GteNumberは5.56かより大きくなければなりません",
expected: "GteNumberは5.56より大きくなければなりません",
},
{
ns: "Test.GteMultiple",
Expand Down Expand Up @@ -485,7 +485,7 @@ func TestTranslations(t *testing.T) {
},
{
ns: "Test.LteNumber",
expected: "LteNumberは5.56かより小さくなければなりません",
expected: "LteNumberは5.56より小さくなければなりません",
},
{
ns: "Test.LteMultiple",
Expand Down Expand Up @@ -541,7 +541,7 @@ func TestTranslations(t *testing.T) {
},
{
ns: "Test.MaxNumber",
expected: "MaxNumberは1,113.00かより小さくなければなりません",
expected: "MaxNumberは1,113.00より小さくなければなりません",
},
{
ns: "Test.MaxMultiple",
Expand All @@ -553,7 +553,7 @@ func TestTranslations(t *testing.T) {
},
{
ns: "Test.MinNumber",
expected: "MinNumberは1,113.00かより大きくなければなりません",
expected: "MinNumberは1,113.00より大きくなければなりません",
},
{
ns: "Test.MinMultiple",
Expand Down
30 changes: 30 additions & 0 deletions translations/zh/zh.go
Expand Up @@ -29,6 +29,36 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_if",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_unless",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_with",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_with_all",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_without",
translation: "{0}为必填字段",
override: false,
},
{
tag: "required_without_all",
translation: "{0}为必填字段",
override: false,
},
{
tag: "len",
customRegisFunc: func(ut ut.Translator) (err error) {
Expand Down
6 changes: 4 additions & 2 deletions validator_instance.go
Expand Up @@ -33,6 +33,8 @@ const (
excludedWithoutTag = "excluded_without"
excludedWithTag = "excluded_with"
excludedWithAllTag = "excluded_with_all"
excludedIfTag = "excluded_if"
excludedUnlessTag = "excluded_unless"
skipValidationTag = "-"
diveTag = "dive"
keysTag = "keys"
Expand Down Expand Up @@ -120,7 +122,7 @@ func New() *Validate {
switch k {
// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
_ = v.registerValidation(k, wrapFunc(val), true, true)
default:
// no need to error check here, baked in will always be valid
Expand Down Expand Up @@ -169,7 +171,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{
return errs
}

// ValidateMap validates map data form a map of tags
// ValidateMap validates map data from a map of tags
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
return v.ValidateMapCtx(context.Background(), data, rules)
}
Expand Down

0 comments on commit 248410d

Please sign in to comment.