Skip to content

Commit 65651f6

Browse files
committed
ListResource: null handling
1 parent 7dce0b7 commit 65651f6

File tree

6 files changed

+95
-103
lines changed

6 files changed

+95
-103
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: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,36 @@ func TestServerListResource(t *testing.T) {
206206
expectedStreamEvents: []fwserver.ListResult{
207207
{
208208
Diagnostics: diag.Diagnostics{
209-
diag.NewErrorDiagnostic(
210-
"Incomplete List Result",
211-
"The provider did not populate the Identity field in the ListResourceResult. This is always a problem with the provider. Please report this to the provider developer.",
212-
),
209+
diag.NewErrorDiagnostic("Incomplete List Result", "..."),
210+
},
211+
},
212+
},
213+
},
214+
"error-on-null-resource-identity": {
215+
server: &fwserver.Server{
216+
Provider: &testprovider.Provider{},
217+
},
218+
request: &fwserver.ListRequest{
219+
Config: &tfsdk.Config{},
220+
ListResource: &testprovider.ListResource{
221+
ListMethod: func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) {
222+
resp.Results = slices.Values([]list.ListResult{
223+
{
224+
Identity: &tfsdk.ResourceIdentity{},
225+
Resource: &tfsdk.Resource{
226+
Schema: testSchema,
227+
Raw: testResourceValue1,
228+
},
229+
DisplayName: "Test Resource 1",
230+
},
231+
})
232+
},
233+
},
234+
},
235+
expectedStreamEvents: []fwserver.ListResult{
236+
{
237+
Diagnostics: diag.Diagnostics{
238+
diag.NewErrorDiagnostic("Incomplete List Result", "..."),
213239
},
214240
},
215241
},
@@ -244,10 +270,43 @@ func TestServerListResource(t *testing.T) {
244270
},
245271
DisplayName: "Test Resource 1",
246272
Diagnostics: diag.Diagnostics{
247-
diag.NewWarningDiagnostic(
248-
"Incomplete List Result",
249-
"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.",
250-
),
273+
diag.NewWarningDiagnostic("Incomplete List Result", "..."),
274+
},
275+
},
276+
},
277+
},
278+
"warning-on-null-resource": {
279+
server: &fwserver.Server{
280+
Provider: &testprovider.Provider{},
281+
},
282+
request: &fwserver.ListRequest{
283+
Config: &tfsdk.Config{},
284+
IncludeResource: true,
285+
ListResource: &testprovider.ListResource{
286+
ListMethod: func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) {
287+
resp.Results = slices.Values([]list.ListResult{
288+
{
289+
Identity: &tfsdk.ResourceIdentity{
290+
Schema: testIdentitySchema,
291+
Raw: testIdentityValue1,
292+
},
293+
Resource: &tfsdk.Resource{},
294+
DisplayName: "Test Resource 1",
295+
},
296+
})
297+
},
298+
},
299+
},
300+
expectedStreamEvents: []fwserver.ListResult{
301+
{
302+
Identity: &tfsdk.ResourceIdentity{
303+
Schema: testIdentitySchema,
304+
Raw: testIdentityValue1,
305+
},
306+
Resource: &tfsdk.Resource{},
307+
DisplayName: "Test Resource 1",
308+
Diagnostics: diag.Diagnostics{
309+
diag.NewWarningDiagnostic("Incomplete List Result", "..."),
251310
},
252311
},
253312
},

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)