edit: I seem to have solved my problem (see bottom of this post)
Hey @bflad, in another thread you asked for feedback about the various *ValueFrom()
methods in 0.15.0
I’ve tried it out, and, while it’s supremely unforgiving to typos in struct tags, I like it! It looks like it’s going to change my provider for the better, but I have some questions…
I’ve got some data I want to represent to the user like this (using HCL for brevity):
parent = {
parent_string = "string"
parent_int = 5
child_1 = {
child_1_string = "string"
child_1_string = 7
}
child_2 = {
child_2_int = 1
child_2_list = ["foo", "bar", "bar"]
}
}
These values do not come from the API this way, but rather need to be gathered from several different data structures.
I now have Go types and methods like this:
type parent struct {
ParentString types.String `tfsdk:"parent_string"`
ParentInt types.Int64 `tfsdk:"parent_int"`
Child1 types.Object `tfsdk:"child_1"`
Child2 types.Object `tfsdk:"child_2"`
}
type child1 struct {
Child1String string `tfsdk:"child_1_string"`
Child1Int int64 `tfsdk:"child_1_int"`
}
func (o child1) attrTypes() map[string]attr.Type {
return map[string]attr.Type{
"child_1_string": types.StringType,
"child_1_int": types.Int64Type,
}
}
type child2 struct {
Child2Val1 int64 `tfsdk:"child_2_int"`
Child2Val2 []string `tfsdk:"child_2_list"`
}
func (o child2) attrTypes() map[string]attr.Type {
return map[string]attr.Type{
"child_2_int": types.StringType,
"child_2_list": types.ListType{ElemType: types.StringType},
}
}
In the Read()
method, I make the required API calls and assemble a parent{}
object:
parentString := getParentStringFromApi()
parentInt := getParentIntFromApi()
ch1 := &child1{}
someFuncWhichFillsInChild1(ch1)
ch2 := &child2{}
someFuncWhichFillsInChild2(ch2)
child1Obj, diags := types.ObjectValueFrom(ctx, ch1.attrTypes(), ch1)
resp.Diagnostics.Append(diags...)
child2Obj, diags := types.ObjectValueFrom(ctx, ch2.attrTypes(), ch2)
resp.Diagnostics.Append(diags...)
foundState := parent{
ParentString: types.StringValue(parentString),
ParentInt: types.Int64Value(parentInt),
Child1: child1Obj,
Child2: child2Obj,
}
diags = resp.State.Set(ctx, foundState)
resp.Diagnostics.Append(diags...)
This kind of thing works great! I’m now able to use native Go structs and quickly/easily populate native terraform types.
Is… Is this the intended use case?
Also, I’m a little stuck:
Some API responses might need special handling: If the API returns 0
or “unassigned”, or an empty list of strings, I might like to set those values null
, for example.
Without access to a types.Object’s Attrs
or a types.List’s Elems
, what’s the right way to nudge go types into appropriately null
types.Value
s?
Thanks!
edit: It looks like any values I want to make null
, can be handled by using a nil-able type (pointer, slice) in the Go struct, and then leaving them nil
at the time *ValueFrom()
is called. I feel silly for not seeing this sooner.