Skip to content

Commit

Permalink
Update required/ignore constraint behavior (#76)
Browse files Browse the repository at this point in the history
Changes related to bufbuild/protovalidate#115
  • Loading branch information
rodaine authored Nov 6, 2023
1 parent 51ba86b commit d5a29f8
Show file tree
Hide file tree
Showing 9 changed files with 2,795 additions and 93 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ ARGS ?= --strict --strict_message --strict_error
# Set to use a different version of protovalidate-conformance.
# Should be kept in sync with the version referenced in proto/buf.lock and
# 'buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go' in go.mod.
CONFORMANCE_VERSION ?= v0.5.1
CONFORMANCE_VERSION ?= v0.5.3

.PHONY: help
help: ## Describe useful make targets
Expand Down
20 changes: 13 additions & 7 deletions internal/evaluator/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,9 @@ func (bldr *Builder) buildField(
cache MessageCache,
) (field, error) {
fld := field{
Descriptor: fieldDescriptor,
Required: fieldConstraints.GetRequired(),
Optional: fieldDescriptor.HasPresence(),
Descriptor: fieldDescriptor,
Required: fieldConstraints.GetRequired(),
IgnoreEmpty: fieldDescriptor.HasPresence() || fieldConstraints.GetIgnoreEmpty(),
}
err := bldr.buildValue(fieldDescriptor, fieldConstraints, false, &fld.Value, cache)
return fld, err
Expand All @@ -218,15 +218,14 @@ func (bldr *Builder) buildValue(
valEval *value,
cache MessageCache,
) (err error) {
valEval.IgnoreEmpty = constraints.GetIgnoreEmpty()
steps := []func(
fdesc protoreflect.FieldDescriptor,
fieldConstraints *validate.FieldConstraints,
forItems bool,
valEval *value,
cache MessageCache,
) error{
bldr.processZeroValue,
bldr.processIgnoreEmpty,
bldr.processFieldExpressions,
bldr.processEmbeddedMessage,
bldr.processWrapperConstraints,
Expand All @@ -245,13 +244,20 @@ func (bldr *Builder) buildValue(
return nil
}

func (bldr *Builder) processZeroValue(
func (bldr *Builder) processIgnoreEmpty(
fdesc protoreflect.FieldDescriptor,
_ *validate.FieldConstraints,
constraints *validate.FieldConstraints,
forItems bool,
val *value,
_ MessageCache,
) error {
// the only time we need to ignore empty on a value is if it's evaluating a
// field item (repeated element or map key/value).
val.IgnoreEmpty = forItems && constraints.GetIgnoreEmpty()
if !val.IgnoreEmpty {
// only need the zero value for checking ignore_empty constraint
return nil
}
val.Zero = fdesc.Default()
if forItems && fdesc.IsList() {
msg := dynamicpb.NewMessage(fdesc.ContainingMessage())
Expand Down
11 changes: 5 additions & 6 deletions internal/evaluator/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ type field struct {
Descriptor protoreflect.FieldDescriptor
// Required indicates that the field must have a set value.
Required bool
// Optional indicates that the evaluators should not be applied to this field
// if the value is unset. Fields that contain messages, are prefixed with
// `optional`, or are part of a oneof are considered optional. evaluators
// will still be applied if the field is set as the zero value.
Optional bool
// IgnoreEmpty indicates if a field should skip validation on its zero value.
// This field is generally true for nullable fields or fields with the
// ignore_empty constraint explicitly set.
IgnoreEmpty bool
}

func (f field) Evaluate(val protoreflect.Value, failFast bool) error {
Expand All @@ -49,7 +48,7 @@ func (f field) EvaluateMessage(msg protoreflect.Message, failFast bool) (err err
}}}
}

if (f.Optional || f.Value.IgnoreEmpty) && !msg.Has(f.Descriptor) {
if f.IgnoreEmpty && !msg.Has(f.Descriptor) {
return nil
}

Expand Down
7 changes: 4 additions & 3 deletions internal/evaluator/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import (
// value performs validation on any concrete value contained within a singular
// field, repeated elements, or the keys/values of a map.
type value struct {
// Zero is the default or zero-value for this value's type
Zero protoreflect.Value
// Constraints are the individual evaluators applied to a value
Constraints evaluators
// IgnoreEmpty indicates that the Constraints should not be applied if the
// field is unset or the default (typically zero) value.
// value is unset or the default (typically zero) value. This only applies to
// repeated elements or map keys/values with an ignore_empty rule.
IgnoreEmpty bool
// Zero is the default or zero-value for this value's type
Zero protoreflect.Value
}

func (v *value) Evaluate(val protoreflect.Value, failFast bool) error {
Expand Down
Loading

0 comments on commit d5a29f8

Please sign in to comment.