Skip to content

Commit

Permalink
feature: all argument
Browse files Browse the repository at this point in the history
  • Loading branch information
strider2038 committed Jun 21, 2022
1 parent e725b31 commit a332144
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
35 changes: 35 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,41 @@ func ExampleAtLeastOneOf() {
// violation at 'brands': This value should not be blank.
}

func ExampleAll() {
book := struct {
Name string
Tags []string
}{
Name: "Very long book name",
Tags: []string{"Fiction", "Thriller", "Science", "Fantasy"},
}

err := validator.Validate(
context.Background(),
validation.Sequentially(
// this block passes
validation.All(
validation.StringProperty("name", book.Name, it.IsNotBlank()),
validation.CountableProperty("tags", len(book.Tags), it.IsNotBlank()),
),
// this block fails
validation.All(
validation.StringProperty("name", book.Name, it.HasMaxLength(10)),
validation.CountableProperty("tags", len(book.Tags), it.HasMaxCount(3)),
),
),
)

if violations, ok := validation.UnwrapViolationList(err); ok {
for violation := violations.First(); violation != nil; violation = violation.Next() {
fmt.Println(violation)
}
}
// Output:
// violation at 'name': This value is too long. It should have 10 characters or less.
// violation at 'tags': This collection should contain 3 elements or less.
}

func ExampleValidator_Validate_basicValidation() {
s := ""

Expand Down
45 changes: 45 additions & 0 deletions flow_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,48 @@ func (arg AtLeastOneOfArgument) setUp(ctx *executionContext) {
return violations, nil
})
}

// AllArgument can be used to interrupt validation process when the first violation is raised.
type AllArgument struct {
isIgnored bool
options []Option
arguments []Argument
}

// All runs validation for each argument. It works exactly as validator.Validate method.
// It can be helpful to build complex validation process.
func All(arguments ...Argument) AllArgument {
return AllArgument{arguments: arguments}
}

// With returns a copy of AllArgument with appended options.
func (arg AllArgument) With(options ...Option) AllArgument {
arg.options = append(arg.options, options...)
return arg
}

// When enables conditional validation of this argument. If the expression evaluates to false,
// then the argument will be ignored.
func (arg AllArgument) When(condition bool) AllArgument {
arg.isIgnored = !condition
return arg
}

func (arg AllArgument) setUp(ctx *executionContext) {
ctx.addValidator(arg.options, func(scope Scope) (*ViolationList, error) {
if arg.isIgnored {
return nil, nil
}

violations := &ViolationList{}

for _, argument := range arg.arguments {
err := violations.AppendFromError(scope.Validate(argument))
if err != nil {
return nil, err
}
}

return violations, nil
})
}
42 changes: 42 additions & 0 deletions test/flow_control_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,45 @@ func TestAtLeastOneOfArgument_WhenValidationIsDisabled_ExpectNoError(t *testing.

assert.NoError(t, err)
}

func TestAllArgument_WhenInvalidValueAtFirstConstraint_ExpectAllViolations(t *testing.T) {
err := newValidator(t).Validate(
context.Background(),
validation.All(
validation.String("", it.IsNotBlank().Code("first")),
validation.String("", it.IsNotBlank().Code("second")),
),
)

validationtest.Assert(t, err).IsViolationList().WithCodes("first", "second")
}

func TestAllArgument_WhenPathIsSet_ExpectOneViolationWithPath(t *testing.T) {
err := newValidator(t).Validate(
context.Background(),
validation.All(
validation.String("", it.IsNotBlank().Code("first")),
validation.String("", it.IsNotBlank().Code("second")),
).With(
validation.PropertyName("properties"),
validation.ArrayIndex(0),
validation.PropertyName("property"),
),
)

violations := validationtest.Assert(t, err).IsViolationList()
violations.HasViolationAt(0).WithCode("first").WithPropertyPath("properties[0].property")
violations.HasViolationAt(1).WithCode("second").WithPropertyPath("properties[0].property")
}

func TestAllArgument_WhenValidationIsDisabled_ExpectNoErrors(t *testing.T) {
err := newValidator(t).Validate(
context.Background(),
validation.All(
validation.String("", it.IsNotBlank().Code("first")),
validation.String("", it.IsNotBlank().Code("second")),
).When(false),
)

assert.NoError(t, err)
}

0 comments on commit a332144

Please sign in to comment.