Documentation
¶
Overview ¶
Package validation provides tools for data validation. It is designed to create complex validation rules with abilities to hook into the validation process.
Index ¶
- func Filter(violations ...error) error
- func IsViolation(err error) bool
- func IsViolationList(err error) bool
- type Argument
- func Bool(value *bool, options ...Option) Argument
- func BoolProperty(name string, value *bool, options ...Option) Argument
- func Context(ctx context.Context) Argument
- func Countable(count int, options ...Option) Argument
- func CountableProperty(name string, count int, options ...Option) Argument
- func Each(value interface{}, options ...Option) Argument
- func EachProperty(name string, value interface{}, options ...Option) Argument
- func EachString(values []string, options ...Option) Argument
- func EachStringProperty(name string, values []string, options ...Option) Argument
- func Iterable(value interface{}, options ...Option) Argument
- func IterableProperty(name string, value interface{}, options ...Option) Argument
- func Language(tag language.Tag) Argument
- func NewArgument(options []Option, validate ValidateByConstraintFunc) Argument
- func Number(value interface{}, options ...Option) Argument
- func NumberProperty(name string, value interface{}, options ...Option) Argument
- func PropertyValue(name string, value interface{}, options ...Option) Argument
- func String(value *string, options ...Option) Argument
- func StringProperty(name string, value *string, options ...Option) Argument
- func Time(value *time.Time, options ...Option) Argument
- func TimeProperty(name string, value *time.Time, options ...Option) Argument
- func Valid(value Validatable, options ...Option) Argument
- func ValidProperty(name string, value Validatable, options ...Option) Argument
- func Value(value interface{}, options ...Option) Argument
- type Arguments
- type ArrayIndexElement
- type BoolConstraint
- type ConditionalConstraint
- type Constraint
- type ConstraintAlreadyStoredError
- type ConstraintNotFoundError
- type CountableConstraint
- type CustomStringConstraint
- func (c CustomStringConstraint) Message(message string) CustomStringConstraint
- func (c CustomStringConstraint) Name() string
- func (c CustomStringConstraint) SetUp() error
- func (c CustomStringConstraint) ValidateString(value *string, scope Scope) error
- func (c CustomStringConstraint) When(condition bool) CustomStringConstraint
- type InapplicableConstraintError
- type IterableConstraint
- type NewViolationFunc
- type NilConstraint
- type NotValidatableError
- type NumberConstraint
- type Option
- type PropertyNameElement
- type PropertyPath
- type PropertyPathElement
- type Scope
- type SequentiallyConstraint
- type StringConstraint
- type TemplateParameter
- type TimeConstraint
- type Translator
- type Validatable
- type ValidateByConstraintFunc
- type Validator
- func (validator *Validator) AtIndex(index int) *Validator
- func (validator *Validator) AtProperty(name string) *Validator
- func (validator *Validator) BuildViolation(code, message string) *ViolationBuilder
- func (validator *Validator) Context() context.Context
- func (validator *Validator) Validate(arguments ...Argument) error
- func (validator *Validator) ValidateBool(value *bool, options ...Option) error
- func (validator *Validator) ValidateBy(constraintKey string) Constraint
- func (validator *Validator) ValidateCountable(count int, options ...Option) error
- func (validator *Validator) ValidateEach(value interface{}, options ...Option) error
- func (validator *Validator) ValidateEachString(values []string, options ...Option) error
- func (validator *Validator) ValidateIterable(value interface{}, options ...Option) error
- func (validator *Validator) ValidateNumber(value interface{}, options ...Option) error
- func (validator *Validator) ValidateString(value *string, options ...Option) error
- func (validator *Validator) ValidateTime(value *time.Time, options ...Option) error
- func (validator *Validator) ValidateValidatable(validatable Validatable, options ...Option) error
- func (validator *Validator) ValidateValue(value interface{}, options ...Option) error
- func (validator *Validator) WithContext(ctx context.Context) *Validator
- func (validator *Validator) WithLanguage(tag language.Tag) *Validator
- type ValidatorOption
- type Violation
- type ViolationBuilder
- func (b *ViolationBuilder) AddParameter(name, value string) *ViolationBuilder
- func (b *ViolationBuilder) BuildViolation(code, message string) *ViolationBuilder
- func (b *ViolationBuilder) CreateViolation() Violation
- func (b *ViolationBuilder) SetLanguage(tag language.Tag) *ViolationBuilder
- func (b *ViolationBuilder) SetParameters(parameters []TemplateParameter) *ViolationBuilder
- func (b *ViolationBuilder) SetPluralCount(pluralCount int) *ViolationBuilder
- func (b *ViolationBuilder) SetPropertyPath(path *PropertyPath) *ViolationBuilder
- type ViolationFactory
- type ViolationList
Examples ¶
- ConditionalConstraint.Else
- ConditionalConstraint.Then
- NewArgument (CustomArgumentConstraintValidator)
- NewCustomStringConstraint
- Sequentially
- Validator.AtIndex
- Validator.AtProperty
- Validator.Context (UsingContextWithRecursion)
- Validator.Validate (BasicStructValidation)
- Validator.Validate (BasicValidation)
- Validator.Validate (ConditionalValidationOnConstraint)
- Validator.Validate (CustomConstraint)
- Validator.Validate (CustomizingErrorMessage)
- Validator.Validate (HttpHandler)
- Validator.Validate (PassingPropertyPathViaOptions)
- Validator.Validate (PropertyPathBySpecialArgument)
- Validator.Validate (PropertyPathWithScopedValidator)
- Validator.Validate (SingletonValidator)
- Validator.Validate (TranslationForCustomMessage)
- Validator.Validate (TranslationsByArgument)
- Validator.Validate (TranslationsByContextArgument)
- Validator.Validate (TranslationsByContextValidator)
- Validator.Validate (TranslationsByDefaultLanguage)
- Validator.ValidateBy (CustomServiceConstraint)
- Validator.ValidateString (ShorthandAlias)
- Validator.ValidateValidatable (ValidatableSlice)
- Validator.ValidateValidatable (ValidatableStruct)
- When
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Filter ¶
Filter is used for processing the list of errors to return a single ViolationList. If there is at least one non-violation error it will return it instead.
func IsViolation ¶
IsViolation can be used to verify that the error implements the Violation interface.
func IsViolationList ¶
IsViolationList can be used to verify that the error implements the ViolationList.
Types ¶
type Argument ¶
type Argument interface {
// contains filtered or unexported methods
}
Argument used to set up the validation process. It is used to set up the current validation scope and to pass arguments for validation values.
func Bool ¶
Bool argument is used to validate boolean values.
Example
v := false err := validator.Validate(Bool(&v, it.IsNotBlank()))
func BoolProperty ¶
BoolProperty argument is an alias for Bool that automatically adds property name to the current scope.
func Context ¶
Context can be used to pass context to validation constraints via scope.
Example
err := validator.Validate( Context(request.Context()), String(&s, it.IsNotBlank()), // now all called constraints will use passed context in their methods )
func Countable ¶
Countable argument can be used to validate size of an array, slice, or map. You can pass result of len() function as an argument.
Example
s := []string{"a", "b"} err := validator.Validate(Countable(len(s), it.HasMinCount(3)))
func CountableProperty ¶
CountableProperty argument is an alias for Countable that automatically adds property name to the current scope.
func Each ¶
Each is used to validate each value of iterable (array, slice, or map). At the moment it uses reflection to iterate over values. So you can expect a performance hit using this method. For better performance it is recommended to make a custom type that implements the Validatable interface. Also, you can use EachString argument to validate slice of strings.
Warning! This argument is subject to change in the final versions of the library.
Example
v := []string{""} err := validator.Validate(Each(&v, it.IsNotBlank()))
func EachProperty ¶
EachProperty argument is an alias for Each that automatically adds property name to the current scope.
func EachString ¶
EachString is used to validate a slice of strings. This is a more performant version of Each argument.
Example
v := []string{""} err := validator.Validate(EachString(&v, it.IsNotBlank()))
func EachStringProperty ¶
EachStringProperty argument is an alias for EachString that automatically adds property name to the current scope.
func Iterable ¶
Iterable argument is used to validate arrays, slices, or maps. At the moment it uses reflection to iterate over values. So you can expect a performance hit using this method. For better performance it is recommended to make a custom type that implements the Validatable interface.
Warning! This argument is subject to change in the final versions of the library.
Example
v := []string{} err := validator.Validate(Iterable(&v, it.IsNotBlank()))
func IterableProperty ¶
IterableProperty argument is an alias for Iterable that automatically adds property name to the current scope.
func Language ¶
Language argument sets the current language for translation of a violation message.
Example
err := validator.Validate( Language(language.Russian), String(&s, it.IsNotBlank()), // all violations created in scope will be translated into Russian )
func NewArgument ¶
func NewArgument(options []Option, validate ValidateByConstraintFunc) Argument
NewArgument can be used to implement your own validation arguments for the specific types. See example for more details.
Example (CustomArgumentConstraintValidator) ¶
package main import ( "context" "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) type Brand struct { Name string } type BrandRepository struct { brands []Brand } func (repository *BrandRepository) FindByName(ctx context.Context, name string) ([]Brand, error) { found := make([]Brand, 0) for _, brand := range repository.brands { if brand.Name == name { found = append(found, brand) } } return found, nil } // You can declare you own constraint interface to create custom constraints. type BrandConstraint interface { validation.Constraint ValidateBrand(brand *Brand, scope validation.Scope) error } // To create your own functional argument for validation simply create a function with // a typed value and use the validation.NewArgument constructor. func BrandArgument(brand *Brand, options ...validation.Option) validation.Argument { return validation.NewArgument(options, func(constraint validation.Constraint, scope validation.Scope) error { if c, ok := constraint.(BrandConstraint); ok { return c.ValidateBrand(brand, scope) } // If you want to use built-in constraints for checking for nil or empty values // such as it.IsNil() or it.IsBlank(). if c, ok := constraint.(validation.NilConstraint); ok { if brand == nil { return c.ValidateNil(scope) } return nil } return validation.NewInapplicableConstraintError(constraint, "Brand") }) } // UniqueBrandConstraint implements BrandConstraint. type UniqueBrandConstraint struct { brands *BrandRepository } func (c *UniqueBrandConstraint) SetUp() error { return nil } func (c *UniqueBrandConstraint) Name() string { return "UniqueBrandConstraint" } func (c *UniqueBrandConstraint) ValidateBrand(brand *Brand, scope validation.Scope) error { // usually, you should ignore empty values // to check for an empty value you should use it.NotBlankConstraint if brand == nil { return nil } // you can pass the context value from the scope brands, err := c.brands.FindByName(scope.Context(), brand.Name) // here you can return a service error so that the validation process // is stopped immediately if err != nil { return err } if len(brands) == 0 { return nil } // use the scope to build violation with translations return scope. BuildViolation("notUniqueBrand", `Brand with name "{{ name }}" already exists.`). // you can inject parameter value to the message here AddParameter("{{ name }}", brand.Name). CreateViolation() } func main() { repository := &BrandRepository{brands: []Brand{{"Apple"}, {"Orange"}}} isEntityUnique := &UniqueBrandConstraint{brands: repository} brand := Brand{Name: "Apple"} ctx := context.WithValue(context.Background(), exampleKey, "value") err := validator.Validate( // you can pass here the context value to the validation scope validation.Context(ctx), BrandArgument(&brand, it.IsNotBlank(), isEntityUnique), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: Brand with name "Apple" already exists.
func Number ¶
Number argument is used to validate numbers (any types of integers or floats). At the moment it uses reflection to detect numeric value. Given value is internally converted into int64 or float64 to make comparisons.
Warning! This method will be changed after generics implementation in Go.
Example
v := 0 err := validator.Validate(Number(&v, it.IsNotBlank()))
func NumberProperty ¶
NumberProperty argument is an alias for Number that automatically adds property name to the current scope.
func PropertyValue ¶
PropertyValue argument is an alias for Value that automatically adds property name to the current scope.
func String ¶
String argument is used to validate strings.
Example
v := "" err := validator.Validate(String(&v, it.IsNotBlank()))
func StringProperty ¶
StringProperty argument is an alias for String that automatically adds property name to the current scope.
func Time ¶
Time argument is used to validate time.Time value.
Example
t := time.Now() err := validator.Validate(Time(&t, it.IsNotBlank()))
func TimeProperty ¶
TimeProperty argument is an alias for Time that automatically adds property name to the current scope.
func Valid ¶
func Valid(value Validatable, options ...Option) Argument
Valid is used to run validation on the Validatable type. This method is recommended to run a complex validation process.
func ValidProperty ¶
func ValidProperty(name string, value Validatable, options ...Option) Argument
ValidProperty argument is an alias for Valid that automatically adds property name to the current scope.
func Value ¶
Value argument is used to validate any supported value. It uses reflection to detect the type of the argument and pass it to a specific validation method.
If the validator cannot determine the value or it is not supported, then NotValidatableError will be returned when calling the validator.Validate method.
Example
v := 0 err := validator.Validate(Value(v, it.IsNotBlank()))
type ArrayIndexElement ¶
type ArrayIndexElement int
ArrayIndexElement holds up array index value under PropertyPath.
func (ArrayIndexElement) IsIndex ¶
func (a ArrayIndexElement) IsIndex() bool
IsIndex on ArrayIndexElement always returns true.
func (ArrayIndexElement) String ¶
func (a ArrayIndexElement) String() string
String returns array index values converted into a string.
type BoolConstraint ¶
type BoolConstraint interface { Constraint ValidateBool(value *bool, scope Scope) error }
BoolConstraint is used to build constraints for boolean values validation.
type ConditionalConstraint ¶ added in v0.2.0
type ConditionalConstraint struct {
// contains filtered or unexported fields
}
ConditionalConstraint is used for conditional validation. Use the When function to initiate a conditional check. If the condition is true, then the constraints passed through the Then function will be applied. Otherwise, the constraints passed through the Else function will apply.
func When ¶ added in v0.2.0
func When(condition bool) ConditionalConstraint
When function is used to initiate conditional validation. If the condition is true, then the constraints passed through the Then function will be applied. Otherwise, the constraints passed through the Else function will apply.
Example ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) type File struct { IsDocument bool DocumentName string Name string } func main() { file := File{ IsDocument: true, Name: "file name", } err := validator.Validate( validation.StringProperty( "name", &file.Name, it.IsNotBlank(), ), validation.StringProperty( "documentName", &file.DocumentName, validation.When(file.IsDocument). Then(it.IsNotBlank()), ), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation at 'documentName': This value should not be blank.
func (ConditionalConstraint) Else ¶ added in v0.2.0
func (c ConditionalConstraint) Else(constraints ...Constraint) ConditionalConstraint
Else function is used to set a sequence of constraints to be applied if a condition is false.
Example ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" "regexp" ) func main() { v := "123" err := validator.ValidateString( &v, validation.When(false). Then( it.Matches(regexp.MustCompile(`^\w+$`)), ). Else( it.Matches(regexp.MustCompile(`^\d+$`)), ), ) fmt.Println(err) }
Output: <nil>
func (ConditionalConstraint) Name ¶ added in v0.2.0
func (c ConditionalConstraint) Name() string
Name is the constraint name.
func (ConditionalConstraint) SetUp ¶ added in v0.2.0
func (c ConditionalConstraint) SetUp() error
SetUp will return an error if Then function did not set any constraints.
func (ConditionalConstraint) Then ¶ added in v0.2.0
func (c ConditionalConstraint) Then(constraints ...Constraint) ConditionalConstraint
Then function is used to set a sequence of constraints to be applied if the condition is true. If the list is empty error will be returned.
Example ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" "regexp" ) func main() { v := "foo" err := validator.ValidateString( &v, validation.When(true). Then( it.Matches(regexp.MustCompile(`^\w+$`)), ), ) fmt.Println(err) }
Output: <nil>
type Constraint ¶
type Constraint interface { Option // Name is a constraint name that can be used in internal errors. Name() string }
Constraint is the base interface to build validation constraints.
type ConstraintAlreadyStoredError ¶
type ConstraintAlreadyStoredError struct {
Key string
}
ConstraintAlreadyStoredError is returned when trying to put a constraint in the validator store using an existing key.
func (ConstraintAlreadyStoredError) Error ¶
func (err ConstraintAlreadyStoredError) Error() string
type ConstraintNotFoundError ¶
type ConstraintNotFoundError struct {
Key string
}
ConstraintNotFoundError is returned when trying to get a constraint from the validator store using a non-existent key.
func (ConstraintNotFoundError) Error ¶
func (err ConstraintNotFoundError) Error() string
type CountableConstraint ¶
type CountableConstraint interface { Constraint ValidateCountable(count int, scope Scope) error }
CountableConstraint is used to build constraints for simpler validation of iterable elements count.
type CustomStringConstraint ¶ added in v0.2.0
type CustomStringConstraint struct {
// contains filtered or unexported fields
}
CustomStringConstraint can be used to create custom constraints for validating string values based on function with signature func(string) bool.
func NewCustomStringConstraint ¶ added in v0.2.0
func NewCustomStringConstraint(isValid func(string) bool, parameters ...string) CustomStringConstraint
NewCustomStringConstraint creates a new string constraint from a function with signature func(string) bool. Optional parameters can be used to set up constraint name (first parameter), violation code (second), message template (third). All other parameters are ignored.
Example ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/validator" ) func main() { validate := func(s string) bool { return s == "valid" } constraint := validation.NewCustomStringConstraint( validate, "ExampleConstraint", // constraint name "exampleCode", // violation code "Unexpected value.", // violation message template ) s := "foo" err := validator.ValidateString(&s, constraint) fmt.Println(err) }
Output: violation: Unexpected value.
func (CustomStringConstraint) Message ¶ added in v0.2.0
func (c CustomStringConstraint) Message(message string) CustomStringConstraint
Message sets the violation message template. You can use template parameters for injecting its values into the final message:
{{ value }} - the current (invalid) value.
func (CustomStringConstraint) Name ¶ added in v0.2.0
func (c CustomStringConstraint) Name() string
Name is the constraint name. It can be set via first parameter of function NewCustomStringConstraint.
func (CustomStringConstraint) SetUp ¶ added in v0.2.0
func (c CustomStringConstraint) SetUp() error
SetUp always returns no error.
func (CustomStringConstraint) ValidateString ¶ added in v0.2.0
func (c CustomStringConstraint) ValidateString(value *string, scope Scope) error
func (CustomStringConstraint) When ¶ added in v0.2.0
func (c CustomStringConstraint) When(condition bool) CustomStringConstraint
When enables conditional validation of this constraint. If the expression evaluates to false, then the constraint will be ignored.
type InapplicableConstraintError ¶
type InapplicableConstraintError struct { Constraint Constraint ValueType string }
InapplicableConstraintError occurs when trying to use constraint on not applicable values. For example, if you are trying to compare slice with a number.
func NewInapplicableConstraintError ¶
func NewInapplicableConstraintError(constraint Constraint, valueType string) InapplicableConstraintError
NewInapplicableConstraintError helps to create a error on trying to use constraint on not applicable values.
func (InapplicableConstraintError) Error ¶
func (err InapplicableConstraintError) Error() string
type IterableConstraint ¶
type IterableConstraint interface { Constraint ValidateIterable(value generic.Iterable, scope Scope) error }
IterableConstraint is used to build constraints for validation of iterables (arrays, slices, or maps).
At this moment working with numbers is based on reflection. Be aware. This constraint is subject to be changed after generics implementation in Go.
type NewViolationFunc ¶
type NewViolationFunc func( code, messageTemplate string, pluralCount int, parameters []TemplateParameter, propertyPath *PropertyPath, lang language.Tag, ) Violation
NewViolationFunc is an adapter that allows you to use ordinary functions as a ViolationFactory.
func (NewViolationFunc) CreateViolation ¶
func (f NewViolationFunc) CreateViolation( code, messageTemplate string, pluralCount int, parameters []TemplateParameter, propertyPath *PropertyPath, lang language.Tag, ) Violation
CreateViolation creates a new instance of a Violation.
type NilConstraint ¶
type NilConstraint interface { Constraint ValidateNil(scope Scope) error }
NilConstraint is used for constraints that need to check value for nil. In common case you do not need to implement it in your constraints because nil values should be ignored.
type NotValidatableError ¶
NotValidatableError occurs when validator cannot determine type by reflection or it is not supported by validator.
func (NotValidatableError) Error ¶
func (err NotValidatableError) Error() string
type NumberConstraint ¶
type NumberConstraint interface { Constraint ValidateNumber(value generic.Number, scope Scope) error }
NumberConstraint is used to build constraints for numeric values validation.
At this moment working with numbers is based on reflection. Be aware. This constraint is subject to be changed after generics implementation in Go.
type Option ¶
type Option interface { // SetUp is called when the validation process is initialized // and can be used to gracefully handle errors when initializing constraints. SetUp() error }
Option is used to set up validation process of a value.
func ArrayIndex ¶
ArrayIndex option adds index of the given array to current validation scope.
func PropertyName ¶
PropertyName option adds name of the given property to current validation scope.
type PropertyNameElement ¶
type PropertyNameElement string
PropertyNameElement holds up property name value under PropertyPath.
func (PropertyNameElement) IsIndex ¶
func (p PropertyNameElement) IsIndex() bool
IsIndex on PropertyNameElement always returns false.
func (PropertyNameElement) String ¶
func (p PropertyNameElement) String() string
String returns property name as is.
type PropertyPath ¶
type PropertyPath struct {
// contains filtered or unexported fields
}
PropertyPath is generated by the validator and indicates how it reached the invalid value from the root element. Property path is denoted by dots, while array access is denoted by square brackets. For example, "book.keywords[0]" means that the violation occurred on the first element of array "keywords" in the "book" object.
Internally PropertyPath is a linked list. You can create a new path using WithProperty or WithIndex methods. PropertyPath should always be used as a pointer value. Nil value is a valid value that means that the property path is empty.
func NewPropertyPath ¶ added in v0.2.0
func NewPropertyPath(elements ...PropertyPathElement) *PropertyPath
NewPropertyPath creates a PropertyPath from the list of elements. If the list is empty nil will be returned. Nil value is a valid value that means that the property path is empty.
func (*PropertyPath) MarshalJSON ¶
func (path *PropertyPath) MarshalJSON() ([]byte, error)
MarshalJSON will marshal property path value to a JSON string.
func (*PropertyPath) String ¶
func (path *PropertyPath) String() string
String is used to format property path to a string.
func (*PropertyPath) WithIndex ¶ added in v0.2.0
func (path *PropertyPath) WithIndex(index int) *PropertyPath
WithIndex returns new PropertyPath with appended ArrayIndexElement to the end of the list.
func (*PropertyPath) WithProperty ¶ added in v0.2.0
func (path *PropertyPath) WithProperty(name string) *PropertyPath
WithProperty returns new PropertyPath with appended PropertyNameElement to the end of the list.
type PropertyPathElement ¶
type PropertyPathElement interface { // IsIndex can be used to determine whether an element is a string (property name) or // an index array. IsIndex() bool fmt.Stringer }
PropertyPathElement is a part of the PropertyPath.
type Scope ¶
type Scope struct {
// contains filtered or unexported fields
}
Scope holds the current state of validation. On the client-side of the package, it can be used to build violations.
func (Scope) BuildViolation ¶
func (s Scope) BuildViolation(code, message string) *ViolationBuilder
BuildViolation is used to create violations in validation methods of constraints. This method automatically injects the property path and language of the current validation scope.
type SequentiallyConstraint ¶ added in v0.2.0
type SequentiallyConstraint struct {
// contains filtered or unexported fields
}
SequentiallyConstraint is used to set constraints allowing to interrupt the validation once the first violation is raised.
func Sequentially ¶ added in v0.2.0
func Sequentially(constraints ...Constraint) SequentiallyConstraint
Sequentially function used to set of constraints that should be validated step-by-step. If the list is empty error will be returned.
Example ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { title := "bar" err := validator.ValidateString( &title, validation.Sequentially( it.IsBlank(), it.HasMinLength(5), ), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: This value should be blank.
func (SequentiallyConstraint) Name ¶ added in v0.2.0
func (c SequentiallyConstraint) Name() string
Name is the constraint name.
func (SequentiallyConstraint) SetUp ¶ added in v0.2.0
func (c SequentiallyConstraint) SetUp() error
SetUp will return an error if the list of constraints is empty.
type StringConstraint ¶
type StringConstraint interface { Constraint ValidateString(value *string, scope Scope) error }
StringConstraint is used to build constraints for string values validation.
type TemplateParameter ¶
type TemplateParameter struct { // Key is the marker in the string that will be replaced by value. // In general, it is recommended to use double curly braces around the key name. // Example: {{ keyName }} Key string // Value is set by constraint when building violation. Value string }
TemplateParameter is injected into the message while rendering the template.
type TimeConstraint ¶
type TimeConstraint interface { Constraint ValidateTime(value *time.Time, scope Scope) error }
TimeConstraint is used to build constraints for date/time validation.
type Translator ¶
type Translator struct {
// contains filtered or unexported fields
}
Translator is used as internal mechanism of message translations. It is bases on the "golang.org/x/text" package.
type Validatable ¶
Validatable is interface for creating validatable types on the client side. By using it you can build complex validation rules on a set of objects used in other objects.
Example
type Book struct { Title string Author string Keywords []string } func (b Book) Validate(validator *validation.Validator) error { return validator.Validate( validation.StringProperty("title", &b.Title, it.IsNotBlank()), validation.StringProperty("author", &b.Author, it.IsNotBlank()), validation.CountableProperty("keywords", len(b.Keywords), it.HasCountBetween(1, 10)), validation.EachStringProperty("keywords", b.Keywords, it.IsNotBlank()), ) }
type ValidateByConstraintFunc ¶
type ValidateByConstraintFunc func(constraint Constraint, scope Scope) error
ValidateByConstraintFunc is used for building validation functions for the values of specific types.
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator is the main validation service. It can be created by NewValidator constructor. Also, you can use singleton version from the package "github.com/muonsoft/validation/validator".
func NewValidator ¶
func NewValidator(options ...ValidatorOption) (*Validator, error)
NewValidator is a constructor for creating an instance of Validator. You can configure it by using validator options.
Example
validator, err := validation.NewValidator( validation.DefaultLanguage(language.Russian), // passing default language of translations validation.Translations(russian.Messages), // setting up custom or built-in translations validation.SetViolationFactory(userViolationFactory), // if you want to override creation of violations ) // don't forget to check for errors if err != nil { fmt.Println(err) }
func (*Validator) AtIndex ¶
AtIndex method creates a new scoped validator with injected array index element to scope property path.
Example ¶
books := []Book{{Title: ""}} err := validator.AtIndex(0).Validate( validation.StringProperty("title", &books[0].Title, it.IsNotBlank()), ) violation := err.(validation.ViolationList)[0] fmt.Println("property path:", violation.PropertyPath().String())
Output: property path: [0].title
func (*Validator) AtProperty ¶
AtProperty method creates a new scoped validator with injected property name element to scope property path.
Example ¶
book := &Book{Title: ""} err := validator.AtProperty("book").Validate( validation.StringProperty("title", &book.Title, it.IsNotBlank()), ) violation := err.(validation.ViolationList)[0] fmt.Println("property path:", violation.PropertyPath().String())
Output: property path: book.title
func (*Validator) BuildViolation ¶
func (validator *Validator) BuildViolation(code, message string) *ViolationBuilder
BuildViolation can be used to build a custom violation on the client-side.
Example
err := validator.BuildViolation("", ""). AddParameter("key", "value"). CreateViolation()
func (*Validator) Context ¶ added in v0.2.0
Context returns context from current validation scope. By default it returns context.Background. You can create scoped validator with context by calling WithContext method.
Example (UsingContextWithRecursion) ¶
package main import ( "context" "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) // It is recommended to make a custom constraint to check for nesting limit. type NestingLimitConstraint struct { limit int } func (c NestingLimitConstraint) SetUp() error { return nil } func (c NestingLimitConstraint) Name() string { return "NestingLimitConstraint" } func (c NestingLimitConstraint) ValidateProperty(property *Property, scope validation.Scope) error { // You can read any passed context value from scope. level, ok := scope.Context().Value(nestingLevelKey).(int) if !ok { // Don't forget to handle missing value. return fmt.Errorf("nesting level not found in context") } if level >= c.limit { return scope. BuildViolation("nestingLimitReached", "Maximum nesting level reached."). CreateViolation() } return nil } func ItIsNotDeeperThan(limit int) NestingLimitConstraint { return NestingLimitConstraint{limit: limit} } // Properties can be nested. type Property struct { Name string Properties []Property } // You can declare you own constraint interface to create custom constraints. type PropertyConstraint interface { validation.Constraint ValidateProperty(property *Property, scope validation.Scope) error } // To create your own functional argument for validation simply create a function with // a typed value and use the validation.NewArgument constructor. func PropertyArgument(property *Property, options ...validation.Option) validation.Argument { return validation.NewArgument(options, func(constraint validation.Constraint, scope validation.Scope) error { if c, ok := constraint.(PropertyConstraint); ok { return c.ValidateProperty(property, scope) } // If you want to use built-in constraints for checking for nil or empty values // such as it.IsNil() or it.IsBlank(). if c, ok := constraint.(validation.NilConstraint); ok { if property == nil { return c.ValidateNil(scope) } return nil } return validation.NewInapplicableConstraintError(constraint, "Property") }) } type recursionKey string const nestingLevelKey recursionKey = "nestingLevel" func (p Property) Validate(validator *validation.Validator) error { // Incrementing nesting level in context with special function. ctx := contextWithNextNestingLevel(validator.Context()) return validator.WithContext(ctx).Validate( // Executing validation for maximum nesting level of properties. PropertyArgument(&p, ItIsNotDeeperThan(3)), validation.StringProperty("name", &p.Name, it.IsNotBlank()), // This should run recursive validation for properties. validation.IterableProperty("properties", p.Properties), ) } // This function increments current nesting level. func contextWithNextNestingLevel(ctx context.Context) context.Context { level, ok := ctx.Value(nestingLevelKey).(int) if !ok { level = -1 } return context.WithValue(ctx, nestingLevelKey, level+1) } func main() { properties := []Property{ { Name: "top", Properties: []Property{ { Name: "middle", Properties: []Property{ { Name: "low", Properties: []Property{ // This property should cause a violation. {Name: "limited"}, }, }, }, }, }, }, } err := validator.ValidateIterable(properties) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation at '[0].properties[0].properties[0].properties[0]': Maximum nesting level reached.
func (*Validator) Validate ¶
Validate is the main validation method. It accepts validation arguments. Arguments can be used to tune up the validation process or to pass values of a specific type.
Example (BasicStructValidation) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) type Document struct { Title string Keywords []string } func main() { document := Document{ Title: "", Keywords: []string{""}, } err := validator.Validate( validation.StringProperty("title", &document.Title, it.IsNotBlank()), validation.CountableProperty("keywords", len(document.Keywords), it.HasCountBetween(2, 10)), validation.EachStringProperty("keywords", document.Keywords, it.IsNotBlank()), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation at 'title': This value should not be blank. violation at 'keywords': This collection should contain 2 elements or more. violation at 'keywords[0]': This value should not be blank.
Example (BasicValidation) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" ) func main() { s := "" validator, _ := validation.NewValidator() err := validator.Validate(validation.String(&s, it.IsNotBlank())) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: This value should not be blank.
Example (ConditionalValidationOnConstraint) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { notes := []struct { Title string IsPublic bool Text string }{ {Title: "published note", IsPublic: true, Text: "text of published note"}, {Title: "draft note", IsPublic: true, Text: ""}, } for i, note := range notes { err := validator.Validate( validation.StringProperty("name", ¬e.Title, it.IsNotBlank()), validation.StringProperty("text", ¬e.Text, it.IsNotBlank().When(note.IsPublic)), ) if err != nil { violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Printf("error on note %d: %s", i, violation.Error()) } } } }
Output: error on note 1: violation at 'text': This value should not be blank.
Example (CustomConstraint) ¶
package main import ( "fmt" "regexp" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) type NumericConstraint struct { matcher *regexp.Regexp } // it is recommended to use semantic constructors for constraints. func IsNumeric() NumericConstraint { return NumericConstraint{matcher: regexp.MustCompile("^[0-9]+$")} } func (c NumericConstraint) SetUp() error { // you may return errors here on the constraint initialization process return nil } func (c NumericConstraint) Name() string { return "NumericConstraint" } func (c NumericConstraint) ValidateString(value *string, scope validation.Scope) error { // usually, you should ignore empty values // to check for an empty value you should use it.NotBlankConstraint if value == nil || *value == "" { return nil } if c.matcher.MatchString(*value) { return nil } // use the scope to build violation with translations return scope.BuildViolation("notNumeric", "This value should be numeric.").CreateViolation() } func main() { s := "alpha" err := validator.Validate( validation.String(&s, it.IsNotBlank(), IsNumeric()), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: This value should be numeric.
Example (CustomizingErrorMessage) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { s := "" err := validator.ValidateString(&s, it.IsNotBlank().Message("this value is required")) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: this value is required
Example (HttpHandler) ¶
package main import ( "encoding/json" "fmt" "net/http" "net/http/httptest" "strings" languagepkg "github.com/muonsoft/language" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/message/translations/russian" "golang.org/x/text/language" ) type Book struct { Title string `json:"title"` Author string `json:"author"` Keywords []string `json:"keywords"` } func (b Book) Validate(validator *validation.Validator) error { return validator.Validate( validation.StringProperty("title", &b.Title, it.IsNotBlank()), validation.StringProperty("author", &b.Author, it.IsNotBlank()), validation.CountableProperty("keywords", len(b.Keywords), it.HasCountBetween(1, 10)), validation.EachStringProperty("keywords", b.Keywords, it.IsNotBlank()), ) } func HandleBooks(writer http.ResponseWriter, request *http.Request) { var book Book err := json.NewDecoder(request.Body).Decode(&book) if err != nil { http.Error(writer, "invalid request", http.StatusBadRequest) return } // setting up validator validator, err := validation.NewValidator(validation.Translations(russian.Messages)) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } err = validator.WithContext(request.Context()).ValidateValidatable(book) if err != nil { violations, ok := validation.UnwrapViolationList(err) if ok { response, _ := json.Marshal(violations) writer.WriteHeader(http.StatusUnprocessableEntity) writer.Header().Set("Content-Type", "application/json") writer.Write(response) return } http.Error(writer, err.Error(), http.StatusInternalServerError) return } // handle valid book writer.WriteHeader(http.StatusCreated) writer.Write([]byte("ok")) } func main() { var handler http.Handler handler = http.HandlerFunc(HandleBooks) // middleware set up: we need to set supported languages // detected language will be passed via request context handler = languagepkg.NewMiddleware(handler, languagepkg.SupportedLanguages(language.English, language.Russian)) // creating request with the language-specific header request := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{}`)) request.Header.Set("Accept-Language", "ru") recorder := httptest.NewRecorder() handler.ServeHTTP(recorder, request) // recorded response should contain array of violations fmt.Println(recorder.Body.String()) }
Output: [{"code":"notBlank","message":"Значение не должно быть пустым.","propertyPath":"title"},{"code":"notBlank","message":"Значение не должно быть пустым.","propertyPath":"author"},{"code":"countTooFew","message":"Эта коллекция должна содержать 1 элемент или больше.","propertyPath":"keywords"}]
Example (PassingPropertyPathViaOptions) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { s := "" err := validator.Validate( validation.String( &s, validation.PropertyName("properties"), validation.ArrayIndex(1), validation.PropertyName("tag"), it.IsNotBlank(), ), ) violation := err.(validation.ViolationList)[0] fmt.Println("property path:", violation.PropertyPath().String()) }
Output: property path: properties[1].tag
Example (PropertyPathBySpecialArgument) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { s := "" err := validator.Validate( // this is an alias for // validation.String(&s, validation.PropertyName("property"), it.IsNotBlank()), validation.StringProperty("property", &s, it.IsNotBlank()), ) violation := err.(validation.ViolationList)[0] fmt.Println("property path:", violation.PropertyPath().String()) }
Output: property path: property
Example (PropertyPathWithScopedValidator) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { s := "" err := validator. AtProperty("properties"). AtIndex(1). AtProperty("tag"). Validate(validation.String(&s, it.IsNotBlank())) violation := err.(validation.ViolationList)[0] fmt.Println("property path:", violation.PropertyPath().String()) }
Output: property path: properties[1].tag
Example (SingletonValidator) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { // import "github.com/muonsoft/validation/validator" s := "" err := validator.Validate(validation.String(&s, it.IsNotBlank())) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: This value should not be blank.
Example (TranslationForCustomMessage) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "golang.org/x/text/feature/plural" "golang.org/x/text/language" "golang.org/x/text/message/catalog" ) func main() { const customMessage = "tags should contain more than {{ limit }} element(s)" validator, _ := validation.NewValidator( validation.Translations(map[language.Tag]map[string]catalog.Message{ language.Russian: { customMessage: plural.Selectf(1, "", plural.One, "теги должны содержать {{ limit }} элемент и более", plural.Few, "теги должны содержать более {{ limit }} элемента", plural.Other, "теги должны содержать более {{ limit }} элементов"), }, }), ) var tags []string err := validator.Validate( validation.Language(language.Russian), validation.Iterable(tags, it.HasMinCount(1).MinMessage(customMessage)), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: теги должны содержать 1 элемент и более
Example (TranslationsByArgument) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/message/translations/russian" "golang.org/x/text/language" ) func main() { validator, _ := validation.NewValidator( validation.Translations(russian.Messages), ) s := "" err := validator.Validate( validation.Language(language.Russian), validation.String(&s, it.IsNotBlank()), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: Значение не должно быть пустым.
Example (TranslationsByContextArgument) ¶
package main import ( "context" "fmt" languagepkg "github.com/muonsoft/language" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/message/translations/russian" "golang.org/x/text/language" ) func main() { validator, _ := validation.NewValidator( validation.Translations(russian.Messages), ) s := "" ctx := languagepkg.WithContext(context.Background(), language.Russian) err := validator.Validate( validation.Context(ctx), validation.String(&s, it.IsNotBlank()), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: Значение не должно быть пустым.
Example (TranslationsByContextValidator) ¶
package main import ( "context" "fmt" languagepkg "github.com/muonsoft/language" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/message/translations/russian" "golang.org/x/text/language" ) func main() { validator, _ := validation.NewValidator( validation.Translations(russian.Messages), ) ctx := languagepkg.WithContext(context.Background(), language.Russian) validator = validator.WithContext(ctx) s := "" err := validator.ValidateString(&s, it.IsNotBlank()) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: Значение не должно быть пустым.
Example (TranslationsByDefaultLanguage) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/message/translations/russian" "golang.org/x/text/language" ) func main() { validator, _ := validation.NewValidator( validation.Translations(russian.Messages), validation.DefaultLanguage(language.Russian), ) s := "" err := validator.ValidateString(&s, it.IsNotBlank()) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: Значение не должно быть пустым.
func (*Validator) ValidateBool ¶
ValidateBool is an alias for validating a single boolean value.
func (*Validator) ValidateBy ¶
func (validator *Validator) ValidateBy(constraintKey string) Constraint
ValidateBy is used to get the constraint from the internal validator store. If the constraint does not exist, then the validator will return a ConstraintNotFoundError during the validation process. For storing a constraint you should use the StoreConstraint method.
Example (CustomServiceConstraint) ¶
package main import ( "context" "errors" "fmt" "log" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" ) type contextKey string const exampleKey contextKey = "exampleKey" type TagStorage struct { // this might be stored in the database tags []string } func (storage *TagStorage) FindByName(ctx context.Context, name string) ([]string, error) { contextValue, ok := ctx.Value(exampleKey).(string) if !ok { return nil, errors.New("context value missing") } if contextValue != "value" { return nil, errors.New("invalid context value") } found := make([]string, 0) for _, tag := range storage.tags { if tag == name { found = append(found, tag) } } return found, nil } type ExistingTagConstraint struct { storage *TagStorage } func (c *ExistingTagConstraint) SetUp() error { return nil } func (c *ExistingTagConstraint) Name() string { return "ExistingTagConstraint" } func (c *ExistingTagConstraint) ValidateString(value *string, scope validation.Scope) error { // usually, you should ignore empty values // to check for an empty value you should use it.NotBlankConstraint if value == nil || *value == "" { return nil } // you can pass the context value from the scope entities, err := c.storage.FindByName(scope.Context(), *value) // here you can return a service error so that the validation process // is stopped immediately if err != nil { return err } if len(entities) > 0 { return nil } // use the scope to build violation with translations return scope. BuildViolation("unknownTag", `Tag "{{ value }}" does not exist.`). // you can inject parameter value to the message here AddParameter("{{ value }}", *value). CreateViolation() } type StockItem struct { Name string Tags []string } func (s StockItem) Validate(validator *validation.Validator) error { return validator.Validate( validation.StringProperty("name", &s.Name, it.IsNotBlank(), it.HasMaxLength(20)), validation.EachStringProperty("tags", s.Tags, validator.ValidateBy("isTagExists")), ) } func main() { storage := &TagStorage{tags: []string{"movie", "book"}} isTagExists := &ExistingTagConstraint{storage: storage} // custom constraint can be stored in the validator's internal store // and can be used later by calling the validator.ValidateBy method validator, err := validation.NewValidator( validation.StoredConstraint("isTagExists", isTagExists), ) if err != nil { log.Fatal(err) } item := StockItem{ Name: "War and peace", Tags: []string{"book", "camera"}, } ctx := context.WithValue(context.Background(), exampleKey, "value") err = validator.Validate( // you can pass here the context value to the validation scope validation.Context(ctx), validation.Valid(item), ) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation at 'tags[1]': Tag "camera" does not exist.
func (*Validator) ValidateCountable ¶
ValidateCountable is an alias for validating a single countable value (an array, slice, or map).
func (*Validator) ValidateEach ¶
ValidateEach is an alias for validating each value of an iterable (an array, slice, or map).
func (*Validator) ValidateEachString ¶
ValidateEachString is an alias for validating each value of a strings slice.
func (*Validator) ValidateIterable ¶
ValidateIterable is an alias for validating a single iterable value (an array, slice, or map).
func (*Validator) ValidateNumber ¶
ValidateNumber is an alias for validating a single numeric value (integer or float).
func (*Validator) ValidateString ¶
ValidateString is an alias for validating a single string value.
Example (ShorthandAlias) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) func main() { s := "" err := validator.ValidateString(&s, it.IsNotBlank()) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation: This value should not be blank.
func (*Validator) ValidateTime ¶
ValidateTime is an alias for validating a single time value.
func (*Validator) ValidateValidatable ¶
func (validator *Validator) ValidateValidatable(validatable Validatable, options ...Option) error
ValidateValidatable is an alias for validating value that implements the Validatable interface.
Example (ValidatableSlice) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) type Company struct { Name string Address string } type Companies []Company func (companies Companies) Validate(validator *validation.Validator) error { violations := validation.ViolationList{} for i, company := range companies { err := validator.AtIndex(i).Validate( validation.StringProperty("name", &company.Name, it.IsNotBlank()), validation.StringProperty("address", &company.Address, it.IsNotBlank(), it.HasMinLength(3)), ) // appending violations from err err = violations.AppendFromError(err) // if append returns a non-nil error we should stop validation because an internal error occurred if err != nil { return err } } // we should always convert ViolationList into error by calling the AsError method // otherwise empty violations list will be interpreted as an error return violations.AsError() } func main() { companies := Companies{ {"MuonSoft", "London"}, {"", "x"}, } err := validator.ValidateValidatable(companies) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation at '[1].name': This value should not be blank. violation at '[1].address': This value is too short. It should have 3 characters or more.
Example (ValidatableStruct) ¶
package main import ( "fmt" "github.com/muonsoft/validation" "github.com/muonsoft/validation/it" "github.com/muonsoft/validation/validator" ) type Product struct { Name string Tags []string Components []Component } func (p Product) Validate(validator *validation.Validator) error { return validator.Validate( validation.StringProperty("name", &p.Name, it.IsNotBlank()), validation.IterableProperty("tags", p.Tags, it.HasMinCount(1)), // this also runs validation on each of the components validation.IterableProperty("components", p.Components, it.HasMinCount(1)), ) } type Component struct { ID int Name string Tags []string } func (c Component) Validate(validator *validation.Validator) error { return validator.Validate( validation.StringProperty("name", &c.Name, it.IsNotBlank()), validation.CountableProperty("tags", len(c.Tags), it.HasMinCount(1)), ) } func main() { p := Product{ Name: "", Components: []Component{ { ID: 1, Name: "", }, }, } err := validator.ValidateValidatable(p) violations := err.(validation.ViolationList) for _, violation := range violations { fmt.Println(violation.Error()) } }
Output: violation at 'name': This value should not be blank. violation at 'tags': This collection should contain 1 element or more. violation at 'components[0].name': This value should not be blank. violation at 'components[0].tags': This collection should contain 1 element or more.
func (*Validator) ValidateValue ¶
ValidateValue is an alias for validating a single value of any supported type.
func (*Validator) WithContext ¶
WithContext method creates a new scoped validator with a given context. You can use this method to pass a context value to all used constraints.
Example
err := validator.WithContext(request.Context()).Validate( String(&s, it.IsNotBlank()), // now all called constraints will use passed context in their methods )
func (*Validator) WithLanguage ¶
WithLanguage method creates a new scoped validator with a given language tag. All created violations will be translated into this language.
Example
err := validator.WithLanguage(language.Russian).Validate( validation.ValidateString(&s, it.IsNotBlank()), // violation from this constraint will be translated )
type ValidatorOption ¶
ValidatorOption is a base type for configuration options used to create a new instance of Validator.
func DefaultLanguage ¶
func DefaultLanguage(tag language.Tag) ValidatorOption
DefaultLanguage is used to set up the default language for translation of violation messages.
func SetViolationFactory ¶
func SetViolationFactory(factory ViolationFactory) ValidatorOption
SetViolationFactory can be used to override the mechanism of violation creation.
func StoredConstraint ¶ added in v0.2.0
func StoredConstraint(key string, constraint Constraint) ValidatorOption
StoredConstraint option can be used to store a constraint in an internal validator store. It can later be used by the validator.ValidateBy method. This can be useful for passing custom or prepared constraints to Validatable.
If the constraint already exists, a ConstraintAlreadyStoredError will be returned.
Example
validator, err := validation.NewValidator( validation.StoredConstraint("isTagExists", isTagExistsConstraint) ) if err != nil { log.Fatal(err) } s := " err = validator.ValidateString(&s, validator.ValidateBy("isTagExists"))
func Translations ¶
Translations is used to load translation messages into the validator.
By default, all violation messages are generated in the English language with pluralization capabilities. To use a custom language you have to load translations on validator initialization. Built-in translations are available in the sub-packages of the package "github.com/muonsoft/message/translations". The translation mechanism is provided by the "golang.org/x/text" package (be aware, it has no stable version yet).
Example
// import "github.com/muonsoft/validation/message/translations/russian" validator, err := validation.NewValidator( validation.Translations(russian.Messages), )
type Violation ¶
type Violation interface { error // Code is unique, short, and semantic string that can be used to programmatically // test for specific violation. All "code" values are defined in the "github.com/muonsoft/validation/code" package // and are protected by backward compatibility rules. Code() string // Is can be used to check that the violation contains one of the specific codes. // For an empty list, it should always returns false. Is(codes ...string) bool // Message is a translated message with injected values from constraint. It can be used to show // a description of a violation to the end-user. Possible values for build-in constraints // are defined in the "github.com/muonsoft/validation/message" package and can be changed at any time, // even in patch versions. Message() string // MessageTemplate is a template for rendering message. Alongside parameters it can be used to // render the message on the client-side of the library. MessageTemplate() string // Parameters is the map of the template variables and their values provided by the specific constraint. Parameters() []TemplateParameter // PropertyPath is a path that points to the violated property. // See PropertyPath type description for more info. PropertyPath() *PropertyPath }
Violation is the abstraction for validator errors. You can use your own implementations on the application side to use it for your needs. In order for the validator to generate application violations, it is necessary to implement the ViolationFactory interface and inject it into the validator. You can do this by using the SetViolationFactory option in the NewValidator constructor.
func UnwrapViolation ¶
UnwrapViolation is a short function to unwrap Violation from the error.
type ViolationBuilder ¶
type ViolationBuilder struct {
// contains filtered or unexported fields
}
ViolationBuilder used to build an instance of a Violation.
func NewViolationBuilder ¶
func NewViolationBuilder(factory ViolationFactory) *ViolationBuilder
NewViolationBuilder creates a new ViolationBuilder.
func (*ViolationBuilder) AddParameter ¶
func (b *ViolationBuilder) AddParameter(name, value string) *ViolationBuilder
AddParameter adds one parameter into a slice of parameters.
func (*ViolationBuilder) BuildViolation ¶
func (b *ViolationBuilder) BuildViolation(code, message string) *ViolationBuilder
BuildViolation creates a new ViolationBuilder for composing Violation object fluently.
func (*ViolationBuilder) CreateViolation ¶
func (b *ViolationBuilder) CreateViolation() Violation
CreateViolation creates a new violation with given parameters and returns it. Violation is created by calling the CreateViolation method of the ViolationFactory.
func (*ViolationBuilder) SetLanguage ¶
func (b *ViolationBuilder) SetLanguage(tag language.Tag) *ViolationBuilder
SetLanguage sets language that will be used to translate the violation message.
func (*ViolationBuilder) SetParameters ¶
func (b *ViolationBuilder) SetParameters(parameters []TemplateParameter) *ViolationBuilder
SetParameters sets parameters that can be injected into the violation message.
func (*ViolationBuilder) SetPluralCount ¶
func (b *ViolationBuilder) SetPluralCount(pluralCount int) *ViolationBuilder
SetPluralCount sets a plural number that will be used for message pluralization during translations.
func (*ViolationBuilder) SetPropertyPath ¶
func (b *ViolationBuilder) SetPropertyPath(path *PropertyPath) *ViolationBuilder
SetPropertyPath sets a property path of violated attribute.
type ViolationFactory ¶
type ViolationFactory interface { // CreateViolation creates a new instance of Violation. CreateViolation( code, messageTemplate string, pluralCount int, parameters []TemplateParameter, propertyPath *PropertyPath, lang language.Tag, ) Violation }
ViolationFactory is the abstraction that can be used to create custom violations on the application side. Use the SetViolationFactory option on the NewValidator constructor to inject your own factory into the validator.
type ViolationList ¶
type ViolationList []Violation
ViolationList is a slice of violations. It is the usual type of error that is returned from a validator.
func UnwrapViolationList ¶
func UnwrapViolationList(err error) (ViolationList, bool)
UnwrapViolationList is a short function to unwrap ViolationList from the error.
func (*ViolationList) AppendFromError ¶
func (violations *ViolationList) AppendFromError(err error) error
AppendFromError appends a single violation or a slice of violations into the end of a given slice. If an error does not implement the Violation or ViolationList interface, it will return an error itself. Otherwise nil will be returned.
Example
violations := make(ViolationList, 0) err := violations.AppendFromError(previousError) if err != nil { // this error is not a violation, processing must fail return err } // violations contain appended violations from the previousError and can be processed further
func (ViolationList) AsError ¶
func (violations ViolationList) AsError() error
AsError converts the list of violations to an error. This method correctly handles cases where the list of violations is empty. It returns nil on an empty list, indicating that the validation was successful.
func (ViolationList) Error ¶
func (violations ViolationList) Error() string
Error returns a formatted list of errors as a string.
func (ViolationList) Filter ¶
func (violations ViolationList) Filter(codes ...string) ViolationList
Filter returns a new list of violations with violations of given codes.
func (ViolationList) Has ¶
func (violations ViolationList) Has(codes ...string) bool
Has can be used to check that at least one of the violations contains one of the specific codes. For an empty list of codes, it should always returns false.
Source Files
¶
Directories
¶
Path | Synopsis |
---|---|
Package code contains a list of unique, short, and semantic violation codes.
|
Package code contains a list of unique, short, and semantic violation codes. |
Package generic contains structures for processing generic values such as numbers and iterables.
|
Package generic contains structures for processing generic values such as numbers and iterables. |
Package is contains standalone functions that can be used for custom validation process.
|
Package is contains standalone functions that can be used for custom validation process. |
Package it contains validation constraints that are used to validate specific types of values.
|
Package it contains validation constraints that are used to validate specific types of values. |
Package message contains violation message texts.
|
Package message contains violation message texts. |
translations/english
Package english contains violation message texts translated into English language.
|
Package english contains violation message texts translated into English language. |
translations/russian
Package russian contains violation message texts translated into Russian language.
|
Package russian contains violation message texts translated into Russian language. |
Package test contains tests for validation
|
Package test contains tests for validation |
Package validate contains standalone functions that can be used for custom validation process.
|
Package validate contains standalone functions that can be used for custom validation process. |
Package validationtest contains helper functions for testing purposes.
|
Package validationtest contains helper functions for testing purposes. |
Package validator contains Validator service singleton.
|
Package validator contains Validator service singleton. |