chore: move go-ozzo/ozzo-validation to invopop/validation

invpop/validation is a fork of go-ozzo/ozzo-validation with some additional
bug fixes. Specifically we need: https://github.com/invopop/validation/pull/1
This commit is contained in:
Ralf Haferkamp
2024-05-30 12:00:17 +02:00
committed by Ralf Haferkamp
parent 1b47707e2f
commit 90f7cc23f4
28 changed files with 183 additions and 190 deletions

2
go.mod
View File

@@ -41,7 +41,6 @@ require (
github.com/go-micro/plugins/v4/wrapper/breaker/gobreaker v1.2.0
github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus v1.2.0
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/go-playground/validator/v10 v10.20.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/golang-jwt/jwt/v4 v4.5.0
@@ -52,6 +51,7 @@ require (
github.com/gookit/config/v2 v2.2.5
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0
github.com/invopop/validation v0.3.0
github.com/jellydator/ttlcache/v2 v2.11.1
github.com/jellydator/ttlcache/v3 v3.2.0
github.com/jinzhu/now v1.1.5

6
go.sum
View File

@@ -871,7 +871,7 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
@@ -1232,8 +1232,6 @@ github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0 h1:e2hgtWMNqJ3
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0/go.mod h1:BBqL7ckGNb7rFfk3vU2Yj/CILVsz/WF19CkAyveQl8A=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -1546,6 +1544,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
github.com/invopop/validation v0.3.0 h1:o260kbjXzoBO/ypXDSSrCLL7SxEFUXBsX09YTE9AxZw=
github.com/invopop/validation v0.3.0/go.mod h1:qIBG6APYLp2Wu3/96p3idYjP8ffTKVmQBfKiZbw0Hts=
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=

View File

@@ -4,8 +4,8 @@ import (
"errors"
"regexp"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
validation "github.com/invopop/validation"
"github.com/invopop/validation/is"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
)
@@ -129,12 +129,12 @@ func validateListRoleAssignmentsFiltered(req *settingssvc.ListRoleAssignmentsFil
}
func validateUserRoleAssignmentFilter(values interface{}) error {
filter, ok := values.(settingsmsg.UserRoleAssignmentFilter)
filter, ok := values.(*settingsmsg.UserRoleAssignmentFilter)
if !ok {
return errors.New("expected UserRoleAssignmentFilter")
}
return validation.ValidateStruct(
&filter,
filter,
validation.Field(&filter.Type,
validation.Required,
validation.In(settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT, settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE),

View File

@@ -1,49 +1,54 @@
# ozzo-validation
# Go Validation
[![GoDoc](https://godoc.org/github.com/go-ozzo/ozzo-validation?status.png)](http://godoc.org/github.com/go-ozzo/ozzo-validation)
[![Build Status](https://travis-ci.org/go-ozzo/ozzo-validation.svg?branch=master)](https://travis-ci.org/go-ozzo/ozzo-validation)
[![Coverage Status](https://coveralls.io/repos/github/go-ozzo/ozzo-validation/badge.svg?branch=master)](https://coveralls.io/github/go-ozzo/ozzo-validation?branch=master)
[![Go Report](https://goreportcard.com/badge/github.com/go-ozzo/ozzo-validation)](https://goreportcard.com/report/github.com/go-ozzo/ozzo-validation)
[![Lint](https://github.com/invopop/validation/actions/workflows/lint.yaml/badge.svg)](https://github.com/invopop/validation/actions/workflows/lint.yaml)
[![Test Go](https://github.com/invopop/validation/actions/workflows/test.yaml/badge.svg)](https://github.com/invopop/validation/actions/workflows/test.yaml)
[![GoDoc](https://godoc.org/github.com/invopop/validation?status.png)](http://godoc.org/github.com/invopop/validation)
[![Coverage Status](https://coveralls.io/repos/github/invopop/validation/badge.svg?branch=main)](https://coveralls.io/github/invopop/validation?branch=main)
[![Go Report](https://goreportcard.com/badge/github.com/invopop/validation)](https://goreportcard.com/report/github.com/invopop/validation)
![Latest Tag](https://img.shields.io/github/v/tag/invopop/validation)
NOTE: This is a fork of the well known [ozzo-validation](https://github.com/go-ozzo/ozzo-validation) package which as of Feb 2023 doesn't appear to be under active maintenance for more than 2 years. At [Invopop](https://invopop.com) we use this library extensively, so it only felt appropriate to be more pro-active. We'll do out best to respond to issues and review or merge any pull requests.
## Description
ozzo-validation is a Go package that provides configurable and extensible data validation capabilities.
validation is a Go package that provides configurable and extensible data validation capabilities.
It has the following features:
* use normal programming constructs rather than error-prone struct tags to specify how data should be validated.
* can validate data of different types, e.g., structs, strings, byte slices, slices, maps, arrays.
* can validate custom data types as long as they implement the `Validatable` interface.
* can validate data types that implement the `sql.Valuer` interface (e.g. `sql.NullString`).
* customizable and well-formatted validation errors.
* error code and message translation support.
* provide a rich set of validation rules right out of box.
* extremely easy to create and use custom validation rules.
- use normal programming constructs rather than error-prone struct tags to specify how data should be validated.
- can validate data of different types, e.g., structs, strings, byte slices, slices, maps, arrays.
- can validate custom data types as long as they implement the `Validatable` interface.
- can validate data types that implement the `sql.Valuer` interface (e.g. `sql.NullString`).
- customizable and well-formatted validation errors.
- error code and message translation support.
- provide a rich set of validation rules right out of box.
- extremely easy to create and use custom validation rules.
For an example on how this library is used in an application, please refer to [go-rest-api](https://github.com/qiangxue/go-rest-api) which is a starter kit for building RESTful APIs in Go.
For further examples, checkout the [GOBL project](https://github.com/invopop/gobl) which uses validation extensively.
## Requirements
Go 1.13 or above.
Go 1.16 or above.
## Getting Started
The ozzo-validation package mainly includes a set of validation rules and two validation methods. You use
The validation package mainly includes a set of validation rules and two validation methods. You use
validation rules to describe how a value should be considered valid, and you call either `validation.Validate()`
or `validation.ValidateStruct()` to validate the value.
### Installation
Run the following command to install the package:
```
go get github.com/go-ozzo/ozzo-validation
go get github.com/invopop/validation
```
### Validating a Simple Value
For a simple value, such as a string or an integer, you may use `validation.Validate()` to validate it. For example,
For a simple value, such as a string or an integer, you may use `validation.Validate()` to validate it. For example,
```go
package main
@@ -51,8 +56,8 @@ package main
import (
"fmt"
"github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
"github.com/invopop/validation"
"github.com/invopop/validation/is"
)
func main() {
@@ -72,13 +77,12 @@ The method `validation.Validate()` will run through the rules in the order that
the validation, the method will return the corresponding error and skip the rest of the rules. The method will
return nil if the value passes all validation rules.
### Validating a Struct
For a struct value, you usually want to check if its fields are valid. For example, in a RESTful application, you
may unmarshal the request payload into a struct and then validate the struct fields. If one or multiple fields
are invalid, you may want to get an error describing which fields are invalid. You can use `validation.ValidateStruct()`
to achieve this purpose. A single struct can have rules for multiple fields, and a field can be associated with multiple
to achieve this purpose. A single struct can have rules for multiple fields, and a field can be associated with multiple
rules. For example,
```go
@@ -91,10 +95,10 @@ type Address struct {
func (a Address) Validate() error {
return validation.ValidateStruct(&a,
// Street cannot be empty, and the length must between 5 and 50
validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
// City cannot be empty, and the length must between 5 and 50
validation.Field(&a.City, validation.Required, validation.Length(5, 50)),
// Street cannot be empty, and the length must between 2 and 50
validation.Field(&a.Street, validation.Required, validation.Length(2, 50)),
// City cannot be empty, and the length must between 2 and 50
validation.Field(&a.City, validation.Required, validation.Length(2, 50)),
// State cannot be empty, and must be a string consisting of two letters in upper case
validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
// State cannot be empty, and must be a string consisting of five digits
@@ -115,19 +119,18 @@ fmt.Println(err)
// Street: the length must be between 5 and 50; State: must be in a valid format.
```
Note that when calling `validation.ValidateStruct` to validate a struct, you should pass to the method a pointer
Note that when calling `validation.ValidateStruct` to validate a struct, you should pass to the method a pointer
to the struct instead of the struct itself. Similarly, when calling `validation.Field` to specify the rules
for a struct field, you should use a pointer to the struct field.
for a struct field, you should use a pointer to the struct field.
When the struct validation is performed, the fields are validated in the order they are specified in `ValidateStruct`.
When the struct validation is performed, the fields are validated in the order they are specified in `ValidateStruct`.
And when each field is validated, its rules are also evaluated in the order they are associated with the field.
If a rule fails, an error is recorded for that field, and the validation will continue with the next field.
### Validating a Map
Sometimes you might need to work with dynamic data stored in maps rather than a typed model. You can use `validation.Map()`
in this situation. A single map can have rules for multiple keys, and a key can be associated with multiple
in this situation. A single map can have rules for multiple keys, and a key can be associated with multiple
rules. For example,
```go
@@ -150,10 +153,10 @@ err := validation.Validate(c,
validation.Key("Email", validation.Required, is.Email),
// Validate Address using its own validation rules
validation.Key("Address", validation.Map(
// Street cannot be empty, and the length must between 5 and 50
validation.Key("Street", validation.Required, validation.Length(5, 50)),
// City cannot be empty, and the length must between 5 and 50
validation.Key("City", validation.Required, validation.Length(5, 50)),
// Street cannot be empty, and the length must between 2 and 50
validation.Key("Street", validation.Required, validation.Length(2, 50)),
// City cannot be empty, and the length must between 2 and 50
validation.Key("City", validation.Required, validation.Length(2, 50)),
// State cannot be empty, and must be a string consisting of two letters in upper case
validation.Key("State", validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
// State cannot be empty, and must be a string consisting of five digits
@@ -166,18 +169,17 @@ fmt.Println(err)
// Address: (State: must be in a valid format; Street: the length must be between 5 and 50.); Email: must be a valid email address.
```
When the map validation is performed, the keys are validated in the order they are specified in `Map`.
When the map validation is performed, the keys are validated in the order they are specified in `Map`.
And when each key is validated, its rules are also evaluated in the order they are associated with the key.
If a rule fails, an error is recorded for that key, and the validation will continue with the next key.
### Validation Errors
The `validation.ValidateStruct` method returns validation errors found in struct fields in terms of `validation.Errors`
The `validation.ValidateStruct` method returns validation errors found in struct fields in terms of `validation.Errors`
which is a map of fields and their corresponding errors. Nil is returned if validation passes.
By default, `validation.Errors` uses the struct tags named `json` to determine what names should be used to
represent the invalid fields. The type also implements the `json.Marshaler` interface so that it can be marshaled
By default, `validation.Errors` uses the struct tags named `json` to determine what names should be used to
represent the invalid fields. The type also implements the `json.Marshaler` interface so that it can be marshaled
into a proper JSON object. For example,
```go
@@ -221,16 +223,15 @@ fmt.Println(err)
// email: must be a valid email address; zip: cannot be blank.
```
In the above example, we build a `validation.Errors` by a list of names and the corresponding validation results.
At the end we call `Errors.Filter()` to remove from `Errors` all nils which correspond to those successful validation
In the above example, we build a `validation.Errors` by a list of names and the corresponding validation results.
At the end we call `Errors.Filter()` to remove from `Errors` all nils which correspond to those successful validation
results. The method will return nil if `Errors` is empty.
The above approach is very flexible as it allows you to freely build up your validation error structure. You can use
it to validate both struct and non-struct values. Compared to using `ValidateStruct` to validate a struct,
it has the drawback that you have to redundantly specify the error keys while `ValidateStruct` can automatically
it to validate both struct and non-struct values. Compared to using `ValidateStruct` to validate a struct,
it has the drawback that you have to redundantly specify the error keys while `ValidateStruct` can automatically
find them out.
### Internal Errors
Internal errors are different from validation errors in that internal errors are caused by malfunctioning code (e.g.
@@ -252,23 +253,22 @@ if err := a.Validate(); err != nil {
}
```
## Validatable Types
A type is validatable if it implements the `validation.Validatable` interface.
A type is validatable if it implements the `validation.Validatable` interface.
When `validation.Validate` is used to validate a validatable value, if it does not find any error with the
given validation rules, it will further call the value's `Validate()` method.
When `validation.Validate` is used to validate a validatable value, if it does not find any error with the
given validation rules, it will further call the value's `Validate()` method.
Similarly, when `validation.ValidateStruct` is validating a struct field whose type is validatable, it will call
Similarly, when `validation.ValidateStruct` is validating a struct field whose type is validatable, it will call
the field's `Validate` method after it passes the listed rules.
> Note: When implementing `validation.Validatable`, do not call `validation.Validate()` to validate the value in its
> original type because this will cause infinite loops. For example, if you define a new type `MyString` as `string`
> and implement `validation.Validatable` for `MyString`, within the `Validate()` function you should cast the value
> and implement `validation.Validatable` for `MyString`, within the `Validate()` function you should cast the value
> to `string` first before calling `validation.Validate()` to validate it.
In the following example, the `Address` field of `Customer` is validatable because `Address` implements
In the following example, the `Address` field of `Customer` is validatable because `Address` implements
`validation.Validatable`. Therefore, when validating a `Customer` struct with `validation.ValidateStruct`,
validation will "dive" into the `Address` field.
@@ -332,8 +332,8 @@ fmt.Println(err)
// 0: (City: cannot be blank; Street: cannot be blank.); 2: (Street: cannot be blank; Zip: must be in a valid format.).
```
When using `validation.ValidateStruct` to validate a struct, the above validation procedure also applies to those struct
fields which are map/slices/arrays of validatables.
When using `validation.ValidateStruct` to validate a struct, the above validation procedure also applies to those struct
fields which are map/slices/arrays of validatables.
#### Each
@@ -376,14 +376,12 @@ If the pointer is nil, these rules will skip the validation.
An exception is the `validation.Required` and `validation.NotNil` rules. When a pointer is nil, they
will report a validation error.
### Types Implementing `sql.Valuer`
If a data type implements the `sql.Valuer` interface (e.g. `sql.NullString`), the built-in validation rules will handle
it properly. In particular, when a rule is validating such data, it will call the `Value()` method and validate
the returned value instead.
### Required vs. Not Nil
When validating input values, there are two different scenarios about checking if input values are provided or not.
@@ -395,7 +393,6 @@ In the second scenario, an input value is considered missing only if it is not e
used in this case so that you can detect if a value is entered or not by checking if the pointer is nil or not.
You can use the `validation.NotNil` rule to ensure a value is entered (even if it is a zero value).
### Embedded Structs
The `validation.ValidateStruct` method will properly validate a struct that contains embedded structs. In particular,
@@ -444,10 +441,9 @@ fmt.Println(err)
// Level: cannot be blank; Name: cannot be blank.
```
### Conditional Validation
Sometimes, we may want to validate a value only when certain condition is met. For example, we want to ensure the
Sometimes, we may want to validate a value only when certain condition is met. For example, we want to ensure the
`unit` struct field is not empty only when the `quantity` field is not empty; or we may want to ensure either `email`
or `phone` is provided. The so-called conditional validation can be achieved with the help of `validation.When`.
The following code implements the aforementioned examples:
@@ -455,8 +451,8 @@ The following code implements the aforementioned examples:
```go
result := validation.ValidateStruct(&a,
validation.Field(&a.Unit, validation.When(a.Quantity != "", validation.Required).Else(validation.Nil)),
validation.Field(&a.Phone, validation.When(a.Email == "", validation.Required.Error('Either phone or Email is required.')),
validation.Field(&a.Email, validation.When(a.Phone == "", validation.Required.Error('Either phone or Email is required.')),
validation.Field(&a.Phone, validation.When(a.Email == "", validation.Required.Error("Either phone or Email is required.")),
validation.Field(&a.Email, validation.When(a.Phone == "", validation.Required.Error("Either phone or Email is required.")),
)
```
@@ -467,8 +463,8 @@ The above code can also be simplified using the shortcut `validation.Required.Wh
```go
result := validation.ValidateStruct(&a,
validation.Field(&a.Unit, validation.Required.When(a.Quantity != ""), validation.Nil.When(a.Quantity == "")),
validation.Field(&a.Phone, validation.Required.When(a.Email == "").Error('Either phone or Email is required.')),
validation.Field(&a.Email, validation.Required.When(a.Phone == "").Error('Either phone or Email is required.')),
validation.Field(&a.Phone, validation.Required.When(a.Email == "").Error("Either phone or Email is required.")),
validation.Field(&a.Email, validation.Required.When(a.Phone == "").Error("Either phone or Email is required.")),
)
```
@@ -488,16 +484,17 @@ fmt.Println(err)
// must be a string with five digits
```
You can also customize the pre-defined error(s) of a built-in rule such that the customization applies to *every*
You can also customize the pre-defined error(s) of a built-in rule such that the customization applies to _every_
instance of the rule. For example, the `Required` rule uses the pre-defined error `ErrRequired`. You can customize it
during the application initialization:
```go
validation.ErrRequired = validation.ErrRequired.SetMessage("the value is required")
validation.ErrRequired = validation.ErrRequired.SetMessage("the value is required")
```
### Error Code and Message Translation
The errors returned by the validation rules implement the `Error` interface which contains the `Code()` method
The errors returned by the validation rules implement the `Error` interface which contains the `Code()` method
to provide the error code information. While the message of a validation error is often customized, the code is immutable.
You can use error code to programmatically check a validation error or look for the translation of the corresponding message.
@@ -549,10 +546,9 @@ fmt.Println(err)
// Output: unexpected string
```
### Rule Groups
When a combination of several rules are used in multiple places, you may use the following trick to create a
When a combination of several rules are used in multiple places, you may use the following trick to create a
rule group so that your code is more maintainable.
```go
@@ -577,21 +573,19 @@ func (u User) Validate() error {
In the above example, we create a rule group `NameRule` which consists of two validation rules. We then use this rule
group to validate both `FirstName` and `LastName`.
## Context-aware Validation
While most validation rules are self-contained, some rules may depend dynamically on a context. A rule may implement the
`validation.RuleWithContext` interface to support the so-called context-aware validation.
To validate an arbitrary value with a context, call `validation.ValidateWithContext()`. The `context.Conext` parameter
To validate an arbitrary value with a context, call `validation.ValidateWithContext()`. The `context.Conext` parameter
will be passed along to those rules that implement `validation.RuleWithContext`.
To validate the fields of a struct with a context, call `validation.ValidateStructWithContext()`.
To validate the fields of a struct with a context, call `validation.ValidateStructWithContext()`.
You can define a context-aware rule from scratch by implementing both `validation.Rule` and `validation.RuleWithContext`.
You can define a context-aware rule from scratch by implementing both `validation.Rule` and `validation.RuleWithContext`.
You can also use `validation.WithContext()` to turn a function into a context-aware rule. For example,
```go
rule := validation.WithContext(func(ctx context.Context, value interface{}) error {
if ctx.Value("secret") == value.(string) {
@@ -609,94 +603,94 @@ fmt.Println(err)
When performing context-aware validation, if a rule does not implement `validation.RuleWithContext`, its
`validation.Rule` will be used instead.
## Built-in Validation Rules
The following rules are provided in the `validation` package:
* `In(...interface{})`: checks if a value can be found in the given list of values.
* `NotIn(...interface{})`: checks if a value is NOT among the given list of values.
* `Length(min, max int)`: checks if the length of a value is within the specified range.
- `In(...interface{})`: checks if a value can be found in the given list of values.
- `NotIn(...interface{})`: checks if a value is NOT among the given list of values.
- `Length(min, max int)`: checks if the length of a value is within the specified range.
This rule should only be used for validating strings, slices, maps, and arrays.
* `RuneLength(min, max int)`: checks if the length of a string is within the specified range.
- `RuneLength(min, max int)`: checks if the length of a string is within the specified range.
This rule is similar as `Length` except that when the value being validated is a string, it checks
its rune length instead of byte length.
* `Min(min interface{})` and `Max(max interface{})`: checks if a value is within the specified range.
- `Min(min interface{})` and `Max(max interface{})`: checks if a value is within the specified range.
These two rules should only be used for validating int, uint, float and time.Time types.
* `Match(*regexp.Regexp)`: checks if a value matches the specified regular expression.
- `Match(*regexp.Regexp)`: checks if a value matches the specified regular expression.
This rule should only be used for strings and byte slices.
* `Date(layout string)`: checks if a string value is a date whose format is specified by the layout.
- `Date(layout string)`: checks if a string value is a date whose format is specified by the layout.
By calling `Min()` and/or `Max()`, you can check additionally if the date is within the specified range.
* `Required`: checks if a value is not empty (neither nil nor zero).
* `NotNil`: checks if a pointer value is not nil. Non-pointer values are considered valid.
* `NilOrNotEmpty`: checks if a value is a nil pointer or a non-empty value. This differs from `Required` in that it treats a nil pointer as valid.
* `Nil`: checks if a value is a nil pointer.
* `Empty`: checks if a value is empty. nil pointers are considered valid.
* `Skip`: this is a special rule used to indicate that all rules following it should be skipped (including the nested ones).
* `MultipleOf`: checks if the value is a multiple of the specified range.
* `Each(rules ...Rule)`: checks the elements within an iterable (map/slice/array) with other rules.
* `When(condition, rules ...Rule)`: validates with the specified rules only when the condition is true.
* `Else(rules ...Rule)`: must be used with `When(condition, rules ...Rule)`, validates with the specified rules only when the condition is false.
- `Required`: checks if a value is not empty (neither nil nor zero).
- `NotNil`: checks if a pointer value is not nil. Non-pointer values are considered valid.
- `NilOrNotEmpty`: checks if a value is a nil pointer or a non-empty value. This differs from `Required` in that it treats a nil pointer as valid.
- `Nil`: checks if a value is a nil pointer.
- `Empty`: checks if a value is empty. nil pointers are considered valid.
- `Skip`: this is a special rule used to indicate that all rules following it should be skipped (including the nested ones).
- `MultipleOf`: checks if the value is a multiple of the specified range.
- `Each(rules ...Rule)`: checks the elements within an iterable (map/slice/array) with other rules.
- `When(condition, rules ...Rule)`: validates with the specified rules only when the condition is true.
- `Else(rules ...Rule)`: must be used with `When(condition, rules ...Rule)`, validates with the specified rules only when the condition is false.
The `is` sub-package provides a list of commonly used string validation rules that can be used to check if the format
of a value satisfies certain requirements. Note that these rules only handle strings and byte slices and if a string
or byte slice is empty, it is considered valid. You may use a `Required` rule to ensure a value is not empty.
or byte slice is empty, it is considered valid. You may use a `Required` rule to ensure a value is not empty.
Below is the whole list of the rules provided by the `is` package:
* `Email`: validates if a string is an email or not. It also checks if the MX record exists for the email domain.
* `EmailFormat`: validates if a string is an email or not. It does NOT check the existence of the MX record.
* `URL`: validates if a string is a valid URL
* `RequestURL`: validates if a string is a valid request URL
* `RequestURI`: validates if a string is a valid request URI
* `Alpha`: validates if a string contains English letters only (a-zA-Z)
* `Digit`: validates if a string contains digits only (0-9)
* `Alphanumeric`: validates if a string contains English letters and digits only (a-zA-Z0-9)
* `UTFLetter`: validates if a string contains unicode letters only
* `UTFDigit`: validates if a string contains unicode decimal digits only
* `UTFLetterNumeric`: validates if a string contains unicode letters and numbers only
* `UTFNumeric`: validates if a string contains unicode number characters (category N) only
* `LowerCase`: validates if a string contains lower case unicode letters only
* `UpperCase`: validates if a string contains upper case unicode letters only
* `Hexadecimal`: validates if a string is a valid hexadecimal number
* `HexColor`: validates if a string is a valid hexadecimal color code
* `RGBColor`: validates if a string is a valid RGB color in the form of rgb(R, G, B)
* `Int`: validates if a string is a valid integer number
* `Float`: validates if a string is a floating point number
* `UUIDv3`: validates if a string is a valid version 3 UUID
* `UUIDv4`: validates if a string is a valid version 4 UUID
* `UUIDv5`: validates if a string is a valid version 5 UUID
* `UUID`: validates if a string is a valid UUID
* `CreditCard`: validates if a string is a valid credit card number
* `ISBN10`: validates if a string is an ISBN version 10
* `ISBN13`: validates if a string is an ISBN version 13
* `ISBN`: validates if a string is an ISBN (either version 10 or 13)
* `JSON`: validates if a string is in valid JSON format
* `ASCII`: validates if a string contains ASCII characters only
* `PrintableASCII`: validates if a string contains printable ASCII characters only
* `Multibyte`: validates if a string contains multibyte characters
* `FullWidth`: validates if a string contains full-width characters
* `HalfWidth`: validates if a string contains half-width characters
* `VariableWidth`: validates if a string contains both full-width and half-width characters
* `Base64`: validates if a string is encoded in Base64
* `DataURI`: validates if a string is a valid base64-encoded data URI
* `E164`: validates if a string is a valid E164 phone number (+19251232233)
* `CountryCode2`: validates if a string is a valid ISO3166 Alpha 2 country code
* `CountryCode3`: validates if a string is a valid ISO3166 Alpha 3 country code
* `DialString`: validates if a string is a valid dial string that can be passed to Dial()
* `MAC`: validates if a string is a MAC address
* `IP`: validates if a string is a valid IP address (either version 4 or 6)
* `IPv4`: validates if a string is a valid version 4 IP address
* `IPv6`: validates if a string is a valid version 6 IP address
* `Subdomain`: validates if a string is valid subdomain
* `Domain`: validates if a string is valid domain
* `DNSName`: validates if a string is valid DNS name
* `Host`: validates if a string is a valid IP (both v4 and v6) or a valid DNS name
* `Port`: validates if a string is a valid port number
* `MongoID`: validates if a string is a valid Mongo ID
* `Latitude`: validates if a string is a valid latitude
* `Longitude`: validates if a string is a valid longitude
* `SSN`: validates if a string is a social security number (SSN)
* `Semver`: validates if a string is a valid semantic version
- `Email`: validates if a string is an email or not. It also checks if the MX record exists for the email domain.
- `EmailFormat`: validates if a string is an email or not. It does NOT check the existence of the MX record.
- `URL`: validates if a string is a valid URL
- `RequestURL`: validates if a string is a valid request URL
- `RequestURI`: validates if a string is a valid request URI
- `Alpha`: validates if a string contains English letters only (a-zA-Z)
- `Digit`: validates if a string contains digits only (0-9)
- `Alphanumeric`: validates if a string contains English letters and digits only (a-zA-Z0-9)
- `UTFLetter`: validates if a string contains unicode letters only
- `UTFDigit`: validates if a string contains unicode decimal digits only
- `UTFLetterNumeric`: validates if a string contains unicode letters and numbers only
- `UTFNumeric`: validates if a string contains unicode number characters (category N) only
- `LowerCase`: validates if a string contains lower case unicode letters only
- `UpperCase`: validates if a string contains upper case unicode letters only
- `Hexadecimal`: validates if a string is a valid hexadecimal number
- `HexColor`: validates if a string is a valid hexadecimal color code
- `RGBColor`: validates if a string is a valid RGB color in the form of rgb(R, G, B)
- `Int`: validates if a string is a valid integer number
- `Float`: validates if a string is a floating point number
- `UUIDv3`: validates if a string is a valid version 3 UUID
- `UUIDv4`: validates if a string is a valid version 4 UUID
- `UUIDv5`: validates if a string is a valid version 5 UUID
- `UUID`: validates if a string is a valid UUID
- `ULID`: validates if a string is a valid ULID
- `CreditCard`: validates if a string is a valid credit card number
- `ISBN10`: validates if a string is an ISBN version 10
- `ISBN13`: validates if a string is an ISBN version 13
- `ISBN`: validates if a string is an ISBN (either version 10 or 13)
- `JSON`: validates if a string is in valid JSON format
- `ASCII`: validates if a string contains ASCII characters only
- `PrintableASCII`: validates if a string contains printable ASCII characters only
- `Multibyte`: validates if a string contains multibyte characters
- `FullWidth`: validates if a string contains full-width characters
- `HalfWidth`: validates if a string contains half-width characters
- `VariableWidth`: validates if a string contains both full-width and half-width characters
- `Base64`: validates if a string is encoded in Base64
- `DataURI`: validates if a string is a valid base64-encoded data URI
- `E164`: validates if a string is a valid E164 phone number (+19251232233)
- `CountryCode2`: validates if a string is a valid ISO3166 Alpha 2 country code
- `CountryCode3`: validates if a string is a valid ISO3166 Alpha 3 country code
- `DialString`: validates if a string is a valid dial string that can be passed to Dial()
- `MAC`: validates if a string is a MAC address
- `IP`: validates if a string is a valid IP address (either version 4 or 6)
- `IPv4`: validates if a string is a valid version 4 IP address
- `IPv6`: validates if a string is a valid version 6 IP address
- `Subdomain`: validates if a string is valid subdomain
- `Domain`: validates if a string is valid domain
- `DNSName`: validates if a string is valid DNS name
- `Host`: validates if a string is a valid IP (both v4 and v6) or a valid DNS name
- `Port`: validates if a string is a valid port number
- `MongoID`: validates if a string is a valid Mongo ID
- `Latitude`: validates if a string is a valid latitude
- `Longitude`: validates if a string is a valid longitude
- `SSN`: validates if a string is a social security number (SSN)
- `Semver`: validates if a string is a valid semantic version
## Credits

View File

@@ -27,7 +27,7 @@ type EachRule struct {
// Validate loops through the given iterable and calls the Ozzo Validate() method for each value.
func (r EachRule) Validate(value interface{}) error {
return r.ValidateWithContext(nil, value)
return r.ValidateWithContext(context.Background(), value)
}
// ValidateWithContext loops through the given iterable and calls the Ozzo ValidateWithContext() method for each value.
@@ -78,10 +78,8 @@ func (r EachRule) getInterface(value reflect.Value) interface{} {
if value.IsNil() {
return nil
}
return value.Elem().Interface()
default:
return value.Interface()
}
return value.Interface()
}
func (r EachRule) getString(value reflect.Value) string {

View File

@@ -10,7 +10,7 @@ import (
"unicode"
"github.com/asaskevich/govalidator"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/invopop/validation"
)
var (
@@ -58,6 +58,8 @@ var (
ErrUUIDv5 = validation.NewError("validation_is_uuid_v5", "must be a valid UUID v5")
// ErrUUID is the error that returns in case of an invalid UUID value.
ErrUUID = validation.NewError("validation_is_uuid", "must be a valid UUID")
// ErrULID is the error that returns in case of an invalid ULID value.
ErrULID = validation.NewError("validation_is_ulid", "must be a valid ULID")
// ErrCreditCard is the error that returns in case of an invalid credit card number.
ErrCreditCard = validation.NewError("validation_is_credit_card", "must be a valid credit card number")
// ErrISBN10 is the error that returns in case of an invalid ISBN-10 value.
@@ -84,7 +86,7 @@ var (
ErrBase64 = validation.NewError("validation_is_base64", "must be encoded in Base64")
// ErrDataURI is the error that returns in case of an invalid data URI.
ErrDataURI = validation.NewError("validation_is_data_uri", "must be a Base64-encoded data URI")
// ErrE164 is the error that returns in case of an invalid e165.
// ErrE164 is the error that returns in case of an invalid e164.
ErrE164 = validation.NewError("validation_is_e164_number", "must be a valid E164 number")
// ErrCountryCode2 is the error that returns in case of an invalid two-letter country code.
ErrCountryCode2 = validation.NewError("validation_is_country_code_2_letter", "must be a valid two-letter country code")
@@ -197,7 +199,7 @@ var (
Base64 = validation.NewStringRuleWithError(govalidator.IsBase64, ErrBase64)
// DataURI validates if a string is a valid base64-encoded data URI
DataURI = validation.NewStringRuleWithError(govalidator.IsDataURI, ErrDataURI)
// E164 validates if a string is a valid ISO3166 Alpha 2 country code
// E164 validates if a string is a valid E164 telephone number
E164 = validation.NewStringRuleWithError(isE164Number, ErrE164)
// CountryCode2 validates if a string is a valid ISO3166 Alpha 2 country code
CountryCode2 = validation.NewStringRuleWithError(govalidator.IsISO3166Alpha2, ErrCountryCode2)
@@ -275,7 +277,7 @@ func isDomain(value string) bool {
func isUTFNumeric(value string) bool {
for _, c := range value {
if unicode.IsNumber(c) == false {
if !unicode.IsNumber(c) {
return false
}
}

View File

@@ -41,10 +41,11 @@ type (
// Use Key() to specify map keys that need to be validated. Each Key() call specifies a single key which can
// be associated with multiple rules.
// For example,
// validation.Map(
// validation.Key("Name", validation.Required),
// validation.Key("Value", validation.Required, validation.Length(5, 10)),
// )
//
// validation.Map(
// validation.Key("Name", validation.Required),
// validation.Key("Value", validation.Required, validation.Length(5, 10)),
// )
//
// A nil value is considered valid. Use the Required rule to make sure a map value is present.
func Map(keys ...*KeyRules) MapRule {
@@ -59,7 +60,7 @@ func (r MapRule) AllowExtraKeys() MapRule {
// Validate checks if the given value is valid or not.
func (r MapRule) Validate(m interface{}) error {
return r.ValidateWithContext(nil, m)
return r.ValidateWithContext(context.Background(), m)
}
// ValidateWithContext checks if the given value is valid or not.

View File

@@ -47,20 +47,20 @@ func (e ErrFieldNotFound) Error() string {
// should be specified as a pointer to the field. A field can be associated with multiple rules.
// For example,
//
// value := struct {
// Name string
// Value string
// }{"name", "demo"}
// err := validation.ValidateStruct(&value,
// validation.Field(&a.Name, validation.Required),
// validation.Field(&a.Value, validation.Required, validation.Length(5, 10)),
// )
// fmt.Println(err)
// // Value: the length must be between 5 and 10.
// value := struct {
// Name string
// Value string
// }{"name", "demo"}
// err := validation.ValidateStruct(&value,
// validation.Field(&a.Name, validation.Required),
// validation.Field(&a.Value, validation.Required, validation.Length(5, 10)),
// )
// fmt.Println(err)
// // Value: the length must be between 5 and 10.
//
// An error will be returned if validation fails.
func ValidateStruct(structPtr interface{}, fields ...*FieldRules) error {
return ValidateStructWithContext(nil, structPtr, fields...)
return ValidateStructWithContext(context.Background(), structPtr, fields...)
}
// ValidateStructWithContext validates a struct with the given context.

View File

@@ -20,7 +20,7 @@ type WhenRule struct {
// Validate checks if the condition is true and if so, it validates the value using the specified rules.
func (r WhenRule) Validate(value interface{}) error {
return r.ValidateWithContext(nil, value)
return r.ValidateWithContext(context.Background(), value)
}
// ValidateWithContext checks if the condition is true and if so, it validates the value using the specified rules.
@@ -28,16 +28,14 @@ func (r WhenRule) ValidateWithContext(ctx context.Context, value interface{}) er
if r.condition {
if ctx == nil {
return Validate(value, r.rules...)
} else {
return ValidateWithContext(ctx, value, r.rules...)
}
return ValidateWithContext(ctx, value, r.rules...)
}
if ctx == nil {
return Validate(value, r.elseRules...)
} else {
return ValidateWithContext(ctx, value, r.elseRules...)
}
return ValidateWithContext(ctx, value, r.elseRules...)
}
// Else returns a validation rule that executes the given list of rules when the condition is false.

8
vendor/modules.txt vendored
View File

@@ -981,10 +981,6 @@ github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus
# github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0
## explicit; go 1.17
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry
# github.com/go-ozzo/ozzo-validation/v4 v4.3.0
## explicit; go 1.13
github.com/go-ozzo/ozzo-validation/v4
github.com/go-ozzo/ozzo-validation/v4/is
# github.com/go-playground/locales v0.14.1
## explicit; go 1.17
github.com/go-playground/locales
@@ -1224,6 +1220,10 @@ github.com/imdario/mergo
# github.com/inconshreveable/mousetrap v1.1.0
## explicit; go 1.18
github.com/inconshreveable/mousetrap
# github.com/invopop/validation v0.3.0
## explicit; go 1.16
github.com/invopop/validation
github.com/invopop/validation/is
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
## explicit
github.com/jbenet/go-context/io