Skip to content

Commit 1433b54

Browse files
authored
Add support for objects (#38)
Add object type.
1 parent f431af6 commit 1433b54

File tree

6 files changed

+1622
-66
lines changed

6 files changed

+1622
-66
lines changed

.changelog/38.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:feature
2+
Added support for Object types.
3+
```

internal/reflect/number.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func Number(ctx context.Context, typ attr.Type, val tftypes.Value, target reflec
177177
} else if acc == big.Below {
178178
if floatResult == math.Inf(-1) || floatResult == -math.MaxFloat64 {
179179
floatResult = -math.MaxFloat64
180-
} else if floatResult == -0.0 || floatResult == -math.SmallestNonzeroFloat64 {
180+
} else if floatResult == -0.0 || floatResult == -math.SmallestNonzeroFloat64 { //nolint:staticcheck
181181
floatResult = math.SmallestNonzeroFloat64
182182
} else {
183183
return target, path.NewErrorf("not sure how to round %s and %f", acc, floatResult)

internal/reflect/struct_test.go

+88-64
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
package reflect_test
22

3-
// TODO: uncomment when Object is merged
4-
/*
5-
func TestReflectObjectIntoStruct_notAnObject(t *testing.T) {
3+
import (
4+
"context"
5+
"math/big"
6+
"reflect"
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-framework/attr"
10+
refl "github.com/hashicorp/terraform-plugin-framework/internal/reflect"
11+
"github.com/hashicorp/terraform-plugin-framework/types"
12+
13+
"github.com/google/go-cmp/cmp"
14+
"github.com/hashicorp/terraform-plugin-go/tftypes"
15+
)
16+
17+
func TestStruct_notAnObject(t *testing.T) {
618
t.Parallel()
719

820
var s struct{}
9-
_, err := refl.ReflectStructFromObject(context.Background(), types.StringType, tftypes.NewValue(tftypes.String, "hello"), reflect.ValueOf(s), refl.Options{}, tftypes.NewAttributePath())
21+
_, err := refl.Struct(context.Background(), types.StringType, tftypes.NewValue(tftypes.String, "hello"), reflect.ValueOf(s), refl.Options{}, tftypes.NewAttributePath())
1022
if err == nil {
1123
t.Error("Expected error, didn't get one")
1224
}
@@ -15,11 +27,11 @@ func TestReflectObjectIntoStruct_notAnObject(t *testing.T) {
1527
}
1628
}
1729

18-
func TestReflectObjectIntoStruct_notAStruct(t *testing.T) {
30+
func TestStruct_notAStruct(t *testing.T) {
1931
t.Parallel()
2032

2133
var s string
22-
_, err := refl.ReflectStructFromObject(context.Background(), types.ObjectType{
34+
_, err := refl.Struct(context.Background(), types.ObjectType{
2335
AttrTypes: map[string]attr.Type{
2436
"a": types.StringType,
2537
},
@@ -38,13 +50,13 @@ func TestReflectObjectIntoStruct_notAStruct(t *testing.T) {
3850
}
3951
}
4052

41-
func TestReflectObjectIntoStruct_objectMissingFields(t *testing.T) {
53+
func TestStruct_objectMissingFields(t *testing.T) {
4254
t.Parallel()
4355

4456
var s struct {
4557
A string `tfsdk:"a"`
4658
}
47-
_, err := refl.ReflectStructFromObject(context.Background(), types.ObjectType{}, tftypes.NewValue(tftypes.Object{
59+
_, err := refl.Struct(context.Background(), types.ObjectType{}, tftypes.NewValue(tftypes.Object{
4860
AttributeTypes: map[string]tftypes.Type{},
4961
}, map[string]tftypes.Value{}), reflect.ValueOf(s), refl.Options{}, tftypes.NewAttributePath())
5062
if err == nil {
@@ -55,11 +67,11 @@ func TestReflectObjectIntoStruct_objectMissingFields(t *testing.T) {
5567
}
5668
}
5769

58-
func TestReflectObjectIntoStruct_structMissingProperties(t *testing.T) {
70+
func TestStruct_structMissingProperties(t *testing.T) {
5971
t.Parallel()
6072

6173
var s struct{}
62-
_, err := refl.ReflectStructFromObject(context.Background(), types.ObjectType{
74+
_, err := refl.Struct(context.Background(), types.ObjectType{
6375
AttrTypes: map[string]attr.Type{
6476
"a": types.StringType,
6577
},
@@ -78,13 +90,13 @@ func TestReflectObjectIntoStruct_structMissingProperties(t *testing.T) {
7890
}
7991
}
8092

81-
func TestReflectObjectIntoStruct_objectMissingFieldsAndStructMissingProperties(t *testing.T) {
93+
func TestStruct_objectMissingFieldsAndStructMissingProperties(t *testing.T) {
8294
t.Parallel()
8395

8496
var s struct {
8597
A string `tfsdk:"a"`
8698
}
87-
_, err := refl.ReflectStructFromObject(context.Background(), types.ObjectType{
99+
_, err := refl.Struct(context.Background(), types.ObjectType{
88100
AttrTypes: map[string]attr.Type{
89101
"a": types.StringType,
90102
},
@@ -103,19 +115,19 @@ func TestReflectObjectIntoStruct_objectMissingFieldsAndStructMissingProperties(t
103115
}
104116
}
105117

106-
func TestReflectObjectIntoStruct_primitives(t *testing.T) {
118+
func TestStruct_primitives(t *testing.T) {
107119
t.Parallel()
108120

109121
var s struct {
110122
A string `tfsdk:"a"`
111123
B *big.Float `tfsdk:"b"`
112124
C bool `tfsdk:"c"`
113125
}
114-
result, err := refl.ReflectStructFromObject(context.Background(), types.ObjectType{
126+
result, err := refl.Struct(context.Background(), types.ObjectType{
115127
AttrTypes: map[string]attr.Type{
116128
"a": types.StringType,
117-
"b": testNumberType{},
118-
"c": testBoolType{},
129+
"b": types.NumberType,
130+
"c": types.BoolType,
119131
},
120132
}, tftypes.NewValue(tftypes.Object{
121133
AttributeTypes: map[string]tftypes.Type{
@@ -143,7 +155,7 @@ func TestReflectObjectIntoStruct_primitives(t *testing.T) {
143155
}
144156
}
145157

146-
func TestReflectObjectIntoStruct_complex(t *testing.T) {
158+
func TestStruct_complex(t *testing.T) {
147159
t.Parallel()
148160

149161
type myStruct struct {
@@ -156,42 +168,46 @@ func TestReflectObjectIntoStruct_complex(t *testing.T) {
156168
A bool `tfsdk:"a"`
157169
Slice []float64 `tfsdk:"slice"`
158170
} `tfsdk:"struct"`
159-
Map map[string][]string `tfsdk:"map"`
160-
Pointer *string `tfsdk:"pointer"`
161-
Unknownable *unknownableString `tfsdk:"unknownable"`
162-
Nullable *nullableString `tfsdk:"nullable"`
163-
AttributeValue *testStringValue `tfsdk:"attribute_value"`
164-
ValueConverter *valueConverter `tfsdk:"value_converter"`
165-
UnhandledNull string `tfsdk:"unhandled_null"`
166-
UnhandledUnknown string `tfsdk:"unhandled_unknown"`
171+
// TODO: uncomment when maps are supported
172+
// Map map[string][]string `tfsdk:"map"`
173+
Pointer *string `tfsdk:"pointer"`
174+
Unknownable *unknownableString `tfsdk:"unknownable"`
175+
Nullable *nullableString `tfsdk:"nullable"`
176+
AttributeValue types.String `tfsdk:"attribute_value"`
177+
ValueConverter *valueConverter `tfsdk:"value_converter"`
178+
UnhandledNull string `tfsdk:"unhandled_null"`
179+
UnhandledUnknown string `tfsdk:"unhandled_unknown"`
167180
}
168181
var s myStruct
169-
result, err := refl.ReflectStructFromObject(context.Background(), types.ObjectType{
182+
result, err := refl.Struct(context.Background(), types.ObjectType{
170183
AttrTypes: map[string]attr.Type{
171-
"slice": testListType{
184+
"slice": types.ListType{
172185
ElemType: types.StringType,
173186
},
174-
"slice_of_structs": testListType{
187+
"slice_of_structs": types.ListType{
175188
ElemType: types.ObjectType{
176189
AttrTypes: map[string]attr.Type{
177190
"a": types.StringType,
178-
"b": testNumberType{},
191+
"b": types.NumberType,
179192
},
180193
},
181194
},
182195
"struct": types.ObjectType{
183196
AttrTypes: map[string]attr.Type{
184-
"a": testBoolType{},
185-
"slice": testListType{
186-
ElemType: testNumberType{},
197+
"a": types.BoolType,
198+
"slice": types.ListType{
199+
ElemType: types.NumberType,
187200
},
188201
},
189202
},
190-
"map": testMapType{
191-
ElemType: testListType{
192-
ElemType: types.StringType,
203+
// TODO: uncomment when maps are supported
204+
/*
205+
"map": testMapType{
206+
ElemType: types.ListType{
207+
ElemType: types.StringType,
208+
},
193209
},
194-
},
210+
*/
195211
"pointer": types.StringType,
196212
"unknownable": types.StringType,
197213
"nullable": types.StringType,
@@ -221,11 +237,14 @@ func TestReflectObjectIntoStruct_complex(t *testing.T) {
221237
},
222238
},
223239
},
224-
"map": tftypes.Map{
225-
AttributeType: tftypes.List{
226-
ElementType: tftypes.String,
240+
// TODO: uncomment when maps are supported
241+
/*
242+
"map": tftypes.Map{
243+
AttributeType: tftypes.List{
244+
ElementType: tftypes.String,
245+
},
227246
},
228-
},
247+
*/
229248
"pointer": tftypes.String,
230249
"unknownable": tftypes.String,
231250
"nullable": tftypes.String,
@@ -286,25 +305,28 @@ func TestReflectObjectIntoStruct_complex(t *testing.T) {
286305
tftypes.NewValue(tftypes.Number, 789),
287306
}),
288307
}),
289-
"map": tftypes.NewValue(tftypes.Map{
290-
AttributeType: tftypes.List{
291-
ElementType: tftypes.String,
292-
},
293-
}, map[string]tftypes.Value{
294-
"colors": tftypes.NewValue(tftypes.List{
295-
ElementType: tftypes.String,
296-
}, []tftypes.Value{
297-
tftypes.NewValue(tftypes.String, "red"),
298-
tftypes.NewValue(tftypes.String, "orange"),
299-
tftypes.NewValue(tftypes.String, "yellow"),
300-
}),
301-
"fruits": tftypes.NewValue(tftypes.List{
302-
ElementType: tftypes.String,
303-
}, []tftypes.Value{
304-
tftypes.NewValue(tftypes.String, "apple"),
305-
tftypes.NewValue(tftypes.String, "banana"),
308+
// TODO: uncomment when maps are supported
309+
/*
310+
"map": tftypes.NewValue(tftypes.Map{
311+
AttributeType: tftypes.List{
312+
ElementType: tftypes.String,
313+
},
314+
}, map[string]tftypes.Value{
315+
"colors": tftypes.NewValue(tftypes.List{
316+
ElementType: tftypes.String,
317+
}, []tftypes.Value{
318+
tftypes.NewValue(tftypes.String, "red"),
319+
tftypes.NewValue(tftypes.String, "orange"),
320+
tftypes.NewValue(tftypes.String, "yellow"),
321+
}),
322+
"fruits": tftypes.NewValue(tftypes.List{
323+
ElementType: tftypes.String,
324+
}, []tftypes.Value{
325+
tftypes.NewValue(tftypes.String, "apple"),
326+
tftypes.NewValue(tftypes.String, "banana"),
327+
}),
306328
}),
307-
}),
329+
*/
308330
"pointer": tftypes.NewValue(tftypes.String, "pointed"),
309331
"unknownable": tftypes.NewValue(tftypes.String, tftypes.UnknownValue),
310332
"nullable": tftypes.NewValue(tftypes.String, nil),
@@ -343,18 +365,21 @@ func TestReflectObjectIntoStruct_complex(t *testing.T) {
343365
A: true,
344366
Slice: []float64{123, 456, 789},
345367
},
346-
Map: map[string][]string{
347-
"colors": {"red", "orange", "yellow"},
348-
"fruits": {"apple", "banana"},
349-
},
368+
// TODO: uncomment when maps are supported
369+
/*
370+
Map: map[string][]string{
371+
"colors": {"red", "orange", "yellow"},
372+
"fruits": {"apple", "banana"},
373+
},
374+
*/
350375
Pointer: &str,
351376
Unknownable: &unknownableString{
352377
Unknown: true,
353378
},
354379
Nullable: &nullableString{
355380
Null: true,
356381
},
357-
AttributeValue: &testStringValue{
382+
AttributeValue: types.String{
358383
Unknown: true,
359384
},
360385
ValueConverter: &valueConverter{
@@ -367,4 +392,3 @@ func TestReflectObjectIntoStruct_complex(t *testing.T) {
367392
t.Errorf("Didn't get expected value. Diff (+ is expected, - is result): %s", diff)
368393
}
369394
}
370-
*/

types/list.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func (l List) ToTerraformValue(ctx context.Context) (interface{}, error) {
157157
}
158158
err = tftypes.ValidateValue(l.ElemType.TerraformType(ctx), val)
159159
if err != nil {
160-
return nil, err
160+
return nil, fmt.Errorf("error validating terraform type: %w", err)
161161
}
162162
vals = append(vals, tftypes.NewValue(l.ElemType.TerraformType(ctx), val))
163163
}

0 commit comments

Comments
 (0)