Skip to content

Commit 13798fb

Browse files
committed
update Set and SetAtPath to accept values of tftypes.Value
1 parent 6a9c372 commit 13798fb

12 files changed

+84
-124
lines changed

internal/fwschemadata/data_set.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,35 @@ package fwschemadata
66
import (
77
"context"
88
"fmt"
9+
"github.com/hashicorp/terraform-plugin-go/tftypes"
910

1011
"github.com/hashicorp/terraform-plugin-framework/diag"
1112
"github.com/hashicorp/terraform-plugin-framework/internal/reflect"
1213
"github.com/hashicorp/terraform-plugin-framework/path"
1314
)
1415

15-
// Set replaces the entire value. The value should be a struct whose fields
16+
// Set replaces the entire value. The value can be a tftypes.Value or a struct whose fields
1617
// have one of the attr.Value types. Each field must have the tfsdk field tag.
1718
func (d *Data) Set(ctx context.Context, val any) diag.Diagnostics {
19+
var diags diag.Diagnostics
20+
21+
if v, ok := val.(tftypes.Value); ok {
22+
objType := d.Schema.Type().TerraformType(ctx)
23+
24+
if !objType.Equal(v.Type()) {
25+
diags.AddError(
26+
d.Description.Title()+" Write Error",
27+
"An unexpected error was encountered trying to write the "+d.Description.String()+". This is always an error in the provider. Please report the following to the provider developer:\n\n"+
28+
fmt.Sprintf("Error: Type mismatch between provided value and type of %s", d.Description.String()),
29+
)
30+
return diags
31+
32+
}
33+
d.TerraformValue = v
34+
35+
return diags
36+
}
37+
1838
attrValue, diags := reflect.FromValue(ctx, d.Schema.Type(), val, path.Empty())
1939

2040
if diags.HasError() {

internal/fwschemadata/data_set_at_path.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,37 @@ import (
2828
// Lists can only have the next element added according to the current length.
2929
func (d *Data) SetAtPath(ctx context.Context, path path.Path, val interface{}) diag.Diagnostics {
3030
var diags diag.Diagnostics
31-
3231
ctx = logging.FrameworkWithAttributePath(ctx, path.String())
3332

33+
if v, ok := val.(tftypes.Value); ok {
34+
atPath, err := d.Schema.AttributeAtPath(ctx, path)
35+
if err != nil {
36+
diags.AddAttributeError(
37+
path,
38+
d.Description.Title()+" Write Error",
39+
"An unexpected error was encountered trying to write the "+d.Description.String()+". This is always an error in the provider. Please report the following to the provider developer:\n\n"+
40+
fmt.Sprintf("Error: %s does not exist at path", path.String()),
41+
)
42+
return diags
43+
}
44+
45+
attrType := atPath.GetType().TerraformType(ctx)
46+
47+
if !attrType.Equal(v.Type()) {
48+
diags.AddAttributeError(
49+
path,
50+
d.Description.Title()+" Write Error",
51+
"An unexpected error was encountered trying to write the "+d.Description.String()+". This is always an error in the provider. Please report the following to the provider developer:\n\n"+
52+
fmt.Sprintf("Error: Type of provided value does not match type of %s", path.String()),
53+
)
54+
return diags
55+
}
56+
57+
d.TerraformValue = v
58+
59+
return diags
60+
}
61+
3462
tftypesPath, tftypesPathDiags := totftypes.AttributePath(ctx, path)
3563

3664
diags.Append(tftypesPathDiags...)

internal/fwserver/server_listresource.go

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -177,19 +177,14 @@ func (s *Server) ListResource(ctx context.Context, fwReq *ListRequest, fwStream
177177
diagsStream.Results = list.NoListResults
178178
}
179179

180-
if stream.Results == nil || stream.Proto5Results == nil {
180+
if stream.Results == nil {
181181
fwStream.Results = processListResults(req, list.NoListResults, diagsStream.Results)
182182
}
183183

184184
if stream.Results != nil {
185185
fwStream.Results = processListResults(req, stream.Results, diagsStream.Results)
186186
}
187187

188-
if stream.Proto5Results != nil {
189-
// TODO merge with diagsStream if needed
190-
fwStream.ResultsProtoV5 = processListResultsProto5(req, stream.Proto5Results)
191-
}
192-
193188
return
194189
}
195190

@@ -239,46 +234,3 @@ func processListResult(req list.ListRequest, result list.ListResult) ListResult
239234

240235
return ListResult(result)
241236
}
242-
243-
// Duplicated functions to handle proto5 result type
244-
func processListResultsProto5(req list.ListRequest, stream iter.Seq[tfprotov5.ListResourceResult]) iter.Seq[tfprotov5.ListResourceResult] {
245-
return func(push func(tfprotov5.ListResourceResult) bool) {
246-
for result := range stream {
247-
if !push(processListResultProto5(req, result)) {
248-
return
249-
}
250-
}
251-
}
252-
}
253-
254-
func processListResultProto5(req list.ListRequest, result tfprotov5.ListResourceResult) tfprotov5.ListResourceResult {
255-
if len(result.Diagnostics) > 0 {
256-
return result
257-
}
258-
259-
if result.Identity == nil {
260-
return ListResultErrorProto5(
261-
"Incomplete List Result",
262-
"When listing resources, an implementation issue was found. "+
263-
"This is always a problem with the provider. Please report this to the provider developers.\n\n"+
264-
"The \"Identity\" field is nil.\n\n",
265-
)
266-
}
267-
268-
if req.IncludeResource {
269-
// We could do an IsNull check here on the DynamicValue as well
270-
if result.Resource == nil {
271-
result.Diagnostics = []*tfprotov5.Diagnostic{
272-
{
273-
Severity: 0,
274-
Summary: "Incomplete List Result",
275-
Detail: "When listing resources, an implementation issue was found. " +
276-
"This is always a problem with the provider. Please report this to the provider developers.\n\n" +
277-
"The \"IncludeResource\" field in the ListRequest is true, but the \"Resource\" field in the ListResult is nil.\n\n",
278-
},
279-
}
280-
}
281-
}
282-
283-
return result
284-
}

internal/proto5server/server_getmetadata_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,14 @@ func TestServerGetMetadata(t *testing.T) {
225225
return []func() list.ListResource{
226226
func() list.ListResource {
227227
return &testprovider.ListResource{
228-
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
228+
MetadataMethod: func(_ context.Context, _ list.MetadataRequest, resp *list.MetadataResponse) {
229229
resp.TypeName = "test_list_resource1"
230230
},
231231
}
232232
},
233233
func() list.ListResource {
234234
return &testprovider.ListResource{
235-
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
235+
MetadataMethod: func(_ context.Context, _ list.MetadataRequest, resp *list.MetadataResponse) {
236236
resp.TypeName = "test_list_resource2"
237237
},
238238
}

internal/proto5server/server_getproviderschema_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ func TestServerGetProviderSchema(t *testing.T) {
381381
},
382382
}
383383
},
384-
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
384+
MetadataMethod: func(_ context.Context, _ list.MetadataRequest, resp *list.MetadataResponse) {
385385
resp.TypeName = "test_list_resource1"
386386
},
387387
}
@@ -397,7 +397,7 @@ func TestServerGetProviderSchema(t *testing.T) {
397397
},
398398
}
399399
},
400-
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
400+
MetadataMethod: func(_ context.Context, _ list.MetadataRequest, resp *list.MetadataResponse) {
401401
resp.TypeName = "test_list_resource2"
402402
},
403403
}

internal/proto5server/server_listresource.go

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -52,54 +52,31 @@ func (s *Server) ListResource(ctx context.Context, protoReq *tfprotov5.ListResou
5252
metadataResp := list.MetadataResponse{}
5353
listResource.Metadata(ctx, list.MetadataRequest{}, &metadataResp)
5454

55+
req := &fwserver.ListRequest{
56+
Config: config,
57+
ListResource: listResource,
58+
IncludeResource: protoReq.IncludeResource,
59+
Limit: protoReq.Limit,
60+
}
61+
5562
// There's validation in xxx that ensures both are set if either is provided so perhaps it's sufficient to only nil check Identity
5663
if metadataResp.ProtoV5IdentitySchema != nil {
57-
identitySchema, _ := fromproto5.IdentitySchema(ctx, metadataResp.ProtoV5IdentitySchema())
58-
resourceSchema, _ := fromproto5.ResourceSchema(ctx, metadataResp.ProtoV5Schema())
59-
60-
req := &fwserver.ListRequest{
61-
Config: config,
62-
ListResource: listResource,
63-
IncludeResource: protoReq.IncludeResource,
64-
ResourceIdentitySchema: identitySchema,
65-
ResourceSchema: resourceSchema,
64+
req.ResourceSchema, _ = fromproto5.ResourceSchema(ctx, metadataResp.ProtoV5Schema())
65+
req.ResourceIdentitySchema, _ = fromproto5.IdentitySchema(ctx, metadataResp.ProtoV5IdentitySchema())
66+
} else {
67+
req.ResourceSchema, diags = s.FrameworkServer.ResourceSchema(ctx, protoReq.TypeName)
68+
allDiags.Append(diags...)
69+
if diags.HasError() {
70+
return ListRequestErrorDiagnostics(ctx, allDiags...)
6671
}
6772

68-
stream := &fwserver.ListResultsStream{}
69-
s.FrameworkServer.ListResource(ctx, req, stream)
70-
71-
protoStream.Results = func(push func(tfprotov5.ListResourceResult) bool) {
72-
for result := range stream.ResultsProtoV5 {
73-
// We pass the result along as-is since it's already in ProtoV5 format
74-
if !push(result) {
75-
return
76-
}
77-
}
73+
req.ResourceIdentitySchema, diags = s.FrameworkServer.ResourceIdentitySchema(ctx, protoReq.TypeName)
74+
allDiags.Append(diags...)
75+
if diags.HasError() {
76+
return ListRequestErrorDiagnostics(ctx, allDiags...)
7877
}
79-
80-
return protoStream, nil
81-
}
82-
83-
resourceSchema, diags := s.FrameworkServer.ResourceSchema(ctx, protoReq.TypeName)
84-
allDiags.Append(diags...)
85-
if diags.HasError() {
86-
return ListRequestErrorDiagnostics(ctx, allDiags...)
87-
}
88-
89-
identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, protoReq.TypeName)
90-
allDiags.Append(diags...)
91-
if diags.HasError() {
92-
return ListRequestErrorDiagnostics(ctx, allDiags...)
93-
}
94-
95-
req := &fwserver.ListRequest{
96-
Config: config,
97-
ListResource: listResource,
98-
ResourceSchema: resourceSchema,
99-
ResourceIdentitySchema: identitySchema,
100-
IncludeResource: protoReq.IncludeResource,
101-
Limit: protoReq.Limit,
10278
}
79+
10380
stream := &fwserver.ListResultsStream{}
10481

10582
s.FrameworkServer.ListResource(ctx, req, stream)

internal/proto5server/server_listresource_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func TestServerListResource(t *testing.T) {
118118
}
119119
resp.Results = slices.Values(results)
120120
},
121-
MetadataMethod: func(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
121+
MetadataMethod: func(ctx context.Context, req list.MetadataRequest, resp *list.MetadataResponse) {
122122
resp.TypeName = "test_resource"
123123
},
124124
}

internal/proto5server/server_validatelistresourceconfig_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func TestServerValidateListResourceConfig(t *testing.T) {
5858
return []func() list.ListResource{
5959
func() list.ListResource {
6060
return &testprovider.ListResource{
61-
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
61+
MetadataMethod: func(_ context.Context, _ list.MetadataRequest, resp *list.MetadataResponse) {
6262
resp.TypeName = "test_resource"
6363
},
6464
ListResourceConfigSchemaMethod: func(_ context.Context, _ list.ListResourceSchemaRequest, resp *list.ListResourceSchemaResponse) {},
@@ -96,7 +96,7 @@ func TestServerValidateListResourceConfig(t *testing.T) {
9696
ListResourceConfigSchemaMethod: func(_ context.Context, _ list.ListResourceSchemaRequest, resp *list.ListResourceSchemaResponse) {
9797
resp.Schema = testSchema
9898
},
99-
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
99+
MetadataMethod: func(_ context.Context, _ list.MetadataRequest, resp *list.MetadataResponse) {
100100
resp.TypeName = "test_resource"
101101
},
102102
}
@@ -135,7 +135,7 @@ func TestServerValidateListResourceConfig(t *testing.T) {
135135
ListResourceConfigSchemaMethod: func(_ context.Context, _ list.ListResourceSchemaRequest, resp *list.ListResourceSchemaResponse) {
136136
resp.Schema = testSchema
137137
},
138-
MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
138+
MetadataMethod: func(_ context.Context, _ list.MetadataRequest, resp *list.MetadataResponse) {
139139
resp.TypeName = "test_resource"
140140
},
141141
},

internal/testing/testprovider/listresource.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,20 @@ import (
77
"context"
88

99
"github.com/hashicorp/terraform-plugin-framework/list"
10-
"github.com/hashicorp/terraform-plugin-framework/resource"
1110
)
1211

1312
var _ list.ListResource = &ListResource{}
1413

1514
// Declarative list.ListResource for unit testing.
1615
type ListResource struct {
1716
// ListResource interface methods
18-
MetadataMethod func(context.Context, resource.MetadataRequest, *resource.MetadataResponse)
17+
MetadataMethod func(context.Context, list.MetadataRequest, *list.MetadataResponse)
1918
ListResourceConfigSchemaMethod func(context.Context, list.ListResourceSchemaRequest, *list.ListResourceSchemaResponse)
2019
ListMethod func(context.Context, list.ListRequest, *list.ListResultsStream)
2120
}
2221

2322
// Metadata satisfies the list.ListResource interface.
24-
func (r *ListResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
23+
func (r *ListResource) Metadata(ctx context.Context, req list.MetadataRequest, resp *list.MetadataResponse) {
2524
if r.MetadataMethod == nil {
2625
return
2726
}

list/list_resource.go

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1212
"github.com/hashicorp/terraform-plugin-framework/resource"
1313
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
14-
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
1514
)
1615

1716
// ListResource represents an implementation of listing instances of a managed resource
@@ -125,17 +124,6 @@ func (r ListRequest) NewListResult() ListResult {
125124
}
126125
}
127126

128-
func (r ListRequest) NewListResultProtoV5() tfprotov5.ListResourceResult {
129-
diags := make([]*tfprotov5.Diagnostic, 0)
130-
131-
return tfprotov5.ListResourceResult{
132-
DisplayName: "",
133-
Resource: nil,
134-
Identity: nil,
135-
Diagnostics: diags,
136-
}
137-
}
138-
139127
// ListResultsStream represents a streaming response to a [ListRequest]. An
140128
// instance of this struct is supplied as an argument to the provider's
141129
// [ListResource.List] function. The provider should set a Results iterator
@@ -150,18 +138,11 @@ type ListResultsStream struct {
150138
// To indicate a fatal processing error, push a [ListResult] that contains
151139
// a [diag.ErrorDiagnostic].
152140
Results iter.Seq[ListResult]
153-
154-
// We could support this as a generic type instead, but it requires threading the generic type through
155-
// all manner of things.. on the other hand this requires duplication of existing functionality
156-
Proto5Results iter.Seq[tfprotov5.ListResourceResult]
157141
}
158142

159143
// NoListResults is an iterator that pushes zero results.
160144
var NoListResults = func(push func(ListResult) bool) {}
161145

162-
// One of the duplication examples
163-
var NoListResultsProtov5 = func(push func(tfprotov5.ListResourceResult) bool) {}
164-
165146
// ListResultsStreamDiagnostics returns a function that yields a single
166147
// [ListResult] with the given Diagnostics
167148
func ListResultsStreamDiagnostics(diags diag.Diagnostics) iter.Seq[ListResult] {

0 commit comments

Comments
 (0)