Skip to content

Commit 62c8251

Browse files
committed
ListResource: null handling
1 parent 7dce0b7 commit 62c8251

File tree

6 files changed

+99
-95
lines changed

6 files changed

+99
-95
lines changed

internal/fwserver/server_listresource.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func processListResult(req list.ListRequest, result list.ListResult) ListResult
127127
return ListResult(result)
128128
}
129129

130-
if result.Identity == nil { // TODO: is result.Identity.Raw.IsNull() a practical concern?
130+
if result.Identity == nil || result.Identity.Raw.IsNull() {
131131
return ListResultError(
132132
"Incomplete List Result",
133133
"When listing resources, an implementation issue was found. "+
@@ -137,7 +137,7 @@ func processListResult(req list.ListRequest, result list.ListResult) ListResult
137137
}
138138

139139
if req.IncludeResource {
140-
if result.Resource == nil { // TODO: is result.Resource.Raw.IsNull() a practical concern?
140+
if result.Resource == nil || result.Resource.Raw.IsNull() {
141141
result.Diagnostics.AddWarning(
142142
"Incomplete List Result",
143143
"When listing resources, an implementation issue was found. "+

internal/fwserver/server_listresource_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,38 @@ func TestServerListResource(t *testing.T) {
214214
},
215215
},
216216
},
217+
"error-on-null-resource-identity": {
218+
server: &fwserver.Server{
219+
Provider: &testprovider.Provider{},
220+
},
221+
request: &fwserver.ListRequest{
222+
Config: &tfsdk.Config{},
223+
ListResource: &testprovider.ListResource{
224+
ListMethod: func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) {
225+
resp.Results = slices.Values([]list.ListResult{
226+
{
227+
Identity: &tfsdk.ResourceIdentity{},
228+
Resource: &tfsdk.Resource{
229+
Schema: testSchema,
230+
Raw: testResourceValue1,
231+
},
232+
DisplayName: "Test Resource 1",
233+
},
234+
})
235+
},
236+
},
237+
},
238+
expectedStreamEvents: []fwserver.ListResult{
239+
{
240+
Diagnostics: diag.Diagnostics{
241+
diag.NewErrorDiagnostic(
242+
"Incomplete List Result",
243+
"The provider did not populate the Identity field in the ListResourceResult. This may be due to an error in the provider's implementation.",
244+
),
245+
},
246+
},
247+
},
248+
},
217249
"warning-on-missing-resource": {
218250
server: &fwserver.Server{
219251
Provider: &testprovider.Provider{},
@@ -252,6 +284,45 @@ func TestServerListResource(t *testing.T) {
252284
},
253285
},
254286
},
287+
"warning-on-null-resource": {
288+
server: &fwserver.Server{
289+
Provider: &testprovider.Provider{},
290+
},
291+
request: &fwserver.ListRequest{
292+
Config: &tfsdk.Config{},
293+
IncludeResource: true,
294+
ListResource: &testprovider.ListResource{
295+
ListMethod: func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) {
296+
resp.Results = slices.Values([]list.ListResult{
297+
{
298+
Identity: &tfsdk.ResourceIdentity{
299+
Schema: testIdentitySchema,
300+
Raw: testIdentityValue1,
301+
},
302+
Resource: &tfsdk.Resource{},
303+
DisplayName: "Test Resource 1",
304+
},
305+
})
306+
},
307+
},
308+
},
309+
expectedStreamEvents: []fwserver.ListResult{
310+
{
311+
Identity: &tfsdk.ResourceIdentity{
312+
Schema: testIdentitySchema,
313+
Raw: testIdentityValue1,
314+
},
315+
Resource: &tfsdk.Resource{},
316+
DisplayName: "Test Resource 1",
317+
Diagnostics: diag.Diagnostics{
318+
diag.NewWarningDiagnostic(
319+
"Incomplete List Result",
320+
"The provider did not populate the Resource field in the ListResourceResult. This may be due to the provider not supporting this functionality or an error in the provider's implementation.",
321+
),
322+
},
323+
},
324+
},
325+
},
255326
}
256327

257328
for name, testCase := range testCases {

internal/proto5server/server_listresource_test.go

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ func TestServerListResource(t *testing.T) {
3131
}
3232

3333
type ThingResource struct {
34-
// TODO: how do we feel about this?
3534
ThingResourceIdentity
3635
Name string `tfsdk:"name"`
3736
}
@@ -106,7 +105,11 @@ func TestServerListResource(t *testing.T) {
106105
continue
107106
}
108107

109-
result := req.ToListResult(ctx, resources[name].ThingResourceIdentity, resources[name], name)
108+
result := req.NewListResult()
109+
result.Identity.Set(ctx, resources[name].ThingResourceIdentity)
110+
result.Resource.Set(ctx, resources[name])
111+
result.DisplayName = name
112+
110113
results = append(results, result)
111114
}
112115
resp.Results = slices.Values(results)
@@ -117,21 +120,6 @@ func TestServerListResource(t *testing.T) {
117120
}
118121
}
119122

120-
listResourceThatDoesNotPopulateResource := func() list.ListResource {
121-
r, ok := listResource().(*testprovider.ListResource)
122-
if !ok {
123-
t.Fatal("listResourceThatDoesNotPopulateResource must be a testprovider.ListResource")
124-
}
125-
126-
r.ListMethod = func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) {
127-
result := req.ToListResult(ctx, resources["plateau"].ThingResourceIdentity, nil, "plateau")
128-
129-
resp.Results = slices.Values([]list.ListResult{result})
130-
}
131-
132-
return r
133-
}
134-
135123
managedResource := func() resource.Resource {
136124
return &testprovider.ResourceWithIdentity{
137125
IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
@@ -251,29 +239,6 @@ func TestServerListResource(t *testing.T) {
251239
},
252240
},
253241
},
254-
"result-with-include-resource-warning": {
255-
server: server(listResourceThatDoesNotPopulateResource, managedResource),
256-
request: &tfprotov5.ListResourceRequest{
257-
TypeName: "test_resource",
258-
Config: plateau,
259-
IncludeResource: true,
260-
},
261-
expectedError: nil,
262-
expectedDiagnostics: diag.Diagnostics{},
263-
expectedResults: []tfprotov5.ListResourceResult{
264-
{
265-
DisplayName: "plateau",
266-
Identity: expectedResourceIdentities["plateau"],
267-
Diagnostics: []*tfprotov5.Diagnostic{
268-
{
269-
Severity: tfprotov5.DiagnosticSeverityWarning,
270-
Summary: "Incomplete List Result",
271-
Detail: "The provider did not populate the Resource field in the ListResourceResult. This may be due to the provider not supporting this functionality or an error in the provider's implementation.",
272-
},
273-
},
274-
},
275-
},
276-
},
277242
}
278243

279244
for name, testCase := range testCases {

internal/proto6server/server_listresource_test.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ func TestServerListResource(t *testing.T) {
3131
}
3232

3333
type ThingResource struct {
34-
// TODO: how do we feel about this?
3534
ThingResourceIdentity
3635
Name string `tfsdk:"name"`
3736
}
@@ -106,7 +105,11 @@ func TestServerListResource(t *testing.T) {
106105
continue
107106
}
108107

109-
result := req.ToListResult(ctx, resources[name].ThingResourceIdentity, resources[name], name)
108+
result := req.NewListResult()
109+
result.Identity.Set(ctx, resources[name].ThingResourceIdentity)
110+
result.Resource.Set(ctx, resources[name])
111+
result.DisplayName = name
112+
110113
results = append(results, result)
111114
}
112115
resp.Results = slices.Values(results)
@@ -124,7 +127,9 @@ func TestServerListResource(t *testing.T) {
124127
}
125128

126129
r.ListMethod = func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) {
127-
result := req.ToListResult(ctx, resources["plateau"].ThingResourceIdentity, nil, "plateau")
130+
result := req.NewListResult()
131+
result.Identity.Set(ctx, resources["plateau"].ThingResourceIdentity)
132+
result.DisplayName = "plateau"
128133

129134
resp.Results = slices.Values([]list.ListResult{result})
130135
}
@@ -264,6 +269,7 @@ func TestServerListResource(t *testing.T) {
264269
{
265270
DisplayName: "plateau",
266271
Identity: expectedResourceIdentities["plateau"],
272+
Resource: &tfprotov6.DynamicValue{MsgPack: []uint8{0xc0}},
267273
Diagnostics: []*tfprotov6.Diagnostic{
268274
{
269275
Severity: tfprotov6.DiagnosticSeverityWarning,

list/list_resource.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ type ListRequest struct {
103103
ResourceIdentitySchema fwschema.Schema
104104
}
105105

106+
func (r ListRequest) NewListResult() ListResult {
107+
identity := &tfsdk.ResourceIdentity{Schema: r.ResourceIdentitySchema}
108+
resource := &tfsdk.Resource{Schema: r.ResourceSchema}
109+
110+
return ListResult{
111+
DisplayName: "",
112+
Resource: resource,
113+
Identity: identity,
114+
Diagnostics: diag.Diagnostics{},
115+
}
116+
}
117+
106118
// ListResultsStream represents a streaming response to a [ListRequest]. An
107119
// instance of this struct is supplied as an argument to the provider's
108120
// [ListResource.List] function. The provider should set a Results iterator

list/tosdk.go

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)