Skip to content

Commit

Permalink
Add parameter-based provider-defined function validation (#971)
Browse files Browse the repository at this point in the history
* Add `function/validator` package

* Implement parameter validators in parameter types

* Initial implementation of parameter validation logic

* Refactor function parameter validators from `function/validator` package to `function` package.

* Add parameter unit tests

* Add parameter tests to arguments_data_test.go

* Switch dynamic parameter test to use `DynamicTypeMust()`

* Correct error handling and dynamic type test cases

* Add variadic parameter test cases

* Add variadic parameter support and test for `tfprotov6`

* Add copyright headers

* Resolve `forcetypeassert` linter errors

* Resolve test failures from merge

* Add changelog entries

* Skip appending parameter value if validation fails

* Support parameter validation for custom types

* Refactor parameter validator interface names for clarity.

* Add website documentation for parameter-based validation

* Add copyright headers

* Update changelog entries

* Rename `Validate()` method in parameter validator interfaces to `ValidateParameter<Type>()`

* Skip appending variadic values if there is an error in validation

* Update website/docs/plugin/framework/validation.mdx

Co-authored-by: Austin Valle <austinvalle@gmail.com>

* Replace `create{type}Value` test helper functions with `New{Type}ValueMust` functions

---------

Co-authored-by: Benjamin Bennett <ben.bennett@hashicorp.com>
Co-authored-by: Austin Valle <austinvalle@gmail.com>
  • Loading branch information
3 people authored Apr 15, 2024
1 parent f6057df commit d36ac87
Show file tree
Hide file tree
Showing 50 changed files with 6,212 additions and 22 deletions.
7 changes: 7 additions & 0 deletions .changes/unreleased/FEATURES-20240405-183917.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: FEATURES
body: '`function`: Add `BoolParameterValidator`, `DynamicParameterValidator`, `Float64ParameterValidator`, `Int64ParameterValidator`,
`ListParameterValidator`, `MapParameterValidator`, `NumberParameterValidator`, `ObjectParameterValidator`, `SetParameterValidator`,
and `StringParameterValidator` interfaces for custom function parameter validation implementations.'
time: 2024-04-05T18:39:17.640444-04:00
custom:
Issue: "971"
9 changes: 9 additions & 0 deletions .changes/unreleased/FEATURES-20240405-184527.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
kind: FEATURES
body: '`function`: Add `ParameterWithBoolValidators`, `ParameterWithInt64Validators`,
`ParameterWithFloat64Validators`, `ParameterWithDynamicValidators`, `ParameterWithListValidators`,
`ParameterWithMapValidators`, `ParameterWithNumberValidators`, `ParameterWithObjectValidators`,
`ParameterWithSetValidators`, and `ParameterWithStringValidators` interfaces to enable
parameter-based validation support'
time: 2024-04-05T18:45:27.979266-04:00
custom:
Issue: "971"
10 changes: 10 additions & 0 deletions function/bool_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

// Ensure the implementation satisifies the desired interfaces.
var _ Parameter = BoolParameter{}
var _ ParameterWithBoolValidators = BoolParameter{}

// BoolParameter represents a function parameter that is a boolean.
//
Expand Down Expand Up @@ -70,6 +71,15 @@ type BoolParameter struct {
// alphabetical character and followed by alphanumeric or underscore
// characters.
Name string

// Validators is a list of bool validators that should be applied to the
// parameter.
Validators []BoolParameterValidator
}

// GetValidators returns the list of validators for the parameter.
func (p BoolParameter) GetValidators() []BoolParameterValidator {
return p.Validators
}

// GetAllowNullValue returns if the parameter accepts a null value.
Expand Down
44 changes: 44 additions & 0 deletions function/bool_parameter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"testing"

"github.com/google/go-cmp/cmp"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/function"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testvalidator"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

Expand Down Expand Up @@ -240,3 +242,45 @@ func TestBoolParameterGetType(t *testing.T) {
})
}
}

func TestBoolParameterBoolValidators(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
parameter function.BoolParameter
expected []function.BoolParameterValidator
}{
"unset": {
parameter: function.BoolParameter{},
expected: nil,
},
"Validators - empty": {
parameter: function.BoolParameter{
Validators: []function.BoolParameterValidator{}},
expected: []function.BoolParameterValidator{},
},
"Validators": {
parameter: function.BoolParameter{
Validators: []function.BoolParameterValidator{
testvalidator.Bool{},
}},
expected: []function.BoolParameterValidator{
testvalidator.Bool{},
},
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

got := testCase.parameter.GetValidators()

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}
33 changes: 33 additions & 0 deletions function/bool_parameter_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package function

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/types"
)

// BoolParameterValidator is a function validator for types.Bool parameters.
type BoolParameterValidator interface {

// ValidateParameterBool performs the validation.
ValidateParameterBool(context.Context, BoolParameterValidatorRequest, *BoolParameterValidatorResponse)
}

// BoolParameterValidatorRequest is a request for types.Bool schema validation.
type BoolParameterValidatorRequest struct {
// ArgumentPosition contains the position of the argument for validation.
// Use this position for any response diagnostics.
ArgumentPosition int64

// Value contains the value of the argument for validation.
Value types.Bool
}

// BoolParameterValidatorResponse is a response to a BoolParameterValidatorRequest.
type BoolParameterValidatorResponse struct {
// Error is a function error generated during validation of the Value.
Error *FuncError
}
10 changes: 10 additions & 0 deletions function/dynamic_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

// Ensure the implementation satisifies the desired interfaces.
var _ Parameter = DynamicParameter{}
var _ ParameterWithDynamicValidators = DynamicParameter{}

// DynamicParameter represents a function parameter that is a dynamic, rather
// than a static type. Static types are always preferable over dynamic
Expand Down Expand Up @@ -65,6 +66,15 @@ type DynamicParameter struct {
// alphabetical character and followed by alphanumeric or underscore
// characters.
Name string

// Validators is a list of dynamic validators that should be applied to the
// parameter.
Validators []DynamicParameterValidator
}

// GetValidators returns the list of validators for the parameter.
func (p DynamicParameter) GetValidators() []DynamicParameterValidator {
return p.Validators
}

// GetAllowNullValue returns if the parameter accepts a null value.
Expand Down
44 changes: 44 additions & 0 deletions function/dynamic_parameter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"testing"

"github.com/google/go-cmp/cmp"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/function"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testvalidator"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

Expand Down Expand Up @@ -240,3 +242,45 @@ func TestDynamicParameterGetType(t *testing.T) {
})
}
}

func TestDynamicParameterDynamicValidators(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
parameter function.DynamicParameter
expected []function.DynamicParameterValidator
}{
"unset": {
parameter: function.DynamicParameter{},
expected: nil,
},
"Validators - empty": {
parameter: function.DynamicParameter{
Validators: []function.DynamicParameterValidator{}},
expected: []function.DynamicParameterValidator{},
},
"Validators": {
parameter: function.DynamicParameter{
Validators: []function.DynamicParameterValidator{
testvalidator.Dynamic{},
}},
expected: []function.DynamicParameterValidator{
testvalidator.Dynamic{},
},
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

got := testCase.parameter.GetValidators()

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}
33 changes: 33 additions & 0 deletions function/dynamic_parameter_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package function

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/types"
)

// DynamicParameterValidator is a function validator for types.Dynamic parameters.
type DynamicParameterValidator interface {

// ValidateParameterDynamic performs the validation.
ValidateParameterDynamic(context.Context, DynamicParameterValidatorRequest, *DynamicParameterValidatorResponse)
}

// DynamicParameterValidatorRequest is a request for types.Dynamic schema validation.
type DynamicParameterValidatorRequest struct {
// ArgumentPosition contains the position of the argument for validation.
// Use this position for any response diagnostics.
ArgumentPosition int64

// Value contains the value of the argument for validation.
Value types.Dynamic
}

// DynamicParameterValidatorResponse is a response to a DynamicParameterValidatorRequest.
type DynamicParameterValidatorResponse struct {
// Error is a function error generated during validation of the Value.
Error *FuncError
}
10 changes: 10 additions & 0 deletions function/float64_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

// Ensure the implementation satisifies the desired interfaces.
var _ Parameter = Float64Parameter{}
var _ ParameterWithFloat64Validators = Float64Parameter{}

// Float64Parameter represents a function parameter that is a 64-bit floating
// point number.
Expand Down Expand Up @@ -67,6 +68,15 @@ type Float64Parameter struct {
// alphabetical character and followed by alphanumeric or underscore
// characters.
Name string

// Validators is a list of float64 validators that should be applied to the
// parameter.
Validators []Float64ParameterValidator
}

// GetValidators returns the list of validators for the parameter.
func (p Float64Parameter) GetValidators() []Float64ParameterValidator {
return p.Validators
}

// GetAllowNullValue returns if the parameter accepts a null value.
Expand Down
44 changes: 44 additions & 0 deletions function/float64_parameter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"testing"

"github.com/google/go-cmp/cmp"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/function"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testvalidator"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

Expand Down Expand Up @@ -240,3 +242,45 @@ func TestFloat64ParameterGetType(t *testing.T) {
})
}
}

func TestFloat64ParameterFloat64Validators(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
parameter function.Float64Parameter
expected []function.Float64ParameterValidator
}{
"unset": {
parameter: function.Float64Parameter{},
expected: nil,
},
"Validators - empty": {
parameter: function.Float64Parameter{
Validators: []function.Float64ParameterValidator{}},
expected: []function.Float64ParameterValidator{},
},
"Validators": {
parameter: function.Float64Parameter{
Validators: []function.Float64ParameterValidator{
testvalidator.Float64{},
}},
expected: []function.Float64ParameterValidator{
testvalidator.Float64{},
},
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

got := testCase.parameter.GetValidators()

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}
33 changes: 33 additions & 0 deletions function/float64_parameter_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package function

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/types"
)

// Float64ParameterValidator is a function validator for types.Float64 parameters.
type Float64ParameterValidator interface {

// ValidateParameterFloat64 performs the validation.
ValidateParameterFloat64(context.Context, Float64ParameterValidatorRequest, *Float64ParameterValidatorResponse)
}

// Float64ParameterValidatorRequest is a request for types.Float64 schema validation.
type Float64ParameterValidatorRequest struct {
// ArgumentPosition contains the position of the argument for validation.
// Use this position for any response diagnostics.
ArgumentPosition int64

// Value contains the value of the argument for validation.
Value types.Float64
}

// Float64ParameterValidatorResponse is a response to a Float64ParameterValidatorRequest.
type Float64ParameterValidatorResponse struct {
// Error is a function error generated during validation of the Value.
Error *FuncError
}
10 changes: 10 additions & 0 deletions function/int64_parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

// Ensure the implementation satisifies the desired interfaces.
var _ Parameter = Int64Parameter{}
var _ ParameterWithInt64Validators = Int64Parameter{}

// Int64Parameter represents a function parameter that is a 64-bit integer.
//
Expand Down Expand Up @@ -66,6 +67,15 @@ type Int64Parameter struct {
// alphabetical character and followed by alphanumeric or underscore
// characters.
Name string

// Validators is a list of int64 validators that should be applied to the
// parameter.
Validators []Int64ParameterValidator
}

// GetValidators returns the list of validators for the parameter.
func (p Int64Parameter) GetValidators() []Int64ParameterValidator {
return p.Validators
}

// GetAllowNullValue returns if the parameter accepts a null value.
Expand Down
Loading

0 comments on commit d36ac87

Please sign in to comment.