diff --git a/internal/fwserver/server_listresource.go b/internal/fwserver/server_listresource.go index 37adb9ff3..f9bd2a211 100644 --- a/internal/fwserver/server_listresource.go +++ b/internal/fwserver/server_listresource.go @@ -127,7 +127,7 @@ func processListResult(req list.ListRequest, result list.ListResult) ListResult return ListResult(result) } - if result.Identity == nil { // TODO: is result.Identity.Raw.IsNull() a practical concern? + if result.Identity == nil || result.Identity.Raw.IsNull() { return ListResultError( "Incomplete List Result", "The provider did not populate the Identity field in the ListResourceResult. This may be due to an error in the provider's implementation.", @@ -135,7 +135,7 @@ func processListResult(req list.ListRequest, result list.ListResult) ListResult } if req.IncludeResource { - if result.Resource == nil { // TODO: is result.Resource.Raw.IsNull() a practical concern? + if result.Resource == nil || result.Resource.Raw.IsNull() { result.Diagnostics.AddWarning( "Incomplete List Result", "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.", diff --git a/internal/fwserver/server_listresource_test.go b/internal/fwserver/server_listresource_test.go index f404d29fa..161ff2f87 100644 --- a/internal/fwserver/server_listresource_test.go +++ b/internal/fwserver/server_listresource_test.go @@ -214,6 +214,38 @@ func TestServerListResource(t *testing.T) { }, }, }, + "error-on-null-resource-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ListRequest{ + Config: &tfsdk.Config{}, + ListResource: &testprovider.ListResource{ + ListMethod: func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) { + resp.Results = slices.Values([]list.ListResult{ + { + Identity: &tfsdk.ResourceIdentity{}, + Resource: &tfsdk.Resource{ + Schema: testSchema, + Raw: testResourceValue1, + }, + DisplayName: "Test Resource 1", + }, + }) + }, + }, + }, + expectedStreamEvents: []fwserver.ListResult{ + { + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Incomplete List Result", + "The provider did not populate the Identity field in the ListResourceResult. This may be due to an error in the provider's implementation.", + ), + }, + }, + }, + }, "warning-on-missing-resource": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/proto5server/server_listresource_test.go b/internal/proto5server/server_listresource_test.go index 69cc206c9..faefd65ef 100644 --- a/internal/proto5server/server_listresource_test.go +++ b/internal/proto5server/server_listresource_test.go @@ -106,7 +106,11 @@ func TestServerListResource(t *testing.T) { continue } - result := req.ToListResult(ctx, resources[name].ThingResourceIdentity, resources[name], name) + result := req.NewListResult() + result.Identity.Set(ctx, resources[name].ThingResourceIdentity) + result.Resource.Set(ctx, resources[name]) + result.DisplayName = name + results = append(results, result) } resp.Results = slices.Values(results) @@ -124,7 +128,9 @@ func TestServerListResource(t *testing.T) { } r.ListMethod = func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) { - result := req.ToListResult(ctx, resources["plateau"].ThingResourceIdentity, nil, "plateau") + result := req.NewListResult() + result.Identity.Set(ctx, resources["plateau"].ThingResourceIdentity) + result.DisplayName = "plateau" resp.Results = slices.Values([]list.ListResult{result}) } @@ -264,6 +270,7 @@ func TestServerListResource(t *testing.T) { { DisplayName: "plateau", Identity: expectedResourceIdentities["plateau"], + Resource: &tfprotov5.DynamicValue{MsgPack: []uint8{0xc0}}, Diagnostics: []*tfprotov5.Diagnostic{ { Severity: tfprotov5.DiagnosticSeverityWarning, diff --git a/internal/proto6server/server_listresource_test.go b/internal/proto6server/server_listresource_test.go index 162666484..695c7d8ab 100644 --- a/internal/proto6server/server_listresource_test.go +++ b/internal/proto6server/server_listresource_test.go @@ -106,7 +106,11 @@ func TestServerListResource(t *testing.T) { continue } - result := req.ToListResult(ctx, resources[name].ThingResourceIdentity, resources[name], name) + result := req.NewListResult() + result.Identity.Set(ctx, resources[name].ThingResourceIdentity) + result.Resource.Set(ctx, resources[name]) + result.DisplayName = name + results = append(results, result) } resp.Results = slices.Values(results) @@ -124,7 +128,9 @@ func TestServerListResource(t *testing.T) { } r.ListMethod = func(ctx context.Context, req list.ListRequest, resp *list.ListResultsStream) { - result := req.ToListResult(ctx, resources["plateau"].ThingResourceIdentity, nil, "plateau") + result := req.NewListResult() + result.Identity.Set(ctx, resources["plateau"].ThingResourceIdentity) + result.DisplayName = "plateau" resp.Results = slices.Values([]list.ListResult{result}) } @@ -264,6 +270,7 @@ func TestServerListResource(t *testing.T) { { DisplayName: "plateau", Identity: expectedResourceIdentities["plateau"], + Resource: &tfprotov6.DynamicValue{MsgPack: []uint8{0xc0}}, Diagnostics: []*tfprotov6.Diagnostic{ { Severity: tfprotov6.DiagnosticSeverityWarning, diff --git a/list/list_resource.go b/list/list_resource.go index 8e793dc70..9779fcc47 100644 --- a/list/list_resource.go +++ b/list/list_resource.go @@ -103,6 +103,18 @@ type ListRequest struct { ResourceIdentitySchema fwschema.Schema } +func (r ListRequest) NewListResult() ListResult { + identity := &tfsdk.ResourceIdentity{Schema: r.ResourceIdentitySchema} + resource := &tfsdk.Resource{Schema: r.ResourceSchema} + + return ListResult{ + DisplayName: "", + Resource: resource, + Identity: identity, + Diagnostics: diag.Diagnostics{}, + } +} + // ListResultsStream represents a streaming response to a [ListRequest]. An // instance of this struct is supplied as an argument to the provider's // [ListResource.List] function. The provider should set a Results iterator diff --git a/list/tosdk.go b/list/tosdk.go deleted file mode 100644 index cabf10bf9..000000000 --- a/list/tosdk.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package list - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/tfsdk" -) - -func (r ListRequest) ToResource(ctx context.Context, val any) (*tfsdk.Resource, diag.Diagnostics) { - resource := &tfsdk.Resource{Schema: r.ResourceSchema} - diags := resource.Set(ctx, val) - return resource, diags -} - -func (r ListRequest) ToIdentity(ctx context.Context, val any) (*tfsdk.ResourceIdentity, diag.Diagnostics) { - identity := &tfsdk.ResourceIdentity{Schema: r.ResourceIdentitySchema} - diags := identity.Set(ctx, val) - - return identity, diags -} - -func (r ListRequest) ToListResult(ctx context.Context, identityVal any, resourceVal any, displayName string) ListResult { - allDiags := diag.Diagnostics{} - - identity, diags := r.ToIdentity(ctx, identityVal) - allDiags.Append(diags...) - if diags.HasError() { - return ListResult{Diagnostics: allDiags} - } - - var resource *tfsdk.Resource - if r.IncludeResource && resourceVal != nil { - resource, diags = r.ToResource(ctx, resourceVal) - allDiags.Append(diags...) - if diags.HasError() { - return ListResult{Diagnostics: allDiags} - } - } - - return ListResult{ - DisplayName: displayName, - Resource: resource, - Identity: identity, - Diagnostics: allDiags, - } -}