Skip to content

Commit 74368f9

Browse files
committed
Finish implementing and testing Object.
Finish getting Object and ObjectType implemented and tested.
1 parent e235ad9 commit 74368f9

File tree

2 files changed

+1256
-73
lines changed

2 files changed

+1256
-73
lines changed

types/object.go

+75-65
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ type ObjectType struct {
1414
AttributeTypes map[string]attr.Type
1515
}
1616

17+
// WithAttributesTypes returns a new copy of the type with its
18+
// attribute types set.
19+
func (o ObjectType) WithAttributesTypes(typs map[string]attr.Type) attr.AttributesType {
20+
return ObjectType{
21+
AttributeTypes: typs,
22+
}
23+
}
24+
25+
// AttributesTypes returns the type's attribute types.
26+
func (o ObjectType) AttributesTypes() map[string]attr.Type {
27+
return o.AttributeTypes
28+
}
29+
1730
// TerraformType returns the tftypes.Type that should be used to
1831
// represent this type. This constrains what user input will be
1932
// accepted and what kind of data can be set in state. The framework
@@ -33,11 +46,37 @@ func (o ObjectType) TerraformType(ctx context.Context) tftypes.Type {
3346
// This is meant to convert the tftypes.Value into a more convenient Go
3447
// type for the provider to consume the data with.
3548
func (o ObjectType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
36-
object := &Object{
49+
object := Object{
3750
AttributeTypes: o.AttributeTypes,
3851
}
39-
err := object.SetTerraformValue(ctx, in)
40-
return object, err
52+
if !in.Type().Is(o.TerraformType(ctx)) {
53+
return nil, fmt.Errorf("expected %s, got %s", o.TerraformType(ctx), in.Type())
54+
}
55+
if !in.IsKnown() {
56+
object.Unknown = true
57+
return object, nil
58+
}
59+
if in.IsNull() {
60+
object.Null = true
61+
return object, nil
62+
}
63+
attributes := map[string]attr.Value{}
64+
65+
val := map[string]tftypes.Value{}
66+
err := in.As(&val)
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
for k, v := range val {
72+
a, err := object.AttributeTypes[k].ValueFromTerraform(ctx, v)
73+
if err != nil {
74+
return nil, err
75+
}
76+
attributes[k] = a
77+
}
78+
object.Attributes = attributes
79+
return object, nil
4180
}
4281

4382
// Equal returns true if `candidate` is also an ObjectType and has the same
@@ -82,47 +121,53 @@ type Object struct {
82121
AttributeTypes map[string]attr.Type
83122
}
84123

124+
// ObjectAsOptions is a collection of toggles to control the behavior of
125+
// Object.As.
126+
type ObjectAsOptions struct {
127+
// UnhandledNullAsEmpty controls what happens when As needs to put a
128+
// null value in a type that has no way to preserve that distinction.
129+
// When set to true, the type's empty value will be used. When set to
130+
// false, an error will be returned.
131+
UnhandledNullAsEmpty bool
132+
133+
// UnhandledUnknownAsEmpty controls what happens when As needs to put
134+
// an unknown value in a type that has no way to preserve that
135+
// distinction. When set to true, the type's empty value will be used.
136+
// When set to false, an error will be returned.
137+
UnhandledUnknownAsEmpty bool
138+
}
139+
85140
// As populates `target` with the data in the Object, throwing an error if the
86141
// data cannot be stored in `target`.
87-
func (o *Object) As(ctx context.Context, target interface{}, allowUnhandled bool) error {
142+
func (o Object) As(ctx context.Context, target interface{}, opts ObjectAsOptions) error {
88143
// we need a tftypes.Value for this Object to be able to use it with
89144
// our reflection code
90-
values := map[string]tftypes.Value{}
91-
types := map[string]tftypes.Type{}
92-
for key, attr := range o.Attributes {
93-
val, err := attr.ToTerraformValue(ctx)
94-
if err != nil {
95-
return fmt.Errorf("error getting Terraform value for attribute %q: %w", key, err)
96-
}
97-
typ, ok := o.AttributeTypes[key]
98-
if !ok {
99-
return fmt.Errorf("no AttributeType defined for attribute %q: %w", key, err)
100-
}
101-
types[key] = typ.TerraformType(ctx)
102-
err = tftypes.ValidateValue(typ.TerraformType(ctx), val)
103-
if err != nil {
104-
return fmt.Errorf("error using created Terraform value for element %q: %w", key, err)
105-
}
106-
values[key] = tftypes.NewValue(typ.TerraformType(ctx), val)
145+
obj := ObjectType{AttributeTypes: o.AttributeTypes}
146+
typ := obj.TerraformType(ctx)
147+
val, err := o.ToTerraformValue(ctx)
148+
if err != nil {
149+
return err
150+
}
151+
err = tftypes.ValidateValue(typ, val)
152+
if err != nil {
153+
return err
107154
}
108-
return reflect.Into(ctx, tftypes.NewValue(tftypes.Object{
109-
AttributeTypes: types,
110-
}, values), target, reflect.Options{
111-
UnhandledNullAsEmpty: allowUnhandled,
112-
UnhandledUnknownAsEmpty: allowUnhandled,
155+
return reflect.Into(ctx, obj, tftypes.NewValue(typ, val), target, reflect.Options{
156+
UnhandledNullAsEmpty: opts.UnhandledNullAsEmpty,
157+
UnhandledUnknownAsEmpty: opts.UnhandledUnknownAsEmpty,
113158
})
114159
}
115160

116161
// ToTerraformValue returns the data contained in the AttributeValue as
117162
// a Go type that tftypes.NewValue will accept.
118-
func (o *Object) ToTerraformValue(ctx context.Context) (interface{}, error) {
163+
func (o Object) ToTerraformValue(ctx context.Context) (interface{}, error) {
119164
if o.Unknown {
120165
return tftypes.UnknownValue, nil
121166
}
122167
if o.Null {
123168
return nil, nil
124169
}
125-
var vals map[string]tftypes.Value
170+
vals := map[string]tftypes.Value{}
126171

127172
for k, v := range o.Attributes {
128173
val, err := v.ToTerraformValue(ctx)
@@ -140,8 +185,8 @@ func (o *Object) ToTerraformValue(ctx context.Context) (interface{}, error) {
140185

141186
// Equal must return true if the AttributeValue is considered
142187
// semantically equal to the AttributeValue passed as an argument.
143-
func (o *Object) Equal(c attr.Value) bool {
144-
other, ok := c.(*Object)
188+
func (o Object) Equal(c attr.Value) bool {
189+
other, ok := c.(Object)
145190
if !ok {
146191
return false
147192
}
@@ -178,38 +223,3 @@ func (o *Object) Equal(c attr.Value) bool {
178223

179224
return true
180225
}
181-
182-
// SetTerraformValue updates `o` to reflect the data stored in `in`.
183-
func (o *Object) SetTerraformValue(ctx context.Context, in tftypes.Value) error {
184-
o.Unknown = false
185-
o.Null = false
186-
o.Attributes = nil
187-
if !in.IsKnown() {
188-
o.Unknown = true
189-
return nil
190-
}
191-
if in.IsNull() {
192-
o.Null = true
193-
return nil
194-
}
195-
attributes := map[string]attr.Value{}
196-
197-
val := map[string]tftypes.Value{}
198-
err := in.As(&val)
199-
if err != nil {
200-
return err
201-
}
202-
203-
for k, v := range val {
204-
a, err := o.AttributeTypes[k].ValueFromTerraform(ctx, v)
205-
if err != nil {
206-
return err
207-
}
208-
attributes[k] = a
209-
}
210-
o.Attributes = attributes
211-
// we can't set AttributeTypes, we have no way of knowing them
212-
// callers should set them before or after calling SetTerraformValue
213-
// this is largely because reflection has no way of knowing about them
214-
return nil
215-
}

0 commit comments

Comments
 (0)