Merge pull request #28067 from hashicorp/alisdair/defaults-fix-null-structural-types

functions: Fix defaults for null objects/tuples
This commit is contained in:
Alisdair McDiarmid 2021-03-15 08:03:08 -04:00 committed by GitHub
commit 783936f4c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 0 deletions

View File

@ -94,6 +94,16 @@ func defaultsApply(input, fallback cty.Value) cty.Value {
return v
case wantTy.IsObjectType():
// For structural types, a null input value must be passed through. We
// do not apply default values for missing optional structural values,
// only their contents.
//
// We also pass through the input if the fallback value is null. This
// can happen if the given defaults do not include a value for this
// attribute.
if input.IsNull() || fallback.IsNull() {
return input
}
atys := wantTy.AttributeTypes()
ret := map[string]cty.Value{}
for attr, aty := range atys {
@ -107,6 +117,17 @@ func defaultsApply(input, fallback cty.Value) cty.Value {
return cty.ObjectVal(ret)
case wantTy.IsTupleType():
// For structural types, a null input value must be passed through. We
// do not apply default values for missing optional structural values,
// only their contents.
//
// We also pass through the input if the fallback value is null. This
// can happen if the given defaults do not include a value for this
// attribute.
if input.IsNull() || fallback.IsNull() {
return input
}
l := wantTy.Length()
ret := make([]cty.Value, l)
for i := 0; i < l; i++ {

View File

@ -370,6 +370,79 @@ func TestDefaults(t *testing.T) {
Defaults: cty.StringVal("hello"),
WantErr: `only object types and collections of object types can have defaults applied`,
},
// When applying default values to structural types, null objects or
// tuples in the input should be passed through.
{
Input: cty.ObjectVal(map[string]cty.Value{
"a": cty.NullVal(cty.Object(map[string]cty.Type{
"x": cty.String,
"y": cty.String,
})),
"b": cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})),
}),
Defaults: cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"x": cty.StringVal("hello"),
"y": cty.StringVal("there"),
}),
"b": cty.TupleVal([]cty.Value{
cty.StringVal("how are"),
cty.StringVal("you?"),
}),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"a": cty.NullVal(cty.Object(map[string]cty.Type{
"x": cty.String,
"y": cty.String,
})),
"b": cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})),
}),
},
// When applying default values to structural types, we permit null
// values in the defaults, and just pass through the input value.
{
Input: cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"p": cty.StringVal("xyz"),
"q": cty.StringVal("xyz"),
}),
}),
"b": cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{
cty.NumberIntVal(0),
cty.NumberIntVal(2),
}),
cty.TupleVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(3),
}),
}),
"c": cty.NullVal(cty.String),
}),
Defaults: cty.ObjectVal(map[string]cty.Value{
"c": cty.StringVal("tada"),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"a": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"p": cty.StringVal("xyz"),
"q": cty.StringVal("xyz"),
}),
}),
"b": cty.SetVal([]cty.Value{
cty.TupleVal([]cty.Value{
cty.NumberIntVal(0),
cty.NumberIntVal(2),
}),
cty.TupleVal([]cty.Value{
cty.NumberIntVal(1),
cty.NumberIntVal(3),
}),
}),
"c": cty.StringVal("tada"),
}),
},
// When applying default values to collection types, null collections in the
// input should result in empty collections in the output.
{