Skip to content

Commit cca5c5a

Browse files
authored
action: Add standard validation methods/interfaces for ValidateActionConfig RPCs (#1188)
* go mod * generate RPC methods * protov5 and fwserver impl * protov6 copy * add initial schema attributes and unlinked schema * implement unlinked schemas, some attributes, and the rpcs * the rest of the tests * fix double import * external interfaces for plan / configure * plan action impl and fwserver tests * proto server tests * from/to plan tests * from invoke * add invoke impl with just completed event * fix map access in unit tests * implementation of sending progress events * mention progress events * comments * all attributes (primitive, collection, nested) * add blocks and docs * update all commented out custom type tests * go mod * external interface * test provider * add validators to schemas * fwserver * proto5 and proto6 impl * update config validator comments
1 parent 63b1416 commit cca5c5a

File tree

68 files changed

+2698
-32
lines changed

Some content is hidden

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

68 files changed

+2698
-32
lines changed

action/action.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,33 @@ type ActionWithModifyPlan interface {
4949
// diagnostics to practitioners, such as validation errors.
5050
ModifyPlan(context.Context, ModifyPlanRequest, *ModifyPlanResponse)
5151
}
52+
53+
// ActionWithConfigValidators is an interface type that extends Action to include declarative validations.
54+
//
55+
// Declaring validation using this methodology simplifies implementation of
56+
// reusable functionality. These also include descriptions, which can be used
57+
// for automating documentation.
58+
//
59+
// Validation will include ConfigValidators and ValidateConfig, if both are
60+
// implemented, in addition to any Attribute or Type validation.
61+
type ActionWithConfigValidators interface {
62+
Action
63+
64+
// ConfigValidators returns a list of functions which will all be performed during validation.
65+
ConfigValidators(context.Context) []ConfigValidator
66+
}
67+
68+
// ActionWithValidateConfig is an interface type that extends Action to include imperative validation.
69+
//
70+
// Declaring validation using this methodology simplifies one-off
71+
// functionality that typically applies to a single action. Any documentation
72+
// of this functionality must be manually added into schema descriptions.
73+
//
74+
// Validation will include ConfigValidators and ValidateConfig, if both are
75+
// implemented, in addition to any Attribute or Type validation.
76+
type ActionWithValidateConfig interface {
77+
Action
78+
79+
// ValidateConfig performs the validation.
80+
ValidateConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse)
81+
}

action/config_validator.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package action
5+
6+
import "context"
7+
8+
// ConfigValidator describes reusable Action configuration validation functionality.
9+
type ConfigValidator interface {
10+
// Description describes the validation in plain text formatting.
11+
//
12+
// This information may be automatically added to action plain text
13+
// descriptions by external tooling.
14+
Description(context.Context) string
15+
16+
// MarkdownDescription describes the validation in Markdown formatting.
17+
//
18+
// This information may be automatically added to action Markdown
19+
// descriptions by external tooling.
20+
MarkdownDescription(context.Context) string
21+
22+
// ValidateAction performs the validation.
23+
//
24+
// This method name is separate from ConfigValidators in resource and other packages in
25+
// order to allow generic validators.
26+
ValidateAction(context.Context, ValidateConfigRequest, *ValidateConfigResponse)
27+
}

action/schema/bool_attribute.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import (
88

99
"github.com/hashicorp/terraform-plugin-framework/attr"
1010
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
11+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
12+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1315
)
1416

1517
// Ensure the implementation satisfies the desired interfaces.
1618
var (
17-
_ Attribute = BoolAttribute{}
19+
_ Attribute = BoolAttribute{}
20+
_ fwxschema.AttributeWithBoolValidators = BoolAttribute{}
1821
)
1922

2023
// BoolAttribute represents a schema attribute that is a boolean. When
@@ -89,6 +92,18 @@ type BoolAttribute struct {
8992
// - https://github.com/hashicorp/terraform/issues/7569
9093
//
9194
DeprecationMessage string
95+
96+
// Validators define value validation functionality for the attribute. All
97+
// elements of the slice of AttributeValidator are run, regardless of any
98+
// previous error diagnostics.
99+
//
100+
// Many common use case validators can be found in the
101+
// github.com/hashicorp/terraform-plugin-framework-validators Go module.
102+
//
103+
// If the Type field points to a custom type that implements the
104+
// xattr.TypeWithValidate interface, the validators defined in this field
105+
// are run in addition to the validation defined by the type.
106+
Validators []validator.Bool
92107
}
93108

94109
// ApplyTerraform5AttributePathStep always returns an error as it is not
@@ -167,3 +182,8 @@ func (a BoolAttribute) IsRequiredForImport() bool {
167182
func (a BoolAttribute) IsOptionalForImport() bool {
168183
return false
169184
}
185+
186+
// BoolValidators returns the Validators field value.
187+
func (a BoolAttribute) BoolValidators() []validator.Bool {
188+
return a.Validators
189+
}

action/schema/bool_attribute_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1717
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema"
1818
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
19+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1920
"github.com/hashicorp/terraform-plugin-framework/types"
2021
)
2122

@@ -435,3 +436,35 @@ func TestBoolAttributeIsOptionalForImport(t *testing.T) {
435436
})
436437
}
437438
}
439+
440+
func TestBoolAttributeBoolValidators(t *testing.T) {
441+
t.Parallel()
442+
443+
testCases := map[string]struct {
444+
attribute schema.BoolAttribute
445+
expected []validator.Bool
446+
}{
447+
"no-validators": {
448+
attribute: schema.BoolAttribute{},
449+
expected: nil,
450+
},
451+
"validators": {
452+
attribute: schema.BoolAttribute{
453+
Validators: []validator.Bool{},
454+
},
455+
expected: []validator.Bool{},
456+
},
457+
}
458+
459+
for name, testCase := range testCases {
460+
t.Run(name, func(t *testing.T) {
461+
t.Parallel()
462+
463+
got := testCase.attribute.BoolValidators()
464+
465+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
466+
t.Errorf("unexpected difference: %s", diff)
467+
}
468+
})
469+
}
470+
}

action/schema/dynamic_attribute.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import (
88

99
"github.com/hashicorp/terraform-plugin-framework/attr"
1010
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
11+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
12+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1315
)
1416

1517
// Ensure the implementation satisifies the desired interfaces.
1618
var (
17-
_ Attribute = DynamicAttribute{}
19+
_ Attribute = DynamicAttribute{}
20+
_ fwxschema.AttributeWithDynamicValidators = DynamicAttribute{}
1821
)
1922

2023
// DynamicAttribute represents a schema attribute that is a dynamic, rather
@@ -90,6 +93,18 @@ type DynamicAttribute struct {
9093
// - https://github.com/hashicorp/terraform/issues/7569
9194
//
9295
DeprecationMessage string
96+
97+
// Validators define value validation functionality for the attribute. All
98+
// elements of the slice of AttributeValidator are run, regardless of any
99+
// previous error diagnostics.
100+
//
101+
// Many common use case validators can be found in the
102+
// github.com/hashicorp/terraform-plugin-framework-validators Go module.
103+
//
104+
// If the Type field points to a custom type that implements the
105+
// xattr.TypeWithValidate interface, the validators defined in this field
106+
// are run in addition to the validation defined by the type.
107+
Validators []validator.Dynamic
93108
}
94109

95110
// ApplyTerraform5AttributePathStep always returns an error as it is not
@@ -168,3 +183,8 @@ func (a DynamicAttribute) IsRequiredForImport() bool {
168183
func (a DynamicAttribute) IsOptionalForImport() bool {
169184
return false
170185
}
186+
187+
// DynamicValidators returns the Validators field value.
188+
func (a DynamicAttribute) DynamicValidators() []validator.Dynamic {
189+
return a.Validators
190+
}

action/schema/dynamic_attribute_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1515
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema"
1616
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
17+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1718
"github.com/hashicorp/terraform-plugin-framework/types"
1819
"github.com/hashicorp/terraform-plugin-go/tftypes"
1920
)
@@ -434,3 +435,35 @@ func TestDynamicAttributeIsOptionalForImport(t *testing.T) {
434435
})
435436
}
436437
}
438+
439+
func TestDynamicAttributeDynamicValidators(t *testing.T) {
440+
t.Parallel()
441+
442+
testCases := map[string]struct {
443+
attribute schema.DynamicAttribute
444+
expected []validator.Dynamic
445+
}{
446+
"no-validators": {
447+
attribute: schema.DynamicAttribute{},
448+
expected: nil,
449+
},
450+
"validators": {
451+
attribute: schema.DynamicAttribute{
452+
Validators: []validator.Dynamic{},
453+
},
454+
expected: []validator.Dynamic{},
455+
},
456+
}
457+
458+
for name, testCase := range testCases {
459+
t.Run(name, func(t *testing.T) {
460+
t.Parallel()
461+
462+
got := testCase.attribute.DynamicValidators()
463+
464+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
465+
t.Errorf("unexpected difference: %s", diff)
466+
}
467+
})
468+
}
469+
}

action/schema/float32_attribute.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import (
88

99
"github.com/hashicorp/terraform-plugin-framework/attr"
1010
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
11+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
12+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1315
)
1416

1517
// Ensure the implementation satisifies the desired interfaces.
1618
var (
17-
_ Attribute = Float32Attribute{}
19+
_ Attribute = Float32Attribute{}
20+
_ fwxschema.AttributeWithFloat32Validators = Float32Attribute{}
1821
)
1922

2023
// Float32Attribute represents a schema attribute that is a 32-bit floating
@@ -92,6 +95,18 @@ type Float32Attribute struct {
9295
// - https://github.com/hashicorp/terraform/issues/7569
9396
//
9497
DeprecationMessage string
98+
99+
// Validators define value validation functionality for the attribute. All
100+
// elements of the slice of AttributeValidator are run, regardless of any
101+
// previous error diagnostics.
102+
//
103+
// Many common use case validators can be found in the
104+
// github.com/hashicorp/terraform-plugin-framework-validators Go module.
105+
//
106+
// If the Type field points to a custom type that implements the
107+
// xattr.TypeWithValidate interface, the validators defined in this field
108+
// are run in addition to the validation defined by the type.
109+
Validators []validator.Float32
95110
}
96111

97112
// ApplyTerraform5AttributePathStep always returns an error as it is not
@@ -170,3 +185,8 @@ func (a Float32Attribute) IsRequiredForImport() bool {
170185
func (a Float32Attribute) IsOptionalForImport() bool {
171186
return false
172187
}
188+
189+
// Float32Validators returns the Validators field value.
190+
func (a Float32Attribute) Float32Validators() []validator.Float32 {
191+
return a.Validators
192+
}

action/schema/float32_attribute_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1717
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema"
1818
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
19+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1920
"github.com/hashicorp/terraform-plugin-framework/types"
2021
)
2122

@@ -435,3 +436,35 @@ func TestFloat32AttributeIsOptionalForImport(t *testing.T) {
435436
})
436437
}
437438
}
439+
440+
func TestFloat32AttributeFloat32Validators(t *testing.T) {
441+
t.Parallel()
442+
443+
testCases := map[string]struct {
444+
attribute schema.Float32Attribute
445+
expected []validator.Float32
446+
}{
447+
"no-validators": {
448+
attribute: schema.Float32Attribute{},
449+
expected: nil,
450+
},
451+
"validators": {
452+
attribute: schema.Float32Attribute{
453+
Validators: []validator.Float32{},
454+
},
455+
expected: []validator.Float32{},
456+
},
457+
}
458+
459+
for name, testCase := range testCases {
460+
t.Run(name, func(t *testing.T) {
461+
t.Parallel()
462+
463+
got := testCase.attribute.Float32Validators()
464+
465+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
466+
t.Errorf("unexpected difference: %s", diff)
467+
}
468+
})
469+
}
470+
}

action/schema/float64_attribute.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import (
88

99
"github.com/hashicorp/terraform-plugin-framework/attr"
1010
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
11+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
12+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1315
)
1416

1517
// Ensure the implementation satisifies the desired interfaces.
1618
var (
17-
_ Attribute = Float64Attribute{}
19+
_ Attribute = Float64Attribute{}
20+
_ fwxschema.AttributeWithFloat64Validators = Float64Attribute{}
1821
)
1922

2023
// Float64Attribute represents a schema attribute that is a 64-bit floating
@@ -92,6 +95,18 @@ type Float64Attribute struct {
9295
// - https://github.com/hashicorp/terraform/issues/7569
9396
//
9497
DeprecationMessage string
98+
99+
// Validators define value validation functionality for the attribute. All
100+
// elements of the slice of AttributeValidator are run, regardless of any
101+
// previous error diagnostics.
102+
//
103+
// Many common use case validators can be found in the
104+
// github.com/hashicorp/terraform-plugin-framework-validators Go module.
105+
//
106+
// If the Type field points to a custom type that implements the
107+
// xattr.TypeWithValidate interface, the validators defined in this field
108+
// are run in addition to the validation defined by the type.
109+
Validators []validator.Float64
95110
}
96111

97112
// ApplyTerraform5AttributePathStep always returns an error as it is not
@@ -170,3 +185,8 @@ func (a Float64Attribute) IsRequiredForImport() bool {
170185
func (a Float64Attribute) IsOptionalForImport() bool {
171186
return false
172187
}
188+
189+
// Float64Validators returns the Validators field value.
190+
func (a Float64Attribute) Float64Validators() []validator.Float64 {
191+
return a.Validators
192+
}

0 commit comments

Comments
 (0)