Skip to content

Commit e33a533

Browse files
authored
tfprotov5+tfprotov6: Add action validation RPC + fix proto field numbering (#539)
* proto changes * add validate rpc
1 parent cf99b16 commit e33a533

File tree

18 files changed

+1623
-813
lines changed

18 files changed

+1623
-813
lines changed

tfprotov5/action.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ type ActionMetadata struct {
1616

1717
// ActionServer is an interface containing the methods an action implementation needs to fill.
1818
type ActionServer interface {
19+
// ValidateActionConfig is called when Terraform is checking that an
20+
// action configuration is valid. It is guaranteed to have types
21+
// conforming to your schema, but it is not guaranteed that all values
22+
// will be known. This is your opportunity to do custom or advanced
23+
// validation prior to an action being planned/invoked.
24+
ValidateActionConfig(context.Context, *ValidateActionConfigRequest) (*ValidateActionConfigResponse, error)
25+
1926
// PlanAction is called when Terraform is attempting to
2027
// calculate a plan for an action. Depending on the type defined in
2128
// the action schema, Terraform may also pass the plan of linked resources
@@ -32,6 +39,35 @@ type ActionServer interface {
3239
InvokeAction(context.Context, *InvokeActionRequest) (*InvokeActionServerStream, error)
3340
}
3441

42+
// ValidateActionConfigRequest is the request Terraform sends when it
43+
// wants to validate an action's configuration.
44+
type ValidateActionConfigRequest struct {
45+
// ActionType is the type of action Terraform is validating.
46+
ActionType string
47+
48+
// Config is the configuration the user supplied for that action. See
49+
// the documentation on `DynamicValue` for more information about
50+
// safely accessing the configuration.
51+
//
52+
// The configuration is represented as a tftypes.Object, with each
53+
// attribute and nested block getting its own key and value.
54+
//
55+
// This configuration may contain unknown values if a user uses
56+
// interpolation or other functionality that would prevent Terraform
57+
// from knowing the value at request time. Any attributes not directly
58+
// set in the configuration will be null.
59+
Config *DynamicValue
60+
}
61+
62+
// ValidateActionConfigResponse is the response from the provider about
63+
// the validity of an action's configuration.
64+
type ValidateActionConfigResponse struct {
65+
// Diagnostics report errors or warnings related to the given
66+
// configuration. Returning an empty slice indicates a successful
67+
// validation with no warnings or errors generated.
68+
Diagnostics []*Diagnostic
69+
}
70+
3571
// PlanActionRequest is the request Terraform sends when it is attempting to
3672
// calculate a plan for an action.
3773
type PlanActionRequest struct {

tfprotov5/internal/fromproto/action.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ import (
88
"github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5"
99
)
1010

11+
func ValidateActionConfigRequest(in *tfplugin5.ValidateActionConfig_Request) *tfprotov5.ValidateActionConfigRequest {
12+
if in == nil {
13+
return nil
14+
}
15+
16+
return &tfprotov5.ValidateActionConfigRequest{
17+
ActionType: in.ActionType,
18+
Config: DynamicValue(in.Config),
19+
}
20+
}
21+
1122
func PlanActionRequest(in *tfplugin5.PlanAction_Request) *tfprotov5.PlanActionRequest {
1223
if in == nil {
1324
return nil

tfprotov5/internal/fromproto/actions_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,52 @@ import (
1212
"github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5"
1313
)
1414

15+
func TestValidateActionConfigRequest(t *testing.T) {
16+
t.Parallel()
17+
18+
testCases := map[string]struct {
19+
in *tfplugin5.ValidateActionConfig_Request
20+
expected *tfprotov5.ValidateActionConfigRequest
21+
}{
22+
"nil": {
23+
in: nil,
24+
expected: nil,
25+
},
26+
"zero": {
27+
in: &tfplugin5.ValidateActionConfig_Request{},
28+
expected: &tfprotov5.ValidateActionConfigRequest{},
29+
},
30+
"Config": {
31+
in: &tfplugin5.ValidateActionConfig_Request{
32+
Config: testTfplugin5DynamicValue(),
33+
},
34+
expected: &tfprotov5.ValidateActionConfigRequest{
35+
Config: testTfprotov5DynamicValue(),
36+
},
37+
},
38+
"ActionType": {
39+
in: &tfplugin5.ValidateActionConfig_Request{
40+
ActionType: "test",
41+
},
42+
expected: &tfprotov5.ValidateActionConfigRequest{
43+
ActionType: "test",
44+
},
45+
},
46+
}
47+
48+
for name, testCase := range testCases {
49+
t.Run(name, func(t *testing.T) {
50+
t.Parallel()
51+
52+
got := fromproto.ValidateActionConfigRequest(testCase.in)
53+
54+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
55+
t.Errorf("unexpected difference: %s", diff)
56+
}
57+
})
58+
}
59+
}
60+
1561
func TestPlanActionRequest(t *testing.T) {
1662
t.Parallel()
1763

0 commit comments

Comments
 (0)