Skip to content

Commit a822670

Browse files
author
Ivan De Marino
committed
Allowing ValueFrom to set the resulting converted value on any compatible pointer provided as target
1 parent 0907958 commit a822670

File tree

3 files changed

+71
-34
lines changed

3 files changed

+71
-34
lines changed

tfsdk/value_as.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
"github.com/hashicorp/terraform-plugin-framework/internal/reflect"
1010
)
1111

12-
// ValueAs populates the Go value passed as `target` with
13-
// the contents of `val`, using the reflection rules
14-
// defined for `Get` and `GetAttribute`.
12+
// ValueAs takes the attr.Value `val` and populates the Go value `target` with its content.
13+
//
14+
// This is achieved using reflection rules provided by the internal/reflect package.
1515
func ValueAs(ctx context.Context, val attr.Value, target interface{}) diag.Diagnostics {
1616
if reflect.IsGenericAttrValue(ctx, target) {
1717
*(target.(*attr.Value)) = val

tfsdk/value_from.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@ package tfsdk
22

33
import (
44
"context"
5+
56
"github.com/hashicorp/terraform-plugin-framework/attr"
67
"github.com/hashicorp/terraform-plugin-framework/diag"
78
"github.com/hashicorp/terraform-plugin-framework/internal/reflect"
89
"github.com/hashicorp/terraform-plugin-go/tftypes"
910
)
1011

11-
// ValueFrom populates the attr value passed as `val` with
12-
// the contents of `target`, using the reflection rules
13-
// defined for `Get` and `GetAttribute`.
14-
func ValueFrom(ctx context.Context, typ attr.Type, val *attr.Value, target interface{}) diag.Diagnostics {
15-
v, diags := reflect.FromValue(ctx, typ, target, tftypes.NewAttributePath())
12+
// ValueFrom takes the Go value `val` and populates `target` with an attr.Value,
13+
// based on the type definition provided in `targetType`.
14+
//
15+
// This is achieved using reflection rules provided by the internal/reflect package.
16+
func ValueFrom(ctx context.Context, val interface{}, targetType attr.Type, target interface{}) diag.Diagnostics {
17+
v, diags := reflect.FromValue(ctx, targetType, val, tftypes.NewAttributePath())
1618
if diags.HasError() {
1719
return diags
1820
}
1921

20-
*val = v
21-
return diags
22+
return ValueAs(ctx, v, target)
2223
}

tfsdk/value_from_test.go

+60-24
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import (
88
"github.com/hashicorp/terraform-plugin-framework/attr"
99
"github.com/hashicorp/terraform-plugin-framework/diag"
1010
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/hashicorp/terraform-plugin-go/tftypes"
1112
)
1213

1314
type person struct {
1415
Name types.String `tfsdk:"name"`
1516
Age types.Int64 `tfsdk:"age"`
1617
OptedIn types.Bool `tfsdk:"opted_in"`
18+
Address types.List `tfsdk:"address"`
1719
}
1820

1921
func TestValueFrom(t *testing.T) {
@@ -23,41 +25,76 @@ func TestValueFrom(t *testing.T) {
2325
"name": types.StringType,
2426
"age": types.Int64Type,
2527
"opted_in": types.BoolType,
28+
"address": types.ListType{
29+
ElemType: types.StringType,
30+
},
2631
}
2732

28-
x := person{
33+
mrX := person{
2934
Name: types.String{Value: "x"},
3035
Age: types.Int64{Value: 30},
3136
OptedIn: types.Bool{Value: true},
37+
Address: types.List{
38+
ElemType: types.StringType,
39+
Elems: []attr.Value{
40+
types.String{Value: "1"},
41+
types.String{Value: "Beckford Close"},
42+
types.String{Value: "Gotham"},
43+
},
44+
},
3245
}
3346

34-
y := person{
47+
mrsY := person{
3548
Name: types.String{Value: "y"},
3649
Age: types.Int64{Value: 23},
3750
OptedIn: types.Bool{Value: false},
51+
Address: types.List{
52+
ElemType: types.StringType,
53+
Elems: []attr.Value{
54+
types.String{Value: "2"},
55+
types.String{Value: "Windmill Close"},
56+
types.String{Value: "Smallville"},
57+
},
58+
},
3859
}
3960

40-
xObj := types.Object{
61+
expectedMrXObj := types.Object{
4162
AttrTypes: personAttrTypes,
4263
Attrs: map[string]attr.Value{
4364
"name": types.String{Value: "x", Unknown: false, Null: false},
4465
"age": types.Int64{Value: 30, Unknown: false, Null: false},
4566
"opted_in": types.Bool{Value: true, Unknown: false, Null: false},
67+
"address": types.List{
68+
ElemType: types.StringType,
69+
Elems: []attr.Value{
70+
types.String{Value: "1"},
71+
types.String{Value: "Beckford Close"},
72+
types.String{Value: "Gotham"},
73+
},
74+
},
4675
},
4776
}
4877

49-
yObj := types.Object{
78+
expectedMrsYObj := types.Object{
5079
AttrTypes: personAttrTypes,
5180
Attrs: map[string]attr.Value{
5281
"name": types.String{Value: "y", Unknown: false, Null: false},
5382
"age": types.Int64{Value: 23, Unknown: false, Null: false},
5483
"opted_in": types.Bool{Value: false, Unknown: false, Null: false},
84+
"address": types.List{
85+
ElemType: types.StringType,
86+
Elems: []attr.Value{
87+
types.String{Value: "2"},
88+
types.String{Value: "Windmill Close"},
89+
types.String{Value: "Smallville"},
90+
},
91+
},
5592
},
5693
}
5794

5895
type testCase struct {
59-
target attr.Value
6096
val interface{}
97+
target attr.Value
6198
expected attr.Value
6299
expectedDiags diag.Diagnostics
63100
}
@@ -69,14 +106,14 @@ func TestValueFrom(t *testing.T) {
69106
expected: types.String{Value: "hello", Unknown: false, Null: false},
70107
},
71108
"struct": {
72-
val: x,
109+
val: mrX,
73110
target: types.Object{
74111
AttrTypes: personAttrTypes,
75112
},
76-
expected: xObj,
113+
expected: expectedMrXObj,
77114
},
78115
"list": {
79-
val: []person{x, y},
116+
val: []person{mrX, mrsY},
80117
target: types.List{
81118
ElemType: types.ObjectType{
82119
AttrTypes: personAttrTypes,
@@ -86,31 +123,30 @@ func TestValueFrom(t *testing.T) {
86123
ElemType: types.ObjectType{
87124
AttrTypes: personAttrTypes,
88125
},
89-
Elems: []attr.Value{xObj, yObj},
126+
Elems: []attr.Value{expectedMrXObj, expectedMrsYObj},
127+
},
128+
},
129+
"incompatible-type": {
130+
val: 0,
131+
target: types.String{},
132+
expectedDiags: diag.Diagnostics{
133+
diag.WithPath(
134+
tftypes.NewAttributePath(),
135+
diag.NewErrorDiagnostic(
136+
"Value Conversion Error",
137+
"An unexpected error was encountered trying to convert the Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\ncan't unmarshal tftypes.Number into *string, expected string",
138+
),
139+
),
90140
},
91141
},
92-
//"incompatible-type": {
93-
// val: 0,
94-
// target: types.String{},
95-
// expectedDiags: diag.Diagnostics{
96-
// diag.WithPath(
97-
// tftypes.NewAttributePath(),
98-
// reflect.DiagIntoIncompatibleType{
99-
// Val: tftypes.NewValue(tftypes.String, ""),
100-
// TargetType: goreflect.TypeOf(int64(0)),
101-
// Err: fmt.Errorf("unexpected error was encountered trying to convert from value"),
102-
// },
103-
// ),
104-
// },
105-
//},
106142
}
107143

108144
for name, tc := range tests {
109145
name, tc := name, tc
110146
t.Run(name, func(t *testing.T) {
111147
t.Parallel()
112148

113-
diags := ValueFrom(context.Background(), tc.target.Type(context.Background()), &tc.target, &tc.val)
149+
diags := ValueFrom(context.Background(), tc.val, tc.target.Type(context.Background()), &tc.target)
114150

115151
if diff := cmp.Diff(tc.expectedDiags, diags); diff != "" {
116152
t.Fatalf("Unexpected diff in diagnostics (-wanted, +got): %s", diff)

0 commit comments

Comments
 (0)