Skip to content

Commit 47eb900

Browse files
authored
Move findMapPaths to directory.go (#570)
* Move findMapPaths to directory.go * Add util helper SchemaEntryPathNoChoiceCase (#571) Sometimes we want not just the string path, but the entry path as well.
1 parent 539522f commit 47eb900

File tree

6 files changed

+479
-426
lines changed

6 files changed

+479
-426
lines changed

util/path.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,26 @@ func SchemaTreePathNoModule(e *yang.Entry) string {
104104
// its path, expressed as a slice of strings, which is returned.
105105
func SchemaPathNoChoiceCase(elem *yang.Entry) []string {
106106
var pp []string
107+
for _, e := range SchemaEntryPathNoChoiceCase(elem) {
108+
pp = append(pp, e.Name)
109+
}
110+
return pp
111+
}
112+
113+
// SchemaEntryPathNoChoiceCase takes an input yang.Entry and walks up the tree to find
114+
// its path, expressed as a slice of Entrys, which is returned.
115+
func SchemaEntryPathNoChoiceCase(elem *yang.Entry) []*yang.Entry {
116+
var pp []*yang.Entry
107117
if elem == nil {
108118
return pp
109119
}
110120
e := elem
111121
for ; e.Parent != nil; e = e.Parent {
112122
if !IsChoiceOrCase(e) {
113-
pp = append(pp, e.Name)
123+
pp = append(pp, e)
114124
}
115125
}
116-
pp = append(pp, e.Name)
126+
pp = append(pp, e)
117127

118128
// Reverse the slice that was specified to us as it was appended to
119129
// from the leaf to the root.

util/path_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,50 @@ func TestSchemaPathNoChoiceCase(t *testing.T) {
359359
}
360360
}
361361

362+
func TestSchemaEntryPathNoChoiceCase(t *testing.T) {
363+
containerWithChoiceSchema := getContainerWithChoiceSchema()
364+
365+
tests := []struct {
366+
desc string
367+
entry *yang.Entry
368+
want []*yang.Entry
369+
}{
370+
{
371+
desc: "nil entry",
372+
entry: nil,
373+
want: nil,
374+
},
375+
{
376+
desc: "choice entry",
377+
entry: containerWithChoiceSchema.Dir["choice1"],
378+
want: []*yang.Entry{containerWithChoiceSchema},
379+
},
380+
{
381+
desc: "case entry",
382+
entry: containerWithChoiceSchema.Dir["choice1"].Dir["case1"],
383+
want: []*yang.Entry{containerWithChoiceSchema},
384+
},
385+
{
386+
desc: "case1-leaf1",
387+
entry: containerWithChoiceSchema.Dir["choice1"].Dir["case1"].Dir["case1-leaf1"],
388+
want: []*yang.Entry{containerWithChoiceSchema, containerWithChoiceSchema.Dir["choice1"].Dir["case1"].Dir["case1-leaf1"]},
389+
},
390+
{
391+
desc: "case21-leaf",
392+
entry: containerWithChoiceSchema.Dir["choice1"].Dir["case2"].Dir["case2_choice1"].Dir["case21"].Dir["case21-leaf"],
393+
want: []*yang.Entry{containerWithChoiceSchema, containerWithChoiceSchema.Dir["choice1"].Dir["case2"].Dir["case2_choice1"].Dir["case21"].Dir["case21-leaf"]},
394+
},
395+
}
396+
397+
for _, tt := range tests {
398+
t.Run(tt.desc, func(t *testing.T) {
399+
if got, want := SchemaEntryPathNoChoiceCase(tt.entry), tt.want; !reflect.DeepEqual(got, want) {
400+
t.Errorf("got:\n%v\nwant:\n%v\n", got, want)
401+
}
402+
})
403+
}
404+
}
405+
362406
func TestRemoveXPATHPredicates(t *testing.T) {
363407
tests := []struct {
364408
desc string

ygen/directory.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"sort"
2424

25+
"github.com/google/go-cmp/cmp"
2526
"github.com/openconfig/goyang/pkg/yang"
2627
"github.com/openconfig/ygot/genutil"
2728
"github.com/openconfig/ygot/util"
@@ -169,3 +170,68 @@ func findSchemaPath(parent *Directory, fieldName string, shadowSchemaPaths, abso
169170
}
170171
return fieldSlicePath[len(parent.Path)-1:], nil
171172
}
173+
174+
// findMapPaths takes an input field name for a parent Directory and calculates the set of schemapaths that it represents.
175+
// If absolutePaths is set, the paths are absolute otherwise they are relative to the parent. If
176+
// the input entry is a key to a list, and is of type leafref, then the corresponding target leaf's
177+
// path is also returned. If shadowSchemaPaths is set, then the path of the
178+
// field deprioritized via compression is returned instead of the prioritized paths.
179+
// The first returned path is the path of the direct child, with the shadow
180+
// child's path afterwards, and the key leafref, if any, last.
181+
func findMapPaths(parent *Directory, fieldName string, compressPaths, shadowSchemaPaths, absolutePaths bool) ([][]string, error) {
182+
childPath, err := findSchemaPath(parent, fieldName, shadowSchemaPaths, absolutePaths)
183+
if err != nil {
184+
return nil, err
185+
}
186+
var mapPaths [][]string
187+
if childPath != nil {
188+
mapPaths = append(mapPaths, childPath)
189+
}
190+
// Only for compressed data schema paths for list fields do we have the
191+
// possibility for a direct leafref path as a second path for the field.
192+
if !compressPaths || parent.ListAttr == nil {
193+
return mapPaths, nil
194+
}
195+
196+
field, ok := parent.Fields[fieldName]
197+
if !ok {
198+
return nil, fmt.Errorf("field name %s does not exist in Directory %s", fieldName, parent.Path)
199+
}
200+
fieldSlicePath := util.SchemaPathNoChoiceCase(field)
201+
202+
// Handle specific issue of compressed path schemas, where a key of the
203+
// parent list is a leafref to this leaf.
204+
for _, k := range parent.ListAttr.KeyElems {
205+
// If the key element has the same path as this element, and the
206+
// corresponding element that is within the parent's container is of
207+
// type leafref, then within an OpenConfig schema this means that
208+
// the key leaf was a pointer to this leaf. To this end, we set
209+
// isKey to true so that the struct field can be mapped to the
210+
// leafref leaf within the schema as well as the target of the
211+
// leafref.
212+
if k.Parent == nil || k.Parent.Parent == nil || k.Parent.Parent.Dir[k.Name] == nil || k.Parent.Parent.Dir[k.Name].Type == nil {
213+
return nil, fmt.Errorf("invalid compressed schema, could not find the key %s or the grandparent of %s", k.Name, k.Path())
214+
}
215+
216+
// If a key of the list is a leafref that points to the field,
217+
// then add this as an alternative path.
218+
// Note: if k is a leafref, buildListKey() would have already
219+
// resolved it the field that the leafref points to. So, we
220+
// compare their absolute paths for equality.
221+
if k.Parent.Parent.Dir[k.Name].Type.Kind == yang.Yleafref && cmp.Equal(util.SchemaPathNoChoiceCase(k), fieldSlicePath) {
222+
// The path of the key element is simply the name of the leaf under the
223+
// list, since the YANG specification enforces that keys are direct
224+
// children of the list.
225+
keyPath := []string{fieldSlicePath[len(fieldSlicePath)-1]}
226+
if absolutePaths {
227+
// If absolute paths are required, then the 'config' or 'state' container needs to be omitted from
228+
// the complete path for the secondary mapping.
229+
keyPath = append([]string{""}, fieldSlicePath[1:len(fieldSlicePath)-2]...)
230+
keyPath = append(keyPath, fieldSlicePath[len(fieldSlicePath)-1])
231+
}
232+
mapPaths = append(mapPaths, keyPath)
233+
break
234+
}
235+
}
236+
return mapPaths, nil
237+
}

0 commit comments

Comments
 (0)