Skip to content

Commit

Permalink
introducing ygot.OptimizedDiff
Browse files Browse the repository at this point in the history
  • Loading branch information
ythadhani committed Mar 21, 2023
1 parent e92fb25 commit 482949e
Show file tree
Hide file tree
Showing 17 changed files with 539 additions and 296 deletions.
2 changes: 2 additions & 0 deletions ygen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@ func (cg *YANGCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*
AbsoluteMapPaths: false,
AppendEnumSuffixForSimpleUnionEnums: cg.Config.GoOptions.AppendEnumSuffixForSimpleUnionEnums,
UseConsistentNamesForProtoUnionEnums: false,
GenerateSwaggerCompliantCode: cg.Config.GoOptions.GenerateSwaggerCompliantCode,
GenerateExtensionTags: cg.Config.GoOptions.GenerateExtensionTags,
}

var codegenErr util.Errors
Expand Down
79 changes: 79 additions & 0 deletions ygen/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ package ygen
// processing goyang Entry elements that helps in the code generation process.

import (
"bytes"
"fmt"
"reflect"
"sort"
"strings"

"github.com/openconfig/goyang/pkg/yang"
"github.com/openconfig/ygot/genutil"
Expand Down Expand Up @@ -254,6 +256,13 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory
ShadowMappedPathModules: smm,
}

if opts.GenerateSwaggerCompliantCode {
nd.SwaggerTags = generateSwaggerTags(field)
}
if opts.GenerateExtensionTags {
nd.ExtensionTags = generateExtensionTags(field)
}

switch {
case field.IsLeaf(), field.IsLeafList():
mtype, err := langMapper.LeafType(field, opts)
Expand Down Expand Up @@ -296,6 +305,76 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory
return dirDets, nil
}

func generateExtensionTags(field *yang.Entry) string {
var (
extensions []*yang.Statement = []*yang.Statement{}
buf bytes.Buffer
)
if len(field.Exts) != 0 {
extensions = append(extensions, field.Exts...)
}
// TODO(ythadhani) For now we only react to Type extensions on Leaf.
// Revisit once goyang is updated.
if field.IsLeaf() {
leafNode := field.Node.(*yang.Leaf)
if len(leafNode.Type.Extensions) != 0 {
extensions = append(extensions, leafNode.Type.Extensions...)
}
}

uniqueExtKeywordArgs := map[uniqueExtParams]struct{}{}
if len(extensions) != 0 {
buf.WriteString(` extensions:"`)
for iter, extension := range extensions {
e := uniqueExtParams{keyword: extension.Keyword, argument: extension.Argument}
if _, present := uniqueExtKeywordArgs[e]; present {
continue
}
uniqueExtKeywordArgs[e] = struct{}{}
buf.WriteString(extension.Keyword)
if extension.HasArgument {
buf.WriteString(fmt.Sprintf(",%s", extension.Argument))
}
if iter != len(extensions)-1 {
buf.WriteString(";")
}
}
buf.WriteString(`"`)
}
return buf.String()
}

func generateSwaggerTags(field *yang.Entry) string {
fieldType := field.Type
var buf bytes.Buffer
if fieldType == nil {
return buf.String()
}

switch fieldType.Kind {
case yang.Yenum:
enumNamesCsv := strings.Join(fieldType.Enum.Names(), ",")
if field.IsLeafList() {
buf.WriteString(fmt.Sprintf(` swaggertype:"array,string" enums:"%s"`, enumNamesCsv))
} else if field.IsLeaf() {
buf.WriteString(fmt.Sprintf(` swaggertype:"string" enums:"%s"`, enumNamesCsv))
}
case yang.Yidentityref:
enumNames := make([]string, len(fieldType.IdentityBase.Values))
for i, val := range fieldType.IdentityBase.Values {
enumNames[i] = val.Name
}
sort.Strings(enumNames)
enumNamesCsv := strings.Join(enumNames, ",")
if field.IsLeafList() {
buf.WriteString(fmt.Sprintf(` swaggertype:"array,string" enums:"%s"`, enumNamesCsv))
} else if field.IsLeaf() {
buf.WriteString(fmt.Sprintf(` swaggertype:"string" enums:"%s"`, enumNamesCsv))
}
}
return buf.String()
}

// FindSchemaPath finds the relative or absolute schema path of a given field
// of a Directory. The Field is specified as a name in order to guarantee its
// existence before processing.
Expand Down
64 changes: 63 additions & 1 deletion ygen/directory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package ygen

import (
"fmt"
"testing"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -267,7 +268,6 @@ func compileModules(t *testing.T, inModules map[string]string) *yang.Modules {
t.Fatalf("modules processing failed: %v", errs)
}
return ms

}

// findEntry gets the entry for the module given the path.
Expand Down Expand Up @@ -712,3 +712,65 @@ func TestFindMapPaths(t *testing.T) {
})
}
}

func TestGenerateSwaggerTags(t *testing.T) {
testEnum := yang.NewEnumType()
testEnum.Set("UP", 0)
testEnum.Set("DOWN", 1)
testEnum.Set("TESTING", 2)

testEnum2 := yang.NewEnumType()
testEnum2.Set("fc0", 0)
testEnum2.Set("fc1", 1)

identityVals := []*yang.Identity{{Name: "tunnel"}, {Name: "dcn"}}

tests := []struct {
name string
field *yang.Entry
want string
}{{
name: "Tag generation for leaf enum",
field: &yang.Entry{
Name: "admin-status",
Kind: yang.LeafEntry,
Type: &yang.YangType{Kind: yang.Yenum, Enum: testEnum},
},
want: fmt.Sprintf(` swaggertype:"string" enums:"%s"`, "DOWN,TESTING,UP"),
}, {
name: "Tag generation for leaflist enum",
field: &yang.Entry{
Name: "forwarding-class",
Kind: yang.LeafEntry,
ListAttr: &yang.ListAttr{},
Type: &yang.YangType{Kind: yang.Yenum, Enum: testEnum2},
},
want: fmt.Sprintf(` swaggertype:"array,string" enums:"%s"`, "fc0,fc1"),
}, {
name: "Tag generation for leaf identityref",
field: &yang.Entry{
Name: "type",
Kind: yang.LeafEntry,
Type: &yang.YangType{Kind: yang.Yidentityref, IdentityBase: &yang.Identity{Values: identityVals}},
},
want: fmt.Sprintf(` swaggertype:"string" enums:"%s"`, "dcn,tunnel"),
}, {
name: "Tag generation for leaflist identityref",
field: &yang.Entry{
Name: "type",
Kind: yang.LeafEntry,
ListAttr: &yang.ListAttr{},
Type: &yang.YangType{Kind: yang.Yidentityref, IdentityBase: &yang.Identity{Values: identityVals}},
},
want: fmt.Sprintf(` swaggertype:"array,string" enums:"%s"`, "dcn,tunnel"),
}}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generateSwaggerTags(tt.field)
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Fatalf("did not get expected swagger tags, (-want, +got):\n%s", diff)
}
})
}
}
2 changes: 2 additions & 0 deletions ygen/genir.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type IROptions struct {
// code, and also appends a suffix to non-typedef union enums.
// NOTE: This flag will be removed by v1 release.
UseConsistentNamesForProtoUnionEnums bool
GenerateSwaggerCompliantCode bool
GenerateExtensionTags bool
}

// GenerateIR creates the ygen intermediate representation for a set of
Expand Down
153 changes: 0 additions & 153 deletions ygen/gogen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -729,97 +729,6 @@ func (*Tstruct) ΛBelongingModule() string {
}
`,
},
}, {
name: "struct with single key list - swagger compliant code",
inStructToMap: &Directory{
Name: "Tstruct",
Fields: map[string]*yang.Entry{
"listWithKey": {
Name: "listWithKey",
ListAttr: &yang.ListAttr{},
Key: "keyLeaf",
Parent: &yang.Entry{
Name: "tstruct",
Parent: &yang.Entry{
Name: "root-module",
Node: &yang.Module{
Name: "exmod",
Namespace: &yang.Value{
Name: "u:exmod",
},
Modules: modules,
},
},
},
Kind: yang.DirectoryEntry,
Dir: map[string]*yang.Entry{
"keyLeaf": {
Name: "keyLeaf",
Type: &yang.YangType{Kind: yang.Ystring},
},
},
Node: &yang.Leaf{
Parent: &yang.Module{
Name: "exmod",
Namespace: &yang.Value{
Name: "u:exmod",
},
Modules: modules,
},
},
},
},
Path: []string{"", "root-module", "tstruct"},
},
inMappableEntities: map[string]*Directory{
"/root-module/tstruct/listWithKey": {
Name: "ListWithKey",
ListAttr: &YangListAttr{
Keys: map[string]*MappedType{
"keyLeaf": {NativeType: "string"},
},
KeyElems: []*yang.Entry{
{
Name: "keyLeaf",
},
},
},
Path: []string{"", "root-module", "tstruct", "listWithKey"},
},
},
inUniqueDirectoryNames: map[string]string{
"/root-module/tstruct/listWithKey": "ListWithKey",
},
inGoOpts: GoOpts{
GenerateSwaggerCompliantCode: true,
},
wantCompressed: wantGoStructOut{
structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
ListWithKey []*ListWithKey ` + "`" + `path:"listWithKey" module:"exmod"` + "`" + `
}
// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
methods: `
// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) Validate(opts ...ygot.ValidationOption) error {
if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
return err
}
return nil
}
// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }
`,
},
wantSame: true,
}, {
name: "missing list definition element",
inStructToMap: &ParsedDirectory{
Expand Down Expand Up @@ -2261,65 +2170,3 @@ func TestGoLeafDefaults(t *testing.T) {
})
}
}

func TestGenerateSwaggerTags(t *testing.T) {
testEnum := yang.NewEnumType()
testEnum.Set("UP", 0)
testEnum.Set("DOWN", 1)
testEnum.Set("TESTING", 2)

testEnum2 := yang.NewEnumType()
testEnum2.Set("fc0", 0)
testEnum2.Set("fc1", 1)

identityVals := []*yang.Identity{{Name: "tunnel"}, {Name: "dcn"}}

tests := []struct {
name string
field *yang.Entry
want string
}{{
name: "Tag generation for leaf enum",
field: &yang.Entry{
Name: "admin-status",
Kind: yang.LeafEntry,
Type: &yang.YangType{Kind: yang.Yenum, Enum: testEnum},
},
want: fmt.Sprintf(` swaggertype:"string" enums:"%s"`, "DOWN,TESTING,UP"),
}, {
name: "Tag generation for leaflist enum",
field: &yang.Entry{
Name: "forwarding-class",
Kind: yang.LeafEntry,
ListAttr: &yang.ListAttr{},
Type: &yang.YangType{Kind: yang.Yenum, Enum: testEnum2},
},
want: fmt.Sprintf(` swaggertype:"array,string" enums:"%s"`, "fc0,fc1"),
}, {
name: "Tag generation for leaf identityref",
field: &yang.Entry{
Name: "type",
Kind: yang.LeafEntry,
Type: &yang.YangType{Kind: yang.Yidentityref, IdentityBase: &yang.Identity{Values: identityVals}},
},
want: fmt.Sprintf(` swaggertype:"string" enums:"%s"`, "dcn,tunnel"),
}, {
name: "Tag generation for leaflist identityref",
field: &yang.Entry{
Name: "type",
Kind: yang.LeafEntry,
ListAttr: &yang.ListAttr{},
Type: &yang.YangType{Kind: yang.Yidentityref, IdentityBase: &yang.Identity{Values: identityVals}},
},
want: fmt.Sprintf(` swaggertype:"array,string" enums:"%s"`, "dcn,tunnel"),
}}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generateSwaggerTags(tt.field)
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Fatalf("did not get expected swagger tags, (-want, +got):\n%s", diff)
}
})
}
}
Loading

0 comments on commit 482949e

Please sign in to comment.