validationext

package
v0.11.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2023 License: MPL-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package validationext provides helpers to extend the ozzo-validation. There are two primary goals with this package: (1) to ease validating deeply nested structures that are common with protobuf-based APIs and (2) to convert errors from ozzo-validation into proto InvalidArgument errors with field violation extra details.

Index

Constants

This section is empty.

Variables

View Source
var (
	// MeetsVersionConstraint implements validation.Rule to check if a
	// supplied version is a valid according to the constraints supplied.
	MeetsVersionConstraint validation.Rule = &ConstraintVersionRule{}
	// IsVersion implements validation.Rule to check if the supplied string
	// is considered a valid Semantic Versioning (semver) string.
	IsVersion validation.Rule = &ParseVersionRule{}
)
View Source
var (
	// IsDuration implements validation.Rule to check if a value is a valid
	// duration from a proto duration.
	IsDuration validation.Rule = &isDurationRule{}
)
View Source
var IsPrivateCIDRBlock validation.Rule = &isPrivateCIDRBlockRule{}

IsPrivateCIDRBlock implements validation.Rule to check if a value is a valid IPv4 CIDR block within the ranges that are considered valid.

Functions

func Error

func Error(err error) error

Error takes an error and turns an ozzo-validation.Errors into a gRPC status error with field violations populated. If the error is nil or not an ozzo-validation error, it is returned as-is.

Note that validate.Validate doesn't return a validate.Errors. Only validation on structs and other containers will return the proper structure that will be wrapped by this call. This should be used against request structures.

func IsDurationRange

func IsDurationRange(min, max time.Duration) validation.Rule

IsDurationRange implements validation.Rule to check if a proto duration is in the given range. Both ends of the duration are inclusive.

func MeetsConstraints

func MeetsConstraints(constraints ...string) validation.Rule

func StructField

func StructField(sv interface{}, f func() []*validation.FieldRules) *validation.FieldRules

StructField returns a *validation.FieldRules (can be used as an arg to validation.ValidateStruct) that validates a nested struct value only if the struct value is non-nil.

This is useful to apply validation to nested pointer structs all within a single call to validation.ValidateStruct. Otherwise, nil-checks with complex field rule slice building is necessary.

The struct value (sv) parameter needs to be a _pointer to the struct field_, even if that's a pointer. So it should always be in the form `&s.Field`. Otherwise, you'll get a "field #N not in struct" internal error from ozzo-validation.

Full example:

s := &Person{
  Address: &Address{Number: 0},
}

validation.ValidateStruct(&s,
  validation.Field(&s.Address, validation.Required),
  StructField(&s.Address, func() []*validation.FieldRules {
    return []*validation.FieldRules{
      validation.Field(&s.Address.Number, validation.Required),
    }
  }),
)

In this example, the address number will be required, but will only be checked if the address is non-nil. Without StructField, you either have to do nil-checks outside of the validation call or you get a crash.

func StructInterface

func StructInterface(sv, t interface{}, f func() []*validation.FieldRules) *validation.FieldRules

StructInterface is similar to StructField but validates interface-type fields that are non-nil and match the type t exactly. This is useful for "oneof" fields created by protobufs.

If the function f is called, it is guaranteed that the field pointed to by sv is of type t. You can type assert it without ok-checking safely.

See the docs for StructField for additional details on how this works.

func StructJSONPB

func StructJSONPB(sv interface{}, v proto.Message, f func() []*validation.FieldRules) *validation.FieldRules

StructJSONPB validates a jsonpb-encoded field (type []byte) within a struct. This will decode the value sv into the proto struct v and validate it with the rules returned by f.

A validation error will be returned if jsonpb-decoding fails or if the value is not a byte slice.

A side effect of this validation is that the field is decoded into v. After validation, you can continue to use this decoded value. Prior to decoding, we call v.Reset() so that the values are fully reset.

Example:

req := struct{
  Employee []byte
}

var e pb.Employee
validation.ValidateStruct(&req,
  StructJSONPB(&req.Employee, &e, func() []*validation.FieldRules {
    return []*validation.FieldRules{
      validation.Field(&e.Name, validation.Required),
    }
  }),
)

func StructOneof

func StructOneof(sv, t interface{}, f func() []*validation.FieldRules) *validation.FieldRules

StructOneof is a special-case helper to validate struct values within a oneof field from a protobuf-generated struct. This behaves like StructInterface but automatically sets up validation directly into the nested oneof value. The returned fieldrules from f can be directly on the nested value which is implicitly required to be set.

For oneof values that are NOT message types (structs) and are primitives like string, int, etc. then you should use StructInterface directly.

Example:

Given the protobuf of:

message Employee {
  oneof role {
    Eng eng = 1;
    Sales sales = 2;
  }
}

The generated types look something lke this:

type Employee { Role Role }
type Role interface{}
type Role_Eng struct { Eng *Eng }
type Role_Sales struct { Sales *Sales }
type Eng { Language string }
...

To validate this, you can do this:

var e *Employee
validation.ValidateStruct(&e,
  StructOneof(&e.Role, (*Eng)(nil), func() []*validation.FieldRules {
    v := e.Role.(*Role_Eng)
    return []*validation.FieldRules{
      validation.Field(&v.Eng.Language, validation.Required),
    }
  }),
)

Notice how the callback sets validation on the nested `e.Role.Eng` directly. This helper saves a few lines of boilerplate and complicated pointer addressing to make this possible.

The existence and non-emptiness of `e.Role.Eng` is validated automatically and does not need to be verified in the consumer code. This is done because the protobuf compiler adds these intermediate structs for type-safety reasons, but there is no use-case for the specific value to be nil if the intermediate wrapper struct is set. This results in the following cases:

Valid:

Employee{ Role: nil }
Employee{ Role: &Role_Eng{ Eng: &Eng {} } }

Invalid (a validation error is produced):

Employee{ Role: &Role_Eng{ Eng: nil } }

Types

type ConstraintVersionRule

type ConstraintVersionRule struct {
	Constraint goversion.Constraints
}

func (*ConstraintVersionRule) Validate

func (v *ConstraintVersionRule) Validate(value interface{}) error

type EachRule added in v0.10.0

type EachRule struct {
	// contains filtered or unexported fields
}

EachRule is a validation rule that validates elements in a map/slice/array using the specified list of rules.

func Each added in v0.10.0

func Each(rules ...validation.Rule) EachRule

Each returns a validation rule that loops through an iterable (map, slice or array) and validates each value inside with the provided rules. An empty iterable is considered valid. Use the Required rule to make sure the iterable is not empty.

func (EachRule) Validate added in v0.10.0

func (r EachRule) Validate(value interface{}) error

Validate loops through the given iterable and calls the Ozzo Validate() method for each value.

type ParseVersionRule

type ParseVersionRule struct{}

func (*ParseVersionRule) Validate

func (v *ParseVersionRule) Validate(value interface{}) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL