From f6f60feea00c1586dab19b1a59504727b7d1abe9 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Wed, 22 Jun 2022 16:30:57 +0100 Subject: [PATCH 1/9] Relocating `validatordiag` package to `helpers/validatordiag` --- float64validator/at_least.go | 4 +- float64validator/at_most.go | 4 +- float64validator/between.go | 4 +- helpers/validatordiag/diag.go | 91 +++++++++++++++++++ .../validatordiag}/diag_test.go | 0 .../validatordiag}/doc.go | 3 +- int64validator/at_least.go | 4 +- int64validator/at_most.go | 4 +- int64validator/between.go | 4 +- stringvalidator/length_at_least.go | 4 +- stringvalidator/length_at_most.go | 4 +- stringvalidator/length_between.go | 4 +- stringvalidator/regex_matches.go | 4 +- validatordiag/diag.go | 47 ---------- 14 files changed, 112 insertions(+), 69 deletions(-) create mode 100644 helpers/validatordiag/diag.go rename {validatordiag => helpers/validatordiag}/diag_test.go (100%) rename {validatordiag => helpers/validatordiag}/doc.go (78%) delete mode 100644 validatordiag/diag.go diff --git a/float64validator/at_least.go b/float64validator/at_least.go index 2a5c89fe..2b137074 100644 --- a/float64validator/at_least.go +++ b/float64validator/at_least.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.Va } if f < validator.min { - response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%f", f), diff --git a/float64validator/at_most.go b/float64validator/at_most.go index 79c41a11..51dc455e 100644 --- a/float64validator/at_most.go +++ b/float64validator/at_most.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator atMostValidator) Validate(ctx context.Context, request tfsdk.Val } if f > validator.max { - response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%f", f), diff --git a/float64validator/between.go b/float64validator/between.go index 40c50d14..f1630e8f 100644 --- a/float64validator/between.go +++ b/float64validator/between.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator betweenValidator) Validate(ctx context.Context, request tfsdk.Va } if f < validator.min || f > validator.max { - response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%f", f), diff --git a/helpers/validatordiag/diag.go b/helpers/validatordiag/diag.go new file mode 100644 index 00000000..de9cf4e9 --- /dev/null +++ b/helpers/validatordiag/diag.go @@ -0,0 +1,91 @@ +package validatordiag + +import ( + "unicode" + "unicode/utf8" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// InvalidValueDiagnostic returns an error Diagnostic to be used when an attribute has an invalid value. +func InvalidValueDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { + return diag.NewAttributeErrorDiagnostic( + path, + "Invalid Attribute Value", + capitalize(description)+", got: "+value, + ) +} + +// InvalidValueLengthDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid length. +func InvalidValueLengthDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { + return diag.NewAttributeErrorDiagnostic( + path, + "Invalid Attribute Value Length", + capitalize(description)+", got: "+value, + ) +} + +// InvalidValueMatchDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid match. +func InvalidValueMatchDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { + return diag.NewAttributeErrorDiagnostic( + path, + "Invalid Attribute Value Match", + capitalize(description)+", got: "+value, + ) +} + +// InvalidSchemaDiagnostic returns an error Diagnostic to be used when a schemavalidator of attributes is invalid. +func InvalidSchemaDiagnostic(path *tftypes.AttributePath, description string) diag.Diagnostic { + return diag.NewAttributeErrorDiagnostic( + path, + "Invalid Attribute Combination", + capitalize(description), + ) +} + +// InvalidTypeDiagnostic returns an error Diagnostic to be used when an attribute has an invalid type. +func InvalidTypeDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { + return diag.NewAttributeErrorDiagnostic( + path, + "Invalid Attribute Type", + capitalize(description)+", got: "+value, + ) +} + +// ErrorsCount returns the amount of diag.Diagnostic in diag.Diagnostics that are diag.SeverityError. +func ErrorsCount(diags diag.Diagnostics) int { + count := 0 + + for _, d := range diags { + if diag.SeverityError == d.Severity() { + count++ + } + } + + return count +} + +// WarningsCount returns the amount of diag.Diagnostic in diag.Diagnostics that are diag.SeverityWarning. +func WarningsCount(diags diag.Diagnostics) int { + count := 0 + + for _, d := range diags { + if diag.SeverityWarning == d.Severity() { + count++ + } + } + + return count +} + +// capitalize will uppercase the first letter in a UTF-8 string. +func capitalize(str string) string { + if str == "" { + return "" + } + + firstRune, size := utf8.DecodeRuneInString(str) + + return string(unicode.ToUpper(firstRune)) + str[size:] +} diff --git a/validatordiag/diag_test.go b/helpers/validatordiag/diag_test.go similarity index 100% rename from validatordiag/diag_test.go rename to helpers/validatordiag/diag_test.go diff --git a/validatordiag/doc.go b/helpers/validatordiag/doc.go similarity index 78% rename from validatordiag/doc.go rename to helpers/validatordiag/doc.go index 372bd460..c31457a0 100644 --- a/validatordiag/doc.go +++ b/helpers/validatordiag/doc.go @@ -1,3 +1,2 @@ -// Package validatordiag provides diagnostics helpers for validator -// implementations. +// Package validatordiag provides diagnostics helpers for validator implementations. package validatordiag diff --git a/int64validator/at_least.go b/int64validator/at_least.go index 894aa789..edec0d58 100644 --- a/int64validator/at_least.go +++ b/int64validator/at_least.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.Va } if i < validator.min { - response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", i), diff --git a/int64validator/at_most.go b/int64validator/at_most.go index 6fe9954a..f55ba0bc 100644 --- a/int64validator/at_most.go +++ b/int64validator/at_most.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator atMostValidator) Validate(ctx context.Context, request tfsdk.Val } if i > validator.max { - response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", i), diff --git a/int64validator/between.go b/int64validator/between.go index cb590f1a..7d137d86 100644 --- a/int64validator/between.go +++ b/int64validator/between.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator betweenValidator) Validate(ctx context.Context, request tfsdk.Va } if i < validator.min || i > validator.max { - response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", i), diff --git a/stringvalidator/length_at_least.go b/stringvalidator/length_at_least.go index cbbce1f0..becceacd 100644 --- a/stringvalidator/length_at_least.go +++ b/stringvalidator/length_at_least.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator lengthAtLeastValidator) Validate(ctx context.Context, request tf } if l := len(s); l < validator.minLength { - response.Diagnostics.Append(validatordiag.AttributeValueLengthDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueLengthDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", l), diff --git a/stringvalidator/length_at_most.go b/stringvalidator/length_at_most.go index d9e8c28d..25213af9 100644 --- a/stringvalidator/length_at_most.go +++ b/stringvalidator/length_at_most.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator lengthAtMostValidator) Validate(ctx context.Context, request tfs } if l := len(s); l > validator.maxLength { - response.Diagnostics.Append(validatordiag.AttributeValueLengthDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueLengthDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", l), diff --git a/stringvalidator/length_between.go b/stringvalidator/length_between.go index 5656103b..b45b4601 100644 --- a/stringvalidator/length_between.go +++ b/stringvalidator/length_between.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -34,7 +34,7 @@ func (validator lengthBetweenValidator) Validate(ctx context.Context, request tf } if l := len(s); l < validator.minLength || l > validator.maxLength { - response.Diagnostics.Append(validatordiag.AttributeValueLengthDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueLengthDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", l), diff --git a/stringvalidator/regex_matches.go b/stringvalidator/regex_matches.go index 1d3fbf9f..5a7ec825 100644 --- a/stringvalidator/regex_matches.go +++ b/stringvalidator/regex_matches.go @@ -5,7 +5,7 @@ import ( "fmt" "regexp" - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -39,7 +39,7 @@ func (validator regexMatchesValidator) Validate(ctx context.Context, request tfs } if ok := validator.regexp.MatchString(s); !ok { - response.Diagnostics.Append(validatordiag.AttributeValueMatchesDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidValueMatchDiagnostic( request.AttributePath, validator.Description(ctx), s, diff --git a/validatordiag/diag.go b/validatordiag/diag.go deleted file mode 100644 index c43567f2..00000000 --- a/validatordiag/diag.go +++ /dev/null @@ -1,47 +0,0 @@ -package validatordiag - -import ( - "unicode" - "unicode/utf8" - - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -// AttributeValueDiagnostic returns an error Diagnostic to be used when an attribute has an invalid value. -func AttributeValueDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { - return diag.NewAttributeErrorDiagnostic( - path, - "Invalid Attribute Value", - capitalize(description)+", got: "+value, - ) -} - -// AttributeValueLengthDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid length. -func AttributeValueLengthDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { - return diag.NewAttributeErrorDiagnostic( - path, - "Invalid Attribute Value Length", - capitalize(description)+", got: "+value, - ) -} - -// AttributeValueMatchesDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid match. -func AttributeValueMatchesDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { - return diag.NewAttributeErrorDiagnostic( - path, - "Invalid Attribute Value Match", - capitalize(description)+", got: "+value, - ) -} - -// capitalize will uppercase the first letter in a UTF-8 string. -func capitalize(str string) string { - if str == "" { - return "" - } - - firstRune, size := utf8.DecodeRuneInString(str) - - return string(unicode.ToUpper(firstRune)) + str[size:] -} From 9f2d94b57a37d2dfe8e58880f92d37f8c85734fa Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Wed, 22 Jun 2022 16:46:47 +0100 Subject: [PATCH 2/9] typo in `.gitignore` --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8c095eee..6ba0a499 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -# Jetbrains IDEs +# JetBrains IDEs project files .idea/ *.iws From 19ff1905893b9128ddcf468339a5e74784d1ee2c Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Wed, 22 Jun 2022 17:37:37 +0100 Subject: [PATCH 3/9] Adding `OneOf()` and `NoneOf()` validators, a pair for each type. The underlying implementation sits in `primitivevalidator` package. --- .changelog/42.txt | 19 ++ float64validator/none_of.go | 19 ++ float64validator/one_of.go | 19 ++ int64validator/none_of.go | 19 ++ int64validator/one_of.go | 19 ++ numbervalidator/doc.go | 2 + numbervalidator/none_of.go | 21 ++ numbervalidator/one_of.go | 21 ++ .../acceptable_values_validator.go | 70 +++++++ primitivevalidator/doc.go | 4 + primitivevalidator/none_of.go | 19 ++ primitivevalidator/none_of_test.go | 190 ++++++++++++++++++ primitivevalidator/one_of.go | 19 ++ primitivevalidator/one_of_test.go | 189 +++++++++++++++++ .../acceptable_strings_validator.go | 63 ++++++ stringvalidator/none_of.go | 17 ++ stringvalidator/none_of_test.go | 177 ++++++++++++++++ stringvalidator/one_of.go | 17 ++ stringvalidator/one_of_test.go | 176 ++++++++++++++++ stringvalidator/type_validation.go | 17 +- 20 files changed, 1090 insertions(+), 7 deletions(-) create mode 100644 .changelog/42.txt create mode 100644 float64validator/none_of.go create mode 100644 float64validator/one_of.go create mode 100644 int64validator/none_of.go create mode 100644 int64validator/one_of.go create mode 100644 numbervalidator/doc.go create mode 100644 numbervalidator/none_of.go create mode 100644 numbervalidator/one_of.go create mode 100644 primitivevalidator/acceptable_values_validator.go create mode 100644 primitivevalidator/doc.go create mode 100644 primitivevalidator/none_of.go create mode 100644 primitivevalidator/none_of_test.go create mode 100644 primitivevalidator/one_of.go create mode 100644 primitivevalidator/one_of_test.go create mode 100644 stringvalidator/acceptable_strings_validator.go create mode 100644 stringvalidator/none_of.go create mode 100644 stringvalidator/none_of_test.go create mode 100644 stringvalidator/one_of.go create mode 100644 stringvalidator/one_of_test.go diff --git a/.changelog/42.txt b/.changelog/42.txt new file mode 100644 index 00000000..c4af27c0 --- /dev/null +++ b/.changelog/42.txt @@ -0,0 +1,19 @@ +```release-note:enhancement +floatvalidator: 2 new validation functions, `OneOf()` and `NoneOf()` +``` + +```release-note:enhancement +int64validator: 2 new validation functions, `OneOf()` and `NoneOf()` +``` + +```release-note:feature +numbervalidator: New package that starts with 2 validation functions, `OneOf()` and `NoneOf()` +``` + +```release-note:enhancement +stringvalidator: 2 new validation functions, `OneOf()` and `NoneOf()`, similar to the ones in `genericvalidator` but option to control case-sensitivity +``` + +```release-note:note +Introduced a new package `primitivevalidator`, holding the shared code used by type-specific validators (ex. `OneOf()` and `NoneOf()`) +``` diff --git a/float64validator/none_of.go b/float64validator/none_of.go new file mode 100644 index 00000000..3550c3fb --- /dev/null +++ b/float64validator/none_of.go @@ -0,0 +1,19 @@ +package float64validator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// NoneOf checks that the float64 held in the attribute +// is none of the given `unacceptableFloats`. +func NoneOf(unacceptableFloats ...float64) tfsdk.AttributeValidator { + unacceptableFloatValues := make([]attr.Value, 0, len(unacceptableFloats)) + for _, f := range unacceptableFloats { + unacceptableFloatValues = append(unacceptableFloatValues, types.Float64{Value: f}) + } + + return primitivevalidator.NoneOf(unacceptableFloatValues...) +} diff --git a/float64validator/one_of.go b/float64validator/one_of.go new file mode 100644 index 00000000..a4c0de25 --- /dev/null +++ b/float64validator/one_of.go @@ -0,0 +1,19 @@ +package float64validator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// OneOf checks that the float64 held in the attribute +// is one of the given `acceptableFloats`. +func OneOf(acceptableFloats ...float64) tfsdk.AttributeValidator { + acceptableFloatValues := make([]attr.Value, 0, len(acceptableFloats)) + for _, f := range acceptableFloats { + acceptableFloatValues = append(acceptableFloatValues, types.Float64{Value: f}) + } + + return primitivevalidator.OneOf(acceptableFloatValues...) +} diff --git a/int64validator/none_of.go b/int64validator/none_of.go new file mode 100644 index 00000000..5aae6efa --- /dev/null +++ b/int64validator/none_of.go @@ -0,0 +1,19 @@ +package int64validator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// NoneOf checks that the int64 held in the attribute +// is none of the given `unacceptableInts`. +func NoneOf(unacceptableInts ...int64) tfsdk.AttributeValidator { + unacceptableIntValues := make([]attr.Value, 0, len(unacceptableInts)) + for _, i := range unacceptableInts { + unacceptableIntValues = append(unacceptableIntValues, types.Int64{Value: i}) + } + + return primitivevalidator.NoneOf(unacceptableIntValues...) +} diff --git a/int64validator/one_of.go b/int64validator/one_of.go new file mode 100644 index 00000000..de4eaf82 --- /dev/null +++ b/int64validator/one_of.go @@ -0,0 +1,19 @@ +package int64validator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// OneOf checks that the int64 held in the attribute +// is one of the given `acceptableInts`. +func OneOf(acceptableInts ...int64) tfsdk.AttributeValidator { + acceptableIntValues := make([]attr.Value, 0, len(acceptableInts)) + for _, i := range acceptableInts { + acceptableIntValues = append(acceptableIntValues, types.Int64{Value: i}) + } + + return primitivevalidator.OneOf(acceptableIntValues...) +} diff --git a/numbervalidator/doc.go b/numbervalidator/doc.go new file mode 100644 index 00000000..43f291aa --- /dev/null +++ b/numbervalidator/doc.go @@ -0,0 +1,2 @@ +// Package numbervalidator provides validators for types.Number attributes. +package numbervalidator diff --git a/numbervalidator/none_of.go b/numbervalidator/none_of.go new file mode 100644 index 00000000..df5f0717 --- /dev/null +++ b/numbervalidator/none_of.go @@ -0,0 +1,21 @@ +package numbervalidator + +import ( + "math/big" + + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// NoneOf checks that the *big.Float held in the attribute +// is none of the given `unacceptableFloats`. +func NoneOf(unacceptableFloats ...*big.Float) tfsdk.AttributeValidator { + unacceptableFloatValues := make([]attr.Value, 0, len(unacceptableFloats)) + for _, f := range unacceptableFloats { + unacceptableFloatValues = append(unacceptableFloatValues, types.Number{Value: f}) + } + + return primitivevalidator.NoneOf(unacceptableFloatValues...) +} diff --git a/numbervalidator/one_of.go b/numbervalidator/one_of.go new file mode 100644 index 00000000..6271cf61 --- /dev/null +++ b/numbervalidator/one_of.go @@ -0,0 +1,21 @@ +package numbervalidator + +import ( + "math/big" + + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// OneOf checks that the *big.Float held in the attribute +// is one of the given `acceptableFloats`. +func OneOf(acceptableFloats ...*big.Float) tfsdk.AttributeValidator { + acceptableFloatValues := make([]attr.Value, 0, len(acceptableFloats)) + for _, f := range acceptableFloats { + acceptableFloatValues = append(acceptableFloatValues, types.Number{Value: f}) + } + + return primitivevalidator.OneOf(acceptableFloatValues...) +} diff --git a/primitivevalidator/acceptable_values_validator.go b/primitivevalidator/acceptable_values_validator.go new file mode 100644 index 00000000..1f69fc00 --- /dev/null +++ b/primitivevalidator/acceptable_values_validator.go @@ -0,0 +1,70 @@ +package primitivevalidator + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// acceptablePrimitiveValuesAttributeValidator is the underlying struct implementing OneOf and NoneOf. +type acceptablePrimitiveValuesAttributeValidator struct { + acceptableValues []attr.Value + shouldMatch bool +} + +var _ tfsdk.AttributeValidator = (*acceptablePrimitiveValuesAttributeValidator)(nil) + +func (av *acceptablePrimitiveValuesAttributeValidator) Description(ctx context.Context) string { + return av.MarkdownDescription(ctx) +} + +func (av *acceptablePrimitiveValuesAttributeValidator) MarkdownDescription(_ context.Context) string { + if av.shouldMatch { + return fmt.Sprintf("Value must be one of: %q", av.acceptableValues) + } else { + return fmt.Sprintf("Value must be none of: %q", av.acceptableValues) + } + +} + +func (av *acceptablePrimitiveValuesAttributeValidator) Validate(ctx context.Context, req tfsdk.ValidateAttributeRequest, res *tfsdk.ValidateAttributeResponse) { + if req.AttributeConfig.IsNull() || req.AttributeConfig.IsUnknown() { + return + } + + var value attr.Value + switch typedAttributeConfig := req.AttributeConfig.(type) { + case types.String, types.Bool, types.Int64, types.Float64, types.Number: + value = typedAttributeConfig + default: + res.Diagnostics.AddAttributeError( + req.AttributePath, + "This validator should be used against primitive types (String, Bool, Number, Int64, Float64).", + "This is always indicative of a bug within the provider.", + ) + return + } + + if av.shouldMatch && !av.isAcceptableValue(value) || //< EITHER should match but it does not + !av.shouldMatch && av.isAcceptableValue(value) { //< OR should not match but it does + res.Diagnostics.Append(validatordiag.InvalidValueMatchDiagnostic( + req.AttributePath, + av.Description(ctx), + value.String(), + )) + } +} + +func (av *acceptablePrimitiveValuesAttributeValidator) isAcceptableValue(v attr.Value) bool { + for _, acceptableV := range av.acceptableValues { + if v.Equal(acceptableV) { + return true + } + } + + return false +} diff --git a/primitivevalidator/doc.go b/primitivevalidator/doc.go new file mode 100644 index 00000000..d9011e85 --- /dev/null +++ b/primitivevalidator/doc.go @@ -0,0 +1,4 @@ +// Package primitivevalidator provides validators for attributes +// of "primitive" type (i.e. `types.String`, `types.Number`, `types.Int64`, +// `types.Float64` and `types.Bool`). +package primitivevalidator diff --git a/primitivevalidator/none_of.go b/primitivevalidator/none_of.go new file mode 100644 index 00000000..cca7c804 --- /dev/null +++ b/primitivevalidator/none_of.go @@ -0,0 +1,19 @@ +package primitivevalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// NoneOf checks that the value held in the attribute +// is none of the given `unacceptableValues`. +// +// This validator can be used only against primitives like +// `types.String`, `types.Number`, `types.Int64`, +// `types.Float64` and `types.Bool`. +func NoneOf(unacceptableValues ...attr.Value) tfsdk.AttributeValidator { + return &acceptablePrimitiveValuesAttributeValidator{ + acceptableValues: unacceptableValues, + shouldMatch: false, + } +} diff --git a/primitivevalidator/none_of_test.go b/primitivevalidator/none_of_test.go new file mode 100644 index 00000000..7d21c5a5 --- /dev/null +++ b/primitivevalidator/none_of_test.go @@ -0,0 +1,190 @@ +package primitivevalidator_test + +import ( + "context" + "math" + "math/big" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestNoneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + objPersonAttrTypes := map[string]attr.Type{ + "Name": types.StringType, + "Age": types.Int64Type, + } + objAttrTypes := map[string]attr.Type{ + "Person": types.ObjectType{ + AttrTypes: objPersonAttrTypes, + }, + "Address": types.StringType, + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.String{Value: "foo"}, + validator: primitivevalidator.NoneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + expErrors: 1, + }, + "simple-mismatch": { + in: types.String{Value: "foz"}, + validator: primitivevalidator.NoneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + }, + "mixed": { + in: types.Float64{Value: 1.234}, + validator: primitivevalidator.NoneOf( + types.String{Value: "foo"}, + types.Int64{Value: 567}, + types.Float64{Value: 1.234}, + ), + expErrors: 1, + }, + "list-not-allowed": { + in: types.List{ + ElemType: types.Int64Type, + Elems: []attr.Value{ + types.Int64{Value: 10}, + types.Int64{Value: 20}, + types.Int64{Value: 30}, + }, + }, + validator: primitivevalidator.NoneOf( + types.Int64{Value: 10}, + types.Int64{Value: 20}, + types.Int64{Value: 30}, + types.Int64{Value: 40}, + types.Int64{Value: 50}, + ), + expErrors: 1, + }, + "set-not-allowed": { + in: types.Set{ + ElemType: types.StringType, + Elems: []attr.Value{ + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + }, + }, + validator: primitivevalidator.NoneOf( + types.String{Value: "bob"}, + types.String{Value: "alice"}, + types.String{Value: "john"}, + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + expErrors: 1, + }, + "map-not-allowed": { + in: types.Map{ + ElemType: types.NumberType, + Elems: map[string]attr.Value{ + "one.one": types.Number{Value: big.NewFloat(1.1)}, + "ten.twenty": types.Number{Value: big.NewFloat(10.20)}, + "five.four": types.Number{Value: big.NewFloat(5.4)}, + }, + }, + validator: primitivevalidator.NoneOf( + types.Number{Value: big.NewFloat(1.1)}, + types.Number{Value: big.NewFloat(math.MaxFloat64)}, + types.Number{Value: big.NewFloat(math.SmallestNonzeroFloat64)}, + types.Number{Value: big.NewFloat(10.20)}, + types.Number{Value: big.NewFloat(5.4)}, + ), + expErrors: 1, + }, + "object-not-allowed": { + in: types.Object{ + AttrTypes: objAttrTypes, + Attrs: map[string]attr.Value{ + "Person": types.Object{ + AttrTypes: objPersonAttrTypes, + Attrs: map[string]attr.Value{ + "Name": types.String{Value: "Bob Parr"}, + "Age": types.Int64{Value: 40}, + }, + }, + "Address": types.String{Value: "1200 Park Avenue Emeryville"}, + }, + }, + validator: primitivevalidator.NoneOf( + types.Object{ + AttrTypes: map[string]attr.Type{}, + Attrs: map[string]attr.Value{}, + }, + types.Object{ + AttrTypes: objPersonAttrTypes, + Attrs: map[string]attr.Value{ + "Name": types.String{Value: "Bob Parr"}, + "Age": types.Int64{Value: 40}, + }, + }, + types.String{Value: "1200 Park Avenue Emeryville"}, + types.Int64{Value: 123}, + types.String{Value: "Bob Parr"}, + ), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.String{Null: true}, + validator: primitivevalidator.NoneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + }, + "skip-validation-on-unknown": { + in: types.String{Unknown: true}, + validator: primitivevalidator.NoneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/primitivevalidator/one_of.go b/primitivevalidator/one_of.go new file mode 100644 index 00000000..7bacc6d2 --- /dev/null +++ b/primitivevalidator/one_of.go @@ -0,0 +1,19 @@ +package primitivevalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// OneOf checks that the value held in the attribute +// is one of the given `acceptableValues`. +// +// This validator can be used only against primitives like +// `types.String`, `types.Number`, `types.Int64`, +// `types.Float64` and `types.Bool`. +func OneOf(acceptableValues ...attr.Value) tfsdk.AttributeValidator { + return &acceptablePrimitiveValuesAttributeValidator{ + acceptableValues: acceptableValues, + shouldMatch: true, + } +} diff --git a/primitivevalidator/one_of_test.go b/primitivevalidator/one_of_test.go new file mode 100644 index 00000000..5052e199 --- /dev/null +++ b/primitivevalidator/one_of_test.go @@ -0,0 +1,189 @@ +package primitivevalidator_test + +import ( + "context" + "math" + "math/big" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestOneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + objPersonAttrTypes := map[string]attr.Type{ + "Name": types.StringType, + "Age": types.Int64Type, + } + objAttrTypes := map[string]attr.Type{ + "Person": types.ObjectType{ + AttrTypes: objPersonAttrTypes, + }, + "Address": types.StringType, + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.String{Value: "foo"}, + validator: primitivevalidator.OneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + }, + "simple-mismatch": { + in: types.String{Value: "foz"}, + validator: primitivevalidator.OneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + expErrors: 1, + }, + "mixed": { + in: types.Float64{Value: 1.234}, + validator: primitivevalidator.OneOf( + types.String{Value: "foo"}, + types.Int64{Value: 567}, + types.Float64{Value: 1.234}, + ), + }, + "list-not-allowed": { + in: types.List{ + ElemType: types.Int64Type, + Elems: []attr.Value{ + types.Int64{Value: 10}, + types.Int64{Value: 20}, + types.Int64{Value: 30}, + }, + }, + validator: primitivevalidator.OneOf( + types.Int64{Value: 10}, + types.Int64{Value: 20}, + types.Int64{Value: 30}, + types.Int64{Value: 40}, + types.Int64{Value: 50}, + ), + expErrors: 1, + }, + "set-not-allowed": { + in: types.Set{ + ElemType: types.StringType, + Elems: []attr.Value{ + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + }, + }, + validator: primitivevalidator.OneOf( + types.String{Value: "bob"}, + types.String{Value: "alice"}, + types.String{Value: "john"}, + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + expErrors: 1, + }, + "map-not-allowed": { + in: types.Map{ + ElemType: types.NumberType, + Elems: map[string]attr.Value{ + "one.one": types.Number{Value: big.NewFloat(1.1)}, + "ten.twenty": types.Number{Value: big.NewFloat(10.20)}, + "five.four": types.Number{Value: big.NewFloat(5.4)}, + }, + }, + validator: primitivevalidator.OneOf( + types.Number{Value: big.NewFloat(1.1)}, + types.Number{Value: big.NewFloat(math.MaxFloat64)}, + types.Number{Value: big.NewFloat(math.SmallestNonzeroFloat64)}, + types.Number{Value: big.NewFloat(10.20)}, + types.Number{Value: big.NewFloat(5.4)}, + ), + expErrors: 1, + }, + "object-not-allowed": { + in: types.Object{ + AttrTypes: objAttrTypes, + Attrs: map[string]attr.Value{ + "Person": types.Object{ + AttrTypes: objPersonAttrTypes, + Attrs: map[string]attr.Value{ + "Name": types.String{Value: "Bob Parr"}, + "Age": types.Int64{Value: 40}, + }, + }, + "Address": types.String{Value: "1200 Park Avenue Emeryville"}, + }, + }, + validator: primitivevalidator.OneOf( + types.Object{ + AttrTypes: map[string]attr.Type{}, + Attrs: map[string]attr.Value{}, + }, + types.Object{ + AttrTypes: objPersonAttrTypes, + Attrs: map[string]attr.Value{ + "Name": types.String{Value: "Bob Parr"}, + "Age": types.Int64{Value: 40}, + }, + }, + types.String{Value: "1200 Park Avenue Emeryville"}, + types.Int64{Value: 123}, + types.String{Value: "Bob Parr"}, + ), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.String{Null: true}, + validator: primitivevalidator.OneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + }, + "skip-validation-on-unknown": { + in: types.String{Unknown: true}, + validator: primitivevalidator.OneOf( + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + ), + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/stringvalidator/acceptable_strings_validator.go b/stringvalidator/acceptable_strings_validator.go new file mode 100644 index 00000000..7f59cee1 --- /dev/null +++ b/stringvalidator/acceptable_strings_validator.go @@ -0,0 +1,63 @@ +package stringvalidator + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// acceptableStringsAttributeValidator is the underlying struct implementing OneOf and NoneOf. +type acceptableStringsAttributeValidator struct { + acceptableStrings []string + caseSensitive bool + shouldMatch bool +} + +var _ tfsdk.AttributeValidator = (*acceptableStringsAttributeValidator)(nil) + +func (av *acceptableStringsAttributeValidator) Description(ctx context.Context) string { + return av.MarkdownDescription(ctx) +} + +func (av *acceptableStringsAttributeValidator) MarkdownDescription(_ context.Context) string { + if av.shouldMatch { + return fmt.Sprintf("String must match one of: %q", av.acceptableStrings) + } else { + return fmt.Sprintf("String must match none of: %q", av.acceptableStrings) + } +} + +func (av *acceptableStringsAttributeValidator) Validate(ctx context.Context, req tfsdk.ValidateAttributeRequest, res *tfsdk.ValidateAttributeResponse) { + value, ok := validateString(ctx, req, res) + if !ok { + return + } + + if av.shouldMatch && !av.isAcceptableValue(value) || //< EITHER should match but it does not + !av.shouldMatch && av.isAcceptableValue(value) { //< OR should not match but it does + res.Diagnostics.Append(validatordiag.InvalidValueMatchDiagnostic( + req.AttributePath, + av.Description(ctx), + value, + )) + } +} + +func (av *acceptableStringsAttributeValidator) isAcceptableValue(v string) bool { + for _, acceptableV := range av.acceptableStrings { + if av.caseSensitive { + if v == acceptableV { + return true + } + } else { + if strings.EqualFold(v, acceptableV) { + return true + } + } + } + + return false +} diff --git a/stringvalidator/none_of.go b/stringvalidator/none_of.go new file mode 100644 index 00000000..0fbc057a --- /dev/null +++ b/stringvalidator/none_of.go @@ -0,0 +1,17 @@ +package stringvalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// NoneOf checks that the string held in the attribute +// is none of the given `unacceptableStrings`. +// +// String comparison case sensitiveness is controlled by the `caseSensitive` argument. +func NoneOf(caseSensitive bool, unacceptableStrings ...string) tfsdk.AttributeValidator { + return &acceptableStringsAttributeValidator{ + unacceptableStrings, + caseSensitive, + false, + } +} diff --git a/stringvalidator/none_of_test.go b/stringvalidator/none_of_test.go new file mode 100644 index 00000000..11e5b56a --- /dev/null +++ b/stringvalidator/none_of_test.go @@ -0,0 +1,177 @@ +package stringvalidator_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestNoneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + objAttrTypes := map[string]attr.Type{ + "Name": types.StringType, + "Age": types.StringType, + "Address": types.StringType, + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.String{Value: "foo"}, + validator: stringvalidator.NoneOf( + true, + "foo", + "bar", + "baz", + ), + expErrors: 1, + }, + "simple-match-case-insensitive": { + in: types.String{Value: "foo"}, + validator: stringvalidator.NoneOf( + false, + "FOO", + "bar", + "baz", + ), + expErrors: 1, + }, + "simple-mismatch": { + in: types.String{Value: "foz"}, + validator: stringvalidator.NoneOf( + true, + "foo", + "bar", + "baz", + ), + }, + "list-not-allowed": { + in: types.List{ + ElemType: types.StringType, + Elems: []attr.Value{ + types.String{Value: "10"}, + types.String{Value: "20"}, + types.String{Value: "30"}, + }, + }, + validator: stringvalidator.NoneOf( + true, + "10", + "20", + "30", + "40", + "50", + ), + expErrors: 1, + }, + "set-not-allowed": { + in: types.Set{ + ElemType: types.StringType, + Elems: []attr.Value{ + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + }, + }, + validator: stringvalidator.NoneOf( + true, + "bob", + "alice", + "john", + "foo", + "bar", + "baz", + ), + expErrors: 1, + }, + "map-not-allowed": { + in: types.Map{ + ElemType: types.StringType, + Elems: map[string]attr.Value{ + "one.one": types.String{Value: "1.1"}, + "ten.twenty": types.String{Value: "10.20"}, + "five.four": types.String{Value: "5.4"}, + }, + }, + validator: stringvalidator.NoneOf( + true, + "1.1", + "10.20", + "5.4", + "geronimo", + "bob", + ), + expErrors: 1, + }, + "object-not-allowed": { + in: types.Object{ + AttrTypes: objAttrTypes, + Attrs: map[string]attr.Value{ + "Name": types.String{Value: "Bob Parr"}, + "Age": types.String{Value: "40"}, + "Address": types.String{Value: "1200 Park Avenue Emeryville"}, + }, + }, + validator: stringvalidator.NoneOf( + true, + "Bob Parr", + "40", + "1200 Park Avenue Emeryville", + "123", + ), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.String{Null: true}, + validator: stringvalidator.NoneOf( + true, + "foo", + "bar", + "baz", + ), + }, + "skip-validation-on-unknown": { + in: types.String{Unknown: true}, + validator: stringvalidator.NoneOf( + true, + "foo", + "bar", + "baz", + ), + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/stringvalidator/one_of.go b/stringvalidator/one_of.go new file mode 100644 index 00000000..d02df037 --- /dev/null +++ b/stringvalidator/one_of.go @@ -0,0 +1,17 @@ +package stringvalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework/tfsdk" +) + +// OneOf checks that the string held in the attribute +// is one of the given `acceptableStrings`. +// +// String comparison case sensitiveness is controlled by the `caseSensitive` argument. +func OneOf(caseSensitive bool, acceptableStrings ...string) tfsdk.AttributeValidator { + return &acceptableStringsAttributeValidator{ + acceptableStrings, + caseSensitive, + true, + } +} diff --git a/stringvalidator/one_of_test.go b/stringvalidator/one_of_test.go new file mode 100644 index 00000000..258eaa07 --- /dev/null +++ b/stringvalidator/one_of_test.go @@ -0,0 +1,176 @@ +package stringvalidator_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestOneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + objAttrTypes := map[string]attr.Type{ + "Name": types.StringType, + "Age": types.StringType, + "Address": types.StringType, + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.String{Value: "foo"}, + validator: stringvalidator.OneOf( + true, + "foo", + "bar", + "baz", + ), + }, + "simple-match-case-insensitive": { + in: types.String{Value: "foo"}, + validator: stringvalidator.OneOf( + false, + "FOO", + "bar", + "baz", + ), + }, + "simple-mismatch": { + in: types.String{Value: "foz"}, + validator: stringvalidator.OneOf( + true, + "foo", + "bar", + "baz", + ), + expErrors: 1, + }, + "list-not-allowed": { + in: types.List{ + ElemType: types.StringType, + Elems: []attr.Value{ + types.String{Value: "10"}, + types.String{Value: "20"}, + types.String{Value: "30"}, + }, + }, + validator: stringvalidator.OneOf( + true, + "10", + "20", + "30", + "40", + "50", + ), + expErrors: 1, + }, + "set-not-allowed": { + in: types.Set{ + ElemType: types.StringType, + Elems: []attr.Value{ + types.String{Value: "foo"}, + types.String{Value: "bar"}, + types.String{Value: "baz"}, + }, + }, + validator: stringvalidator.OneOf( + true, + "bob", + "alice", + "john", + "foo", + "bar", + "baz", + ), + expErrors: 1, + }, + "map-not-allowed": { + in: types.Map{ + ElemType: types.StringType, + Elems: map[string]attr.Value{ + "one.one": types.String{Value: "1.1"}, + "ten.twenty": types.String{Value: "10.20"}, + "five.four": types.String{Value: "5.4"}, + }, + }, + validator: stringvalidator.OneOf( + true, + "1.1", + "10.20", + "5.4", + "geronimo", + "bob", + ), + expErrors: 1, + }, + "object-not-allowed": { + in: types.Object{ + AttrTypes: objAttrTypes, + Attrs: map[string]attr.Value{ + "Name": types.String{Value: "Bob Parr"}, + "Age": types.String{Value: "40"}, + "Address": types.String{Value: "1200 Park Avenue Emeryville"}, + }, + }, + validator: stringvalidator.OneOf( + true, + "Bob Parr", + "40", + "1200 Park Avenue Emeryville", + "123", + ), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.String{Null: true}, + validator: stringvalidator.OneOf( + true, + "foo", + "bar", + "baz", + ), + }, + "skip-validation-on-unknown": { + in: types.String{Unknown: true}, + validator: stringvalidator.OneOf( + true, + "foo", + "bar", + "baz", + ), + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/stringvalidator/type_validation.go b/stringvalidator/type_validation.go index 2a0ec479..39c4c97c 100644 --- a/stringvalidator/type_validation.go +++ b/stringvalidator/type_validation.go @@ -3,22 +3,25 @@ package stringvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" ) // validateString ensures that the request contains a String value. func validateString(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) (string, bool) { - var s types.String - - diags := tfsdk.ValueAs(ctx, request.AttributeConfig, &s) - - if diags.HasError() { - response.Diagnostics = append(response.Diagnostics, diags...) - + t := request.AttributeConfig.Type(ctx) + if t != types.StringType { + response.Diagnostics.Append(validatordiag.InvalidTypeDiagnostic( + request.AttributePath, + "Expected value of type string", + t.String(), + )) return "", false } + s := request.AttributeConfig.(types.String) + if s.Unknown || s.Null { return "", false } From 789bc5a38761daef6fcfeaa4f227f6b1ee79d752 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Wed, 22 Jun 2022 17:49:06 +0100 Subject: [PATCH 4/9] Use explicit `.Type()` interrogation instead of reflection to validate type --- float64validator/type_validation.go | 25 ++++++++++++++----------- int64validator/type_validation.go | 21 ++++++++++++--------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/float64validator/type_validation.go b/float64validator/type_validation.go index d4d99473..b45fc6b3 100644 --- a/float64validator/type_validation.go +++ b/float64validator/type_validation.go @@ -3,25 +3,28 @@ package float64validator import ( "context" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" ) // validateFloat ensures that the request contains a Float64 value. func validateFloat(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) (float64, bool) { - var n types.Float64 - - diags := tfsdk.ValueAs(ctx, request.AttributeConfig, &n) - - if diags.HasError() { - response.Diagnostics = append(response.Diagnostics, diags...) - - return 0, false + t := request.AttributeConfig.Type(ctx) + if t != types.Float64Type { + response.Diagnostics.Append(validatordiag.InvalidTypeDiagnostic( + request.AttributePath, + "Expected value of type float64", + t.String(), + )) + return 0.0, false } - if n.Unknown || n.Null { - return 0, false + f := request.AttributeConfig.(types.Float64) + + if f.Unknown || f.Null { + return 0.0, false } - return n.Value, true + return f.Value, true } diff --git a/int64validator/type_validation.go b/int64validator/type_validation.go index a7b10b44..7d00ba06 100644 --- a/int64validator/type_validation.go +++ b/int64validator/type_validation.go @@ -3,25 +3,28 @@ package int64validator import ( "context" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" ) // validateInt ensures that the request contains an Int64 value. func validateInt(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) (int64, bool) { - var n types.Int64 - - diags := tfsdk.ValueAs(ctx, request.AttributeConfig, &n) - - if diags.HasError() { - response.Diagnostics = append(response.Diagnostics, diags...) - + t := request.AttributeConfig.Type(ctx) + if t != types.Int64Type { + response.Diagnostics.Append(validatordiag.InvalidTypeDiagnostic( + request.AttributePath, + "Expected value of type int64", + t.String(), + )) return 0, false } - if n.Unknown || n.Null { + i := request.AttributeConfig.(types.Int64) + + if i.Unknown || i.Null { return 0, false } - return n.Value, true + return i.Value, true } From 71627f696e4858d2c58aabf890f2ae10c2febe11 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Wed, 22 Jun 2022 18:00:17 +0100 Subject: [PATCH 5/9] Locating `primitivevalidator` into `internal/` --- float64validator/none_of.go | 2 +- float64validator/one_of.go | 2 +- int64validator/none_of.go | 2 +- int64validator/one_of.go | 2 +- .../primitivevalidator}/acceptable_values_validator.go | 0 {primitivevalidator => internal/primitivevalidator}/doc.go | 0 {primitivevalidator => internal/primitivevalidator}/none_of.go | 0 .../primitivevalidator}/none_of_test.go | 2 +- {primitivevalidator => internal/primitivevalidator}/one_of.go | 0 .../primitivevalidator}/one_of_test.go | 2 +- numbervalidator/none_of.go | 2 +- numbervalidator/one_of.go | 2 +- 12 files changed, 8 insertions(+), 8 deletions(-) rename {primitivevalidator => internal/primitivevalidator}/acceptable_values_validator.go (100%) rename {primitivevalidator => internal/primitivevalidator}/doc.go (100%) rename {primitivevalidator => internal/primitivevalidator}/none_of.go (100%) rename {primitivevalidator => internal/primitivevalidator}/none_of_test.go (98%) rename {primitivevalidator => internal/primitivevalidator}/one_of.go (100%) rename {primitivevalidator => internal/primitivevalidator}/one_of_test.go (98%) diff --git a/float64validator/none_of.go b/float64validator/none_of.go index 3550c3fb..85aeced7 100644 --- a/float64validator/none_of.go +++ b/float64validator/none_of.go @@ -1,7 +1,7 @@ package float64validator import ( - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" diff --git a/float64validator/one_of.go b/float64validator/one_of.go index a4c0de25..cd5fba49 100644 --- a/float64validator/one_of.go +++ b/float64validator/one_of.go @@ -1,7 +1,7 @@ package float64validator import ( - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" diff --git a/int64validator/none_of.go b/int64validator/none_of.go index 5aae6efa..fedff940 100644 --- a/int64validator/none_of.go +++ b/int64validator/none_of.go @@ -1,7 +1,7 @@ package int64validator import ( - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" diff --git a/int64validator/one_of.go b/int64validator/one_of.go index de4eaf82..13fa3df5 100644 --- a/int64validator/one_of.go +++ b/int64validator/one_of.go @@ -1,7 +1,7 @@ package int64validator import ( - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" diff --git a/primitivevalidator/acceptable_values_validator.go b/internal/primitivevalidator/acceptable_values_validator.go similarity index 100% rename from primitivevalidator/acceptable_values_validator.go rename to internal/primitivevalidator/acceptable_values_validator.go diff --git a/primitivevalidator/doc.go b/internal/primitivevalidator/doc.go similarity index 100% rename from primitivevalidator/doc.go rename to internal/primitivevalidator/doc.go diff --git a/primitivevalidator/none_of.go b/internal/primitivevalidator/none_of.go similarity index 100% rename from primitivevalidator/none_of.go rename to internal/primitivevalidator/none_of.go diff --git a/primitivevalidator/none_of_test.go b/internal/primitivevalidator/none_of_test.go similarity index 98% rename from primitivevalidator/none_of_test.go rename to internal/primitivevalidator/none_of_test.go index 7d21c5a5..7e86cb99 100644 --- a/primitivevalidator/none_of_test.go +++ b/internal/primitivevalidator/none_of_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" diff --git a/primitivevalidator/one_of.go b/internal/primitivevalidator/one_of.go similarity index 100% rename from primitivevalidator/one_of.go rename to internal/primitivevalidator/one_of.go diff --git a/primitivevalidator/one_of_test.go b/internal/primitivevalidator/one_of_test.go similarity index 98% rename from primitivevalidator/one_of_test.go rename to internal/primitivevalidator/one_of_test.go index 5052e199..80cc0162 100644 --- a/primitivevalidator/one_of_test.go +++ b/internal/primitivevalidator/one_of_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" diff --git a/numbervalidator/none_of.go b/numbervalidator/none_of.go index df5f0717..0650a16a 100644 --- a/numbervalidator/none_of.go +++ b/numbervalidator/none_of.go @@ -3,7 +3,7 @@ package numbervalidator import ( "math/big" - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" diff --git a/numbervalidator/one_of.go b/numbervalidator/one_of.go index 6271cf61..f18fa3ab 100644 --- a/numbervalidator/one_of.go +++ b/numbervalidator/one_of.go @@ -3,7 +3,7 @@ package numbervalidator import ( "math/big" - "github.com/hashicorp/terraform-plugin-framework-validators/primitivevalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/internal/primitivevalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" From 3f2da3729c7a926bf9f5c9b9c450ac98dee51b97 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Wed, 22 Jun 2022 18:23:33 +0100 Subject: [PATCH 6/9] Remove "public" mention of `primitivevalidator` package --- .changelog/42.txt | 4 ---- internal/primitivevalidator/doc.go | 4 ---- 2 files changed, 8 deletions(-) delete mode 100644 internal/primitivevalidator/doc.go diff --git a/.changelog/42.txt b/.changelog/42.txt index c4af27c0..e8f7f8c3 100644 --- a/.changelog/42.txt +++ b/.changelog/42.txt @@ -13,7 +13,3 @@ numbervalidator: New package that starts with 2 validation functions, `OneOf()` ```release-note:enhancement stringvalidator: 2 new validation functions, `OneOf()` and `NoneOf()`, similar to the ones in `genericvalidator` but option to control case-sensitivity ``` - -```release-note:note -Introduced a new package `primitivevalidator`, holding the shared code used by type-specific validators (ex. `OneOf()` and `NoneOf()`) -``` diff --git a/internal/primitivevalidator/doc.go b/internal/primitivevalidator/doc.go deleted file mode 100644 index d9011e85..00000000 --- a/internal/primitivevalidator/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package primitivevalidator provides validators for attributes -// of "primitive" type (i.e. `types.String`, `types.Number`, `types.Int64`, -// `types.Float64` and `types.Bool`). -package primitivevalidator From 5f7ea16c667600c45ba7509bd97e69036b6e2ca1 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Thu, 23 Jun 2022 11:56:49 +0100 Subject: [PATCH 7/9] PR review --- .changelog/42.txt | 2 +- float64validator/at_least.go | 2 +- float64validator/at_most.go | 2 +- float64validator/between.go | 2 +- float64validator/type_validation.go | 2 +- helpers/validatordiag/diag.go | 20 +++++++++---------- int64validator/at_least.go | 2 +- int64validator/at_most.go | 2 +- int64validator/between.go | 2 +- int64validator/type_validation.go | 2 +- .../acceptable_values_validator.go | 2 +- internal/primitivevalidator/none_of_test.go | 3 +++ internal/primitivevalidator/one_of_test.go | 4 ++++ .../acceptable_strings_validator.go | 2 +- stringvalidator/length_at_least.go | 2 +- stringvalidator/length_at_most.go | 2 +- stringvalidator/length_between.go | 2 +- stringvalidator/none_of_test.go | 3 +++ stringvalidator/one_of_test.go | 4 ++++ stringvalidator/regex_matches.go | 2 +- stringvalidator/type_validation.go | 2 +- 21 files changed, 40 insertions(+), 26 deletions(-) diff --git a/.changelog/42.txt b/.changelog/42.txt index e8f7f8c3..ff2acc66 100644 --- a/.changelog/42.txt +++ b/.changelog/42.txt @@ -11,5 +11,5 @@ numbervalidator: New package that starts with 2 validation functions, `OneOf()` ``` ```release-note:enhancement -stringvalidator: 2 new validation functions, `OneOf()` and `NoneOf()`, similar to the ones in `genericvalidator` but option to control case-sensitivity +stringvalidator: 2 new validation functions, `OneOf()` and `NoneOf()`, that offer case-sensitivity control ``` diff --git a/float64validator/at_least.go b/float64validator/at_least.go index 2b137074..546e74b3 100644 --- a/float64validator/at_least.go +++ b/float64validator/at_least.go @@ -34,7 +34,7 @@ func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.Va } if f < validator.min { - response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%f", f), diff --git a/float64validator/at_most.go b/float64validator/at_most.go index 51dc455e..b79868e3 100644 --- a/float64validator/at_most.go +++ b/float64validator/at_most.go @@ -34,7 +34,7 @@ func (validator atMostValidator) Validate(ctx context.Context, request tfsdk.Val } if f > validator.max { - response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%f", f), diff --git a/float64validator/between.go b/float64validator/between.go index f1630e8f..440a1dd0 100644 --- a/float64validator/between.go +++ b/float64validator/between.go @@ -34,7 +34,7 @@ func (validator betweenValidator) Validate(ctx context.Context, request tfsdk.Va } if f < validator.min || f > validator.max { - response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%f", f), diff --git a/float64validator/type_validation.go b/float64validator/type_validation.go index b45fc6b3..d02e2183 100644 --- a/float64validator/type_validation.go +++ b/float64validator/type_validation.go @@ -12,7 +12,7 @@ import ( func validateFloat(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) (float64, bool) { t := request.AttributeConfig.Type(ctx) if t != types.Float64Type { - response.Diagnostics.Append(validatordiag.InvalidTypeDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeTypeDiagnostic( request.AttributePath, "Expected value of type float64", t.String(), diff --git a/helpers/validatordiag/diag.go b/helpers/validatordiag/diag.go index de9cf4e9..4ea742e4 100644 --- a/helpers/validatordiag/diag.go +++ b/helpers/validatordiag/diag.go @@ -8,8 +8,8 @@ import ( "github.com/hashicorp/terraform-plugin-go/tftypes" ) -// InvalidValueDiagnostic returns an error Diagnostic to be used when an attribute has an invalid value. -func InvalidValueDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { +// InvalidAttributeValueDiagnostic returns an error Diagnostic to be used when an attribute has an invalid value. +func InvalidAttributeValueDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { return diag.NewAttributeErrorDiagnostic( path, "Invalid Attribute Value", @@ -17,8 +17,8 @@ func InvalidValueDiagnostic(path *tftypes.AttributePath, description string, val ) } -// InvalidValueLengthDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid length. -func InvalidValueLengthDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { +// InvalidAttributeValueLengthDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid length. +func InvalidAttributeValueLengthDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { return diag.NewAttributeErrorDiagnostic( path, "Invalid Attribute Value Length", @@ -26,8 +26,8 @@ func InvalidValueLengthDiagnostic(path *tftypes.AttributePath, description strin ) } -// InvalidValueMatchDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid match. -func InvalidValueMatchDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { +// InvalidAttributeValueMatchDiagnostic returns an error Diagnostic to be used when an attribute's value has an invalid match. +func InvalidAttributeValueMatchDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { return diag.NewAttributeErrorDiagnostic( path, "Invalid Attribute Value Match", @@ -35,8 +35,8 @@ func InvalidValueMatchDiagnostic(path *tftypes.AttributePath, description string ) } -// InvalidSchemaDiagnostic returns an error Diagnostic to be used when a schemavalidator of attributes is invalid. -func InvalidSchemaDiagnostic(path *tftypes.AttributePath, description string) diag.Diagnostic { +// InvalidAttributeSchemaDiagnostic returns an error Diagnostic to be used when a schemavalidator of attributes is invalid. +func InvalidAttributeSchemaDiagnostic(path *tftypes.AttributePath, description string) diag.Diagnostic { return diag.NewAttributeErrorDiagnostic( path, "Invalid Attribute Combination", @@ -44,8 +44,8 @@ func InvalidSchemaDiagnostic(path *tftypes.AttributePath, description string) di ) } -// InvalidTypeDiagnostic returns an error Diagnostic to be used when an attribute has an invalid type. -func InvalidTypeDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { +// InvalidAttributeTypeDiagnostic returns an error Diagnostic to be used when an attribute has an invalid type. +func InvalidAttributeTypeDiagnostic(path *tftypes.AttributePath, description string, value string) diag.Diagnostic { return diag.NewAttributeErrorDiagnostic( path, "Invalid Attribute Type", diff --git a/int64validator/at_least.go b/int64validator/at_least.go index edec0d58..d371a098 100644 --- a/int64validator/at_least.go +++ b/int64validator/at_least.go @@ -34,7 +34,7 @@ func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.Va } if i < validator.min { - response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", i), diff --git a/int64validator/at_most.go b/int64validator/at_most.go index f55ba0bc..050ebe7c 100644 --- a/int64validator/at_most.go +++ b/int64validator/at_most.go @@ -34,7 +34,7 @@ func (validator atMostValidator) Validate(ctx context.Context, request tfsdk.Val } if i > validator.max { - response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", i), diff --git a/int64validator/between.go b/int64validator/between.go index 7d137d86..e093f203 100644 --- a/int64validator/between.go +++ b/int64validator/between.go @@ -34,7 +34,7 @@ func (validator betweenValidator) Validate(ctx context.Context, request tfsdk.Va } if i < validator.min || i > validator.max { - response.Diagnostics.Append(validatordiag.InvalidValueDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", i), diff --git a/int64validator/type_validation.go b/int64validator/type_validation.go index 7d00ba06..383ac6e5 100644 --- a/int64validator/type_validation.go +++ b/int64validator/type_validation.go @@ -12,7 +12,7 @@ import ( func validateInt(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) (int64, bool) { t := request.AttributeConfig.Type(ctx) if t != types.Int64Type { - response.Diagnostics.Append(validatordiag.InvalidTypeDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeTypeDiagnostic( request.AttributePath, "Expected value of type int64", t.String(), diff --git a/internal/primitivevalidator/acceptable_values_validator.go b/internal/primitivevalidator/acceptable_values_validator.go index 1f69fc00..9312afdb 100644 --- a/internal/primitivevalidator/acceptable_values_validator.go +++ b/internal/primitivevalidator/acceptable_values_validator.go @@ -51,7 +51,7 @@ func (av *acceptablePrimitiveValuesAttributeValidator) Validate(ctx context.Cont if av.shouldMatch && !av.isAcceptableValue(value) || //< EITHER should match but it does not !av.shouldMatch && av.isAcceptableValue(value) { //< OR should not match but it does - res.Diagnostics.Append(validatordiag.InvalidValueMatchDiagnostic( + res.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic( req.AttributePath, av.Description(ctx), value.String(), diff --git a/internal/primitivevalidator/none_of_test.go b/internal/primitivevalidator/none_of_test.go index 7e86cb99..c0329660 100644 --- a/internal/primitivevalidator/none_of_test.go +++ b/internal/primitivevalidator/none_of_test.go @@ -50,6 +50,7 @@ func TestNoneOfValidator(t *testing.T) { types.String{Value: "bar"}, types.String{Value: "baz"}, ), + expErrors: 0, }, "mixed": { in: types.Float64{Value: 1.234}, @@ -154,6 +155,7 @@ func TestNoneOfValidator(t *testing.T) { types.String{Value: "bar"}, types.String{Value: "baz"}, ), + expErrors: 0, }, "skip-validation-on-unknown": { in: types.String{Unknown: true}, @@ -162,6 +164,7 @@ func TestNoneOfValidator(t *testing.T) { types.String{Value: "bar"}, types.String{Value: "baz"}, ), + expErrors: 0, }, } diff --git a/internal/primitivevalidator/one_of_test.go b/internal/primitivevalidator/one_of_test.go index 80cc0162..d132aff0 100644 --- a/internal/primitivevalidator/one_of_test.go +++ b/internal/primitivevalidator/one_of_test.go @@ -41,6 +41,7 @@ func TestOneOfValidator(t *testing.T) { types.String{Value: "bar"}, types.String{Value: "baz"}, ), + expErrors: 0, }, "simple-mismatch": { in: types.String{Value: "foz"}, @@ -58,6 +59,7 @@ func TestOneOfValidator(t *testing.T) { types.Int64{Value: 567}, types.Float64{Value: 1.234}, ), + expErrors: 0, }, "list-not-allowed": { in: types.List{ @@ -153,6 +155,7 @@ func TestOneOfValidator(t *testing.T) { types.String{Value: "bar"}, types.String{Value: "baz"}, ), + expErrors: 0, }, "skip-validation-on-unknown": { in: types.String{Unknown: true}, @@ -161,6 +164,7 @@ func TestOneOfValidator(t *testing.T) { types.String{Value: "bar"}, types.String{Value: "baz"}, ), + expErrors: 0, }, } diff --git a/stringvalidator/acceptable_strings_validator.go b/stringvalidator/acceptable_strings_validator.go index 7f59cee1..7f1e9592 100644 --- a/stringvalidator/acceptable_strings_validator.go +++ b/stringvalidator/acceptable_strings_validator.go @@ -38,7 +38,7 @@ func (av *acceptableStringsAttributeValidator) Validate(ctx context.Context, req if av.shouldMatch && !av.isAcceptableValue(value) || //< EITHER should match but it does not !av.shouldMatch && av.isAcceptableValue(value) { //< OR should not match but it does - res.Diagnostics.Append(validatordiag.InvalidValueMatchDiagnostic( + res.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic( req.AttributePath, av.Description(ctx), value, diff --git a/stringvalidator/length_at_least.go b/stringvalidator/length_at_least.go index becceacd..f6a2dfdb 100644 --- a/stringvalidator/length_at_least.go +++ b/stringvalidator/length_at_least.go @@ -34,7 +34,7 @@ func (validator lengthAtLeastValidator) Validate(ctx context.Context, request tf } if l := len(s); l < validator.minLength { - response.Diagnostics.Append(validatordiag.InvalidValueLengthDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueLengthDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", l), diff --git a/stringvalidator/length_at_most.go b/stringvalidator/length_at_most.go index 25213af9..96ff04eb 100644 --- a/stringvalidator/length_at_most.go +++ b/stringvalidator/length_at_most.go @@ -34,7 +34,7 @@ func (validator lengthAtMostValidator) Validate(ctx context.Context, request tfs } if l := len(s); l > validator.maxLength { - response.Diagnostics.Append(validatordiag.InvalidValueLengthDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueLengthDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", l), diff --git a/stringvalidator/length_between.go b/stringvalidator/length_between.go index b45b4601..7b8e0090 100644 --- a/stringvalidator/length_between.go +++ b/stringvalidator/length_between.go @@ -34,7 +34,7 @@ func (validator lengthBetweenValidator) Validate(ctx context.Context, request tf } if l := len(s); l < validator.minLength || l > validator.maxLength { - response.Diagnostics.Append(validatordiag.InvalidValueLengthDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueLengthDiagnostic( request.AttributePath, validator.Description(ctx), fmt.Sprintf("%d", l), diff --git a/stringvalidator/none_of_test.go b/stringvalidator/none_of_test.go index 11e5b56a..536640cb 100644 --- a/stringvalidator/none_of_test.go +++ b/stringvalidator/none_of_test.go @@ -55,6 +55,7 @@ func TestNoneOfValidator(t *testing.T) { "bar", "baz", ), + expErrors: 0, }, "list-not-allowed": { in: types.List{ @@ -140,6 +141,7 @@ func TestNoneOfValidator(t *testing.T) { "bar", "baz", ), + expErrors: 0, }, "skip-validation-on-unknown": { in: types.String{Unknown: true}, @@ -149,6 +151,7 @@ func TestNoneOfValidator(t *testing.T) { "bar", "baz", ), + expErrors: 0, }, } diff --git a/stringvalidator/one_of_test.go b/stringvalidator/one_of_test.go index 258eaa07..d62b1949 100644 --- a/stringvalidator/one_of_test.go +++ b/stringvalidator/one_of_test.go @@ -35,6 +35,7 @@ func TestOneOfValidator(t *testing.T) { "bar", "baz", ), + expErrors: 0, }, "simple-match-case-insensitive": { in: types.String{Value: "foo"}, @@ -44,6 +45,7 @@ func TestOneOfValidator(t *testing.T) { "bar", "baz", ), + expErrors: 0, }, "simple-mismatch": { in: types.String{Value: "foz"}, @@ -139,6 +141,7 @@ func TestOneOfValidator(t *testing.T) { "bar", "baz", ), + expErrors: 0, }, "skip-validation-on-unknown": { in: types.String{Unknown: true}, @@ -148,6 +151,7 @@ func TestOneOfValidator(t *testing.T) { "bar", "baz", ), + expErrors: 0, }, } diff --git a/stringvalidator/regex_matches.go b/stringvalidator/regex_matches.go index 5a7ec825..03b1f51f 100644 --- a/stringvalidator/regex_matches.go +++ b/stringvalidator/regex_matches.go @@ -39,7 +39,7 @@ func (validator regexMatchesValidator) Validate(ctx context.Context, request tfs } if ok := validator.regexp.MatchString(s); !ok { - response.Diagnostics.Append(validatordiag.InvalidValueMatchDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic( request.AttributePath, validator.Description(ctx), s, diff --git a/stringvalidator/type_validation.go b/stringvalidator/type_validation.go index 39c4c97c..8b8ecf2b 100644 --- a/stringvalidator/type_validation.go +++ b/stringvalidator/type_validation.go @@ -12,7 +12,7 @@ import ( func validateString(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) (string, bool) { t := request.AttributeConfig.Type(ctx) if t != types.StringType { - response.Diagnostics.Append(validatordiag.InvalidTypeDiagnostic( + response.Diagnostics.Append(validatordiag.InvalidAttributeTypeDiagnostic( request.AttributePath, "Expected value of type string", t.String(), From 2fc01f4a50f4d36dc19a501fcdf1cac67fc62473 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Thu, 23 Jun 2022 15:25:04 +0100 Subject: [PATCH 8/9] Adding more tests for `OneOf()` and `NoneOf()` for all packages --- float64validator/at_least_test.go | 5 +- float64validator/at_most_test.go | 5 +- float64validator/between_test.go | 5 +- float64validator/none_of_test.go | 85 ++++++++++++++++++++++++ float64validator/one_of_test.go | 85 ++++++++++++++++++++++++ int64validator/at_least_test.go | 5 +- int64validator/at_most_test.go | 5 +- int64validator/between_test.go | 5 +- int64validator/none_of_test.go | 85 ++++++++++++++++++++++++ int64validator/one_of_test.go | 85 ++++++++++++++++++++++++ numbervalidator/none_of_test.go | 86 +++++++++++++++++++++++++ numbervalidator/one_of_test.go | 86 +++++++++++++++++++++++++ stringvalidator/length_at_least_test.go | 5 +- stringvalidator/length_at_most_test.go | 5 +- stringvalidator/length_between_test.go | 5 +- stringvalidator/regex_matches_test.go | 5 +- 16 files changed, 542 insertions(+), 20 deletions(-) create mode 100644 float64validator/none_of_test.go create mode 100644 float64validator/one_of_test.go create mode 100644 int64validator/none_of_test.go create mode 100644 int64validator/one_of_test.go create mode 100644 numbervalidator/none_of_test.go create mode 100644 numbervalidator/one_of_test.go diff --git a/float64validator/at_least_test.go b/float64validator/at_least_test.go index eec6e429..2848a263 100644 --- a/float64validator/at_least_test.go +++ b/float64validator/at_least_test.go @@ -1,9 +1,10 @@ -package float64validator +package float64validator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -58,7 +59,7 @@ func TestAtLeastValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - AtLeast(test.min).Validate(context.TODO(), request, &response) + float64validator.AtLeast(test.min).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/float64validator/at_most_test.go b/float64validator/at_most_test.go index 0ac5e1b5..d9c3d44c 100644 --- a/float64validator/at_most_test.go +++ b/float64validator/at_most_test.go @@ -1,9 +1,10 @@ -package float64validator +package float64validator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -58,7 +59,7 @@ func TestAtMostValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - AtMost(test.max).Validate(context.TODO(), request, &response) + float64validator.AtMost(test.max).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/float64validator/between_test.go b/float64validator/between_test.go index 745666f2..6effb36d 100644 --- a/float64validator/between_test.go +++ b/float64validator/between_test.go @@ -1,9 +1,10 @@ -package float64validator +package float64validator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -76,7 +77,7 @@ func TestBetweenValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - Between(test.min, test.max).Validate(context.TODO(), request, &response) + float64validator.Between(test.min, test.max).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/float64validator/none_of_test.go b/float64validator/none_of_test.go new file mode 100644 index 00000000..102f779b --- /dev/null +++ b/float64validator/none_of_test.go @@ -0,0 +1,85 @@ +package float64validator_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestNoneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.Float64{Value: 123.456}, + validator: float64validator.NoneOf( + 123.456, + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 1, + }, + "simple-mismatch": { + in: types.Float64{Value: 123.456}, + validator: float64validator.NoneOf( + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 0, + }, + "skip-validation-on-null": { + in: types.Float64{Null: true}, + validator: float64validator.NoneOf( + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.Float64{Unknown: true}, + validator: float64validator.NoneOf( + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 0, + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/float64validator/one_of_test.go b/float64validator/one_of_test.go new file mode 100644 index 00000000..afe79b3b --- /dev/null +++ b/float64validator/one_of_test.go @@ -0,0 +1,85 @@ +package float64validator_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/float64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestOneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.Float64{Value: 123.456}, + validator: float64validator.OneOf( + 123.456, + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 0, + }, + "simple-mismatch": { + in: types.Float64{Value: 123.456}, + validator: float64validator.OneOf( + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.Float64{Null: true}, + validator: float64validator.OneOf( + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.Float64{Unknown: true}, + validator: float64validator.OneOf( + 234.567, + 8910.11, + 1213.1415, + ), + expErrors: 0, + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/int64validator/at_least_test.go b/int64validator/at_least_test.go index 5763a12e..72c939f4 100644 --- a/int64validator/at_least_test.go +++ b/int64validator/at_least_test.go @@ -1,9 +1,10 @@ -package int64validator +package int64validator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -54,7 +55,7 @@ func TestAtLeastValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - AtLeast(test.min).Validate(context.TODO(), request, &response) + int64validator.AtLeast(test.min).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/int64validator/at_most_test.go b/int64validator/at_most_test.go index 5302eed0..7e54edf5 100644 --- a/int64validator/at_most_test.go +++ b/int64validator/at_most_test.go @@ -1,9 +1,10 @@ -package int64validator +package int64validator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -54,7 +55,7 @@ func TestAtMostValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - AtMost(test.max).Validate(context.TODO(), request, &response) + int64validator.AtMost(test.max).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/int64validator/between_test.go b/int64validator/between_test.go index f0126ee7..119ec104 100644 --- a/int64validator/between_test.go +++ b/int64validator/between_test.go @@ -1,9 +1,10 @@ -package int64validator +package int64validator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -71,7 +72,7 @@ func TestBetweenValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - Between(test.min, test.max).Validate(context.TODO(), request, &response) + int64validator.Between(test.min, test.max).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/int64validator/none_of_test.go b/int64validator/none_of_test.go new file mode 100644 index 00000000..3d7dd0ee --- /dev/null +++ b/int64validator/none_of_test.go @@ -0,0 +1,85 @@ +package int64validator_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestNoneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.Int64{Value: 123}, + validator: int64validator.NoneOf( + 123, + 234, + 8910, + 1213, + ), + expErrors: 1, + }, + "simple-mismatch": { + in: types.Int64{Value: 123}, + validator: int64validator.NoneOf( + 234, + 8910, + 1213, + ), + expErrors: 0, + }, + "skip-validation-on-null": { + in: types.Int64{Null: true}, + validator: int64validator.NoneOf( + 234, + 8910, + 1213, + ), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.Int64{Unknown: true}, + validator: int64validator.NoneOf( + 234, + 8910, + 1213, + ), + expErrors: 0, + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/int64validator/one_of_test.go b/int64validator/one_of_test.go new file mode 100644 index 00000000..95517cb1 --- /dev/null +++ b/int64validator/one_of_test.go @@ -0,0 +1,85 @@ +package int64validator_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestOneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.Int64{Value: 123}, + validator: int64validator.OneOf( + 123, + 234, + 8910, + 1213, + ), + expErrors: 0, + }, + "simple-mismatch": { + in: types.Int64{Value: 123}, + validator: int64validator.OneOf( + 234, + 8910, + 1213, + ), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.Int64{Null: true}, + validator: int64validator.OneOf( + 234, + 8910, + 1213, + ), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.Int64{Unknown: true}, + validator: int64validator.OneOf( + 234, + 8910, + 1213, + ), + expErrors: 0, + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/numbervalidator/none_of_test.go b/numbervalidator/none_of_test.go new file mode 100644 index 00000000..b9eb386f --- /dev/null +++ b/numbervalidator/none_of_test.go @@ -0,0 +1,86 @@ +package numbervalidator_test + +import ( + "context" + "math/big" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/numbervalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestNoneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.Number{Value: big.NewFloat(123.456)}, + validator: numbervalidator.NoneOf( + big.NewFloat(123.456), + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 1, + }, + "simple-mismatch": { + in: types.Number{Value: big.NewFloat(123.456)}, + validator: numbervalidator.NoneOf( + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 0, + }, + "skip-validation-on-null": { + in: types.Number{Null: true}, + validator: numbervalidator.NoneOf( + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.Number{Unknown: true}, + validator: numbervalidator.NoneOf( + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 0, + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/numbervalidator/one_of_test.go b/numbervalidator/one_of_test.go new file mode 100644 index 00000000..f90da2f4 --- /dev/null +++ b/numbervalidator/one_of_test.go @@ -0,0 +1,86 @@ +package numbervalidator_test + +import ( + "context" + "math/big" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework-validators/numbervalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestOneOfValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in attr.Value + validator tfsdk.AttributeValidator + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.Number{Value: big.NewFloat(123.456)}, + validator: numbervalidator.OneOf( + big.NewFloat(123.456), + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 0, + }, + "simple-mismatch": { + in: types.Number{Value: big.NewFloat(123.456)}, + validator: numbervalidator.OneOf( + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.Number{Null: true}, + validator: numbervalidator.OneOf( + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.Number{Unknown: true}, + validator: numbervalidator.OneOf( + big.NewFloat(234.567), + big.NewFloat(8910.11), + big.NewFloat(1213.1415), + ), + expErrors: 0, + }, + } + + for name, test := range testCases { + name, test := name, test + t.Run(name, func(t *testing.T) { + req := tfsdk.ValidateAttributeRequest{ + AttributeConfig: test.in, + } + res := tfsdk.ValidateAttributeResponse{} + test.validator.Validate(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != validatordiag.ErrorsCount(res.Diagnostics) { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", validatordiag.ErrorsCount(res.Diagnostics), res.Diagnostics) + } + }) + } +} diff --git a/stringvalidator/length_at_least_test.go b/stringvalidator/length_at_least_test.go index 3620c6d6..d6c32769 100644 --- a/stringvalidator/length_at_least_test.go +++ b/stringvalidator/length_at_least_test.go @@ -1,9 +1,10 @@ -package stringvalidator +package stringvalidator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -50,7 +51,7 @@ func TestLengthAtLeastValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - LengthAtLeast(test.minLength).Validate(context.TODO(), request, &response) + stringvalidator.LengthAtLeast(test.minLength).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/stringvalidator/length_at_most_test.go b/stringvalidator/length_at_most_test.go index df337644..ef293c94 100644 --- a/stringvalidator/length_at_most_test.go +++ b/stringvalidator/length_at_most_test.go @@ -1,9 +1,10 @@ -package stringvalidator +package stringvalidator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -50,7 +51,7 @@ func TestLengthAtMostValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - LengthAtMost(test.maxLength).Validate(context.TODO(), request, &response) + stringvalidator.LengthAtMost(test.maxLength).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/stringvalidator/length_between_test.go b/stringvalidator/length_between_test.go index a07e0c22..ab9052f0 100644 --- a/stringvalidator/length_between_test.go +++ b/stringvalidator/length_between_test.go @@ -1,9 +1,10 @@ -package stringvalidator +package stringvalidator_test import ( "context" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -61,7 +62,7 @@ func TestLengthBetweenValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - LengthBetween(test.minLength, test.maxLength).Validate(context.TODO(), request, &response) + stringvalidator.LengthBetween(test.minLength, test.maxLength).Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") diff --git a/stringvalidator/regex_matches_test.go b/stringvalidator/regex_matches_test.go index 2f4ce12b..f0516810 100644 --- a/stringvalidator/regex_matches_test.go +++ b/stringvalidator/regex_matches_test.go @@ -1,10 +1,11 @@ -package stringvalidator +package stringvalidator_test import ( "context" "regexp" "testing" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -51,7 +52,7 @@ func TestRegexMatchesValidator(t *testing.T) { AttributeConfig: test.val, } response := tfsdk.ValidateAttributeResponse{} - RegexMatches(test.regexp, "").Validate(context.TODO(), request, &response) + stringvalidator.RegexMatches(test.regexp, "").Validate(context.TODO(), request, &response) if !response.Diagnostics.HasError() && test.expectError { t.Fatal("expected error, got no error") From d7a008e85efaa7a29ec6e8f2dcb320d2430297f4 Mon Sep 17 00:00:00 2001 From: Ivan De Marino Date: Thu, 23 Jun 2022 15:31:47 +0100 Subject: [PATCH 9/9] Fix conflicts from rebase --- listvalidator/size_at_least.go | 5 ++--- listvalidator/size_at_most.go | 5 ++--- listvalidator/size_between.go | 5 ++--- mapvalidator/size_at_least.go | 5 ++--- mapvalidator/size_at_most.go | 5 ++--- mapvalidator/size_between.go | 5 ++--- setvalidator/size_at_least.go | 5 ++--- setvalidator/size_at_most.go | 5 ++--- setvalidator/size_between.go | 5 ++--- 9 files changed, 18 insertions(+), 27 deletions(-) diff --git a/listvalidator/size_at_least.go b/listvalidator/size_at_least.go index 372a3785..f7724e2e 100644 --- a/listvalidator/size_at_least.go +++ b/listvalidator/size_at_least.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeAtLeastValidator{} @@ -34,7 +33,7 @@ func (v sizeAtLeastValidator) Validate(ctx context.Context, req tfsdk.ValidateAt } if len(elems) < v.min { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/listvalidator/size_at_most.go b/listvalidator/size_at_most.go index 42bbc8be..eb767290 100644 --- a/listvalidator/size_at_most.go +++ b/listvalidator/size_at_most.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeAtMostValidator{} @@ -34,7 +33,7 @@ func (v sizeAtMostValidator) Validate(ctx context.Context, req tfsdk.ValidateAtt } if len(elems) > v.max { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/listvalidator/size_between.go b/listvalidator/size_between.go index 3e3ded12..7b7e0672 100644 --- a/listvalidator/size_between.go +++ b/listvalidator/size_between.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeBetweenValidator{} @@ -36,7 +35,7 @@ func (v sizeBetweenValidator) Validate(ctx context.Context, req tfsdk.ValidateAt } if len(elems) < v.min || len(elems) > v.max { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/mapvalidator/size_at_least.go b/mapvalidator/size_at_least.go index c46918a1..04bb6417 100644 --- a/mapvalidator/size_at_least.go +++ b/mapvalidator/size_at_least.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeAtLeastValidator{} @@ -34,7 +33,7 @@ func (v sizeAtLeastValidator) Validate(ctx context.Context, req tfsdk.ValidateAt } if len(elems) < v.min { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/mapvalidator/size_at_most.go b/mapvalidator/size_at_most.go index 994a8c86..db6f6d75 100644 --- a/mapvalidator/size_at_most.go +++ b/mapvalidator/size_at_most.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeAtMostValidator{} @@ -34,7 +33,7 @@ func (v sizeAtMostValidator) Validate(ctx context.Context, req tfsdk.ValidateAtt } if len(elems) > v.max { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/mapvalidator/size_between.go b/mapvalidator/size_between.go index 96be810f..e7d284e1 100644 --- a/mapvalidator/size_between.go +++ b/mapvalidator/size_between.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeBetweenValidator{} @@ -36,7 +35,7 @@ func (v sizeBetweenValidator) Validate(ctx context.Context, req tfsdk.ValidateAt } if len(elems) < v.min || len(elems) > v.max { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/setvalidator/size_at_least.go b/setvalidator/size_at_least.go index c3d99e3b..78caea18 100644 --- a/setvalidator/size_at_least.go +++ b/setvalidator/size_at_least.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeAtLeastValidator{} @@ -34,7 +33,7 @@ func (v sizeAtLeastValidator) Validate(ctx context.Context, req tfsdk.ValidateAt } if len(elems) < v.min { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/setvalidator/size_at_most.go b/setvalidator/size_at_most.go index 8fe1cc91..fe296bdb 100644 --- a/setvalidator/size_at_most.go +++ b/setvalidator/size_at_most.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeAtMostValidator{} @@ -34,7 +33,7 @@ func (v sizeAtMostValidator) Validate(ctx context.Context, req tfsdk.ValidateAtt } if len(elems) > v.max { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)), diff --git a/setvalidator/size_between.go b/setvalidator/size_between.go index 894d327a..79ea8065 100644 --- a/setvalidator/size_between.go +++ b/setvalidator/size_between.go @@ -4,9 +4,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - - "github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" ) var _ tfsdk.AttributeValidator = sizeBetweenValidator{} @@ -36,7 +35,7 @@ func (v sizeBetweenValidator) Validate(ctx context.Context, req tfsdk.ValidateAt } if len(elems) < v.min || len(elems) > v.max { - resp.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.AttributePath, v.Description(ctx), fmt.Sprintf("%d", len(elems)),