Skip to content

Commit 59a937e

Browse files
authored
Add List Resource Config Validation (#1178)
* Added List Resource Config Validation * Updating ListResource to return diag.Diagnotics instead of error * Fix linter complaint * Updated ListResource to return diags inside ListResult * Added List Resource to tests in getmetadata * Added List Resource to tests in getmetadata * Added ResourcesMethod to tests in server_validatelistresourceconfig_test.go * gofmt error * Added actions * rebasing * Added actions * updated with more actions * Addressed some PR comments * Updated Configure to be resource instead of List * Updated for linter * Updated to add test as suggested in PR
1 parent a95b280 commit 59a937e

34 files changed

+1909
-161
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto5
5+
6+
import (
7+
"context"
8+
9+
"github.com/hashicorp/terraform-plugin-framework/diag"
10+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
11+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
12+
"github.com/hashicorp/terraform-plugin-framework/list"
13+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
14+
)
15+
16+
// ValidateListResourceConfigRequest returns the *fwserver.ValidateListResourceConfigRequest
17+
// equivalent of a *tfprotov5.ValidateListResourceConfigRequest.
18+
func ValidateListResourceConfigRequest(ctx context.Context, proto5 *tfprotov5.ValidateListResourceConfigRequest, listResource list.ListResource, listResourceSchema fwschema.Schema) (*fwserver.ValidateListResourceConfigRequest, diag.Diagnostics) {
19+
if proto5 == nil {
20+
return nil, nil
21+
}
22+
23+
fw := &fwserver.ValidateListResourceConfigRequest{}
24+
25+
config, diags := Config(ctx, proto5.Config, listResourceSchema)
26+
27+
fw.Config = config
28+
fw.ListResource = listResource
29+
30+
return fw, diags
31+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto5_test
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"github.com/hashicorp/terraform-plugin-framework/diag"
12+
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto5"
13+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
14+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
15+
"github.com/hashicorp/terraform-plugin-framework/list"
16+
"github.com/hashicorp/terraform-plugin-framework/list/schema"
17+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
18+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
19+
"github.com/hashicorp/terraform-plugin-go/tftypes"
20+
)
21+
22+
func TestValidateListResourceConfigRequest(t *testing.T) {
23+
t.Parallel()
24+
25+
testProto5Type := tftypes.Object{
26+
AttributeTypes: map[string]tftypes.Type{
27+
"test_attribute": tftypes.String,
28+
},
29+
}
30+
31+
testProto5Value := tftypes.NewValue(testProto5Type, map[string]tftypes.Value{
32+
"test_attribute": tftypes.NewValue(tftypes.String, "test-value"),
33+
})
34+
35+
testProto5DynamicValue, err := tfprotov5.NewDynamicValue(testProto5Type, testProto5Value)
36+
37+
if err != nil {
38+
t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err)
39+
}
40+
41+
testFwSchema := schema.Schema{
42+
Attributes: map[string]schema.Attribute{
43+
"test_attribute": schema.StringAttribute{
44+
Required: true,
45+
},
46+
},
47+
}
48+
49+
testCases := map[string]struct {
50+
input *tfprotov5.ValidateListResourceConfigRequest
51+
listResourceSchema fwschema.Schema
52+
listResource list.ListResource
53+
expected *fwserver.ValidateListResourceConfigRequest
54+
expectedDiagnostics diag.Diagnostics
55+
}{
56+
"nil": {
57+
input: nil,
58+
expected: nil,
59+
},
60+
"empty": {
61+
input: &tfprotov5.ValidateListResourceConfigRequest{},
62+
expected: &fwserver.ValidateListResourceConfigRequest{},
63+
},
64+
"config-missing-schema": {
65+
input: &tfprotov5.ValidateListResourceConfigRequest{
66+
Config: &testProto5DynamicValue,
67+
},
68+
expected: &fwserver.ValidateListResourceConfigRequest{},
69+
expectedDiagnostics: diag.Diagnostics{
70+
diag.NewErrorDiagnostic(
71+
"Unable to Convert Configuration",
72+
"An unexpected error was encountered when converting the configuration from the protocol type. "+
73+
"This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+
74+
"Please report this to the provider developer:\n\n"+
75+
"Missing schema.",
76+
),
77+
},
78+
},
79+
"config": {
80+
input: &tfprotov5.ValidateListResourceConfigRequest{
81+
Config: &testProto5DynamicValue,
82+
},
83+
listResourceSchema: testFwSchema,
84+
expected: &fwserver.ValidateListResourceConfigRequest{
85+
Config: &tfsdk.Config{
86+
Raw: testProto5Value,
87+
Schema: testFwSchema,
88+
},
89+
},
90+
},
91+
}
92+
93+
for name, testCase := range testCases {
94+
t.Run(name, func(t *testing.T) {
95+
t.Parallel()
96+
97+
got, diags := fromproto5.ValidateListResourceConfigRequest(context.Background(), testCase.input, testCase.listResource, testCase.listResourceSchema)
98+
99+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
100+
t.Errorf("unexpected difference: %s", diff)
101+
}
102+
103+
if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" {
104+
t.Errorf("unexpected diagnostics difference: %s", diff)
105+
}
106+
})
107+
}
108+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto6
5+
6+
import (
7+
"context"
8+
9+
"github.com/hashicorp/terraform-plugin-framework/diag"
10+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
11+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
12+
"github.com/hashicorp/terraform-plugin-framework/list"
13+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
14+
)
15+
16+
// ValidateListResourceConfigRequest returns the *fwserver.ValidateListResourceConfigRequest
17+
// equivalent of a *tfprotov6.ValidateListResourceConfigRequest.
18+
func ValidateListResourceConfigRequest(ctx context.Context, proto6 *tfprotov6.ValidateListResourceConfigRequest, listResource list.ListResource, listResourceSchema fwschema.Schema) (*fwserver.ValidateListResourceConfigRequest, diag.Diagnostics) {
19+
if proto6 == nil {
20+
return nil, nil
21+
}
22+
23+
fw := &fwserver.ValidateListResourceConfigRequest{}
24+
25+
config, diags := Config(ctx, proto6.Config, listResourceSchema)
26+
27+
fw.Config = config
28+
fw.ListResource = listResource
29+
30+
return fw, diags
31+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto6_test
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"github.com/hashicorp/terraform-plugin-framework/diag"
12+
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto6"
13+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
14+
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
15+
"github.com/hashicorp/terraform-plugin-framework/list"
16+
"github.com/hashicorp/terraform-plugin-framework/list/schema"
17+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
18+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
19+
"github.com/hashicorp/terraform-plugin-go/tftypes"
20+
)
21+
22+
func TestValidateListResourceConfigRequest(t *testing.T) {
23+
t.Parallel()
24+
25+
testProto6Type := tftypes.Object{
26+
AttributeTypes: map[string]tftypes.Type{
27+
"test_attribute": tftypes.String,
28+
},
29+
}
30+
31+
testProto6Value := tftypes.NewValue(testProto6Type, map[string]tftypes.Value{
32+
"test_attribute": tftypes.NewValue(tftypes.String, "test-value"),
33+
})
34+
35+
testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value)
36+
37+
if err != nil {
38+
t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err)
39+
}
40+
41+
testFwSchema := schema.Schema{
42+
Attributes: map[string]schema.Attribute{
43+
"test_attribute": schema.StringAttribute{
44+
Required: true,
45+
},
46+
},
47+
}
48+
49+
testCases := map[string]struct {
50+
input *tfprotov6.ValidateListResourceConfigRequest
51+
listResourceSchema fwschema.Schema
52+
listResource list.ListResource
53+
expected *fwserver.ValidateListResourceConfigRequest
54+
expectedDiagnostics diag.Diagnostics
55+
}{
56+
"nil": {
57+
input: nil,
58+
expected: nil,
59+
},
60+
"empty": {
61+
input: &tfprotov6.ValidateListResourceConfigRequest{},
62+
expected: &fwserver.ValidateListResourceConfigRequest{},
63+
},
64+
"config-missing-schema": {
65+
input: &tfprotov6.ValidateListResourceConfigRequest{
66+
Config: &testProto6DynamicValue,
67+
},
68+
expected: &fwserver.ValidateListResourceConfigRequest{},
69+
expectedDiagnostics: diag.Diagnostics{
70+
diag.NewErrorDiagnostic(
71+
"Unable to Convert Configuration",
72+
"An unexpected error was encountered when converting the configuration from the protocol type. "+
73+
"This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+
74+
"Please report this to the provider developer:\n\n"+
75+
"Missing schema.",
76+
),
77+
},
78+
},
79+
"config": {
80+
input: &tfprotov6.ValidateListResourceConfigRequest{
81+
Config: &testProto6DynamicValue,
82+
},
83+
listResourceSchema: testFwSchema,
84+
expected: &fwserver.ValidateListResourceConfigRequest{
85+
Config: &tfsdk.Config{
86+
Raw: testProto6Value,
87+
Schema: testFwSchema,
88+
},
89+
},
90+
},
91+
}
92+
93+
for name, testCase := range testCases {
94+
t.Run(name, func(t *testing.T) {
95+
t.Parallel()
96+
97+
got, diags := fromproto6.ValidateListResourceConfigRequest(context.Background(), testCase.input, testCase.listResource, testCase.listResourceSchema)
98+
99+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
100+
t.Errorf("unexpected difference: %s", diff)
101+
}
102+
103+
if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" {
104+
t.Errorf("unexpected diagnostics difference: %s", diff)
105+
}
106+
})
107+
}
108+
}

internal/fwserver/server.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ type Server struct {
4242
// to [ephemeral.ConfigureRequest.ProviderData].
4343
EphemeralResourceConfigureData any
4444

45+
// ListResourceConfigureData is the
46+
// [provider.ConfigureResponse.ListResourceData] field value which is passed
47+
// to [list.ConfigureRequest.ProviderData].
48+
ListResourceConfigureData any
49+
4550
// ActionConfigureData is the
4651
// [provider.ConfigureResponse.ActionData] field value which is passed
4752
// to [action.ConfigureRequest.ProviderData].

internal/fwserver/server_listresource.go

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

66
import (
77
"context"
8-
"errors"
8+
"github.com/hashicorp/terraform-plugin-go/tftypes"
99
"iter"
1010

1111
"github.com/hashicorp/terraform-plugin-framework/diag"
@@ -84,12 +84,19 @@ type ListResult struct {
8484
var NoListResults = func(func(ListResult) bool) {}
8585

8686
// ListResource implements the framework server ListResource RPC.
87-
func (s *Server) ListResource(ctx context.Context, fwReq *ListRequest, fwStream *ListResultsStream) error {
87+
func (s *Server) ListResource(ctx context.Context, fwReq *ListRequest, fwStream *ListResultsStream) {
8888
listResource := fwReq.ListResource
8989

90-
if fwReq.Config == nil {
91-
fwStream.Results = NoListResults
92-
return errors.New("Invalid ListResource request: Config cannot be nil")
90+
if fwReq.Config == nil && fwReq.ResourceSchema != nil {
91+
fwReq.Config = &tfsdk.Config{
92+
Raw: tftypes.NewValue(fwReq.ResourceSchema.Type().TerraformType(ctx), nil),
93+
Schema: fwReq.ResourceSchema,
94+
}
95+
} else if fwReq.Config == nil && fwReq.ResourceIdentitySchema == nil {
96+
fwReq.Config = &tfsdk.Config{
97+
Raw: tftypes.NewValue(tftypes.Object{}, nil),
98+
Schema: fwReq.ResourceSchema,
99+
}
93100
}
94101

95102
req := list.ListRequest{
@@ -112,7 +119,6 @@ func (s *Server) ListResource(ctx context.Context, fwReq *ListRequest, fwStream
112119
}
113120

114121
fwStream.Results = processListResults(req, stream.Results)
115-
return nil
116122
}
117123

118124
func processListResults(req list.ListRequest, stream iter.Seq[list.ListResult]) iter.Seq[ListResult] {

0 commit comments

Comments
 (0)