Skip to content

Commit

Permalink
Recognize custom list and set types (#1525)
Browse files Browse the repository at this point in the history
In recent AWS versions resources such as aws_resourceexplorer2_view
introduce custom list types, which causes tfgen panics for the bridged
provider. This change extends the schema recognizer to accept custom
list and set types.

Inspiration for the test taken from:

https://github.com/hashicorp/terraform-provider-aws/blob/648f3d705be10da8b6df6e286be91bdfe32eccad/internal/service/resourceexplorer2/view.go#L83
  • Loading branch information
t0yv0 authored Nov 15, 2023
1 parent 0ac62de commit 7498935
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 18 deletions.
42 changes: 26 additions & 16 deletions pf/internal/schemashim/block_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ package schemashim

import (
"fmt"

bridge "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"

"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"

"github.com/pulumi/pulumi-terraform-bridge/pf/internal/pfutils"
Expand Down Expand Up @@ -72,24 +73,33 @@ func (s *blockSchema) Elem() interface{} {
return r
}

switch tt := s.block.Type().(type) {
case types.ListType:
r, ok := asObjectType(tt.ElemType)
if !ok {
panic(fmt.Errorf("List-nested block expect an ObjectTypeable "+
"block.Type().ElemType, but got %v", tt.ElemType))
if _, ok := s.block.Type().(basetypes.ListTypable); ok {
if twet, ok := s.block.Type().(attr.TypeWithElementType); ok {
r, ok := asObjectType(twet.ElementType())
if !ok {
panic(fmt.Errorf("List-nested block expect an ObjectTypeable "+
"block.Type().ElemType, but got %v", s.block.Type()))
}
return r
}
return r
case types.SetType:
r, ok := asObjectType(tt.ElemType)
if !ok {
panic(fmt.Errorf("Set-nested block expect an ObjectTypeable "+
"block.Type().ElemType, but got %v", tt.ElemType))
panic(fmt.Errorf("List-nested block has a ListTypeable type that does not implement "+
"TypeWithElementType: %v", s.block.Type()))
}

if _, ok := s.block.Type().(basetypes.SetTypable); ok {
if twet, ok := s.block.Type().(attr.TypeWithElementType); ok {
r, ok := asObjectType(twet.ElementType())
if !ok {
panic(fmt.Errorf("Set-nested block expect an ObjectTypeable "+
"block.Type().ElemType, but got %v", twet.ElementType()))
}
return r
}
return r
default:
panic(fmt.Errorf("block.Type()==%v is not supported for blocks", t))
panic(fmt.Errorf("List-nested block has a SetTypeable type that does not implement "+
"TypeWithElementType: %v", s.block.Type()))
}

panic(fmt.Errorf("block.Type()==%v is not supported for blocks", t))
}

func (s *blockSchema) Optional() bool {
Expand Down
85 changes: 83 additions & 2 deletions pf/internal/schemashim/custom_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
package schemashim

import (
"context"
"testing"

"github.com/stretchr/testify/assert"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/stretchr/testify/assert"

"github.com/pulumi/pulumi-terraform-bridge/pf/internal/pfutils"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
Expand Down Expand Up @@ -59,3 +60,83 @@ func TestCustomTypeEmbeddingObjectType(t *testing.T) {
create := shimmed.Elem().(shim.Resource).Schema().Get("create")
assert.Equal(t, shim.TypeString, create.Type())
}

func TestCustomListType(t *testing.T) {
ctx := context.Background()

raw := schema.ListNestedBlock{
CustomType: newListNestedObjectTypeOf[searchFilterModel](ctx, types.ObjectType{
AttrTypes: map[string]attr.Type{
"filter_string": basetypes.StringType{},
},
}),
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"filter_string": schema.StringAttribute{
Required: true,
},
},
},
}

shimmed := &blockSchema{"key", pfutils.FromBlockLike(raw)}
assert.Equal(t, shim.TypeList, shimmed.Type())
assert.NotNil(t, shimmed.Elem())
_, isPseudoResource := shimmed.Elem().(shim.Resource)
assert.Truef(t, isPseudoResource, "expected shim.Elem() to be of type shim.Resource, encoding an object type")

create := shimmed.Elem().(shim.Resource).Schema().Get("filter_string")
assert.Equal(t, shim.TypeString, create.Type())
}

func TestCustomSetType(t *testing.T) {
ctx := context.Background()

raw := schema.SetNestedBlock{
CustomType: newSetNestedObjectTypeOf[searchFilterModel](ctx, types.ObjectType{
AttrTypes: map[string]attr.Type{
"filter_string": basetypes.StringType{},
},
}),
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"filter_string": schema.StringAttribute{
Required: true,
},
},
},
}

shimmed := &blockSchema{"key", pfutils.FromBlockLike(raw)}
assert.Equal(t, shim.TypeSet, shimmed.Type())
assert.NotNil(t, shimmed.Elem())
_, isPseudoResource := shimmed.Elem().(shim.Resource)
assert.Truef(t, isPseudoResource, "expected shim.Elem() to be of type shim.Resource, encoding an object type")

create := shimmed.Elem().(shim.Resource).Schema().Get("filter_string")
assert.Equal(t, shim.TypeString, create.Type())
}

type searchFilterModel struct {
FilterString types.String `tfsdk:"filter_string"`
}

type listNestedObjectTypeOf[T any] struct {
basetypes.ListType
}

type setNestedObjectTypeOf[T any] struct {
basetypes.SetType
}

var (
_ basetypes.ListTypable = (*listNestedObjectTypeOf[struct{}])(nil)
)

func newListNestedObjectTypeOf[T any](ctx context.Context, elemType attr.Type) listNestedObjectTypeOf[T] {
return listNestedObjectTypeOf[T]{basetypes.ListType{ElemType: elemType}}
}

func newSetNestedObjectTypeOf[T any](ctx context.Context, elemType attr.Type) setNestedObjectTypeOf[T] {
return setNestedObjectTypeOf[T]{basetypes.SetType{ElemType: elemType}}
}

0 comments on commit 7498935

Please sign in to comment.