handle NestedTypes in Block.CoerceValue

The CoerceValue code was not updated to handle NestedTypes, and while
none of the new codepaths make use of this method, there are still some
internal uses.
This commit is contained in:
James Bardin 2021-09-14 15:55:20 -04:00
parent 332ea1f233
commit 9029870613
2 changed files with 84 additions and 25 deletions

View File

@ -27,16 +27,19 @@ func (b *Block) CoerceValue(in cty.Value) (cty.Value, error) {
} }
func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) { func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
convType := b.specType()
impliedType := convType.WithoutOptionalAttributesDeep()
switch { switch {
case in.IsNull(): case in.IsNull():
return cty.NullVal(b.ImpliedType()), nil return cty.NullVal(impliedType), nil
case !in.IsKnown(): case !in.IsKnown():
return cty.UnknownVal(b.ImpliedType()), nil return cty.UnknownVal(impliedType), nil
} }
ty := in.Type() ty := in.Type()
if !ty.IsObjectType() { if !ty.IsObjectType() {
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("an object is required") return cty.UnknownVal(impliedType), path.NewErrorf("an object is required")
} }
for name := range ty.AttributeTypes() { for name := range ty.AttributeTypes() {
@ -46,29 +49,32 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
if _, defined := b.BlockTypes[name]; defined { if _, defined := b.BlockTypes[name]; defined {
continue continue
} }
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("unexpected attribute %q", name) return cty.UnknownVal(impliedType), path.NewErrorf("unexpected attribute %q", name)
} }
attrs := make(map[string]cty.Value) attrs := make(map[string]cty.Value)
for name, attrS := range b.Attributes { for name, attrS := range b.Attributes {
attrType := impliedType.AttributeType(name)
attrConvType := convType.AttributeType(name)
var val cty.Value var val cty.Value
switch { switch {
case ty.HasAttribute(name): case ty.HasAttribute(name):
val = in.GetAttr(name) val = in.GetAttr(name)
case attrS.Computed || attrS.Optional: case attrS.Computed || attrS.Optional:
val = cty.NullVal(attrS.Type) val = cty.NullVal(attrType)
default: default:
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("attribute %q is required", name) return cty.UnknownVal(impliedType), path.NewErrorf("attribute %q is required", name)
} }
val, err := attrS.coerceValue(val, append(path, cty.GetAttrStep{Name: name})) val, err := convert.Convert(val, attrConvType)
if err != nil { if err != nil {
return cty.UnknownVal(b.ImpliedType()), err return cty.UnknownVal(impliedType), append(path, cty.GetAttrStep{Name: name}).NewError(err)
} }
attrs[name] = val attrs[name] = val
} }
for typeName, blockS := range b.BlockTypes { for typeName, blockS := range b.BlockTypes {
switch blockS.Nesting { switch blockS.Nesting {
@ -79,7 +85,7 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
val := in.GetAttr(typeName) val := in.GetAttr(typeName)
attrs[typeName], err = blockS.coerceValue(val, append(path, cty.GetAttrStep{Name: typeName})) attrs[typeName], err = blockS.coerceValue(val, append(path, cty.GetAttrStep{Name: typeName}))
if err != nil { if err != nil {
return cty.UnknownVal(b.ImpliedType()), err return cty.UnknownVal(impliedType), err
} }
default: default:
attrs[typeName] = blockS.EmptyValue() attrs[typeName] = blockS.EmptyValue()
@ -100,7 +106,7 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
} }
if !coll.CanIterateElements() { if !coll.CanIterateElements() {
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a list") return cty.UnknownVal(impliedType), path.NewErrorf("must be a list")
} }
l := coll.LengthInt() l := coll.LengthInt()
@ -116,7 +122,7 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
idx, val := it.Element() idx, val := it.Element()
val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: idx})) val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: idx}))
if err != nil { if err != nil {
return cty.UnknownVal(b.ImpliedType()), err return cty.UnknownVal(impliedType), err
} }
elems = append(elems, val) elems = append(elems, val)
} }
@ -141,7 +147,7 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
} }
if !coll.CanIterateElements() { if !coll.CanIterateElements() {
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a set") return cty.UnknownVal(impliedType), path.NewErrorf("must be a set")
} }
l := coll.LengthInt() l := coll.LengthInt()
@ -157,7 +163,7 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
idx, val := it.Element() idx, val := it.Element()
val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: idx})) val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: idx}))
if err != nil { if err != nil {
return cty.UnknownVal(b.ImpliedType()), err return cty.UnknownVal(impliedType), err
} }
elems = append(elems, val) elems = append(elems, val)
} }
@ -182,7 +188,7 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
} }
if !coll.CanIterateElements() { if !coll.CanIterateElements() {
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a map") return cty.UnknownVal(impliedType), path.NewErrorf("must be a map")
} }
l := coll.LengthInt() l := coll.LengthInt()
if l == 0 { if l == 0 {
@ -196,11 +202,11 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
var err error var err error
key, val := it.Element() key, val := it.Element()
if key.Type() != cty.String || key.IsNull() || !key.IsKnown() { if key.Type() != cty.String || key.IsNull() || !key.IsKnown() {
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a map") return cty.UnknownVal(impliedType), path.NewErrorf("must be a map")
} }
val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: key})) val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: key}))
if err != nil { if err != nil {
return cty.UnknownVal(b.ImpliedType()), err return cty.UnknownVal(impliedType), err
} }
elems[key.AsString()] = val elems[key.AsString()] = val
} }
@ -240,11 +246,3 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
return cty.ObjectVal(attrs), nil return cty.ObjectVal(attrs), nil
} }
func (a *Attribute) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
val, err := convert.Convert(in, a.Type)
if err != nil {
return cty.UnknownVal(a.Type), path.NewError(err)
}
return val, nil
}

View File

@ -538,6 +538,67 @@ func TestCoerceValue(t *testing.T) {
}), }),
``, ``,
}, },
"nested types": {
// handle NestedTypes
&Block{
Attributes: map[string]*Attribute{
"foo": {
NestedType: &Object{
Nesting: NestingList,
Attributes: map[string]*Attribute{
"bar": {
Type: cty.String,
Required: true,
},
"baz": {
Type: cty.Map(cty.String),
Optional: true,
},
},
},
Optional: true,
},
"fob": {
NestedType: &Object{
Nesting: NestingSet,
Attributes: map[string]*Attribute{
"bar": {
Type: cty.String,
Optional: true,
},
},
},
Optional: true,
},
},
},
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("beep"),
}),
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("boop"),
}),
}),
}),
cty.ObjectVal(map[string]cty.Value{
"foo": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("beep"),
"baz": cty.NullVal(cty.Map(cty.String)),
}),
cty.ObjectVal(map[string]cty.Value{
"bar": cty.StringVal("boop"),
"baz": cty.NullVal(cty.Map(cty.String)),
}),
}),
"fob": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
"bar": cty.String,
}))),
}),
``,
},
} }
for name, test := range tests { for name, test := range tests {