diff --git a/.changes/unreleased/FEATURES-20240405-183917.yaml b/.changes/unreleased/FEATURES-20240405-183917.yaml new file mode 100644 index 000000000..d2270bf69 --- /dev/null +++ b/.changes/unreleased/FEATURES-20240405-183917.yaml @@ -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" diff --git a/.changes/unreleased/FEATURES-20240405-184527.yaml b/.changes/unreleased/FEATURES-20240405-184527.yaml new file mode 100644 index 000000000..187ea5b56 --- /dev/null +++ b/.changes/unreleased/FEATURES-20240405-184527.yaml @@ -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" diff --git a/function/bool_parameter.go b/function/bool_parameter.go index 4c6ec94a9..67929c31f 100644 --- a/function/bool_parameter.go +++ b/function/bool_parameter.go @@ -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. // @@ -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. diff --git a/function/bool_parameter_test.go b/function/bool_parameter_test.go index f82113cf7..bdcf2a32e 100644 --- a/function/bool_parameter_test.go +++ b/function/bool_parameter_test.go @@ -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" ) @@ -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) + } + }) + } +} diff --git a/function/bool_parameter_validator.go b/function/bool_parameter_validator.go new file mode 100644 index 000000000..145519af2 --- /dev/null +++ b/function/bool_parameter_validator.go @@ -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 +} diff --git a/function/dynamic_parameter.go b/function/dynamic_parameter.go index 1af3c71d0..cbf2ea33e 100644 --- a/function/dynamic_parameter.go +++ b/function/dynamic_parameter.go @@ -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 @@ -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. diff --git a/function/dynamic_parameter_test.go b/function/dynamic_parameter_test.go index a6e50c2b6..ccf7ab3f1 100644 --- a/function/dynamic_parameter_test.go +++ b/function/dynamic_parameter_test.go @@ -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" ) @@ -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) + } + }) + } +} diff --git a/function/dynamic_parameter_validator.go b/function/dynamic_parameter_validator.go new file mode 100644 index 000000000..43c6e1a41 --- /dev/null +++ b/function/dynamic_parameter_validator.go @@ -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 +} diff --git a/function/float64_parameter.go b/function/float64_parameter.go index 221fb4f22..11e31c7ef 100644 --- a/function/float64_parameter.go +++ b/function/float64_parameter.go @@ -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. @@ -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. diff --git a/function/float64_parameter_test.go b/function/float64_parameter_test.go index 479d06437..1c364d793 100644 --- a/function/float64_parameter_test.go +++ b/function/float64_parameter_test.go @@ -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" ) @@ -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) + } + }) + } +} diff --git a/function/float64_parameter_validator.go b/function/float64_parameter_validator.go new file mode 100644 index 000000000..076128884 --- /dev/null +++ b/function/float64_parameter_validator.go @@ -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 +} diff --git a/function/int64_parameter.go b/function/int64_parameter.go index 3fd6d8852..15a9700a7 100644 --- a/function/int64_parameter.go +++ b/function/int64_parameter.go @@ -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. // @@ -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. diff --git a/function/int64_parameter_test.go b/function/int64_parameter_test.go index 5422dcd69..21b608bb2 100644 --- a/function/int64_parameter_test.go +++ b/function/int64_parameter_test.go @@ -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" ) @@ -240,3 +242,45 @@ func TestInt64ParameterGetType(t *testing.T) { }) } } + +func TestInt64ParameterInt64Validators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + parameter function.Int64Parameter + expected []function.Int64ParameterValidator + }{ + "unset": { + parameter: function.Int64Parameter{}, + expected: nil, + }, + "Validators - empty": { + parameter: function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{}}, + expected: []function.Int64ParameterValidator{}, + }, + "Validators": { + parameter: function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{}, + }}, + expected: []function.Int64ParameterValidator{ + testvalidator.Int64{}, + }, + }, + } + + 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) + } + }) + } +} diff --git a/function/int64_parameter_validator.go b/function/int64_parameter_validator.go new file mode 100644 index 000000000..d938c4b93 --- /dev/null +++ b/function/int64_parameter_validator.go @@ -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" +) + +// Int64ParameterValidator is a function validator for types.Int64 parameters. +type Int64ParameterValidator interface { + + // ValidateParameterInt64 performs the validation. + ValidateParameterInt64(context.Context, Int64ParameterValidatorRequest, *Int64ParameterValidatorResponse) +} + +// Int64ParameterValidatorRequest is a request for types.Int64 schema validation. +type Int64ParameterValidatorRequest 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.Int64 +} + +// Int64ParameterValidatorResponse is a response to a Int64ParameterValidatorRequest. +type Int64ParameterValidatorResponse struct { + // Error is a function error generated during validation of the Value. + Error *FuncError +} diff --git a/function/list_parameter.go b/function/list_parameter.go index 69d54da94..cdca5a280 100644 --- a/function/list_parameter.go +++ b/function/list_parameter.go @@ -17,6 +17,7 @@ import ( var ( _ Parameter = ListParameter{} _ fwfunction.ParameterWithValidateImplementation = ListParameter{} + _ ParameterWithListValidators = ListParameter{} ) // ListParameter represents a function parameter that is an ordered list of a @@ -82,6 +83,15 @@ type ListParameter struct { // alphabetical character and followed by alphanumeric or underscore // characters. Name string + + // Validators is a list of list validators that should be applied to the + // parameter. + Validators []ListParameterValidator +} + +// GetValidators returns the list of validators for the parameter. +func (p ListParameter) GetValidators() []ListParameterValidator { + return p.Validators } // GetAllowNullValue returns if the parameter accepts a null value. diff --git a/function/list_parameter_test.go b/function/list_parameter_test.go index 3736a65c8..7eb530c41 100644 --- a/function/list_parameter_test.go +++ b/function/list_parameter_test.go @@ -8,11 +8,13 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/internal/fwfunction" "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" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) @@ -257,6 +259,48 @@ func TestListParameterGetType(t *testing.T) { } } +func TestListParameterListValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + parameter function.ListParameter + expected []function.ListParameterValidator + }{ + "unset": { + parameter: function.ListParameter{}, + expected: nil, + }, + "Validators - empty": { + parameter: function.ListParameter{ + Validators: []function.ListParameterValidator{}}, + expected: []function.ListParameterValidator{}, + }, + "Validators": { + parameter: function.ListParameter{ + Validators: []function.ListParameterValidator{ + testvalidator.List{}, + }}, + expected: []function.ListParameterValidator{ + testvalidator.List{}, + }, + }, + } + + 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) + } + }) + } +} + func TestListParameterValidateImplementation(t *testing.T) { t.Parallel() diff --git a/function/list_parameter_validator.go b/function/list_parameter_validator.go new file mode 100644 index 000000000..3ab9a776a --- /dev/null +++ b/function/list_parameter_validator.go @@ -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" +) + +// ListParameterValidator is a function validator for types.List parameters. +type ListParameterValidator interface { + + // ValidateParameterList performs the validation. + ValidateParameterList(context.Context, ListParameterValidatorRequest, *ListParameterValidatorResponse) +} + +// ListParameterValidatorRequest is a request for types.List schema validation. +type ListParameterValidatorRequest 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.List +} + +// ListParameterValidatorResponse is a response to a ListParameterValidatorRequest. +type ListParameterValidatorResponse struct { + // Error is a function error generated during validation of the Value. + Error *FuncError +} diff --git a/function/map_parameter.go b/function/map_parameter.go index 457720ea2..626781352 100644 --- a/function/map_parameter.go +++ b/function/map_parameter.go @@ -17,6 +17,7 @@ import ( var ( _ Parameter = MapParameter{} _ fwfunction.ParameterWithValidateImplementation = MapParameter{} + _ ParameterWithMapValidators = MapParameter{} ) // MapParameter represents a function parameter that is a mapping of a single @@ -82,6 +83,15 @@ type MapParameter struct { // alphabetical character and followed by alphanumeric or underscore // characters. Name string + + // Validators is a list of map validators that should be applied to the + // parameter. + Validators []MapParameterValidator +} + +// GetValidators returns the list of validators for the parameter. +func (p MapParameter) GetValidators() []MapParameterValidator { + return p.Validators } // GetAllowNullValue returns if the parameter accepts a null value. diff --git a/function/map_parameter_test.go b/function/map_parameter_test.go index 0992a5452..de8b1dc78 100644 --- a/function/map_parameter_test.go +++ b/function/map_parameter_test.go @@ -8,11 +8,13 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/internal/fwfunction" "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" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) @@ -257,6 +259,48 @@ func TestMapParameterGetType(t *testing.T) { } } +func TestMapParameterMapValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + parameter function.MapParameter + expected []function.MapParameterValidator + }{ + "unset": { + parameter: function.MapParameter{}, + expected: nil, + }, + "Validators - empty": { + parameter: function.MapParameter{ + Validators: []function.MapParameterValidator{}}, + expected: []function.MapParameterValidator{}, + }, + "Validators": { + parameter: function.MapParameter{ + Validators: []function.MapParameterValidator{ + testvalidator.Map{}, + }}, + expected: []function.MapParameterValidator{ + testvalidator.Map{}, + }, + }, + } + + 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) + } + }) + } +} + func TestMapParameterValidateImplementation(t *testing.T) { t.Parallel() diff --git a/function/map_parameter_validator.go b/function/map_parameter_validator.go new file mode 100644 index 000000000..97c13c5cd --- /dev/null +++ b/function/map_parameter_validator.go @@ -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" +) + +// MapParameterValidator is a function validator for types.Map parameters. +type MapParameterValidator interface { + + // ValidateParameterMap performs the validation. + ValidateParameterMap(context.Context, MapParameterValidatorRequest, *MapParameterValidatorResponse) +} + +// MapParameterValidatorRequest is a request for types.Map schema validation. +type MapParameterValidatorRequest 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.Map +} + +// MapParameterValidatorResponse is a response to a MapParameterValidatorRequest. +type MapParameterValidatorResponse struct { + // Error is a function error generated during validation of the Value. + Error *FuncError +} diff --git a/function/number_parameter.go b/function/number_parameter.go index a4899fe79..1114f2354 100644 --- a/function/number_parameter.go +++ b/function/number_parameter.go @@ -10,6 +10,7 @@ import ( // Ensure the implementation satisifies the desired interfaces. var _ Parameter = NumberParameter{} +var _ ParameterWithNumberValidators = NumberParameter{} // NumberParameter represents a function parameter that is a 512-bit arbitrary // precision number. @@ -65,6 +66,15 @@ type NumberParameter struct { // alphabetical character and followed by alphanumeric or underscore // characters. Name string + + // Validators is a list of validators that can be used to validate the + // parameter. + Validators []NumberParameterValidator +} + +// GetValidators returns the list of validators for the parameter. +func (p NumberParameter) GetValidators() []NumberParameterValidator { + return p.Validators } // GetAllowNullValue returns if the parameter accepts a null value. diff --git a/function/number_parameter_test.go b/function/number_parameter_test.go index dcc097c1e..d9b11bbb3 100644 --- a/function/number_parameter_test.go +++ b/function/number_parameter_test.go @@ -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" ) @@ -240,3 +242,45 @@ func TestNumberParameterGetType(t *testing.T) { }) } } + +func TestNumberParameterNumberValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + parameter function.NumberParameter + expected []function.NumberParameterValidator + }{ + "unset": { + parameter: function.NumberParameter{}, + expected: nil, + }, + "Validators - empty": { + parameter: function.NumberParameter{ + Validators: []function.NumberParameterValidator{}}, + expected: []function.NumberParameterValidator{}, + }, + "Validators": { + parameter: function.NumberParameter{ + Validators: []function.NumberParameterValidator{ + testvalidator.Number{}, + }}, + expected: []function.NumberParameterValidator{ + testvalidator.Number{}, + }, + }, + } + + 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) + } + }) + } +} diff --git a/function/number_parameter_validator.go b/function/number_parameter_validator.go new file mode 100644 index 000000000..d6ea27e31 --- /dev/null +++ b/function/number_parameter_validator.go @@ -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" +) + +// NumberParameterValidator is a function validator for types.Number parameters. +type NumberParameterValidator interface { + + // ValidateParameterNumber performs the validation. + ValidateParameterNumber(context.Context, NumberParameterValidatorRequest, *NumberParameterValidatorResponse) +} + +// NumberParameterValidatorRequest is a request for types.Number schema validation. +type NumberParameterValidatorRequest 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.Number +} + +// NumberParameterValidatorResponse is a response to a NumberParameterValidatorRequest. +type NumberParameterValidatorResponse struct { + // Error is a function error generated during validation of the Value. + Error *FuncError +} diff --git a/function/object_parameter.go b/function/object_parameter.go index e558a7549..13120c144 100644 --- a/function/object_parameter.go +++ b/function/object_parameter.go @@ -17,6 +17,7 @@ import ( var ( _ Parameter = ObjectParameter{} _ fwfunction.ParameterWithValidateImplementation = ObjectParameter{} + _ ParameterWithObjectValidators = ObjectParameter{} ) // ObjectParameter represents a function parameter that is a mapping of @@ -84,6 +85,15 @@ type ObjectParameter struct { // alphabetical character and followed by alphanumeric or underscore // characters. Name string + + // Validators is a list of object validators that should be applied to the + // parameter. + Validators []ObjectParameterValidator +} + +// GetValidators returns the list of validators for the parameter. +func (p ObjectParameter) GetValidators() []ObjectParameterValidator { + return p.Validators } // GetAllowNullValue returns if the parameter accepts a null value. diff --git a/function/object_parameter_test.go b/function/object_parameter_test.go index d2794e5e6..ef42e56b3 100644 --- a/function/object_parameter_test.go +++ b/function/object_parameter_test.go @@ -8,11 +8,13 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/internal/fwfunction" "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" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) @@ -265,6 +267,48 @@ func TestObjectParameterGetType(t *testing.T) { } } +func TestObjectParameterObjectValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + parameter function.ObjectParameter + expected []function.ObjectParameterValidator + }{ + "unset": { + parameter: function.ObjectParameter{}, + expected: nil, + }, + "Validators - empty": { + parameter: function.ObjectParameter{ + Validators: []function.ObjectParameterValidator{}}, + expected: []function.ObjectParameterValidator{}, + }, + "Validators": { + parameter: function.ObjectParameter{ + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{}, + }}, + expected: []function.ObjectParameterValidator{ + testvalidator.Object{}, + }, + }, + } + + 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) + } + }) + } +} + func TestObjectParameterValidateImplementation(t *testing.T) { t.Parallel() diff --git a/function/object_parameter_validator.go b/function/object_parameter_validator.go new file mode 100644 index 000000000..b1143da20 --- /dev/null +++ b/function/object_parameter_validator.go @@ -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" +) + +// ObjectParameterValidator is a function validator for types.Object parameters. +type ObjectParameterValidator interface { + + // ValidateParameterObject ValidateParameterSet performs the validation. + ValidateParameterObject(context.Context, ObjectParameterValidatorRequest, *ObjectParameterValidatorResponse) +} + +// ObjectParameterValidatorRequest is a request for types.Object schema validation. +type ObjectParameterValidatorRequest 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.Object +} + +// ObjectParameterValidatorResponse is a response to a ObjectParameterValidatorRequest. +type ObjectParameterValidatorResponse struct { + // Error is a function error generated during validation of the Value. + Error *FuncError +} diff --git a/function/parameter_validation.go b/function/parameter_validation.go new file mode 100644 index 000000000..df5957600 --- /dev/null +++ b/function/parameter_validation.go @@ -0,0 +1,94 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package function + +// ParameterWithBoolValidators is an optional interface on Parameter which +// enables Bool validation support. +type ParameterWithBoolValidators interface { + Parameter + + // GetValidators should return a list of Bool validators. + GetValidators() []BoolParameterValidator +} + +// ParameterWithInt64Validators is an optional interface on Parameter which +// enables Int64 validation support. +type ParameterWithInt64Validators interface { + Parameter + + // GetValidators should return a list of Int64 validators. + GetValidators() []Int64ParameterValidator +} + +// ParameterWithFloat64Validators is an optional interface on Parameter which +// enables Float64 validation support. +type ParameterWithFloat64Validators interface { + Parameter + + // GetValidators should return a list of Float64 validators. + GetValidators() []Float64ParameterValidator +} + +// ParameterWithDynamicValidators is an optional interface on Parameter which +// enables Dynamic validation support. +type ParameterWithDynamicValidators interface { + Parameter + + // GetValidators should return a list of Dynamic validators. + GetValidators() []DynamicParameterValidator +} + +// ParameterWithListValidators is an optional interface on Parameter which +// enables List validation support. +type ParameterWithListValidators interface { + Parameter + + // GetValidators should return a list of List validators. + GetValidators() []ListParameterValidator +} + +// ParameterWithMapValidators is an optional interface on Parameter which +// enables Map validation support. +type ParameterWithMapValidators interface { + Parameter + + // GetValidators should return a list of Map validators. + GetValidators() []MapParameterValidator +} + +// ParameterWithNumberValidators is an optional interface on Parameter which +// enables Number validation support. +type ParameterWithNumberValidators interface { + Parameter + + // GetValidators should return a list of Map validators. + GetValidators() []NumberParameterValidator +} + +// ParameterWithObjectValidators is an optional interface on Parameter which +// enables Object validation support. +type ParameterWithObjectValidators interface { + Parameter + + // GetValidators should return a list of Object validators. + GetValidators() []ObjectParameterValidator +} + +// ParameterWithSetValidators is an optional interface on Parameter which +// enables Set validation support. +type ParameterWithSetValidators interface { + Parameter + + // GetValidators should return a list of Set validators. + GetValidators() []SetParameterValidator +} + +// ParameterWithStringValidators is an optional interface on Parameter which +// enables String validation support. +type ParameterWithStringValidators interface { + Parameter + + // GetValidators should return a list of String validators. + GetValidators() []StringParameterValidator +} diff --git a/function/set_parameter.go b/function/set_parameter.go index 0774c559f..16a0c312b 100644 --- a/function/set_parameter.go +++ b/function/set_parameter.go @@ -17,6 +17,7 @@ import ( var ( _ Parameter = SetParameter{} _ fwfunction.ParameterWithValidateImplementation = SetParameter{} + _ ParameterWithSetValidators = SetParameter{} ) // SetParameter represents a function parameter that is an unordered set of a @@ -82,6 +83,15 @@ type SetParameter struct { // alphabetical character and followed by alphanumeric or underscore // characters. Name string + + // Validators is a list of set validators that should be applied to the + // parameter. + Validators []SetParameterValidator +} + +// GetValidators returns the list of validators for the parameter. +func (p SetParameter) GetValidators() []SetParameterValidator { + return p.Validators } // GetAllowNullValue returns if the parameter accepts a null value. diff --git a/function/set_parameter_test.go b/function/set_parameter_test.go index d0fc4fa5d..1b7f43499 100644 --- a/function/set_parameter_test.go +++ b/function/set_parameter_test.go @@ -8,11 +8,13 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/internal/fwfunction" "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" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) @@ -257,6 +259,48 @@ func TestSetParameterGetType(t *testing.T) { } } +func TestSetParameterSetValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + parameter function.SetParameter + expected []function.SetParameterValidator + }{ + "unset": { + parameter: function.SetParameter{}, + expected: nil, + }, + "Validators - empty": { + parameter: function.SetParameter{ + Validators: []function.SetParameterValidator{}}, + expected: []function.SetParameterValidator{}, + }, + "Validators": { + parameter: function.SetParameter{ + Validators: []function.SetParameterValidator{ + testvalidator.Set{}, + }}, + expected: []function.SetParameterValidator{ + testvalidator.Set{}, + }, + }, + } + + 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) + } + }) + } +} + func TestSetParameterValidateImplementation(t *testing.T) { t.Parallel() diff --git a/function/set_parameter_validator.go b/function/set_parameter_validator.go new file mode 100644 index 000000000..7dcd12c9a --- /dev/null +++ b/function/set_parameter_validator.go @@ -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" +) + +// SetParameterValidator is a function validator for types.Set parameters. +type SetParameterValidator interface { + + // ValidateParameterSet performs the validation. + ValidateParameterSet(context.Context, SetParameterValidatorRequest, *SetParameterValidatorResponse) +} + +// SetParameterValidatorRequest is a request for types.Set schema validation. +type SetParameterValidatorRequest 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.Set +} + +// SetParameterValidatorResponse is a response to a SetParameterValidatorRequest. +type SetParameterValidatorResponse struct { + // Error is a function error generated during validation of the Value. + Error *FuncError +} diff --git a/function/string_parameter.go b/function/string_parameter.go index 8584f94d4..6e6bfe10b 100644 --- a/function/string_parameter.go +++ b/function/string_parameter.go @@ -10,6 +10,7 @@ import ( // Ensure the implementation satisifies the desired interfaces. var _ Parameter = StringParameter{} +var _ ParameterWithStringValidators = StringParameter{} // StringParameter represents a function parameter that is a string. // @@ -66,6 +67,15 @@ type StringParameter struct { // alphabetical character and followed by alphanumeric or underscore // characters. Name string + + // Validators is a list of string validators that should be applied to the + // parameter. + Validators []StringParameterValidator +} + +// GetValidators returns the string validators for the parameter. +func (p StringParameter) GetValidators() []StringParameterValidator { + return p.Validators } // GetAllowNullValue returns if the parameter accepts a null value. diff --git a/function/string_parameter_test.go b/function/string_parameter_test.go index 1cf9bbcbf..772695ec4 100644 --- a/function/string_parameter_test.go +++ b/function/string_parameter_test.go @@ -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" ) @@ -240,3 +242,45 @@ func TestStringParameterGetType(t *testing.T) { }) } } + +func TestStringParameterStringValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + parameter function.StringParameter + expected []function.StringParameterValidator + }{ + "unset": { + parameter: function.StringParameter{}, + expected: nil, + }, + "Validators - empty": { + parameter: function.StringParameter{ + Validators: []function.StringParameterValidator{}}, + expected: []function.StringParameterValidator{}, + }, + "Validators": { + parameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{}, + }}, + expected: []function.StringParameterValidator{ + testvalidator.String{}, + }, + }, + } + + 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) + } + }) + } +} diff --git a/function/string_parameter_validator.go b/function/string_parameter_validator.go new file mode 100644 index 000000000..f7b51600b --- /dev/null +++ b/function/string_parameter_validator.go @@ -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" +) + +// StringParameterValidator is a function validator for types.String parameters. +type StringParameterValidator interface { + + // ValidateParameterString performs the validation. + ValidateParameterString(context.Context, StringParameterValidatorRequest, *StringParameterValidatorResponse) +} + +// StringParameterValidatorRequest is a request for types.String schema validation. +type StringParameterValidatorRequest 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.String +} + +// StringParameterValidatorResponse is a response to a StringParameterValidatorRequest. +type StringParameterValidatorResponse struct { + // Error is a function error generated during validation of the Value. + Error *FuncError +} diff --git a/internal/fromproto5/arguments_data.go b/internal/fromproto5/arguments_data.go index 0ae09d5db..fefd8a191 100644 --- a/internal/fromproto5/arguments_data.go +++ b/internal/fromproto5/arguments_data.go @@ -159,12 +159,350 @@ func ArgumentsData(ctx context.Context, arguments []*tfprotov5.DynamicValue, def } } + switch parameterWithValidators := parameter.(type) { + case function.ParameterWithBoolValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + boolValuable, ok := attrValue.(basetypes.BoolValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.BoolValuable at position %d", pos), + )) + + continue + } + boolVal, diags := boolValuable.ToBoolValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.BoolParameterValidatorRequest{ + ArgumentPosition: pos, + Value: boolVal, + } + resp := &function.BoolParameterValidatorResponse{} + functionValidator.ValidateParameterBool(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithDynamicValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + dynamicValuable, ok := attrValue.(basetypes.DynamicValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.DynamicValuable at position %d", pos), + )) + + continue + } + dynamicVal, diags := dynamicValuable.ToDynamicValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.DynamicParameterValidatorRequest{ + ArgumentPosition: pos, + Value: dynamicVal, + } + resp := &function.DynamicParameterValidatorResponse{} + functionValidator.ValidateParameterDynamic(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithFloat64Validators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + float64Valuable, ok := attrValue.(basetypes.Float64Valuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.Float64Valuable at position %d", pos), + )) + + continue + } + float64Val, diags := float64Valuable.ToFloat64Value(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.Float64ParameterValidatorRequest{ + ArgumentPosition: pos, + Value: float64Val, + } + resp := &function.Float64ParameterValidatorResponse{} + functionValidator.ValidateParameterFloat64(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithInt64Validators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + int64Valuable, ok := attrValue.(basetypes.Int64Valuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.Int64Valuable at position %d", pos), + )) + + continue + } + int64Val, diags := int64Valuable.ToInt64Value(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.Int64ParameterValidatorRequest{ + ArgumentPosition: pos, + Value: int64Val, + } + resp := &function.Int64ParameterValidatorResponse{} + functionValidator.ValidateParameterInt64(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithListValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + listValue, ok := attrValue.(basetypes.ListValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.ListValuable at position %d", pos), + )) + + continue + } + listVal, diags := listValue.ToListValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.ListParameterValidatorRequest{ + ArgumentPosition: pos, + Value: listVal, + } + resp := &function.ListParameterValidatorResponse{} + functionValidator.ValidateParameterList(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithMapValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + mapValuable, ok := attrValue.(basetypes.MapValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.MapValuable at position %d", pos), + )) + + continue + } + mapVal, diags := mapValuable.ToMapValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.MapParameterValidatorRequest{ + ArgumentPosition: pos, + Value: mapVal, + } + resp := &function.MapParameterValidatorResponse{} + functionValidator.ValidateParameterMap(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithNumberValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + numberValuable, ok := attrValue.(basetypes.NumberValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.NumberValuable at position %d", pos), + )) + + continue + } + numberVal, diags := numberValuable.ToNumberValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.NumberParameterValidatorRequest{ + ArgumentPosition: pos, + Value: numberVal, + } + resp := &function.NumberParameterValidatorResponse{} + functionValidator.ValidateParameterNumber(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithObjectValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + objectValuable, ok := attrValue.(basetypes.ObjectValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.ObjectValuable at position %d", pos), + )) + + continue + } + objectVal, diags := objectValuable.ToObjectValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.ObjectParameterValidatorRequest{ + ArgumentPosition: pos, + Value: objectVal, + } + resp := &function.ObjectParameterValidatorResponse{} + functionValidator.ValidateParameterObject(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithSetValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + setValuable, ok := attrValue.(basetypes.SetValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.SetValuable at position %d", pos), + )) + + continue + } + setVal, diags := setValuable.ToSetValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.SetParameterValidatorRequest{ + ArgumentPosition: pos, + Value: setVal, + } + resp := &function.SetParameterValidatorResponse{} + functionValidator.ValidateParameterSet(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithStringValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + stringValuable, ok := attrValue.(basetypes.StringValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.StringValuable at position %d", pos), + )) + + continue + } + stringVal, diags := stringValuable.ToStringValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.StringParameterValidatorRequest{ + ArgumentPosition: pos, + Value: stringVal, + } + resp := &function.StringParameterValidatorResponse{} + functionValidator.ValidateParameterString(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + } + if definition.VariadicParameter != nil && position >= len(definition.Parameters) { variadicValues = append(variadicValues, attrValue) continue } + // Skip appending argument values if parameter validation raises an error. + if funcError != nil { + continue + } + argumentValues = append(argumentValues, attrValue) } diff --git a/internal/fromproto5/arguments_data_test.go b/internal/fromproto5/arguments_data_test.go index 86b9349a4..a7948fafb 100644 --- a/internal/fromproto5/arguments_data_test.go +++ b/internal/fromproto5/arguments_data_test.go @@ -6,6 +6,7 @@ package fromproto5_test import ( "bytes" "context" + "math/big" "testing" "github.com/google/go-cmp/cmp" @@ -17,6 +18,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" "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" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) @@ -717,3 +720,2091 @@ func TestArgumentsData(t *testing.T) { }) } } + +func TestArgumentsData_ParameterValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input []*tfprotov5.DynamicValue + definition function.Definition + expected function.ArgumentsData + expectedFuncError *function.FuncError + }{ + "bool-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewBoolValue(true), + }), + }, + "bool-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "bool-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "bool-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + CustomType: testtypes.BoolType{}, + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + testtypes.Bool{ + Bool: basetypes.NewBoolValue(true), + }, + }), + }, + "bool-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + CustomType: testtypes.BoolType{}, + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "dynamic-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(true)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewDynamicValue(types.BoolValue(true)), + }), + }, + "dynamic-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "dynamic-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "dynamic-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + CustomType: testtypes.DynamicType{}, + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(true)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewDynamicValue(types.BoolValue(true)), + }), + }, + "dynamic-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + CustomType: testtypes.DynamicType{}, + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "float64-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(1.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewFloat64Value(1.0), + }), + }, + "float64-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(2.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "float64-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(2.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(3.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "float64-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + CustomType: testtypes.Float64Type{}, + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(1.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewFloat64Value(1.0), + }), + }, + "float64-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + CustomType: testtypes.Float64Type{}, + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(2.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "int64-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(1) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewInt64Value(1), + }), + }, + "int64-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(2) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "int64-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(2) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(3) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "int64-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + CustomType: testtypes.Int64Type{}, + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(1) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewInt64Value(1), + }), + }, + "int64-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + CustomType: testtypes.Int64Type{}, + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(2) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "list-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewListValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "list-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "list-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "list-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + CustomType: testtypes.ListType{ + ListType: types.ListType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewListValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "list-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + CustomType: testtypes.ListType{ + ListType: types.ListType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "map-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewMapValueMust(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}), + }), + }, + "map-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "map-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key1": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "map-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + CustomType: testtypes.MapType{ + MapType: types.MapType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewMapValueMust(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}), + }), + }, + "map-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + CustomType: testtypes.MapType{ + MapType: types.MapType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "number-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(1)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewNumberValue(big.NewFloat(1)), + }), + }, + "number-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(2)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "number-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(2)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(3)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "number-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + CustomType: testtypes.NumberType{}, + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(1)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + testtypes.Number{ + Number: basetypes.NewNumberValue(big.NewFloat(1)), + }, + }), + }, + "number-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + CustomType: testtypes.NumberType{}, + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(2)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "object-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewObjectValueMust(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}), + }), + }, + "object-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "object-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute1": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute1": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "object-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + CustomType: testtypes.ObjectType{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{"boolAttribute": types.BoolType}, + }, + }, + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewObjectValueMust(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}), + }), + }, + "object-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + CustomType: testtypes.ObjectType{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{"boolAttribute": types.BoolType}, + }, + }, + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "set-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewSetValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "set-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "set-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "set-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + CustomType: testtypes.SetType{ + SetType: types.SetType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewSetValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "set-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + CustomType: testtypes.SetType{ + SetType: types.SetType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "string-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewStringValue("true"), + }), + }, + "string-parameter-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "string-parameter-Validators-multiple-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, + "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "string-parameter-custom-type-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + CustomType: testtypes.StringType{}, + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + testtypes.String{ + InternalString: basetypes.NewStringValue("true"), + }, + }), + }, + "string-parameter-custom-type-Validators-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + CustomType: testtypes.StringType{}, + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "multiple-parameter-Validators": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewBoolValue(true), + basetypes.NewStringValue("true"), + }), + }, + "multiple-parameter-Validators-errors": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: bool validator error.", + ) + } + }, + }, + }, + }, + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(0, "Error Diagnostic: bool validator error."), + function.NewArgumentFuncError(1, "Error Diagnostic: string validator error."), + ), + }, + "variadicparameter-one": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewTupleValueMust( + []attr.Type{ + basetypes.StringType{}, + }, + []attr.Value{ + basetypes.NewStringValue("false"), + }, + ), + }), + }, + "variadicparameter-one-error": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(0, "Error Diagnostic: string validator error."), + ), + }, + "variadicparameter-multiple": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewTupleValueMust( + []attr.Type{ + basetypes.StringType{}, + basetypes.StringType{}, + }, + []attr.Value{ + basetypes.NewStringValue("false"), + basetypes.NewStringValue("false"), + }, + ), + }), + }, + "variadicparameter-multiple-error-single": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(1, "Error Diagnostic: string validator error."), + ), + }, + "variadicparameter-multiple-errors-multiple": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(0, "Error Diagnostic: string validator error."), + function.NewArgumentFuncError(0, "Error Diagnostic: string validator error."), + ), + }, + "boolparameter-and-variadicparameter-multiple-error-single": { + input: []*tfprotov5.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewBoolValue(true), + }), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(2, "Error Diagnostic: string validator error."), + ), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := fromproto5.ArgumentsData(context.Background(), testCase.input, testCase.definition) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedFuncError); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} + +func createDynamicValue(value tftypes.Value) *tfprotov5.DynamicValue { + dynamicVal, _ := tfprotov5.NewDynamicValue(tftypes.DynamicPseudoType, value) + return &dynamicVal +} diff --git a/internal/fromproto6/arguments_data.go b/internal/fromproto6/arguments_data.go index 18d1949f2..1fcf39229 100644 --- a/internal/fromproto6/arguments_data.go +++ b/internal/fromproto6/arguments_data.go @@ -159,12 +159,350 @@ func ArgumentsData(ctx context.Context, arguments []*tfprotov6.DynamicValue, def } } + switch parameterWithValidators := parameter.(type) { + case function.ParameterWithBoolValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + boolValuable, ok := attrValue.(basetypes.BoolValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.BoolValuable at position %d", pos), + )) + + continue + } + boolVal, diags := boolValuable.ToBoolValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.BoolParameterValidatorRequest{ + ArgumentPosition: pos, + Value: boolVal, + } + resp := &function.BoolParameterValidatorResponse{} + functionValidator.ValidateParameterBool(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithDynamicValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + dynamicValuable, ok := attrValue.(basetypes.DynamicValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.DynamicValuable at position %d", pos), + )) + + continue + } + dynamicVal, diags := dynamicValuable.ToDynamicValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.DynamicParameterValidatorRequest{ + ArgumentPosition: pos, + Value: dynamicVal, + } + resp := &function.DynamicParameterValidatorResponse{} + functionValidator.ValidateParameterDynamic(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithFloat64Validators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + float64Valuable, ok := attrValue.(basetypes.Float64Valuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.Float64Valuable at position %d", pos), + )) + + continue + } + float64Val, diags := float64Valuable.ToFloat64Value(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.Float64ParameterValidatorRequest{ + ArgumentPosition: pos, + Value: float64Val, + } + resp := &function.Float64ParameterValidatorResponse{} + functionValidator.ValidateParameterFloat64(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithInt64Validators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + int64Valuable, ok := attrValue.(basetypes.Int64Valuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.Int64Valuable at position %d", pos), + )) + + continue + } + int64Val, diags := int64Valuable.ToInt64Value(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.Int64ParameterValidatorRequest{ + ArgumentPosition: pos, + Value: int64Val, + } + resp := &function.Int64ParameterValidatorResponse{} + functionValidator.ValidateParameterInt64(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithListValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + listValue, ok := attrValue.(basetypes.ListValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.ListValuable at position %d", pos), + )) + + continue + } + listVal, diags := listValue.ToListValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.ListParameterValidatorRequest{ + ArgumentPosition: pos, + Value: listVal, + } + resp := &function.ListParameterValidatorResponse{} + functionValidator.ValidateParameterList(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithMapValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + mapValuable, ok := attrValue.(basetypes.MapValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.MapValuable at position %d", pos), + )) + + continue + } + mapVal, diags := mapValuable.ToMapValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.MapParameterValidatorRequest{ + ArgumentPosition: pos, + Value: mapVal, + } + resp := &function.MapParameterValidatorResponse{} + functionValidator.ValidateParameterMap(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithNumberValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + numberValuable, ok := attrValue.(basetypes.NumberValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.NumberValuable at position %d", pos), + )) + + continue + } + numberVal, diags := numberValuable.ToNumberValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.NumberParameterValidatorRequest{ + ArgumentPosition: pos, + Value: numberVal, + } + resp := &function.NumberParameterValidatorResponse{} + functionValidator.ValidateParameterNumber(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithObjectValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + objectValuable, ok := attrValue.(basetypes.ObjectValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.ObjectValuable at position %d", pos), + )) + + continue + } + objectVal, diags := objectValuable.ToObjectValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.ObjectParameterValidatorRequest{ + ArgumentPosition: pos, + Value: objectVal, + } + resp := &function.ObjectParameterValidatorResponse{} + functionValidator.ValidateParameterObject(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithSetValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + setValuable, ok := attrValue.(basetypes.SetValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.SetValuable at position %d", pos), + )) + + continue + } + setVal, diags := setValuable.ToSetValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.SetParameterValidatorRequest{ + ArgumentPosition: pos, + Value: setVal, + } + resp := &function.SetParameterValidatorResponse{} + functionValidator.ValidateParameterSet(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + case function.ParameterWithStringValidators: + for _, functionValidator := range parameterWithValidators.GetValidators() { + stringValuable, ok := attrValue.(basetypes.StringValuable) + if !ok { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + "Invalid Argument Type: "+ + "An unexpected error was encountered when converting the function argument from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + fmt.Sprintf("Expected basetypes.StringValuable at position %d", pos), + )) + + continue + } + stringVal, diags := stringValuable.ToStringValue(ctx) + if diags.HasError() { + funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, diags)) + continue + } + req := function.StringParameterValidatorRequest{ + ArgumentPosition: pos, + Value: stringVal, + } + resp := &function.StringParameterValidatorResponse{} + functionValidator.ValidateParameterString(ctx, req, resp) + if resp.Error != nil { + funcError = function.ConcatFuncErrors(funcError, function.NewArgumentFuncError( + pos, + resp.Error.Error(), + )) + } + } + } + if definition.VariadicParameter != nil && position >= len(definition.Parameters) { variadicValues = append(variadicValues, attrValue) continue } + // Skip appending argument values if parameter validation raises an error. + if funcError != nil { + continue + } + argumentValues = append(argumentValues, attrValue) } @@ -188,6 +526,7 @@ func ArgumentsData(ctx context.Context, arguments []*tfprotov6.DynamicValue, def tupleTypes[i] = variadicType tupleValues[i] = val } + variadicValue, variadicValueDiags := basetypes.NewTupleValue(tupleTypes, tupleValues) funcError = function.ConcatFuncErrors(funcError, function.FuncErrorFromDiags(ctx, variadicValueDiags)) diff --git a/internal/fromproto6/arguments_data_test.go b/internal/fromproto6/arguments_data_test.go index aa49743e8..8aae7abd5 100644 --- a/internal/fromproto6/arguments_data_test.go +++ b/internal/fromproto6/arguments_data_test.go @@ -6,6 +6,7 @@ package fromproto6_test import ( "bytes" "context" + "math/big" "testing" "github.com/google/go-cmp/cmp" @@ -17,6 +18,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" "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" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) @@ -717,3 +721,2091 @@ func TestArgumentsData(t *testing.T) { }) } } + +func TestArgumentsData_ParameterValidators(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input []*tfprotov6.DynamicValue + definition function.Definition + expected function.ArgumentsData + expectedFuncError *function.FuncError + }{ + "bool-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewBoolValue(true), + }), + }, + "bool-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "bool-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "bool-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + CustomType: testtypes.BoolType{}, + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + testtypes.Bool{ + Bool: basetypes.NewBoolValue(true), + }, + }), + }, + "bool-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + CustomType: testtypes.BoolType{}, + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "dynamic-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(true)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewDynamicValue(types.BoolValue(true)), + }), + }, + "dynamic-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "dynamic-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "dynamic-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + CustomType: testtypes.DynamicType{}, + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(true)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewDynamicValue(types.BoolValue(true)), + }), + }, + "dynamic-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + createDynamicValue(tftypes.NewValue(tftypes.Bool, true)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.DynamicParameter{ + CustomType: testtypes.DynamicType{}, + Validators: []function.DynamicParameterValidator{ + testvalidator.Dynamic{ + ValidateMethod: func(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + got := req.Value + expected := types.DynamicValue(types.BoolValue(false)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "float64-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(1.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewFloat64Value(1.0), + }), + }, + "float64-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(2.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "float64-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(2.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(3.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "float64-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + CustomType: testtypes.Float64Type{}, + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(1.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewFloat64Value(1.0), + }), + }, + "float64-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1.0)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Float64Parameter{ + CustomType: testtypes.Float64Type{}, + Validators: []function.Float64ParameterValidator{ + testvalidator.Float64{ + ValidateMethod: func(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + got := req.Value + expected := types.Float64Value(2.0) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "int64-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(1) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewInt64Value(1), + }), + }, + "int64-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(2) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "int64-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(2) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(3) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "int64-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + CustomType: testtypes.Int64Type{}, + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(1) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewInt64Value(1), + }), + }, + "int64-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.Int64Parameter{ + CustomType: testtypes.Int64Type{}, + Validators: []function.Int64ParameterValidator{ + testvalidator.Int64{ + ValidateMethod: func(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + got := req.Value + expected := types.Int64Value(2) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "list-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewListValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "list-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "list-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "list-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + CustomType: testtypes.ListType{ + ListType: types.ListType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewListValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "list-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ListParameter{ + CustomType: testtypes.ListType{ + ListType: types.ListType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.ListParameterValidator{ + testvalidator.List{ + ValidateMethod: func(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + got := req.Value + expected, _ := types.ListValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "map-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewMapValueMust(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}), + }), + }, + "map-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "map-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key1": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "map-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + CustomType: testtypes.MapType{ + MapType: types.MapType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewMapValueMust(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true)}), + }), + }, + "map-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Map{ElementType: tftypes.Bool}, + map[string]tftypes.Value{"key": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.MapParameter{ + CustomType: testtypes.MapType{ + MapType: types.MapType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.MapParameterValidator{ + testvalidator.Map{ + ValidateMethod: func(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + got := req.Value + expected, _ := types.MapValue(types.BoolType, map[string]attr.Value{"key": types.BoolValue(true), + "key2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "number-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(1)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewNumberValue(big.NewFloat(1)), + }), + }, + "number-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(2)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "number-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(2)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(3)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "number-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + CustomType: testtypes.NumberType{}, + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(1)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + testtypes.Number{ + Number: basetypes.NewNumberValue(big.NewFloat(1)), + }, + }), + }, + "number-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Number, 1)), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.NumberParameter{ + CustomType: testtypes.NumberType{}, + Validators: []function.NumberParameterValidator{ + testvalidator.Number{ + ValidateMethod: func(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + got := req.Value + expected := types.NumberValue(big.NewFloat(2)) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "object-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewObjectValueMust(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}), + }), + }, + "object-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "object-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute1": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute1": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "object-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + CustomType: testtypes.ObjectType{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{"boolAttribute": types.BoolType}, + }, + }, + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewObjectValueMust(map[string]attr.Type{"boolAttribute": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true)}), + }), + }, + "object-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Object{AttributeTypes: map[string]tftypes.Type{"boolAttribute": tftypes.Bool}}, + map[string]tftypes.Value{"boolAttribute": tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.ObjectParameter{ + CustomType: testtypes.ObjectType{ + ObjectType: types.ObjectType{ + AttrTypes: map[string]attr.Type{"boolAttribute": types.BoolType}, + }, + }, + AttributeTypes: map[string]attr.Type{ + "boolAttribute": types.BoolType, + }, + Validators: []function.ObjectParameterValidator{ + testvalidator.Object{ + ValidateMethod: func(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + got := req.Value + expected, _ := types.ObjectValue(map[string]attr.Type{"boolAttribute": types.BoolType, + "boolAttribute2": types.BoolType}, + map[string]attr.Value{"boolAttribute": types.BoolValue(true), + "boolAttribute2": types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "set-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewSetValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "set-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "set-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "set-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + CustomType: testtypes.SetType{ + SetType: types.SetType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewSetValueMust(types.BoolType, []attr.Value{types.BoolValue(true)}), + }), + }, + "set-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Set{ElementType: tftypes.Bool}, []tftypes.Value{tftypes.NewValue(tftypes.Bool, true)})), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.SetParameter{ + CustomType: testtypes.SetType{ + SetType: types.SetType{ + ElemType: types.BoolType, + }, + }, + ElementType: types.BoolType, + Validators: []function.SetParameterValidator{ + testvalidator.Set{ + ValidateMethod: func(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + got := req.Value + expected, _ := types.SetValue(types.BoolType, []attr.Value{types.BoolValue(true), + types.BoolValue(false)}) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "string-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewStringValue("true"), + }), + }, + "string-parameter-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "string-parameter-Validators-multiple-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 1.", + ) + } + }, + }, + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: error 2.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, + "Error Diagnostic: error 1."+ + "\nError Diagnostic: error 2.", + ), + }, + "string-parameter-custom-type-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + CustomType: testtypes.StringType{}, + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + testtypes.String{ + InternalString: basetypes.NewStringValue("true"), + }, + }), + }, + "string-parameter-custom-type-Validators-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.StringParameter{ + CustomType: testtypes.StringType{}, + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.NewArgumentFuncError( + 0, "Error Diagnostic: This is an error.", + ), + }, + "multiple-parameter-Validators": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewBoolValue(true), + basetypes.NewStringValue("true"), + }), + }, + "multiple-parameter-Validators-errors": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(false) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: bool validator error.", + ) + } + }, + }, + }, + }, + function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(0, "Error Diagnostic: bool validator error."), + function.NewArgumentFuncError(1, "Error Diagnostic: string validator error."), + ), + }, + "variadicparameter-one": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewTupleValueMust( + []attr.Type{ + basetypes.StringType{}, + }, + []attr.Value{ + basetypes.NewStringValue("false"), + }, + ), + }), + }, + "variadicparameter-one-error": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(0, "Error Diagnostic: string validator error."), + ), + }, + "variadicparameter-multiple": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("false") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewTupleValueMust( + []attr.Type{ + basetypes.StringType{}, + basetypes.StringType{}, + }, + []attr.Value{ + basetypes.NewStringValue("false"), + basetypes.NewStringValue("false"), + }, + ), + }), + }, + "variadicparameter-multiple-error-single": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(1, "Error Diagnostic: string validator error."), + ), + }, + "variadicparameter-multiple-errors-multiple": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData(nil), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(0, "Error Diagnostic: string validator error."), + function.NewArgumentFuncError(0, "Error Diagnostic: string validator error."), + ), + }, + "boolparameter-and-variadicparameter-multiple-error-single": { + input: []*tfprotov6.DynamicValue{ + DynamicValueMust(tftypes.NewValue(tftypes.Bool, true)), + DynamicValueMust(tftypes.NewValue(tftypes.String, "true")), + DynamicValueMust(tftypes.NewValue(tftypes.String, "false")), + }, + definition: function.Definition{ + Parameters: []function.Parameter{ + function.BoolParameter{ + Validators: []function.BoolParameterValidator{ + testvalidator.Bool{ + ValidateMethod: func(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + got := req.Value + expected := types.BoolValue(true) + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: This is an error.", + ) + } + }, + }, + }, + }, + }, + VariadicParameter: function.StringParameter{ + Validators: []function.StringParameterValidator{ + testvalidator.String{ + ValidateMethod: func(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + got := req.Value + expected := types.StringValue("true") + + if !got.Equal(expected) { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + "Error Diagnostic: string validator error.", + ) + } + }, + }, + }, + }, + }, + expected: function.NewArgumentsData([]attr.Value{ + basetypes.NewBoolValue(true), + }), + expectedFuncError: function.ConcatFuncErrors( + function.NewArgumentFuncError(2, "Error Diagnostic: string validator error."), + ), + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := fromproto6.ArgumentsData(context.Background(), testCase.input, testCase.definition) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedFuncError); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} + +func createDynamicValue(value tftypes.Value) *tfprotov6.DynamicValue { + dynamicVal, _ := tfprotov6.NewDynamicValue(tftypes.DynamicPseudoType, value) + return &dynamicVal +} diff --git a/internal/testing/testtypes/float64.go b/internal/testing/testtypes/float64.go new file mode 100644 index 000000000..9de3502f3 --- /dev/null +++ b/internal/testing/testtypes/float64.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testtypes + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ basetypes.Float64Typable = Float64Type{} + _ basetypes.Float64Valuable = Float64Value{} +) + +type Float64Type struct { + basetypes.Float64Type +} + +func (t Float64Type) Equal(o attr.Type) bool { + other, ok := o.(Float64Type) + + if !ok { + return false + } + + return t.Float64Type.Equal(other.Float64Type) +} + +type Float64Value struct { + basetypes.Float64Value +} + +func (v Float64Value) Equal(o attr.Value) bool { + other, ok := o.(Float64Value) + + if !ok { + return false + } + + return v.Float64Value.Equal(other.Float64Value) +} diff --git a/internal/testing/testtypes/int64.go b/internal/testing/testtypes/int64.go new file mode 100644 index 000000000..bb206343b --- /dev/null +++ b/internal/testing/testtypes/int64.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testtypes + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ basetypes.Int64Typable = Int64Type{} + _ basetypes.Int64Valuable = Int64Value{} +) + +type Int64Type struct { + basetypes.Int64Type +} + +func (t Int64Type) Equal(o attr.Type) bool { + other, ok := o.(Int64Type) + + if !ok { + return false + } + + return t.Int64Type.Equal(other.Int64Type) +} + +type Int64Value struct { + basetypes.Int64Value +} + +func (v Int64Value) Equal(o attr.Value) bool { + other, ok := o.(Int64Value) + + if !ok { + return false + } + + return v.Int64Value.Equal(other.Int64Value) +} diff --git a/internal/testing/testvalidator/bool.go b/internal/testing/testvalidator/bool.go index f7acabfa7..73ef5d374 100644 --- a/internal/testing/testvalidator/bool.go +++ b/internal/testing/testvalidator/bool.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Bool = &Bool{} +var ( + _ validator.Bool = &Bool{} + _ function.BoolParameterValidator = &Bool{} +) // Declarative validator.Bool for unit testing. type Bool struct { @@ -17,6 +21,7 @@ type Bool struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateBoolMethod func(context.Context, validator.BoolRequest, *validator.BoolResponse) + ValidateMethod func(context.Context, function.BoolParameterValidatorRequest, *function.BoolParameterValidatorResponse) } // Description satisfies the validator.Bool interface. @@ -37,7 +42,7 @@ func (v Bool) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Bool interface. +// ValidateBool satisfies the validator.Bool interface. func (v Bool) ValidateBool(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) { if v.ValidateBoolMethod == nil { return @@ -45,3 +50,12 @@ func (v Bool) ValidateBool(ctx context.Context, req validator.BoolRequest, resp v.ValidateBoolMethod(ctx, req, resp) } + +// ValidateParameterBool satisfies the function.BoolParameterValidator interface. +func (v Bool) ValidateParameterBool(ctx context.Context, req function.BoolParameterValidatorRequest, resp *function.BoolParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/dynamic.go b/internal/testing/testvalidator/dynamic.go index 6be786569..3b16920e2 100644 --- a/internal/testing/testvalidator/dynamic.go +++ b/internal/testing/testvalidator/dynamic.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Dynamic = &Dynamic{} +var ( + _ validator.Dynamic = &Dynamic{} + _ function.DynamicParameterValidator = &Dynamic{} +) // Declarative validator.Dynamic for unit testing. type Dynamic struct { @@ -17,6 +21,7 @@ type Dynamic struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateDynamicMethod func(context.Context, validator.DynamicRequest, *validator.DynamicResponse) + ValidateMethod func(context.Context, function.DynamicParameterValidatorRequest, *function.DynamicParameterValidatorResponse) } // Description satisfies the validator.Dynamic interface. @@ -37,7 +42,7 @@ func (v Dynamic) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Dynamic interface. +// ValidateDynamic satisfies the validator.Dynamic interface. func (v Dynamic) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { if v.ValidateDynamicMethod == nil { return @@ -45,3 +50,12 @@ func (v Dynamic) ValidateDynamic(ctx context.Context, req validator.DynamicReque v.ValidateDynamicMethod(ctx, req, resp) } + +// ValidateParameterDynamic satisfies the function.DynamicParameterValidator interface. +func (v Dynamic) ValidateParameterDynamic(ctx context.Context, req function.DynamicParameterValidatorRequest, resp *function.DynamicParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/float64.go b/internal/testing/testvalidator/float64.go index 8ecdc2351..a92b8e068 100644 --- a/internal/testing/testvalidator/float64.go +++ b/internal/testing/testvalidator/float64.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Float64 = &Float64{} +var ( + _ validator.Float64 = &Float64{} + _ function.Float64ParameterValidator = &Float64{} +) // Declarative validator.Float64 for unit testing. type Float64 struct { @@ -17,6 +21,7 @@ type Float64 struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateFloat64Method func(context.Context, validator.Float64Request, *validator.Float64Response) + ValidateMethod func(context.Context, function.Float64ParameterValidatorRequest, *function.Float64ParameterValidatorResponse) } // Description satisfies the validator.Float64 interface. @@ -37,7 +42,7 @@ func (v Float64) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Float64 interface. +// ValidateFloat64 satisfies the validator.Float64 interface. func (v Float64) ValidateFloat64(ctx context.Context, req validator.Float64Request, resp *validator.Float64Response) { if v.ValidateFloat64Method == nil { return @@ -45,3 +50,12 @@ func (v Float64) ValidateFloat64(ctx context.Context, req validator.Float64Reque v.ValidateFloat64Method(ctx, req, resp) } + +// ValidateParameterFloat64 satisfies the function.Float64ParameterValidator interface. +func (v Float64) ValidateParameterFloat64(ctx context.Context, req function.Float64ParameterValidatorRequest, resp *function.Float64ParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/int64.go b/internal/testing/testvalidator/int64.go index 94cbc5a68..56c495211 100644 --- a/internal/testing/testvalidator/int64.go +++ b/internal/testing/testvalidator/int64.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Int64 = &Int64{} +var ( + _ validator.Int64 = &Int64{} + _ function.Int64ParameterValidator = &Int64{} +) // Declarative validator.Int64 for unit testing. type Int64 struct { @@ -17,6 +21,7 @@ type Int64 struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateInt64Method func(context.Context, validator.Int64Request, *validator.Int64Response) + ValidateMethod func(context.Context, function.Int64ParameterValidatorRequest, *function.Int64ParameterValidatorResponse) } // Description satisfies the validator.Int64 interface. @@ -37,7 +42,7 @@ func (v Int64) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Int64 interface. +// ValidateInt64 satisfies the validator.Int64 interface. func (v Int64) ValidateInt64(ctx context.Context, req validator.Int64Request, resp *validator.Int64Response) { if v.ValidateInt64Method == nil { return @@ -45,3 +50,12 @@ func (v Int64) ValidateInt64(ctx context.Context, req validator.Int64Request, re v.ValidateInt64Method(ctx, req, resp) } + +// ValidateParameterInt64 satisfies the function.Int64ParameterValidator interface. +func (v Int64) ValidateParameterInt64(ctx context.Context, req function.Int64ParameterValidatorRequest, resp *function.Int64ParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/list.go b/internal/testing/testvalidator/list.go index d10901ba2..fe881a83a 100644 --- a/internal/testing/testvalidator/list.go +++ b/internal/testing/testvalidator/list.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.List = &List{} +var ( + _ validator.List = &List{} + _ function.ListParameterValidator = &List{} +) // Declarative validator.List for unit testing. type List struct { @@ -17,6 +21,7 @@ type List struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateListMethod func(context.Context, validator.ListRequest, *validator.ListResponse) + ValidateMethod func(context.Context, function.ListParameterValidatorRequest, *function.ListParameterValidatorResponse) } // Description satisfies the validator.List interface. @@ -37,7 +42,7 @@ func (v List) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.List interface. +// ValidateList satisfies the validator.List interface. func (v List) ValidateList(ctx context.Context, req validator.ListRequest, resp *validator.ListResponse) { if v.ValidateListMethod == nil { return @@ -45,3 +50,12 @@ func (v List) ValidateList(ctx context.Context, req validator.ListRequest, resp v.ValidateListMethod(ctx, req, resp) } + +// ValidateParameterList satisfies the function.ListParameterValidator interface. +func (v List) ValidateParameterList(ctx context.Context, req function.ListParameterValidatorRequest, resp *function.ListParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/map.go b/internal/testing/testvalidator/map.go index 0eefe7325..29f6a83bd 100644 --- a/internal/testing/testvalidator/map.go +++ b/internal/testing/testvalidator/map.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Map = &Map{} +var ( + _ validator.Map = &Map{} + _ function.MapParameterValidator = &Map{} +) // Declarative validator.Map for unit testing. type Map struct { @@ -17,6 +21,7 @@ type Map struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateMapMethod func(context.Context, validator.MapRequest, *validator.MapResponse) + ValidateMethod func(context.Context, function.MapParameterValidatorRequest, *function.MapParameterValidatorResponse) } // Description satisfies the validator.Map interface. @@ -37,7 +42,7 @@ func (v Map) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Map interface. +// ValidateMap satisfies the validator.Map interface. func (v Map) ValidateMap(ctx context.Context, req validator.MapRequest, resp *validator.MapResponse) { if v.ValidateMapMethod == nil { return @@ -45,3 +50,12 @@ func (v Map) ValidateMap(ctx context.Context, req validator.MapRequest, resp *va v.ValidateMapMethod(ctx, req, resp) } + +// ValidateParameterMap satisfies the function.MapParameterValidator interface. +func (v Map) ValidateParameterMap(ctx context.Context, req function.MapParameterValidatorRequest, resp *function.MapParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/number.go b/internal/testing/testvalidator/number.go index 36bf8fa40..ca510560c 100644 --- a/internal/testing/testvalidator/number.go +++ b/internal/testing/testvalidator/number.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Number = &Number{} +var ( + _ validator.Number = &Number{} + _ function.NumberParameterValidator = &Number{} +) // Declarative validator.Number for unit testing. type Number struct { @@ -17,6 +21,7 @@ type Number struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateNumberMethod func(context.Context, validator.NumberRequest, *validator.NumberResponse) + ValidateMethod func(context.Context, function.NumberParameterValidatorRequest, *function.NumberParameterValidatorResponse) } // Description satisfies the validator.Number interface. @@ -37,7 +42,7 @@ func (v Number) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Number interface. +// ValidateNumber satisfies the validator.Number interface. func (v Number) ValidateNumber(ctx context.Context, req validator.NumberRequest, resp *validator.NumberResponse) { if v.ValidateNumberMethod == nil { return @@ -45,3 +50,12 @@ func (v Number) ValidateNumber(ctx context.Context, req validator.NumberRequest, v.ValidateNumberMethod(ctx, req, resp) } + +// ValidateParameterNumber satisfies the function.NumberParameterValidator interface. +func (v Number) ValidateParameterNumber(ctx context.Context, req function.NumberParameterValidatorRequest, resp *function.NumberParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/object.go b/internal/testing/testvalidator/object.go index 0625731ed..0875446d4 100644 --- a/internal/testing/testvalidator/object.go +++ b/internal/testing/testvalidator/object.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Object = &Object{} +var ( + _ validator.Object = &Object{} + _ function.ObjectParameterValidator = &Object{} +) // Declarative validator.Object for unit testing. type Object struct { @@ -17,6 +21,7 @@ type Object struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateObjectMethod func(context.Context, validator.ObjectRequest, *validator.ObjectResponse) + ValidateMethod func(context.Context, function.ObjectParameterValidatorRequest, *function.ObjectParameterValidatorResponse) } // Description satisfies the validator.Object interface. @@ -37,7 +42,7 @@ func (v Object) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Object interface. +// ValidateObject satisfies the validator.Object interface. func (v Object) ValidateObject(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { if v.ValidateObjectMethod == nil { return @@ -45,3 +50,12 @@ func (v Object) ValidateObject(ctx context.Context, req validator.ObjectRequest, v.ValidateObjectMethod(ctx, req, resp) } + +// ValidateParameterObject satisfies the function.ObjectParameterValidator interface. +func (v Object) ValidateParameterObject(ctx context.Context, req function.ObjectParameterValidatorRequest, resp *function.ObjectParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/set.go b/internal/testing/testvalidator/set.go index e383b9e33..fe22bc4a3 100644 --- a/internal/testing/testvalidator/set.go +++ b/internal/testing/testvalidator/set.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.Set = &Set{} +var ( + _ validator.Set = &Set{} + _ function.SetParameterValidator = &Set{} +) // Declarative validator.Set for unit testing. type Set struct { @@ -17,6 +21,7 @@ type Set struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateSetMethod func(context.Context, validator.SetRequest, *validator.SetResponse) + ValidateMethod func(context.Context, function.SetParameterValidatorRequest, *function.SetParameterValidatorResponse) } // Description satisfies the validator.Set interface. @@ -37,7 +42,7 @@ func (v Set) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.Set interface. +// ValidateSet satisfies the validator.Set interface. func (v Set) ValidateSet(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) { if v.ValidateSetMethod == nil { return @@ -45,3 +50,12 @@ func (v Set) ValidateSet(ctx context.Context, req validator.SetRequest, resp *va v.ValidateSetMethod(ctx, req, resp) } + +// ValidateParameterSet satisfies the function.SetParameterValidator interface. +func (v Set) ValidateParameterSet(ctx context.Context, req function.SetParameterValidatorRequest, resp *function.SetParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/internal/testing/testvalidator/string.go b/internal/testing/testvalidator/string.go index 304b27536..37e7a3a20 100644 --- a/internal/testing/testvalidator/string.go +++ b/internal/testing/testvalidator/string.go @@ -6,10 +6,14 @@ package testvalidator import ( "context" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -var _ validator.String = &String{} +var ( + _ validator.String = &String{} + _ function.StringParameterValidator = &String{} +) // Declarative validator.String for unit testing. type String struct { @@ -17,6 +21,7 @@ type String struct { DescriptionMethod func(context.Context) string MarkdownDescriptionMethod func(context.Context) string ValidateStringMethod func(context.Context, validator.StringRequest, *validator.StringResponse) + ValidateMethod func(context.Context, function.StringParameterValidatorRequest, *function.StringParameterValidatorResponse) } // Description satisfies the validator.String interface. @@ -37,7 +42,7 @@ func (v String) MarkdownDescription(ctx context.Context) string { return v.MarkdownDescriptionMethod(ctx) } -// Validate satisfies the validator.String interface. +// ValidateString satisfies the validator.String interface. func (v String) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { if v.ValidateStringMethod == nil { return @@ -45,3 +50,12 @@ func (v String) ValidateString(ctx context.Context, req validator.StringRequest, v.ValidateStringMethod(ctx, req, resp) } + +// ValidateParameterString satisfies the function.StringParameterValidator interface. +func (v String) ValidateParameterString(ctx context.Context, req function.StringParameterValidatorRequest, resp *function.StringParameterValidatorResponse) { + if v.ValidateMethod == nil { + return + } + + v.ValidateMethod(ctx, req, resp) +} diff --git a/website/docs/plugin/framework/validation.mdx b/website/docs/plugin/framework/validation.mdx index ad4d929cb..b0349fd8a 100644 --- a/website/docs/plugin/framework/validation.mdx +++ b/website/docs/plugin/framework/validation.mdx @@ -5,9 +5,9 @@ description: How to validate configuration values using the provider development # Validation -The framework can return [diagnostics](/terraform/plugin/framework/diagnostics) feedback for values in provider, resource, and data source configurations. This allows you to write validations that give users feedback about required syntax, types, and acceptable values. +The framework can return [diagnostics](/terraform/plugin/framework/diagnostics) feedback for values in provider, resource, and data source configurations or [errors](/terraform/plugin/framework/functions/errors) feedback for values in function parameters. This allows you to write validations that give users feedback about required syntax, types, and acceptable values. -This page describes single attribute and type validation concepts that can be used in any data source, provider, or resource schema. Further documentation is available for other configuration validation concepts: +This page describes single attribute, parameter, and type validation concepts that can be used in any data source schema, provider schema, resource schema, or function definition. Further documentation is available for other configuration validation concepts: - [Data source validation](/terraform/plugin/framework/data-sources/validate-configuration) for multiple attributes declaratively or imperatively. - [Provider validation](/terraform/plugin/framework/providers/validate-configuration) for multiple attributes declaratively or imperatively. @@ -225,6 +225,132 @@ func Int64IsGreaterThan(expressions ...path.Expression) validator.Int64 { } ``` +## Parameter Validation + +You can introduce validation on function parameters using the generic framework-defined types such as [`types.String`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#String). To do this, supply the `Validators` field with a list of validations, and the framework will return errors from all validators. For example: + +```go +// Typically within the function.Definition for a function. +function.StringParameter{ + // ... other Parameter configuration ... + + Validators: []function.StringParameterValidator{ + stringvalidator.LengthBetween(10, 256), + }, +}, +``` + +All validators in the slice will always be run, regardless of whether previous validators returned an error or not. + +### Creating Parameter Validators + +To create a parameter validator, you must implement at least one of the [`function` package](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/function) `ParameterValidator` interfaces. For example: + +```go +// Ensure the implementation satisfies the expected interfaces +var ( + _ function.StringParameterValidator = stringLengthBetweenValidator{} +) + +type stringLengthBetweenValidator struct { + Max int + Min int +} + +// Validate runs the main validation logic of the validator, reading configuration data out of `req` and updating `resp` with diagnostics. +func (v stringLengthBetweenValidator) ValidateParameterString(ctx context.Context, req validator.StringParameterValidatorRequest, resp *validator.StringParameterValidatorResponse) { + // If the value is unknown or null, there is nothing to validate. + if req.Value.IsUnknown() || req.Value.IsNull() { + return + } + + strLen := len(req.Value.ValueString()) + + if strLen < v.Min || strLen > v.Max { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + fmt.Sprintf("Invalid String Length: String length must be between %d and %d, got: %d.", v.Min, v.Max, strLen), + ) + + return + } +} +``` + +Optionally and depending on the complexity, it may be desirable to also create a helper function to instantiate the validator. For example: + +```go +func stringLengthBetween(minLength int, maxLength int) stringLengthBetweenValidator { + return stringLengthBetweenValidator{ + Max: maxLength, + Min: minLength, + } +} +``` + +A single validator type can be used as both an attribute validator and a parameter validator, as long as the validator implements the appropriate interfaces. For example: + +```go +var ( + _ validator.String = stringLengthBetweenValidator{} + _ function.StringParameterValidator = stringLengthBetweenValidator{} + +) +type stringLengthBetweenValidator struct { + Max int + Min int +} + +// Description returns a plain text description of the attribute validator's behavior, suitable for a practitioner to understand its impact. +func (v stringLengthBetweenValidator) Description(ctx context.Context) string { + return fmt.Sprintf("string length must be between %d and %d", v.Min, v.Max) +} + +// MarkdownDescription returns a markdown formatted description of the attribute validator's behavior, suitable for a practitioner to understand its impact. +func (v stringLengthBetweenValidator) MarkdownDescription(ctx context.Context) string { + return fmt.Sprintf("string length must be between `%d` and `%d`", v.Min, v.Max) +} + +// Validate runs the main validation logic of the attribute validator, reading configuration data out of `req` and updating `resp` with diagnostics. +func (v stringLengthBetweenValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + // If the value is unknown or null, there is nothing to validate. + if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() { + return + } + + strLen := len(req.ConfigValue.ValueString()) + + if strLen < v.Min || strLen > v.Max { + resp.Diagnostics.AddAttributeError( + req.AttributePath, + "Invalid String Length", + fmt.Sprintf("String length must be between %d and %d, got: %d.", v.Min, v.Max, strLen), + ) + + return + } +} + +// Validate runs the main validation logic of the parameter validator, reading configuration data out of `req` and updating `resp` with diagnostics. +func (v stringLengthBetweenValidator) ValidateParameterString(ctx context.Context, req validator.StringParameterValidatorRequest, resp *validator.StringParameterValidatorResponse) { + // If the value is unknown or null, there is nothing to validate. + if req.Value.IsUnknown() || req.Value.IsNull() { + return + } + + strLen := len(req.Value.ValueString()) + + if strLen < v.Min || strLen > v.Max { + resp.Error = function.NewArgumentFuncError( + req.ArgumentPosition, + fmt.Sprintf("Invalid String Length: String length must be between %d and %d, got: %d.", v.Min, v.Max, strLen), + ) + + return + } +} +``` + ## Value Validation Validation of custom value types can be used for both attribute values and provider-defined function parameter values. This can be useful if you have consistent validation rules for a specific value type across multiple attributes or function parameters.