Skip to content

Commit dbe2718

Browse files
authored
Add support for rewriting the appended module name. (#545)
* (M) ygot/render.go * (M) ygot/render_test.go - This change allows the contents of the `module` tag to be rewritten when outputting RFC7951-compatible JSON. Particularly, this is useful when a node that was removed from the schema is re-added using an augmentation, and the user wants to specify that ygot should pretend that the node is still in the original module.
1 parent 2713b98 commit dbe2718

File tree

2 files changed

+148
-50
lines changed

2 files changed

+148
-50
lines changed

ygot/render.go

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,19 @@ type RFC7951JSONConfig struct {
921921
// GoStruct to determine the marshalled path elements instead of the
922922
// "path" tag, whenever the former is present.
923923
PreferShadowPath bool
924+
// RewriteModuleNames specifies that, when marshalling to JSON, any
925+
// entry that is found within module A should be assumed to be in
926+
// module B. This allows a user to augment modules with nodes that
927+
// are then rewritten to be part of the augmented (note augmentED)
928+
// module's namespace. The primary reason that a user may this
929+
// functionality is to ensure that when a node is removed from an
930+
// model, but it is to be re-added for backwards compatibility by
931+
// augmentation, then the original output is not modified.
932+
//
933+
// The RewriteModuleNames map is keyed on the name of the module that
934+
// is to be rewritten FROM, and the value of the map is the name of the module
935+
// it is to be rewritten TO.
936+
RewriteModuleNames map[string]string
924937
}
925938

926939
// IsMarshal7951Arg marks the RFC7951JSONConfig struct as a valid argument to
@@ -929,9 +942,8 @@ func (*RFC7951JSONConfig) IsMarshal7951Arg() {}
929942

930943
// ConstructIETFJSON marshals a supplied GoStruct to a map, suitable for
931944
// handing to json.Marshal. It complies with the convention for marshalling
932-
// to JSON described by RFC7951. The appendModName argument determines whether
933-
// the module name should be appended to entities that are defined in a different
934-
// module to their parent.
945+
// to JSON described by RFC7951. The supplied args control options corresponding
946+
// to the method by which JSON is marshalled.
935947
func ConstructIETFJSON(s GoStruct, args *RFC7951JSONConfig) (map[string]interface{}, error) {
936948
return structJSON(s, "", jsonOutputConfig{
937949
jType: RFC7951,
@@ -1017,6 +1029,18 @@ type jsonOutputConfig struct {
10171029
rfc7951Config *RFC7951JSONConfig
10181030
}
10191031

1032+
// rewriteModName rewrites the module mod according to the specified rewrite rules.
1033+
// The rewrite rules are a map keyed by observed module name, with values of
1034+
// the name of the module that is to be rewritten to. It returns the rewritten
1035+
// module name, or the original module name in the case that it does not need
1036+
// to be rewritten.
1037+
func rewriteModName(mod string, rules map[string]string) string {
1038+
if rules == nil || rules[mod] == "" {
1039+
return mod
1040+
}
1041+
return rules[mod]
1042+
}
1043+
10201044
// structJSON marshals a GoStruct to a map[string]interface{} which can be
10211045
// handed to JSON marshal. parentMod specifies the module that the supplied
10221046
// GoStruct is defined within such that RFC7951 format JSON is able to consider
@@ -1040,23 +1064,32 @@ func structJSON(s GoStruct, parentMod string, args jsonOutputConfig) (map[string
10401064

10411065
// Determine whether we should append a module name to the path in RFC7951
10421066
// output mode.
1043-
var appmod string
1067+
var (
1068+
appmod string
1069+
appendModName bool
1070+
)
1071+
10441072
pmod := parentMod
1045-
if chMod, ok := fType.Tag.Lookup("module"); ok {
1046-
// If the child module isn't the same as the parent module,
1047-
// then appmod stores the name of the module to prefix to paths
1048-
// within this context.
1049-
if chMod != parentMod {
1050-
appmod = chMod
1073+
if args.jType == RFC7951 && args.rfc7951Config != nil && args.rfc7951Config.AppendModuleName {
1074+
if chMod, ok := fType.Tag.Lookup("module"); ok {
1075+
// If the child module isn't the same as the parent module,
1076+
// then appmod stores the name of the module to prefix to paths
1077+
// within this context.
1078+
1079+
// First we check whether we are rewriting the name of the module, so that
1080+
// we do the right comparison.
1081+
chMod = rewriteModName(chMod, args.rfc7951Config.RewriteModuleNames)
1082+
1083+
if chMod != parentMod {
1084+
appmod = chMod
1085+
}
1086+
// Update the parent module name to be used for subsequent
1087+
// children.
1088+
pmod = chMod
1089+
}
1090+
if appmod != "" {
1091+
appendModName = true
10511092
}
1052-
// Update the parent module name to be used for subsequent
1053-
// children.
1054-
pmod = chMod
1055-
}
1056-
1057-
var appendModName bool
1058-
if args.jType == RFC7951 && args.rfc7951Config != nil && args.rfc7951Config.AppendModuleName && appmod != "" {
1059-
appendModName = true
10601093
}
10611094

10621095
mapPaths, err := structTagToLibPaths(fType, newStringSliceGNMIPath([]string{}), args.rfc7951Config != nil && args.rfc7951Config.PreferShadowPath)

ygot/render_test.go

Lines changed: 97 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,14 +1794,15 @@ func (t *unmarshalableJSON) UnmarshalJSON(d []byte) error {
17941794

17951795
func TestConstructJSON(t *testing.T) {
17961796
tests := []struct {
1797-
name string
1798-
in GoStruct
1799-
inAppendMod bool
1800-
wantIETF map[string]interface{}
1801-
wantInternal map[string]interface{}
1802-
wantSame bool
1803-
wantErr bool
1804-
wantJSONErr bool
1797+
name string
1798+
in GoStruct
1799+
inAppendMod bool
1800+
inRewriteModuleNameRules map[string]string
1801+
wantIETF map[string]interface{}
1802+
wantInternal map[string]interface{}
1803+
wantSame bool
1804+
wantErr bool
1805+
wantJSONErr bool
18051806
}{{
18061807
name: "invalidGoStruct",
18071808
in: &invalidGoStructChild{
@@ -1873,6 +1874,67 @@ func TestConstructJSON(t *testing.T) {
18731874
},
18741875
},
18751876
},
1877+
}, {
1878+
name: "rewrite module name for an element with children",
1879+
in: &diffModAtRoot{
1880+
Child: &diffModAtRootChild{
1881+
ValueOne: String("one"),
1882+
ValueTwo: String("two"),
1883+
ValueThree: String("three"),
1884+
},
1885+
Elem: &diffModAtRootElem{
1886+
C: &diffModAtRootElemTwo{
1887+
Name: String("baz"),
1888+
},
1889+
},
1890+
},
1891+
inAppendMod: true,
1892+
inRewriteModuleNameRules: map[string]string{
1893+
// rewrite m1 to m2
1894+
"m1": "m2",
1895+
},
1896+
wantIETF: map[string]interface{}{
1897+
"m2:foo": map[string]interface{}{
1898+
"value-one": "one",
1899+
"m3:value-two": "two",
1900+
"value-three": "three",
1901+
},
1902+
"m2:baz": map[string]interface{}{
1903+
"c": map[string]interface{}{
1904+
"name": "baz",
1905+
},
1906+
},
1907+
},
1908+
}, {
1909+
name: "rewrite leaf node module",
1910+
in: &diffModAtRoot{
1911+
Child: &diffModAtRootChild{
1912+
ValueOne: String("one"),
1913+
ValueTwo: String("two"),
1914+
ValueThree: String("three"),
1915+
},
1916+
Elem: &diffModAtRootElem{
1917+
C: &diffModAtRootElemTwo{
1918+
Name: String("baz"),
1919+
},
1920+
},
1921+
},
1922+
inAppendMod: true,
1923+
inRewriteModuleNameRules: map[string]string{
1924+
"m3": "fish",
1925+
},
1926+
wantIETF: map[string]interface{}{
1927+
"m1:foo": map[string]interface{}{
1928+
"m2:value-one": "one",
1929+
"fish:value-two": "two",
1930+
"value-three": "three",
1931+
},
1932+
"m1:baz": map[string]interface{}{
1933+
"c": map[string]interface{}{
1934+
"name": "baz",
1935+
},
1936+
},
1937+
},
18761938
}, {
18771939
name: "simple render",
18781940
in: &renderExample{
@@ -2559,7 +2621,8 @@ func TestConstructJSON(t *testing.T) {
25592621
for _, tt := range tests {
25602622
t.Run(tt.name+" ConstructIETFJSON", func(t *testing.T) {
25612623
gotietf, err := ConstructIETFJSON(tt.in, &RFC7951JSONConfig{
2562-
AppendModuleName: tt.inAppendMod,
2624+
AppendModuleName: tt.inAppendMod,
2625+
RewriteModuleNames: tt.inRewriteModuleNameRules,
25632626
})
25642627
if (err != nil) != tt.wantErr {
25652628
t.Fatalf("ConstructIETFJSON(%v): got unexpected error: %v, want error %v", tt.in, err, tt.wantErr)
@@ -2581,31 +2644,33 @@ func TestConstructJSON(t *testing.T) {
25812644
}
25822645
})
25832646

2584-
t.Run(tt.name+" ConstructInternalJSON", func(t *testing.T) {
2585-
gotjson, err := ConstructInternalJSON(tt.in)
2586-
if (err != nil) != tt.wantErr {
2587-
t.Fatalf("ConstructJSON(%v): got unexpected error: %v", tt.in, err)
2588-
}
2589-
if err != nil {
2590-
return
2591-
}
2647+
if tt.wantSame || tt.wantInternal != nil {
2648+
t.Run(tt.name+" ConstructInternalJSON", func(t *testing.T) {
2649+
gotjson, err := ConstructInternalJSON(tt.in)
2650+
if (err != nil) != tt.wantErr {
2651+
t.Fatalf("ConstructJSON(%v): got unexpected error: %v", tt.in, err)
2652+
}
2653+
if err != nil {
2654+
return
2655+
}
25922656

2593-
_, err = json.Marshal(gotjson)
2594-
if (err != nil) != tt.wantJSONErr {
2595-
t.Fatalf("json.Marshal(%v): got unexpected error: %v, want error: %v", gotjson, err, tt.wantJSONErr)
2596-
}
2597-
if err != nil {
2598-
return
2599-
}
2657+
_, err = json.Marshal(gotjson)
2658+
if (err != nil) != tt.wantJSONErr {
2659+
t.Fatalf("json.Marshal(%v): got unexpected error: %v, want error: %v", gotjson, err, tt.wantJSONErr)
2660+
}
2661+
if err != nil {
2662+
return
2663+
}
26002664

2601-
wantInternal := tt.wantInternal
2602-
if tt.wantSame == true {
2603-
wantInternal = tt.wantIETF
2604-
}
2605-
if diff := pretty.Compare(gotjson, wantInternal); diff != "" {
2606-
t.Errorf("ConstructJSON(%v): did not get expected output, diff(-got,+want):\n%v", tt.in, diff)
2607-
}
2608-
})
2665+
wantInternal := tt.wantInternal
2666+
if tt.wantSame == true {
2667+
wantInternal = tt.wantIETF
2668+
}
2669+
if diff := pretty.Compare(gotjson, wantInternal); diff != "" {
2670+
t.Errorf("ConstructJSON(%v): did not get expected output, diff(-got,+want):\n%v", tt.in, diff)
2671+
}
2672+
})
2673+
}
26092674
}
26102675
}
26112676

0 commit comments

Comments
 (0)