Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions internal/fromproto5/validatelistresourceconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fromproto5

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/list"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
)

// ValidateListResourceConfigRequest returns the *fwserver.ValidateListResourceConfigRequest
// equivalent of a *tfprotov5.ValidateListResourceConfigRequest.
func ValidateListResourceConfigRequest(ctx context.Context, proto5 *tfprotov5.ValidateListResourceConfigRequest, listResource list.ListResource, listResourceSchema fwschema.Schema) (*fwserver.ValidateListResourceConfigRequest, diag.Diagnostics) {
if proto5 == nil {
return nil, nil
}

fw := &fwserver.ValidateListResourceConfigRequest{}

config, diags := Config(ctx, proto5.Config, listResourceSchema)

fw.Config = config
fw.ListResource = listResource

return fw, diags
}
108 changes: 108 additions & 0 deletions internal/fromproto5/validatelistresourceconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fromproto5_test

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto5"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/list"
"github.com/hashicorp/terraform-plugin-framework/list/schema"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

func TestValidateListResourceConfigRequest(t *testing.T) {
t.Parallel()

testProto5Type := tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_attribute": tftypes.String,
},
}

testProto5Value := tftypes.NewValue(testProto5Type, map[string]tftypes.Value{
"test_attribute": tftypes.NewValue(tftypes.String, "test-value"),
})

testProto5DynamicValue, err := tfprotov5.NewDynamicValue(testProto5Type, testProto5Value)

if err != nil {
t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err)
}

testFwSchema := schema.Schema{
Attributes: map[string]schema.Attribute{
"test_attribute": schema.StringAttribute{
Required: true,
},
},
}

testCases := map[string]struct {
input *tfprotov5.ValidateListResourceConfigRequest
listResourceSchema fwschema.Schema
listResource list.ListResource
expected *fwserver.ValidateListResourceConfigRequest
expectedDiagnostics diag.Diagnostics
}{
"nil": {
input: nil,
expected: nil,
},
"empty": {
input: &tfprotov5.ValidateListResourceConfigRequest{},
expected: &fwserver.ValidateListResourceConfigRequest{},
},
"config-missing-schema": {
input: &tfprotov5.ValidateListResourceConfigRequest{
Config: &testProto5DynamicValue,
},
expected: &fwserver.ValidateListResourceConfigRequest{},
expectedDiagnostics: diag.Diagnostics{
diag.NewErrorDiagnostic(
"Unable to Convert Configuration",
"An unexpected error was encountered when converting the configuration from the protocol type. "+
"This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+
"Please report this to the provider developer:\n\n"+
"Missing schema.",
),
},
},
"config": {
input: &tfprotov5.ValidateListResourceConfigRequest{
Config: &testProto5DynamicValue,
},
listResourceSchema: testFwSchema,
expected: &fwserver.ValidateListResourceConfigRequest{
Config: &tfsdk.Config{
Raw: testProto5Value,
Schema: testFwSchema,
},
},
},
}

for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()

got, diags := fromproto5.ValidateListResourceConfigRequest(context.Background(), testCase.input, testCase.listResource, testCase.listResourceSchema)

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}

if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" {
t.Errorf("unexpected diagnostics difference: %s", diff)
}
})
}
}
31 changes: 31 additions & 0 deletions internal/fromproto6/validatelistresourceconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fromproto6

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/list"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)

// ValidateListResourceConfigRequest returns the *fwserver.ValidateListResourceConfigRequest
// equivalent of a *tfprotov6.ValidateListResourceConfigRequest.
func ValidateListResourceConfigRequest(ctx context.Context, proto6 *tfprotov6.ValidateListResourceConfigRequest, listResource list.ListResource, listResourceSchema fwschema.Schema) (*fwserver.ValidateListResourceConfigRequest, diag.Diagnostics) {
if proto6 == nil {
return nil, nil
}

fw := &fwserver.ValidateListResourceConfigRequest{}

config, diags := Config(ctx, proto6.Config, listResourceSchema)

fw.Config = config
fw.ListResource = listResource

return fw, diags
}
108 changes: 108 additions & 0 deletions internal/fromproto6/validatelistresourceconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fromproto6_test

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/internal/fromproto6"
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
"github.com/hashicorp/terraform-plugin-framework/internal/fwserver"
"github.com/hashicorp/terraform-plugin-framework/list"
"github.com/hashicorp/terraform-plugin-framework/list/schema"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

func TestValidateListResourceConfigRequest(t *testing.T) {
t.Parallel()

testProto6Type := tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_attribute": tftypes.String,
},
}

testProto6Value := tftypes.NewValue(testProto6Type, map[string]tftypes.Value{
"test_attribute": tftypes.NewValue(tftypes.String, "test-value"),
})

testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value)

if err != nil {
t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err)
}

testFwSchema := schema.Schema{
Attributes: map[string]schema.Attribute{
"test_attribute": schema.StringAttribute{
Required: true,
},
},
}

testCases := map[string]struct {
input *tfprotov6.ValidateListResourceConfigRequest
listResourceSchema fwschema.Schema
listResource list.ListResource
expected *fwserver.ValidateListResourceConfigRequest
expectedDiagnostics diag.Diagnostics
}{
"nil": {
input: nil,
expected: nil,
},
"empty": {
input: &tfprotov6.ValidateListResourceConfigRequest{},
expected: &fwserver.ValidateListResourceConfigRequest{},
},
"config-missing-schema": {
input: &tfprotov6.ValidateListResourceConfigRequest{
Config: &testProto6DynamicValue,
},
expected: &fwserver.ValidateListResourceConfigRequest{},
expectedDiagnostics: diag.Diagnostics{
diag.NewErrorDiagnostic(
"Unable to Convert Configuration",
"An unexpected error was encountered when converting the configuration from the protocol type. "+
"This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+
"Please report this to the provider developer:\n\n"+
"Missing schema.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 Here's a thought to consider – completely non-blocking for merging this PR.

While we're adding new kinds of schemas, I wonder if this diagnostic is precise enough to be actionable by a provider developer.

"Missing list resource schema for ``random_pet``" seems nice to have here. And also at odds with the universal message in fromproto6.Config().

So I'm curious how this diagnostic reads when it is rendered by the Terraform CLI and whether it includes precise context. If it's not precise, I suggest we adjust the Config diagnostic to be more flexible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this error message is only for our team, and isn't actionable by the provider developer outside of reporting it to us:

// Panic prevention here to simplify the calling implementations.
// This should not happen, but just in case.

The only way we'd hit this error is if there was a bug in Terraform core

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, no action here 🏁

),
},
},
"config": {
input: &tfprotov6.ValidateListResourceConfigRequest{
Config: &testProto6DynamicValue,
},
listResourceSchema: testFwSchema,
expected: &fwserver.ValidateListResourceConfigRequest{
Config: &tfsdk.Config{
Raw: testProto6Value,
Schema: testFwSchema,
},
},
},
}

for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()

got, diags := fromproto6.ValidateListResourceConfigRequest(context.Background(), testCase.input, testCase.listResource, testCase.listResourceSchema)

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}

if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" {
t.Errorf("unexpected diagnostics difference: %s", diff)
}
})
}
}
9 changes: 7 additions & 2 deletions internal/fwserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ package fwserver
import (
"context"
"fmt"
"sync"

"github.com/hashicorp/terraform-plugin-framework/action"
actionschema "github.com/hashicorp/terraform-plugin-framework/action/schema"
"sync"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
Expand Down Expand Up @@ -42,6 +42,11 @@ type Server struct {
// to [ephemeral.ConfigureRequest.ProviderData].
EphemeralResourceConfigureData any

// ListResourceConfigureData is the
// [provider.ConfigureResponse.ListResourceData] field value which is passed
// to [list.ConfigureRequest.ProviderData].
ListResourceConfigureData any

// actionSchemas is the cached Action Schemas for RPCs that need to
// convert configuration data from the protocol. If not found, it will be
// fetched from the Action.Schema() method.
Expand Down
6 changes: 2 additions & 4 deletions internal/fwserver/server_listresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package fwserver

import (
"context"
"errors"
"iter"

"github.com/hashicorp/terraform-plugin-framework/diag"
Expand Down Expand Up @@ -84,12 +83,12 @@ type ListResult struct {
var NoListResults = func(func(ListResult) bool) {}

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

if fwReq.Config == nil {
fwStream.Results = NoListResults
return errors.New("Invalid ListResource request: Config cannot be nil")
return
}

req := list.ListRequest{
Expand All @@ -112,7 +111,6 @@ func (s *Server) ListResource(ctx context.Context, fwReq *ListRequest, fwStream
}

fwStream.Results = processListResults(req, stream.Results)
return nil
}

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