Skip to content

Commit 75ebd15

Browse files
authored
Add option for [Set/Get/GetCreate/Delete]Node for reversing "shadow-path" and "path" behaviour. (#531)
* Add option for [Set/Get/GetCreate/Delete]Node for reversing shadow-path with path tags. This allows a user who is multiplexing a compressed GoStruct to choose which of either config or state data to unmarshal, and which one to silently ignore. * Fix coding style * Add comments for shadow-path processing * Clarify comments
1 parent 5d4a479 commit 75ebd15

File tree

2 files changed

+484
-29
lines changed

2 files changed

+484
-29
lines changed

ytypes/node.go

Lines changed: 141 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ type retrieveNodeArgs struct {
5959
// values are skipped (non-existing GoStructs are still initialized),
6060
// and any value retrieved is always nil.
6161
shadowPath bool
62+
// reverseShadowPath reverses the meaning of the "path" and
63+
// "shadow-path" tags when both are present while processing a
64+
// GoStruct.
65+
reverseShadowPath bool
6266
}
6367

6468
// retrieveNode is an internal function that retrieves the node specified by
@@ -134,11 +138,6 @@ func retrieveNodeContainer(schema *yang.Entry, root interface{}, path *gpb.Path,
134138
}
135139
}
136140

137-
schPaths, err := util.SchemaPaths(ft)
138-
if err != nil {
139-
return nil, status.Errorf(codes.Unknown, "failed to get schema paths for %T, field %s: %s", root, ft.Name, err)
140-
}
141-
142141
checkPath := func(p []string, args retrieveNodeArgs) ([]*TreeNode, error) {
143142
to := len(p)
144143
if util.IsTypeMap(ft.Type) {
@@ -195,18 +194,52 @@ func retrieveNodeContainer(schema *yang.Entry, root interface{}, path *gpb.Path,
195194
return retrieveNode(cschema, fv.Interface(), util.TrimGNMIPathPrefix(path, p[0:to]), np, args)
196195
}
197196

198-
for _, p := range schPaths {
199-
if !util.PathMatchesPrefix(path, p) {
200-
continue
197+
// Continue traversal on the first-encountered annotated
198+
// GoStruct path that forms a prefix of the input path.
199+
//
200+
// Note that we first look through the non-shadow path, and if
201+
// no matches are found, we then look through the shadow path
202+
// to find matches. If the input path matches a shadow path,
203+
// then the traversal continues, although the final operation
204+
// is marked as a no-op.
205+
//
206+
// If the user has opted to reverse the "shadow-path" and
207+
// "path" tags, then the order of the look-ups is reversed.
208+
args := args
209+
if args.reverseShadowPath {
210+
// Look through shadow paths first instead.
211+
schPaths := util.ShadowSchemaPaths(ft)
212+
for _, p := range schPaths {
213+
if util.PathMatchesPrefix(path, p) {
214+
return checkPath(p, args)
215+
}
216+
}
217+
218+
if len(schPaths) != 0 {
219+
// Only if there exists shadow paths do we
220+
// treat the non-shadow paths as no-ops.
221+
// Otherwise, there is no reversal to do.
222+
args.shadowPath = true
201223
}
202-
return checkPath(p, args)
203224
}
204-
for _, p := range util.ShadowSchemaPaths(ft) {
205-
if !util.PathMatchesPrefix(path, p) {
206-
continue
225+
schPaths, err := util.SchemaPaths(ft)
226+
if err != nil {
227+
return nil, status.Errorf(codes.Unknown, "failed to get schema paths for %T, field %s: %s", root, ft.Name, err)
228+
}
229+
for _, p := range schPaths {
230+
if util.PathMatchesPrefix(path, p) {
231+
return checkPath(p, args)
207232
}
233+
}
234+
if !args.reverseShadowPath {
235+
// Look through shadow paths last, and mark operations
236+
// as no-ops.
208237
args.shadowPath = true
209-
return checkPath(p, args)
238+
for _, p := range util.ShadowSchemaPaths(ft) {
239+
if util.PathMatchesPrefix(path, p) {
240+
return checkPath(p, args)
241+
}
242+
}
210243
}
211244
}
212245

@@ -354,14 +387,23 @@ func retrieveNodeList(schema *yang.Entry, root interface{}, path, traversedPath
354387
return matches, nil
355388
}
356389

390+
// GetOrCreateNodeOpt defines an interface that can be used to supply arguments to functions using GetOrCreateNode.
391+
type GetOrCreateNodeOpt interface {
392+
// IsGetOrCreateNodeOpt is a marker method that is used to identify an instance of GetOrCreateNodeOpt.
393+
IsGetOrCreateNodeOpt()
394+
}
395+
357396
// GetOrCreateNode function retrieves the node specified by the supplied path from the root which must have the
358397
// schema supplied. It strictly matches keys in the path, in other words doesn't treat partial match as match.
359398
// However, if there is no match, a new entry in the map is created. GetOrCreateNode also initializes the nodes
360399
// along the path if they are nil.
361400
// Function returns the value and schema of the node as well as error.
362401
// Note that this function may modify the supplied root even if the function fails.
363-
func GetOrCreateNode(schema *yang.Entry, root interface{}, path *gpb.Path) (interface{}, *yang.Entry, error) {
364-
nodes, err := retrieveNode(schema, root, path, nil, retrieveNodeArgs{modifyRoot: true})
402+
func GetOrCreateNode(schema *yang.Entry, root interface{}, path *gpb.Path, opts ...GetOrCreateNodeOpt) (interface{}, *yang.Entry, error) {
403+
nodes, err := retrieveNode(schema, root, path, nil, retrieveNodeArgs{
404+
modifyRoot: true,
405+
reverseShadowPath: hasGetOrCreateNodeReverseShadowPaths(opts),
406+
})
365407
if err != nil {
366408
return nil, nil, err
367409
}
@@ -386,9 +428,10 @@ type TreeNode struct {
386428
func GetNode(schema *yang.Entry, root interface{}, path *gpb.Path, opts ...GetNodeOpt) ([]*TreeNode, error) {
387429
return retrieveNode(schema, root, path, nil, retrieveNodeArgs{
388430
// We never want to modify the input root, so we specify modifyRoot.
389-
modifyRoot: false,
390-
partialKeyMatch: hasPartialKeyMatch(opts),
391-
handleWildcards: hasHandleWildcards(opts),
431+
modifyRoot: false,
432+
partialKeyMatch: hasPartialKeyMatch(opts),
433+
handleWildcards: hasHandleWildcards(opts),
434+
reverseShadowPath: hasGetNodeReverseShadowPaths(opts),
392435
})
393436
}
394437

@@ -455,6 +498,7 @@ func SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface
455498
modifyRoot: hasInitMissingElements(opts),
456499
val: val,
457500
tolerateJSONInconsistenciesForVal: hasTolerateJSONInconsistencies(opts),
501+
reverseShadowPath: hasSetNodeReverseShadowPaths(opts),
458502
})
459503

460504
if err != nil {
@@ -511,13 +555,90 @@ func hasTolerateJSONInconsistencies(opts []SetNodeOpt) bool {
511555
return false
512556
}
513557

558+
// DelNodeOpt defines an interface that can be used to supply arguments to functions using DeleteNode.
559+
type DelNodeOpt interface {
560+
// IsDelNodeOpt is a marker method that is used to identify an instance of DelNodeOpt.
561+
IsDelNodeOpt()
562+
}
563+
564+
// ReverseShadowPaths signals to reverse the meaning of the "path" and
565+
// "shadow-path" tags when both are present while processing a GoStruct. This
566+
// means paths matching "shadow-path" will be unmarshalled, while paths
567+
// matching "path" will be silently ignored.
568+
type ReverseShadowPaths struct{}
569+
570+
// IsGetOrCreateNodeOpt implements the GetOrCreateNodeOpt interface.
571+
func (*ReverseShadowPaths) IsGetOrCreateNodeOpt() {}
572+
573+
// IsGetNodeOpt implements the GetNodeOpt interface.
574+
func (*ReverseShadowPaths) IsGetNodeOpt() {}
575+
576+
// IsSetNodeOpt implements the SetNodeOpt interface.
577+
func (*ReverseShadowPaths) IsSetNodeOpt() {}
578+
579+
// IsDelNodeOpt implements the DelNodeOpt interface.
580+
func (*ReverseShadowPaths) IsDelNodeOpt() {}
581+
582+
// hasGetOrCreateNodeReverseShadowPaths determines whether there is an instance
583+
// of ReverseShadowPaths within the supplied GetOrCreateNodeOpt slice. It is
584+
// used to determine whether to reverse the meaning of the "path" and
585+
// "shadow-path" tags when both are present while processing a GoStruct.
586+
func hasGetOrCreateNodeReverseShadowPaths(opts []GetOrCreateNodeOpt) bool {
587+
for _, o := range opts {
588+
if _, ok := o.(*ReverseShadowPaths); ok {
589+
return true
590+
}
591+
}
592+
return false
593+
}
594+
595+
// hasGetNodeReverseShadowPaths determines whether there is an instance of
596+
// ReverseShadowPaths within the supplied GetNodeOpt slice. It is used to
597+
// determine whether to reverse the meaning of the "path" and "shadow-path"
598+
// tags when both are present while processing a GoStruct.
599+
func hasGetNodeReverseShadowPaths(opts []GetNodeOpt) bool {
600+
for _, o := range opts {
601+
if _, ok := o.(*ReverseShadowPaths); ok {
602+
return true
603+
}
604+
}
605+
return false
606+
}
607+
608+
// hasSetNodeReverseShadowPaths determines whether there is an instance of
609+
// ReverseShadowPaths within the supplied SetNodeOpt slice. It is used to
610+
// determine whether to reverse the meaning of the "path" and "shadow-path"
611+
// tags when both are present while processing a GoStruct.
612+
func hasSetNodeReverseShadowPaths(opts []SetNodeOpt) bool {
613+
for _, o := range opts {
614+
if _, ok := o.(*ReverseShadowPaths); ok {
615+
return true
616+
}
617+
}
618+
return false
619+
}
620+
621+
// hasDelNodeReverseShadowPaths determines whether there is an instance of
622+
// ReverseShadowPaths within the supplied DelNodeOpt slice. It is used to
623+
// determine whether to reverse the meaning of the "path" and "shadow-path"
624+
// tags when both are present while processing a GoStruct.
625+
func hasDelNodeReverseShadowPaths(opts []DelNodeOpt) bool {
626+
for _, o := range opts {
627+
if _, ok := o.(*ReverseShadowPaths); ok {
628+
return true
629+
}
630+
}
631+
return false
632+
}
633+
514634
// DeleteNode zeroes the value of the node specified by the supplied path from
515635
// the specified root, whose schema must also be supplied. If the node
516636
// specified by that path is already its zero value, or an intermediate node
517637
// in the path is nil (implying the node is already deleted), then the call is a no-op.
518-
func DeleteNode(schema *yang.Entry, root interface{}, path *gpb.Path) error {
638+
func DeleteNode(schema *yang.Entry, root interface{}, path *gpb.Path, opts ...DelNodeOpt) error {
519639
_, err := retrieveNode(schema, root, path, nil, retrieveNodeArgs{
520-
delete: true,
640+
delete: true,
641+
reverseShadowPath: hasDelNodeReverseShadowPaths(opts),
521642
})
522643

523644
return err

0 commit comments

Comments
 (0)