helper/validation: Add All() and IntInSlice() SchemaValidateFunc

`All()` combines the outputs of multiple `SchemaValidateFunc`, to reduce the usage of custom validation functions that implement standard validation functions.

Example provider usage:

```go
ValidateFunc: validation.All(
  StringLenBetween(5, 42),
  StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric"),
),
```

`IntInSlice()` is the `int` equivalent of `StringInSlice()`

Example provider usage:

```go
ValidateFunc: validation.IntInSlice([]int{30, 60, 120})
```

Output from unit testing:

```
$ make test TEST=./helper/validation
==> Checking that code complies with gofmt requirements...
go generate ./...
2018/10/17 14:16:03 Generated command/internal_plugin_list.go
go list ./helper/validation | xargs -t -n4 go test  -timeout=2m -parallel=4
go test -timeout=2m -parallel=4 github.com/hashicorp/terraform/helper/validation
ok  	github.com/hashicorp/terraform/helper/validation	1.106s
```
This commit is contained in:
Brian Flad 2018-10-17 14:22:29 -04:00
parent 9143cb5b49
commit 17ac9a5756
No known key found for this signature in database
GPG Key ID: EC6252B42B012823
2 changed files with 83 additions and 0 deletions

View File

@ -13,6 +13,21 @@ import (
"github.com/hashicorp/terraform/helper/structure" "github.com/hashicorp/terraform/helper/structure"
) )
// All returns a SchemaValidateFunc which tests if the provided value
// passes all provided SchemaValidateFunc
func All(validators ...schema.SchemaValidateFunc) schema.SchemaValidateFunc {
return func(i interface{}, k string) ([]string, []error) {
var allErrors []error
var allWarnings []string
for _, validator := range validators {
validatorWarnings, validatorErrors := validator(i, k)
allWarnings = append(allWarnings, validatorWarnings...)
allErrors = append(allErrors, validatorErrors...)
}
return allWarnings, allErrors
}
}
// IntBetween returns a SchemaValidateFunc which tests if the provided value // IntBetween returns a SchemaValidateFunc which tests if the provided value
// is of type int and is between min and max (inclusive) // is of type int and is between min and max (inclusive)
func IntBetween(min, max int) schema.SchemaValidateFunc { func IntBetween(min, max int) schema.SchemaValidateFunc {
@ -70,6 +85,27 @@ func IntAtMost(max int) schema.SchemaValidateFunc {
} }
} }
// IntInSlice returns a SchemaValidateFunc which tests if the provided value
// is of type int and matches the value of an element in the valid slice
func IntInSlice(valid []int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(int)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be an integer", k))
return
}
for _, validInt := range valid {
if v == validInt {
return
}
}
es = append(es, fmt.Errorf("expected %s to be one of %v, got %d", k, valid, v))
return
}
}
// StringInSlice returns a SchemaValidateFunc which tests if the provided value // StringInSlice returns a SchemaValidateFunc which tests if the provided value
// is of type string and matches the value of an element in the valid slice // is of type string and matches the value of an element in the valid slice
// will test with in lower case if ignoreCase is true // will test with in lower case if ignoreCase is true

View File

@ -13,6 +13,34 @@ type testCase struct {
expectedErr *regexp.Regexp expectedErr *regexp.Regexp
} }
func TestValidationAll(t *testing.T) {
runTestCases(t, []testCase{
{
val: "valid",
f: All(
StringLenBetween(5, 42),
StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric"),
),
},
{
val: "foo",
f: All(
StringLenBetween(5, 42),
StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric"),
),
expectedErr: regexp.MustCompile("expected length of [\\w]+ to be in the range \\(5 - 42\\), got foo"),
},
{
val: "!!!!!",
f: All(
StringLenBetween(5, 42),
StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric"),
),
expectedErr: regexp.MustCompile("value must be alphanumeric"),
},
})
}
func TestValidationIntBetween(t *testing.T) { func TestValidationIntBetween(t *testing.T) {
runTestCases(t, []testCase{ runTestCases(t, []testCase{
{ {
@ -82,6 +110,25 @@ func TestValidationIntAtMost(t *testing.T) {
}) })
} }
func TestValidationIntInSlice(t *testing.T) {
runTestCases(t, []testCase{
{
val: 42,
f: IntInSlice([]int{1, 42}),
},
{
val: 42,
f: IntInSlice([]int{10, 20}),
expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[10 20\\], got 42"),
},
{
val: "InvalidValue",
f: IntInSlice([]int{10, 20}),
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be an integer"),
},
})
}
func TestValidationStringInSlice(t *testing.T) { func TestValidationStringInSlice(t *testing.T) {
runTestCases(t, []testCase{ runTestCases(t, []testCase{
{ {