Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cron support #1045

Merged
merged 1 commit into from Mar 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -154,6 +154,7 @@ Baked-in Validations
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| cron | Cron |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
Expand Down
7 changes: 7 additions & 0 deletions baked_in.go
Expand Up @@ -214,6 +214,7 @@ var (
"semver": isSemverFormat,
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
"credit_card": isCreditCard,
"cron": isCron,
}
)

Expand Down Expand Up @@ -2524,3 +2525,9 @@ func isCreditCard(fl FieldLevel) bool {
}
return (sum % 10) == 0
}

// isCron is the validation function for validating if the current field's value is a valid cron expression
func isCron(fl FieldLevel) bool {
cronString := fl.Field().String()
return cronRegex.MatchString(cronString)
}
6 changes: 6 additions & 0 deletions doc.go
Expand Up @@ -1323,6 +1323,12 @@ This validates that a string value contains a valid credit card number using Luh

Usage: credit_card

Cron

This validates that a string value contains a valid cron expression.

Usage: cron

Alias Validators and Tags

NOTE: When returning an error, the tag returned in "FieldError" will be
Expand Down
2 changes: 2 additions & 0 deletions regexes.go
Expand Up @@ -64,6 +64,7 @@ const (
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})`
)

var (
Expand Down Expand Up @@ -128,4 +129,5 @@ var (
bicRegex = regexp.MustCompile(bicRegexString)
semverRegex = regexp.MustCompile(semverRegexString)
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
cronRegex = regexp.MustCompile(cronRegexString)
)
5 changes: 5 additions & 0 deletions translations/en/en.go
Expand Up @@ -1276,6 +1276,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid color",
override: false,
},
{
tag: "cron",
translation: "{0} must be a valid cron expression",
override: false,
},
{
tag: "oneof",
translation: "{0} must be one of [{1}]",
Expand Down
9 changes: 7 additions & 2 deletions translations/it/it.go
Expand Up @@ -124,7 +124,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{
tag: "min",
customRegisFunc: func(ut ut.Translator) (err error) {

if err = ut.Add("min-string", "{0} deve essere lungo almeno {1}", false); err != nil {
return
}
Expand Down Expand Up @@ -432,7 +432,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
{
tag: "lte",
customRegisFunc: func(ut ut.Translator) (err error) {

if err = ut.Add("lte-string", "{0} deve essere lungo al massimo {1}", false); err != nil {
return
}
Expand Down Expand Up @@ -1132,6 +1132,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} deve essere un colore valido",
override: false,
},
{
tag: "cron",
translation: "{0} deve essere una stringa cron valida",
override: false,
},
{
tag: "oneof",
translation: "{0} deve essere uno di [{1}]",
Expand Down
34 changes: 34 additions & 0 deletions validator_test.go
Expand Up @@ -12323,3 +12323,37 @@ func TestMultiOrOperatorGroup(t *testing.T) {
}
}
}

func TestCronExpressionValidation(t *testing.T) {
tests := []struct {
value string `validate:"cron"`
tag string
expected bool
}{
{"0 0 12 * * ?", "cron", true},
{"0 15 10 ? * *", "cron", true},
{"0 15 10 * * ?", "cron", true},
{"0 15 10 * * ? 2005", "cron", true},
{"0 15 10 ? * 6L", "cron", true},
{"0 15 10 ? * 6L 2002-2005", "cron", true},
{"*/20 * * * *", "cron", true},
{"0 15 10 ? * MON-FRI", "cron", true},
{"0 15 10 ? * 6#3", "cron", true},
{"wrong", "cron", false},
}

validate := New()

for i, test := range tests {
errs := validate.Var(test.value, test.tag)
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf(`Index: %d cron "%s" failed Error: %s`, i, test.value, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf(`Index: %d cron "%s" should have errs`, i, test.value)
}
}
}
}