Skip to content

Commit

Permalink
convert: Handle null results correctly for collections of object type…
Browse files Browse the repository at this point in the history
…s with optional attributes
  • Loading branch information
liamcervante committed Oct 19, 2022
1 parent 5597bc1 commit f331651
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 1 deletion.
19 changes: 19 additions & 0 deletions cty/convert/conversion_collection.go
Expand Up @@ -39,6 +39,11 @@ func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
return cty.NilVal, err
}
}

if val.IsNull() {
val = cty.NullVal(val.Type().WithoutOptionalAttributesDeep())
}

elems = append(elems, val)

i++
Expand Down Expand Up @@ -88,6 +93,11 @@ func conversionCollectionToSet(ety cty.Type, conv conversion) conversion {
return cty.NilVal, err
}
}

if val.IsNull() {
val = cty.NullVal(val.Type().WithoutOptionalAttributesDeep())
}

elems = append(elems, val)

i++
Expand Down Expand Up @@ -242,6 +252,11 @@ func conversionTupleToSet(tupleType cty.Type, setEty cty.Type, unsafe bool) conv
return cty.NilVal, err
}
}

if val.IsNull() {
val = cty.NullVal(val.Type().WithoutOptionalAttributesDeep())
}

elems = append(elems, val)

i++
Expand Down Expand Up @@ -524,6 +539,10 @@ func conversionMapToObject(mapType cty.Type, objType cty.Type, unsafe bool) conv
return cty.NilVal, path.NewErrorf("map element type is incompatible with attribute %q: %s", name.AsString(), MismatchMessage(val.Type(), objType.AttributeType(name.AsString())))
}

if val.IsNull() {
val = cty.NullVal(val.Type().WithoutOptionalAttributesDeep())
}

elems[name.AsString()] = val
}

Expand Down
2 changes: 1 addition & 1 deletion cty/convert/public.go
Expand Up @@ -40,7 +40,7 @@ func GetConversionUnsafe(in cty.Type, out cty.Type) Conversion {
// This is a convenience wrapper around calling GetConversionUnsafe and then
// immediately passing the given value to the resulting function.
func Convert(in cty.Value, want cty.Type) (cty.Value, error) {
if in.Type().Equals(want) {
if in.Type().Equals(want.WithoutOptionalAttributesDeep()) {
return in, nil
}

Expand Down
142 changes: 142 additions & 0 deletions cty/convert/public_test.go
Expand Up @@ -1268,6 +1268,148 @@ func TestConvert(t *testing.T) {
})},
),
},
// We should strip optional attributes out of null values in sets, maps,
// lists and tuples.
{
Value: cty.ListVal([]cty.Value{
cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
}),
Type: cty.Set(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
Want: cty.SetVal([]cty.Value{
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
{
Value: cty.TupleVal([]cty.Value{
cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
}),
Type: cty.Set(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
Want: cty.SetVal([]cty.Value{
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
{
Value: cty.SetVal([]cty.Value{
cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
}),
Type: cty.List(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
Want: cty.ListVal([]cty.Value{
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
{
Value: cty.TupleVal([]cty.Value{
cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
}),
Type: cty.List(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
Want: cty.ListVal([]cty.Value{
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
{
Value: cty.ObjectVal(map[string]cty.Value{
"object": cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
}),
Type: cty.Map(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
Want: cty.MapVal(map[string]cty.Value{
"object": cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
{
Value: cty.MapVal(map[string]cty.Value{
"object": cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
}),
Type: cty.Object(map[string]cty.Type{
"object": cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"}),
}),
Want: cty.ObjectVal(map[string]cty.Value{
"object": cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
{
Value: cty.MapVal(map[string]cty.Value{
"object": cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.Number,
}, []string{"a"})),
}),
Type: cty.Map(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
Want: cty.MapVal(map[string]cty.Value{
"object": cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
{
Value: cty.TupleVal([]cty.Value{
cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.Number,
}, []string{"a"})),
}),
Type: cty.Tuple([]cty.Type{
cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"}),
}),
Want: cty.TupleVal([]cty.Value{
cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
// We should strip optional attributes out of types even if they match.
{
Value: cty.MapVal(map[string]cty.Value{
"object": cty.NullVal(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
}),
Type: cty.Map(cty.ObjectWithOptionalAttrs(map[string]cty.Type{
"a": cty.String,
}, []string{"a"})),
Want: cty.MapVal(map[string]cty.Value{
"object": cty.NullVal(cty.Object(map[string]cty.Type{
"a": cty.String,
})),
}),
},
}

for _, test := range tests {
Expand Down

0 comments on commit f331651

Please sign in to comment.