Skip to content

Commit aa6a2de

Browse files
authored
all: Implement function parameter validation interfaces (#238)
* stringvalidator: implement parameter interface * boolvalidator: implement parameter interface * float32validator: implement parameter interface * float64validator: implement parameter interface * numbervalidator: implement parameter interface * int32validator: implement parameter interface * int64validator: implement parameter interface * listvalidator: implement parameter interface validation * setvalidator: implement parameter interface * mapvalidator: implement parameter interface * add changelogs
1 parent 6acd967 commit aa6a2de

File tree

144 files changed

+3333
-660
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+3333
-660
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: ENHANCEMENTS
2+
body: 'all: Implemented parameter interfaces for all value-based validators. This
3+
allows these validators to be used with provider-defined functions.'
4+
time: 2024-10-14T12:12:20.607373-04:00
5+
custom:
6+
Issue: "235"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
kind: NOTES
2+
body: 'all: Previously, creating validators with invalid data would result in a `nil`
3+
value being returned and a panic from `terraform-plugin-framework`. This has been
4+
updated to return an implementation diagnostic referencing the invalid data/validator during config validation.'
5+
time: 2024-10-14T12:17:11.811926-04:00
6+
custom:
7+
Issue: "235"

boolvalidator/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) HashiCorp, Inc.
22
// SPDX-License-Identifier: MPL-2.0
33

4-
// Package boolvalidator provides validators for types.Bool attributes.
4+
// Package boolvalidator provides validators for types.Bool attributes or function parameters.
55
package boolvalidator

boolvalidator/equals.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ import (
88
"fmt"
99

1010
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
11+
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatorfuncerr"
12+
"github.com/hashicorp/terraform-plugin-framework/function"
1113
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1214
"github.com/hashicorp/terraform-plugin-framework/types"
1315
)
1416

1517
var _ validator.Bool = equalsValidator{}
18+
var _ function.BoolParameterValidator = equalsValidator{}
1619

1720
type equalsValidator struct {
1821
value types.Bool
@@ -42,9 +45,25 @@ func (v equalsValidator) ValidateBool(ctx context.Context, req validator.BoolReq
4245
}
4346
}
4447

45-
// Equals returns an AttributeValidator which ensures that the configured boolean attribute
48+
func (v equalsValidator) ValidateParameterBool(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) {
49+
if req.Value.IsNull() || req.Value.IsUnknown() {
50+
return
51+
}
52+
53+
value := req.Value
54+
55+
if !value.Equal(v.value) {
56+
resp.Error = validatorfuncerr.InvalidParameterValueMatchFuncError(
57+
req.ArgumentPosition,
58+
v.Description(ctx),
59+
value.String(),
60+
)
61+
}
62+
}
63+
64+
// Equals returns an AttributeValidator which ensures that the configured boolean attribute or function parameter
4665
// matches the given `value`. Null (unconfigured) and unknown (known after apply) values are skipped.
47-
func Equals(value bool) validator.Bool {
66+
func Equals(value bool) equalsValidator {
4867
return equalsValidator{
4968
value: types.BoolValue(value),
5069
}

boolvalidator/equals_test.go

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ package boolvalidator_test
55

66
import (
77
"context"
8+
"fmt"
89
"testing"
910

1011
"github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator"
12+
"github.com/hashicorp/terraform-plugin-framework/function"
1113
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1214
"github.com/hashicorp/terraform-plugin-framework/types"
1315
)
@@ -16,53 +18,65 @@ func TestEqualsValidator(t *testing.T) {
1618
t.Parallel()
1719

1820
type testCase struct {
19-
in types.Bool
20-
validator validator.Bool
21-
expErrors int
21+
in types.Bool
22+
equalsValue bool
23+
expectError bool
2224
}
2325

2426
testCases := map[string]testCase{
2527
"simple-match": {
26-
in: types.BoolValue(true),
27-
validator: boolvalidator.Equals(true),
28-
expErrors: 0,
28+
in: types.BoolValue(true),
29+
equalsValue: true,
2930
},
3031
"simple-mismatch": {
31-
in: types.BoolValue(false),
32-
validator: boolvalidator.Equals(true),
33-
expErrors: 1,
32+
in: types.BoolValue(false),
33+
equalsValue: true,
34+
expectError: true,
3435
},
3536
"skip-validation-on-null": {
36-
in: types.BoolNull(),
37-
validator: boolvalidator.Equals(true),
38-
expErrors: 0,
37+
in: types.BoolNull(),
38+
equalsValue: true,
3939
},
4040
"skip-validation-on-unknown": {
41-
in: types.BoolUnknown(),
42-
validator: boolvalidator.Equals(true),
43-
expErrors: 0,
41+
in: types.BoolUnknown(),
42+
equalsValue: true,
4443
},
4544
}
4645

4746
for name, test := range testCases {
48-
t.Run(name, func(t *testing.T) {
47+
name, test := name, test
48+
49+
t.Run(fmt.Sprintf("ValidateBool - %s", name), func(t *testing.T) {
4950
t.Parallel()
5051
req := validator.BoolRequest{
5152
ConfigValue: test.in,
5253
}
5354
res := validator.BoolResponse{}
54-
test.validator.ValidateBool(context.TODO(), req, &res)
55+
boolvalidator.Equals(test.equalsValue).ValidateBool(context.TODO(), req, &res)
56+
57+
if !res.Diagnostics.HasError() && test.expectError {
58+
t.Fatal("expected error, got no error")
59+
}
5560

56-
if test.expErrors > 0 && !res.Diagnostics.HasError() {
57-
t.Fatalf("expected %d error(s), got none", test.expErrors)
61+
if res.Diagnostics.HasError() && !test.expectError {
62+
t.Fatalf("got unexpected error: %s", res.Diagnostics)
63+
}
64+
})
65+
66+
t.Run(fmt.Sprintf("ValidateParameterBool - %s", name), func(t *testing.T) {
67+
t.Parallel()
68+
req := function.BoolParameterValidatorRequest{
69+
Value: test.in,
5870
}
71+
res := function.BoolParameterValidatorResponse{}
72+
boolvalidator.Equals(test.equalsValue).ValidateParameterBool(context.TODO(), req, &res)
5973

60-
if test.expErrors > 0 && test.expErrors != res.Diagnostics.ErrorsCount() {
61-
t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, res.Diagnostics.ErrorsCount(), res.Diagnostics)
74+
if res.Error == nil && test.expectError {
75+
t.Fatal("expected error, got no error")
6276
}
6377

64-
if test.expErrors == 0 && res.Diagnostics.HasError() {
65-
t.Fatalf("expected no error(s), got %d: %v", res.Diagnostics.ErrorsCount(), res.Diagnostics)
78+
if res.Error != nil && !test.expectError {
79+
t.Fatalf("got unexpected error: %s", res.Error)
6680
}
6781
})
6882
}

float32validator/at_least.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,28 @@ import (
77
"context"
88
"fmt"
99

10+
"github.com/hashicorp/terraform-plugin-framework/function"
1011
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1112

1213
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
14+
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatorfuncerr"
1315
)
1416

1517
var _ validator.Float32 = atLeastValidator{}
18+
var _ function.Float32ParameterValidator = atLeastValidator{}
1619

17-
// atLeastValidator validates that an float Attribute's value is at least a certain value.
1820
type atLeastValidator struct {
1921
min float32
2022
}
2123

22-
// Description describes the validation in plain text formatting.
2324
func (validator atLeastValidator) Description(_ context.Context) string {
2425
return fmt.Sprintf("value must be at least %f", validator.min)
2526
}
2627

27-
// MarkdownDescription describes the validation in Markdown formatting.
2828
func (validator atLeastValidator) MarkdownDescription(ctx context.Context) string {
2929
return validator.Description(ctx)
3030
}
3131

32-
// ValidateFloat32 performs the validation.
3332
func (validator atLeastValidator) ValidateFloat32(ctx context.Context, request validator.Float32Request, response *validator.Float32Response) {
3433
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
3534
return
@@ -46,14 +45,30 @@ func (validator atLeastValidator) ValidateFloat32(ctx context.Context, request v
4645
}
4746
}
4847

48+
func (validator atLeastValidator) ValidateParameterFloat32(ctx context.Context, request function.Float32ParameterValidatorRequest, response *function.Float32ParameterValidatorResponse) {
49+
if request.Value.IsNull() || request.Value.IsUnknown() {
50+
return
51+
}
52+
53+
value := request.Value.ValueFloat32()
54+
55+
if value < validator.min {
56+
response.Error = validatorfuncerr.InvalidParameterValueFuncError(
57+
request.ArgumentPosition,
58+
validator.Description(ctx),
59+
fmt.Sprintf("%f", value),
60+
)
61+
}
62+
}
63+
4964
// AtLeast returns an AttributeValidator which ensures that any configured
50-
// attribute value:
65+
// attribute or function parameter value:
5166
//
5267
// - Is a number, which can be represented by a 32-bit floating point.
5368
// - Is greater than or equal to the given minimum.
5469
//
5570
// Null (unconfigured) and unknown (known after apply) values are skipped.
56-
func AtLeast(minVal float32) validator.Float32 {
71+
func AtLeast(minVal float32) atLeastValidator {
5772
return atLeastValidator{
5873
min: minVal,
5974
}

float32validator/at_least_example_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package float32validator_test
55

66
import (
77
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
8+
"github.com/hashicorp/terraform-plugin-framework/function"
89
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
910

1011
"github.com/hashicorp/terraform-plugin-framework-validators/float32validator"
@@ -24,3 +25,17 @@ func ExampleAtLeast() {
2425
},
2526
}
2627
}
28+
29+
func ExampleAtLeast_function() {
30+
_ = function.Definition{
31+
Parameters: []function.Parameter{
32+
function.Float32Parameter{
33+
Name: "example_param",
34+
Validators: []function.Float32ParameterValidator{
35+
// Validate floating point value must be at least 42.42
36+
float32validator.AtLeast(42.42),
37+
},
38+
},
39+
},
40+
}
41+
}

float32validator/at_least_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ package float32validator_test
55

66
import (
77
"context"
8+
"fmt"
89
"testing"
910

11+
"github.com/hashicorp/terraform-plugin-framework/function"
1012
"github.com/hashicorp/terraform-plugin-framework/path"
1113
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1214
"github.com/hashicorp/terraform-plugin-framework/types"
@@ -52,7 +54,8 @@ func TestAtLeastValidator(t *testing.T) {
5254

5355
for name, test := range tests {
5456
name, test := name, test
55-
t.Run(name, func(t *testing.T) {
57+
58+
t.Run(fmt.Sprintf("ValidateFloat32 - %s", name), func(t *testing.T) {
5659
t.Parallel()
5760
request := validator.Float32Request{
5861
Path: path.Root("test"),
@@ -70,5 +73,22 @@ func TestAtLeastValidator(t *testing.T) {
7073
t.Fatalf("got unexpected error: %s", response.Diagnostics)
7174
}
7275
})
76+
77+
t.Run(fmt.Sprintf("ValidateParameterFloat32 - %s", name), func(t *testing.T) {
78+
t.Parallel()
79+
request := function.Float32ParameterValidatorRequest{
80+
Value: test.val,
81+
}
82+
response := function.Float32ParameterValidatorResponse{}
83+
float32validator.AtLeast(test.min).ValidateParameterFloat32(context.TODO(), request, &response)
84+
85+
if response.Error == nil && test.expectError {
86+
t.Fatal("expected error, got no error")
87+
}
88+
89+
if response.Error != nil && !test.expectError {
90+
t.Fatalf("got unexpected error: %s", response.Error)
91+
}
92+
})
7393
}
7494
}

float32validator/at_most.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,28 @@ import (
77
"context"
88
"fmt"
99

10+
"github.com/hashicorp/terraform-plugin-framework/function"
1011
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1112

1213
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
14+
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatorfuncerr"
1315
)
1416

1517
var _ validator.Float32 = atMostValidator{}
18+
var _ function.Float32ParameterValidator = atMostValidator{}
1619

17-
// atMostValidator validates that an float Attribute's value is at most a certain value.
1820
type atMostValidator struct {
1921
max float32
2022
}
2123

22-
// Description describes the validation in plain text formatting.
2324
func (validator atMostValidator) Description(_ context.Context) string {
2425
return fmt.Sprintf("value must be at most %f", validator.max)
2526
}
2627

27-
// MarkdownDescription describes the validation in Markdown formatting.
2828
func (validator atMostValidator) MarkdownDescription(ctx context.Context) string {
2929
return validator.Description(ctx)
3030
}
3131

32-
// ValidateFloat32 performs the validation.
3332
func (v atMostValidator) ValidateFloat32(ctx context.Context, request validator.Float32Request, response *validator.Float32Response) {
3433
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
3534
return
@@ -46,14 +45,30 @@ func (v atMostValidator) ValidateFloat32(ctx context.Context, request validator.
4645
}
4746
}
4847

48+
func (v atMostValidator) ValidateParameterFloat32(ctx context.Context, request function.Float32ParameterValidatorRequest, response *function.Float32ParameterValidatorResponse) {
49+
if request.Value.IsNull() || request.Value.IsUnknown() {
50+
return
51+
}
52+
53+
value := request.Value.ValueFloat32()
54+
55+
if value > v.max {
56+
response.Error = validatorfuncerr.InvalidParameterValueFuncError(
57+
request.ArgumentPosition,
58+
v.Description(ctx),
59+
fmt.Sprintf("%f", value),
60+
)
61+
}
62+
}
63+
4964
// AtMost returns an AttributeValidator which ensures that any configured
50-
// attribute value:
65+
// attribute or function parameter value:
5166
//
5267
// - Is a number, which can be represented by a 32-bit floating point.
5368
// - Is less than or equal to the given maximum.
5469
//
5570
// Null (unconfigured) and unknown (known after apply) values are skipped.
56-
func AtMost(maxVal float32) validator.Float32 {
71+
func AtMost(maxVal float32) atMostValidator {
5772
return atMostValidator{
5873
max: maxVal,
5974
}

float32validator/at_most_example_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package float32validator_test
55

66
import (
77
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
8+
"github.com/hashicorp/terraform-plugin-framework/function"
89
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
910

1011
"github.com/hashicorp/terraform-plugin-framework-validators/float32validator"
@@ -24,3 +25,17 @@ func ExampleAtMost() {
2425
},
2526
}
2627
}
28+
29+
func ExampleAtMost_function() {
30+
_ = function.Definition{
31+
Parameters: []function.Parameter{
32+
function.Float32Parameter{
33+
Name: "example_param",
34+
Validators: []function.Float32ParameterValidator{
35+
// Validate floating point value must be at most 42.42
36+
float32validator.AtMost(42.42),
37+
},
38+
},
39+
},
40+
}
41+
}

0 commit comments

Comments
 (0)