Skip to content

Commit 6a9c372

Browse files
committed
framework changes support list in sdkv2
1 parent 0dfd722 commit 6a9c372

File tree

8 files changed

+530
-16
lines changed

8 files changed

+530
-16
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto5
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
11+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
12+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
13+
"github.com/hashicorp/terraform-plugin-go/tftypes"
14+
)
15+
16+
// IdentitySchema converts a *tfprotov5.ResourceIdentitySchema into a resource/identityschema Schema, used for
17+
// converting raw linked resource identity schemas (from another provider server, such as SDKv2 or terraform-plugin-go)
18+
// into Framework identity schemas.
19+
func IdentitySchema(ctx context.Context, s *tfprotov5.ResourceIdentitySchema) (*identityschema.Schema, error) {
20+
if s == nil {
21+
return nil, nil
22+
}
23+
24+
attrs, err := IdentitySchemaAttributes(ctx, s.IdentityAttributes)
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
return &identityschema.Schema{
30+
// MAINTAINER NOTE: At the moment, there isn't a need to copy all of the data from the protocol identity schema
31+
// to the resource identity schema, just enough data to allow provider developers to read and set data.
32+
Attributes: attrs,
33+
}, nil
34+
}
35+
36+
func IdentitySchemaAttributes(ctx context.Context, protoAttrs []*tfprotov5.ResourceIdentitySchemaAttribute) (map[string]identityschema.Attribute, error) {
37+
attrs := make(map[string]identityschema.Attribute, len(protoAttrs))
38+
for _, protoAttr := range protoAttrs {
39+
// MAINTAINER NOTE: At the moment, there isn't a need to copy all of the data from the protocol identity schema
40+
// to the resource identity schema, just enough data to allow provider developers to read and set data.
41+
switch {
42+
case protoAttr.Type.Is(tftypes.Bool):
43+
attrs[protoAttr.Name] = identityschema.BoolAttribute{
44+
RequiredForImport: protoAttr.RequiredForImport,
45+
OptionalForImport: protoAttr.OptionalForImport,
46+
}
47+
case protoAttr.Type.Is(tftypes.Number):
48+
attrs[protoAttr.Name] = identityschema.NumberAttribute{
49+
RequiredForImport: protoAttr.RequiredForImport,
50+
OptionalForImport: protoAttr.OptionalForImport,
51+
}
52+
case protoAttr.Type.Is(tftypes.String):
53+
attrs[protoAttr.Name] = identityschema.StringAttribute{
54+
RequiredForImport: protoAttr.RequiredForImport,
55+
OptionalForImport: protoAttr.OptionalForImport,
56+
}
57+
case protoAttr.Type.Is(tftypes.List{}):
58+
//nolint:forcetypeassert // Type assertion is guaranteed by the above `(tftypes.Type).Is` function
59+
l := protoAttr.Type.(tftypes.List)
60+
61+
elementType, err := basetypes.TerraformTypeToFrameworkType(l.ElementType)
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
attrs[protoAttr.Name] = identityschema.ListAttribute{
67+
ElementType: elementType,
68+
RequiredForImport: protoAttr.RequiredForImport,
69+
OptionalForImport: protoAttr.OptionalForImport,
70+
}
71+
default:
72+
// MAINTAINER NOTE: Not all terraform types are valid identity attribute types. Framework fully supports
73+
// all of the possible identity attribute types, so any errors here would be invalid protocol identities.
74+
return nil, fmt.Errorf("no supported identity attribute for %q, type: %T", protoAttr.Name, protoAttr.Type)
75+
}
76+
}
77+
78+
return attrs, nil
79+
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package fromproto5
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"github.com/hashicorp/terraform-plugin-framework/attr"
11+
resourceschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
12+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
13+
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
14+
"github.com/hashicorp/terraform-plugin-go/tftypes"
15+
)
16+
17+
// ResourceSchema converts a *tfprotov5.Schema into a resource/schema Schema, used for
18+
// converting raw linked resource schemas (from another provider server, such as SDKv2 or terraform-plugin-go)
19+
// into Framework schemas.
20+
func ResourceSchema(ctx context.Context, s *tfprotov5.Schema) (*resourceschema.Schema, error) {
21+
if s == nil || s.Block == nil {
22+
return nil, nil
23+
}
24+
25+
attrs, err := ResourceSchemaAttributes(ctx, s.Block.Attributes)
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
blocks, err := ResourceSchemaNestedBlocks(ctx, s.Block.BlockTypes)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
return &resourceschema.Schema{
36+
// MAINTAINER NOTE: At the moment, there isn't a need to copy all of the data from the protocol schema
37+
// to the resource schema, just enough data to allow provider developers to read and set data.
38+
Attributes: attrs,
39+
Blocks: blocks,
40+
}, nil
41+
}
42+
43+
func ResourceSchemaAttributes(ctx context.Context, protoAttrs []*tfprotov5.SchemaAttribute) (map[string]resourceschema.Attribute, error) {
44+
attrs := make(map[string]resourceschema.Attribute, len(protoAttrs))
45+
for _, protoAttr := range protoAttrs {
46+
// MAINTAINER NOTE: At the moment, there isn't a need to copy all of the data from the protocol schema
47+
// to the resource schema, just enough data to allow provider developers to read and set data.
48+
switch {
49+
case protoAttr.Type.Is(tftypes.Bool):
50+
attrs[protoAttr.Name] = resourceschema.BoolAttribute{
51+
Required: protoAttr.Required,
52+
Optional: protoAttr.Optional,
53+
Computed: protoAttr.Computed,
54+
WriteOnly: protoAttr.WriteOnly,
55+
Sensitive: protoAttr.Sensitive,
56+
}
57+
case protoAttr.Type.Is(tftypes.Number):
58+
attrs[protoAttr.Name] = resourceschema.NumberAttribute{
59+
Required: protoAttr.Required,
60+
Optional: protoAttr.Optional,
61+
Computed: protoAttr.Computed,
62+
WriteOnly: protoAttr.WriteOnly,
63+
Sensitive: protoAttr.Sensitive,
64+
}
65+
case protoAttr.Type.Is(tftypes.String):
66+
attrs[protoAttr.Name] = resourceschema.StringAttribute{
67+
Required: protoAttr.Required,
68+
Optional: protoAttr.Optional,
69+
Computed: protoAttr.Computed,
70+
WriteOnly: protoAttr.WriteOnly,
71+
Sensitive: protoAttr.Sensitive,
72+
}
73+
case protoAttr.Type.Is(tftypes.DynamicPseudoType):
74+
attrs[protoAttr.Name] = resourceschema.DynamicAttribute{
75+
Required: protoAttr.Required,
76+
Optional: protoAttr.Optional,
77+
Computed: protoAttr.Computed,
78+
WriteOnly: protoAttr.WriteOnly,
79+
Sensitive: protoAttr.Sensitive,
80+
}
81+
case protoAttr.Type.Is(tftypes.List{}):
82+
//nolint:forcetypeassert // Type assertion is guaranteed by the above `(tftypes.Type).Is` function
83+
l := protoAttr.Type.(tftypes.List)
84+
85+
elementType, err := basetypes.TerraformTypeToFrameworkType(l.ElementType)
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
attrs[protoAttr.Name] = resourceschema.ListAttribute{
91+
ElementType: elementType,
92+
Required: protoAttr.Required,
93+
Optional: protoAttr.Optional,
94+
Computed: protoAttr.Computed,
95+
WriteOnly: protoAttr.WriteOnly,
96+
Sensitive: protoAttr.Sensitive,
97+
}
98+
case protoAttr.Type.Is(tftypes.Map{}):
99+
//nolint:forcetypeassert // Type assertion is guaranteed by the above `(tftypes.Type).Is` function
100+
m := protoAttr.Type.(tftypes.Map)
101+
102+
elementType, err := basetypes.TerraformTypeToFrameworkType(m.ElementType)
103+
if err != nil {
104+
return nil, err
105+
}
106+
107+
attrs[protoAttr.Name] = resourceschema.MapAttribute{
108+
ElementType: elementType,
109+
Required: protoAttr.Required,
110+
Optional: protoAttr.Optional,
111+
Computed: protoAttr.Computed,
112+
WriteOnly: protoAttr.WriteOnly,
113+
Sensitive: protoAttr.Sensitive,
114+
}
115+
case protoAttr.Type.Is(tftypes.Set{}):
116+
//nolint:forcetypeassert // Type assertion is guaranteed by the above `(tftypes.Type).Is` function
117+
s := protoAttr.Type.(tftypes.Set)
118+
119+
elementType, err := basetypes.TerraformTypeToFrameworkType(s.ElementType)
120+
if err != nil {
121+
return nil, err
122+
}
123+
124+
attrs[protoAttr.Name] = resourceschema.SetAttribute{
125+
ElementType: elementType,
126+
Required: protoAttr.Required,
127+
Optional: protoAttr.Optional,
128+
Computed: protoAttr.Computed,
129+
Sensitive: protoAttr.Sensitive,
130+
}
131+
case protoAttr.Type.Is(tftypes.Object{}):
132+
//nolint:forcetypeassert // Type assertion is guaranteed by the above `(tftypes.Type).Is` function
133+
o := protoAttr.Type.(tftypes.Object)
134+
135+
attrTypes := make(map[string]attr.Type, len(o.AttributeTypes))
136+
for name, tfType := range o.AttributeTypes {
137+
t, err := basetypes.TerraformTypeToFrameworkType(tfType)
138+
if err != nil {
139+
return nil, err
140+
}
141+
attrTypes[name] = t
142+
}
143+
144+
attrs[protoAttr.Name] = resourceschema.ObjectAttribute{
145+
AttributeTypes: attrTypes,
146+
Required: protoAttr.Required,
147+
Optional: protoAttr.Optional,
148+
Computed: protoAttr.Computed,
149+
WriteOnly: protoAttr.WriteOnly,
150+
Sensitive: protoAttr.Sensitive,
151+
}
152+
default:
153+
// MAINTAINER NOTE: Currently the only type not supported by Framework is a tuple, since there
154+
// is no corresponding attribute to represent it.
155+
//
156+
// https://github.com/hashicorp/terraform-plugin-framework/issues/54
157+
return nil, fmt.Errorf("no supported attribute for %q, type: %T", protoAttr.Name, protoAttr.Type)
158+
}
159+
}
160+
161+
return attrs, nil
162+
}
163+
164+
func ResourceSchemaNestedBlocks(ctx context.Context, protoBlocks []*tfprotov5.SchemaNestedBlock) (map[string]resourceschema.Block, error) {
165+
nestedBlocks := make(map[string]resourceschema.Block, len(protoBlocks))
166+
for _, protoBlock := range protoBlocks {
167+
if protoBlock.Block == nil {
168+
continue
169+
}
170+
171+
attrs, err := ResourceSchemaAttributes(ctx, protoBlock.Block.Attributes)
172+
if err != nil {
173+
return nil, err
174+
}
175+
blocks, err := ResourceSchemaNestedBlocks(ctx, protoBlock.Block.BlockTypes)
176+
if err != nil {
177+
return nil, err
178+
}
179+
180+
switch protoBlock.Nesting {
181+
case tfprotov5.SchemaNestedBlockNestingModeList:
182+
nestedBlocks[protoBlock.TypeName] = resourceschema.ListNestedBlock{
183+
NestedObject: resourceschema.NestedBlockObject{
184+
Attributes: attrs,
185+
Blocks: blocks,
186+
},
187+
}
188+
case tfprotov5.SchemaNestedBlockNestingModeSet:
189+
nestedBlocks[protoBlock.TypeName] = resourceschema.SetNestedBlock{
190+
NestedObject: resourceschema.NestedBlockObject{
191+
Attributes: attrs,
192+
Blocks: blocks,
193+
},
194+
}
195+
case tfprotov5.SchemaNestedBlockNestingModeSingle:
196+
nestedBlocks[protoBlock.TypeName] = resourceschema.SingleNestedBlock{
197+
Attributes: attrs,
198+
Blocks: blocks,
199+
}
200+
default:
201+
// MAINTAINER NOTE: Currently the only block type not supported by Framework is a map nested block, since there
202+
// is no corresponding framework block implementation to represent it.
203+
return nil, fmt.Errorf("no supported block for nesting mode %v in nested block %q", protoBlock.Nesting, protoBlock.TypeName)
204+
}
205+
}
206+
207+
return nestedBlocks, nil
208+
}

0 commit comments

Comments
 (0)