mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
configs/configschema: extend block.AttributeByPath to descend into Objects (#29222)
* configs/configschema: extend block.AttributeByPath to descend into Objects This commit adds a recursive Object.AttributeByPath function which will step through Attributes with NestedTypes. If an Attribute without a NestedType is encountered while there is still more to the path, it will return nil.
This commit is contained in:
parent
b9c0cbb1fd
commit
0729e9fdd7
@ -7,13 +7,19 @@ import (
|
|||||||
// AttributeByPath looks up the Attribute schema which corresponds to the given
|
// AttributeByPath looks up the Attribute schema which corresponds to the given
|
||||||
// cty.Path. A nil value is returned if the given path does not correspond to a
|
// cty.Path. A nil value is returned if the given path does not correspond to a
|
||||||
// specific attribute.
|
// specific attribute.
|
||||||
// TODO: this will need to be updated for nested attributes
|
|
||||||
func (b *Block) AttributeByPath(path cty.Path) *Attribute {
|
func (b *Block) AttributeByPath(path cty.Path) *Attribute {
|
||||||
block := b
|
block := b
|
||||||
for _, step := range path {
|
for i, step := range path {
|
||||||
switch step := step.(type) {
|
switch step := step.(type) {
|
||||||
case cty.GetAttrStep:
|
case cty.GetAttrStep:
|
||||||
if attr := block.Attributes[step.Name]; attr != nil {
|
if attr := block.Attributes[step.Name]; attr != nil {
|
||||||
|
// If the Attribute is defined with a NestedType and there's
|
||||||
|
// more to the path, descend into the NestedType
|
||||||
|
if attr.NestedType != nil && i < len(path)-1 {
|
||||||
|
return attr.NestedType.AttributeByPath(path[i+1:])
|
||||||
|
} else if i < len(path)-1 { // There's more to the path, but not more to this Attribute.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return attr
|
return attr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,3 +33,23 @@ func (b *Block) AttributeByPath(path cty.Path) *Attribute {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AttributeByPath recurses through a NestedType to look up the Attribute scheme
|
||||||
|
// which corresponds to the given cty.Path. A nil value is returned if the given
|
||||||
|
// path does not correspond to a specific attribute.
|
||||||
|
func (o *Object) AttributeByPath(path cty.Path) *Attribute {
|
||||||
|
for i, step := range path {
|
||||||
|
switch step := step.(type) {
|
||||||
|
case cty.GetAttrStep:
|
||||||
|
if attr := o.Attributes[step.Name]; attr != nil {
|
||||||
|
if attr.NestedType != nil && i < len(path)-1 {
|
||||||
|
return attr.NestedType.AttributeByPath(path[i+1:])
|
||||||
|
} else if i < len(path)-1 { // There's more to the path, but not more to this Attribute.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return attr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -11,6 +11,24 @@ func TestAttributeByPath(t *testing.T) {
|
|||||||
Attributes: map[string]*Attribute{
|
Attributes: map[string]*Attribute{
|
||||||
"a1": {Description: "a1"},
|
"a1": {Description: "a1"},
|
||||||
"a2": {Description: "a2"},
|
"a2": {Description: "a2"},
|
||||||
|
"a3": {
|
||||||
|
Description: "a3",
|
||||||
|
NestedType: &Object{
|
||||||
|
Nesting: NestingList,
|
||||||
|
Attributes: map[string]*Attribute{
|
||||||
|
"nt1": {Description: "nt1"},
|
||||||
|
"nt2": {
|
||||||
|
Description: "nt2",
|
||||||
|
NestedType: &Object{
|
||||||
|
Nesting: NestingSingle,
|
||||||
|
Attributes: map[string]*Attribute{
|
||||||
|
"deeply_nested": {Description: "deeply_nested"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
BlockTypes: map[string]*NestedBlock{
|
BlockTypes: map[string]*NestedBlock{
|
||||||
"b1": {
|
"b1": {
|
||||||
@ -66,6 +84,16 @@ func TestAttributeByPath(t *testing.T) {
|
|||||||
"a2",
|
"a2",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
cty.GetAttrPath("a3").IndexInt(1).GetAttr("nt2"),
|
||||||
|
"nt2",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.GetAttrPath("a3").IndexInt(1).GetAttr("b2").IndexString("foo").GetAttr("no"),
|
||||||
|
"missing",
|
||||||
|
false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
cty.GetAttrPath("b1"),
|
cty.GetAttrPath("b1"),
|
||||||
"block",
|
"block",
|
||||||
@ -119,3 +147,83 @@ func TestAttributeByPath(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestObject_AttributeByPath(t *testing.T) {
|
||||||
|
obj := &Object{
|
||||||
|
Nesting: NestingList,
|
||||||
|
Attributes: map[string]*Attribute{
|
||||||
|
"a1": {Description: "a1"},
|
||||||
|
"a2": {
|
||||||
|
Description: "a2",
|
||||||
|
NestedType: &Object{
|
||||||
|
Nesting: NestingSingle,
|
||||||
|
Attributes: map[string]*Attribute{
|
||||||
|
"n1": {Description: "n1"},
|
||||||
|
"n2": {
|
||||||
|
Description: "n2",
|
||||||
|
NestedType: &Object{
|
||||||
|
Attributes: map[string]*Attribute{
|
||||||
|
"dn1": {Description: "dn1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
path cty.Path
|
||||||
|
attrDescription string
|
||||||
|
exists bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.GetAttrPath("a2"),
|
||||||
|
"a2",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.GetAttrPath("a3"),
|
||||||
|
"missing",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.GetAttrPath("a2").IndexString("foo").GetAttr("n1"),
|
||||||
|
"n1",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.GetAttrPath("a2").IndexString("foo").GetAttr("n2").IndexInt(11).GetAttr("dn1"),
|
||||||
|
"dn1",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.GetAttrPath("a2").IndexString("foo").GetAttr("n2").IndexInt(11).GetAttr("dn1").IndexString("hello").GetAttr("nope"),
|
||||||
|
"missing_nested",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.attrDescription, func(t *testing.T) {
|
||||||
|
attr := obj.AttributeByPath(tc.path)
|
||||||
|
if !tc.exists && attr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.exists && attr != nil {
|
||||||
|
t.Fatalf("found Attribute, expected nil from path %#v\n", tc.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr == nil {
|
||||||
|
t.Fatalf("missing attribute from path %#v\n", tc.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr.Description != tc.attrDescription {
|
||||||
|
t.Fatalf("expected Attribute for %q, got %#v\n", tc.attrDescription, attr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user