Skip to content

Commit dfb09a6

Browse files
authored
tfsdk: Prevent configuration handling error when Schema contains Blocks (#371)
Reference: #328 Previously the framework could return errors similar to the following: ``` Error: Error parsing config The provider had a problem parsing the config. Report this to the provider developer: AttributeName("output").ElementKeyInt(0): error decoding object; expected 0 attributes, got 5 ``` New unit testing failures before fix: ``` --- FAIL: TestBlockAttributeType (0.00s) --- FAIL: TestBlockAttributeType/NestingMode-Set (0.00s) /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:103: unexpected difference: types.SetType( - s`types.SetType[types.ObjectType["test_attribute":types.StringType]]`, + s`types.SetType[types.ObjectType["test_attribute":types.StringType, "test_block":types.SetType[types.ObjectType["test_block_attribute":types.StringType]]]]`, ) --- FAIL: TestBlockAttributeType/NestingMode-List (0.00s) /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:103: unexpected difference: types.ListType( - s`types.ListType[types.ObjectType["test_attribute":types.StringType]]`, + s`types.ListType[types.ObjectType["test_attribute":types.StringType, "test_block":types.ListType[types.ObjectType["test_block_attribute":types.StringType]]]]`, ) --- FAIL: TestBlockTerraformType (0.00s) --- FAIL: TestBlockTerraformType/NestingMode-List (0.00s) /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:199: unexpected difference: tftypes.List( - s`tftypes.List[tftypes.Object["test_attribute":tftypes.String]]`, + s`tftypes.List[tftypes.Object["test_attribute":tftypes.String, "test_block":tftypes.List[tftypes.Object["test_block_attribute":tftypes.String]]]]`, ) --- FAIL: TestBlockTerraformType/NestingMode-Set (0.00s) /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:199: unexpected difference: tftypes.Set( - s`tftypes.Set[tftypes.Object["test_attribute":tftypes.String]]`, + s`tftypes.Set[tftypes.Object["test_attribute":tftypes.String, "test_block":tftypes.Set[tftypes.Object["test_block_attribute":tftypes.String]]]]`, ) ```
1 parent c76b9a4 commit dfb09a6

File tree

4 files changed

+572
-1
lines changed

4 files changed

+572
-1
lines changed

.changelog/371.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
tfsdk: Prevented configuration handling error when `Schema` contained `Blocks`
3+
```

tfsdk/attribute_test.go

+365
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
package tfsdk
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
"github.com/hashicorp/terraform-plugin-framework/attr"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
"github.com/hashicorp/terraform-plugin-go/tftypes"
11+
)
12+
13+
func TestAttributeAttributeType(t *testing.T) {
14+
t.Parallel()
15+
16+
testCases := map[string]struct {
17+
attribute Attribute
18+
expected attr.Type
19+
}{
20+
"Attributes-ListNestedAttributes": {
21+
attribute: Attribute{
22+
Attributes: ListNestedAttributes(map[string]Attribute{
23+
"test_nested_attribute": {
24+
Required: true,
25+
Type: types.StringType,
26+
},
27+
}),
28+
Required: true,
29+
},
30+
expected: types.ListType{
31+
ElemType: types.ObjectType{
32+
AttrTypes: map[string]attr.Type{
33+
"test_nested_attribute": types.StringType,
34+
},
35+
},
36+
},
37+
},
38+
"Attributes-MapNestedAttributes": {
39+
attribute: Attribute{
40+
Attributes: MapNestedAttributes(map[string]Attribute{
41+
"test_nested_attribute": {
42+
Required: true,
43+
Type: types.StringType,
44+
},
45+
}),
46+
Required: true,
47+
},
48+
expected: types.MapType{
49+
ElemType: types.ObjectType{
50+
AttrTypes: map[string]attr.Type{
51+
"test_nested_attribute": types.StringType,
52+
},
53+
},
54+
},
55+
},
56+
"Attributes-SetNestedAttributes": {
57+
attribute: Attribute{
58+
Attributes: SetNestedAttributes(map[string]Attribute{
59+
"test_nested_attribute": {
60+
Required: true,
61+
Type: types.StringType,
62+
},
63+
}),
64+
Required: true,
65+
},
66+
expected: types.SetType{
67+
ElemType: types.ObjectType{
68+
AttrTypes: map[string]attr.Type{
69+
"test_nested_attribute": types.StringType,
70+
},
71+
},
72+
},
73+
},
74+
"Attributes-SingleNestedAttributes": {
75+
attribute: Attribute{
76+
Attributes: SingleNestedAttributes(map[string]Attribute{
77+
"test_nested_attribute": {
78+
Required: true,
79+
Type: types.StringType,
80+
},
81+
}),
82+
Required: true,
83+
},
84+
expected: types.ObjectType{
85+
AttrTypes: map[string]attr.Type{
86+
"test_nested_attribute": types.StringType,
87+
},
88+
},
89+
},
90+
"Type-BoolType": {
91+
attribute: Attribute{
92+
Required: true,
93+
Type: types.BoolType,
94+
},
95+
expected: types.BoolType,
96+
},
97+
"Type-Float64Type": {
98+
attribute: Attribute{
99+
Required: true,
100+
Type: types.Float64Type,
101+
},
102+
expected: types.Float64Type,
103+
},
104+
"Type-Int64Type": {
105+
attribute: Attribute{
106+
Required: true,
107+
Type: types.Int64Type,
108+
},
109+
expected: types.Int64Type,
110+
},
111+
"Type-ListType": {
112+
attribute: Attribute{
113+
Required: true,
114+
Type: types.ListType{
115+
ElemType: types.StringType,
116+
},
117+
},
118+
expected: types.ListType{
119+
ElemType: types.StringType,
120+
},
121+
},
122+
"Type-MapType": {
123+
attribute: Attribute{
124+
Required: true,
125+
Type: types.MapType{
126+
ElemType: types.StringType,
127+
},
128+
},
129+
expected: types.MapType{
130+
ElemType: types.StringType,
131+
},
132+
},
133+
"Type-ObjectType": {
134+
attribute: Attribute{
135+
Required: true,
136+
Type: types.ObjectType{
137+
AttrTypes: map[string]attr.Type{
138+
"test_object_attribute": types.StringType,
139+
},
140+
},
141+
},
142+
expected: types.ObjectType{
143+
AttrTypes: map[string]attr.Type{
144+
"test_object_attribute": types.StringType,
145+
},
146+
},
147+
},
148+
"Type-NumberType": {
149+
attribute: Attribute{
150+
Required: true,
151+
Type: types.NumberType,
152+
},
153+
expected: types.NumberType,
154+
},
155+
"Type-SetType": {
156+
attribute: Attribute{
157+
Required: true,
158+
Type: types.SetType{
159+
ElemType: types.StringType,
160+
},
161+
},
162+
expected: types.SetType{
163+
ElemType: types.StringType,
164+
},
165+
},
166+
"Type-StringType": {
167+
attribute: Attribute{
168+
Required: true,
169+
Type: types.StringType,
170+
},
171+
expected: types.StringType,
172+
},
173+
}
174+
175+
for name, testCase := range testCases {
176+
name, testCase := name, testCase
177+
178+
t.Run(name, func(t *testing.T) {
179+
t.Parallel()
180+
181+
got := testCase.attribute.attributeType()
182+
183+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
184+
t.Errorf("unexpected difference: %s", diff)
185+
}
186+
})
187+
}
188+
}
189+
190+
func TestAttributeTerraformType(t *testing.T) {
191+
t.Parallel()
192+
193+
testCases := map[string]struct {
194+
attribute Attribute
195+
expected tftypes.Type
196+
}{
197+
"Attributes-ListNestedAttributes": {
198+
attribute: Attribute{
199+
Attributes: ListNestedAttributes(map[string]Attribute{
200+
"test_nested_attribute": {
201+
Required: true,
202+
Type: types.StringType,
203+
},
204+
}),
205+
Required: true,
206+
},
207+
expected: tftypes.List{
208+
ElementType: tftypes.Object{
209+
AttributeTypes: map[string]tftypes.Type{
210+
"test_nested_attribute": tftypes.String,
211+
},
212+
},
213+
},
214+
},
215+
"Attributes-MapNestedAttributes": {
216+
attribute: Attribute{
217+
Attributes: MapNestedAttributes(map[string]Attribute{
218+
"test_nested_attribute": {
219+
Required: true,
220+
Type: types.StringType,
221+
},
222+
}),
223+
Required: true,
224+
},
225+
expected: tftypes.Map{
226+
ElementType: tftypes.Object{
227+
AttributeTypes: map[string]tftypes.Type{
228+
"test_nested_attribute": tftypes.String,
229+
},
230+
},
231+
},
232+
},
233+
"Attributes-SetNestedAttributes": {
234+
attribute: Attribute{
235+
Attributes: SetNestedAttributes(map[string]Attribute{
236+
"test_nested_attribute": {
237+
Required: true,
238+
Type: types.StringType,
239+
},
240+
}),
241+
Required: true,
242+
},
243+
expected: tftypes.Set{
244+
ElementType: tftypes.Object{
245+
AttributeTypes: map[string]tftypes.Type{
246+
"test_nested_attribute": tftypes.String,
247+
},
248+
},
249+
},
250+
},
251+
"Attributes-SingleNestedAttributes": {
252+
attribute: Attribute{
253+
Attributes: SingleNestedAttributes(map[string]Attribute{
254+
"test_nested_attribute": {
255+
Required: true,
256+
Type: types.StringType,
257+
},
258+
}),
259+
Required: true,
260+
},
261+
expected: tftypes.Object{
262+
AttributeTypes: map[string]tftypes.Type{
263+
"test_nested_attribute": tftypes.String,
264+
},
265+
},
266+
},
267+
"Type-BoolType": {
268+
attribute: Attribute{
269+
Required: true,
270+
Type: types.BoolType,
271+
},
272+
expected: tftypes.Bool,
273+
},
274+
"Type-Float64Type": {
275+
attribute: Attribute{
276+
Required: true,
277+
Type: types.Float64Type,
278+
},
279+
expected: tftypes.Number,
280+
},
281+
"Type-Int64Type": {
282+
attribute: Attribute{
283+
Required: true,
284+
Type: types.Int64Type,
285+
},
286+
expected: tftypes.Number,
287+
},
288+
"Type-ListType": {
289+
attribute: Attribute{
290+
Required: true,
291+
Type: types.ListType{
292+
ElemType: types.StringType,
293+
},
294+
},
295+
expected: tftypes.List{
296+
ElementType: tftypes.String,
297+
},
298+
},
299+
"Type-MapType": {
300+
attribute: Attribute{
301+
Required: true,
302+
Type: types.MapType{
303+
ElemType: types.StringType,
304+
},
305+
},
306+
expected: tftypes.Map{
307+
ElementType: tftypes.String,
308+
},
309+
},
310+
"Type-NumberType": {
311+
attribute: Attribute{
312+
Required: true,
313+
Type: types.NumberType,
314+
},
315+
expected: tftypes.Number,
316+
},
317+
"Type-ObjectType": {
318+
attribute: Attribute{
319+
Required: true,
320+
Type: types.ObjectType{
321+
AttrTypes: map[string]attr.Type{
322+
"test_object_attribute": types.StringType,
323+
},
324+
},
325+
},
326+
expected: tftypes.Object{
327+
AttributeTypes: map[string]tftypes.Type{
328+
"test_object_attribute": tftypes.String,
329+
},
330+
},
331+
},
332+
"Type-SetType": {
333+
attribute: Attribute{
334+
Required: true,
335+
Type: types.SetType{
336+
ElemType: types.StringType,
337+
},
338+
},
339+
expected: tftypes.Set{
340+
ElementType: tftypes.String,
341+
},
342+
},
343+
"Type-StringType": {
344+
attribute: Attribute{
345+
Required: true,
346+
Type: types.StringType,
347+
},
348+
expected: tftypes.String,
349+
},
350+
}
351+
352+
for name, testCase := range testCases {
353+
name, testCase := name, testCase
354+
355+
t.Run(name, func(t *testing.T) {
356+
t.Parallel()
357+
358+
got := testCase.attribute.terraformType(context.Background())
359+
360+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
361+
t.Errorf("unexpected difference: %s", diff)
362+
}
363+
})
364+
}
365+
}

tfsdk/block.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func (b Block) attributeType() attr.Type {
134134
attrType.AttrTypes[attrName] = attr.attributeType()
135135
}
136136

137-
for blockName, block := range b.Attributes {
137+
for blockName, block := range b.Blocks {
138138
attrType.AttrTypes[blockName] = block.attributeType()
139139
}
140140

0 commit comments

Comments
 (0)