diff --git a/pf/internal/schemashim/block_schema.go b/pf/internal/schemashim/block_schema.go index bf01f5543..28768fa1b 100644 --- a/pf/internal/schemashim/block_schema.go +++ b/pf/internal/schemashim/block_schema.go @@ -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" @@ -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 { diff --git a/pf/internal/schemashim/custom_type_test.go b/pf/internal/schemashim/custom_type_test.go index d2006e1bd..8abbbae3a 100644 --- a/pf/internal/schemashim/custom_type_test.go +++ b/pf/internal/schemashim/custom_type_test.go @@ -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" @@ -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}} +}