From ad7b9894307d5038f818720d0adfa2061fcd99db Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 12:42:20 +0100 Subject: [PATCH 01/66] add EntryXml struct, render all missing functions for entry.go, extend functions used in templates --- pkg/generate/generator.go | 62 +++---- pkg/translate/funcs.go | 19 +++ pkg/translate/funcs_test.go | 192 +-------------------- pkg/translate/structs.go | 176 +++---------------- pkg/translate/structs_test.go | 128 +------------- templates/sdk/entry.tmpl | 313 +++++++++++++--------------------- 6 files changed, 199 insertions(+), 691 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 7b6d925a..734f763a 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -1,9 +1,7 @@ package generate import ( - "bytes" "fmt" - "io" "log" "os" "path/filepath" @@ -20,7 +18,6 @@ type Creator struct { Spec *properties.Normalization } -// NewCreator initialize Creator instance. func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization) *Creator { return &Creator{ GoOutputDir: goOutputDir, @@ -29,7 +26,6 @@ func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization } } -// RenderTemplate loop through all templates, parse them and render content, which is saved to output file. func (c *Creator) RenderTemplate() error { log.Println("Start rendering templates") @@ -46,40 +42,29 @@ func (c *Creator) RenderTemplate() error { return fmt.Errorf("error creating directories for %s: %w", filePath, err) } + outputFile, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("error creating file %s: %w", filePath, err) + } + defer outputFile.Close() + tmpl, err := c.parseTemplate(templateName) if err != nil { return fmt.Errorf("error parsing template %s: %w", templateName, err) } - var data bytes.Buffer - if err := tmpl.Execute(&data, c.Spec); err != nil { + if err := tmpl.Execute(outputFile, c.Spec); err != nil { return fmt.Errorf("error executing template %s: %w", templateName, err) } - // If from template no data was rendered (e.g. for DNS spec entry should not be created), - // then we don't need to create empty file (e.g. `entry.go`) with no content - if data.Len() > 0 { - outputFile, err := os.Create(filePath) - if err != nil { - return fmt.Errorf("error creating file %s: %w", filePath, err) - } - defer outputFile.Close() - - _, err = io.Copy(outputFile, &data) - if err != nil { - return err - } - } } return nil } -// createFullFilePath returns a full path for output file generated from template passed as argument to function. func (c *Creator) createFullFilePath(templateName string) string { fileBaseName := strings.TrimSuffix(templateName, filepath.Ext(templateName)) - return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fmt.Sprintf("%s.go", fileBaseName)) + return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fileBaseName+".go") } -// listOfTemplates return list of templates defined in TemplatesDir. func (c *Creator) listOfTemplates() ([]string, error) { var files []string err := filepath.WalkDir(c.TemplatesDir, func(path string, entry os.DirEntry, err error) error { @@ -100,13 +85,11 @@ func (c *Creator) listOfTemplates() ([]string, error) { return files, nil } -// makeAllDirs creates all required directories, which are in the file path. func (c *Creator) makeAllDirs(filePath string) error { dirPath := filepath.Dir(filePath) return os.MkdirAll(dirPath, os.ModePerm) } -// createFile just create a file and return it. func (c *Creator) createFile(filePath string) (*os.File, error) { outputFile, err := os.Create(filePath) if err != nil { @@ -115,29 +98,24 @@ func (c *Creator) createFile(filePath string) (*os.File, error) { return outputFile, nil } -// parseTemplate parse template passed as argument and with function map defined below. func (c *Creator) parseTemplate(templateName string) (*template.Template, error) { templatePath := filepath.Join(c.TemplatesDir, templateName) funcMap := template.FuncMap{ - "packageName": translate.PackageName, - "locationType": translate.LocationType, - "specParamType": translate.SpecParamType, - "xmlParamType": translate.XmlParamType, - "xmlName": translate.XmlName, - "xmlTag": translate.XmlTag, - "specifyEntryAssignment": translate.SpecifyEntryAssignment, - "normalizeAssignment": translate.NormalizeAssignment, - "specMatchesFunction": translate.SpecMatchesFunction, - "nestedSpecMatchesFunction": translate.NestedSpecMatchesFunction, - "omitEmpty": translate.OmitEmpty, - "contains": strings.Contains, + "packageName": translate.PackageName, + "locationType": translate.LocationType, + "specParamType": translate.SpecParamType, + "xmlParamType": translate.XmlParamType, + "xmlTag": translate.XmlTag, + "specifyEntryAssignment": translate.SpecifyEntryAssignment, + "specMatchesFunction": translate.SpecMatchesFunction, + "omitEmpty": translate.OmitEmpty, + "contains": func(full, part string) bool { + return strings.Contains(full, part) + }, "subtract": func(a, b int) int { return a - b }, - "generateEntryXpath": translate.GenerateEntryXpathForLocation, - "nestedSpecs": translate.NestedSpecs, - "createGoSuffixFromVersion": translate.CreateGoSuffixFromVersion, - "paramSupportedInVersion": translate.ParamSupportedInVersion, + "asEntryXpath": translate.AsEntryXpath, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 902434bc..c3a05b5f 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -223,3 +223,22 @@ func argumentTypeForSpecMatchesFunction(parent []string, param *properties.SpecP strings.Join(parent, ""), param.Name.CamelCase) } } + +func SpecifyEntryAssignment(param *properties.SpecParam) string { + calculatedAssignment := "" + if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { + calculatedAssignment = "util.StrToMem(o." + param.Name.CamelCase + ")" + } else { + calculatedAssignment = "o." + param.Name.CamelCase + } + + return calculatedAssignment +} + +func SpecMatchesFunction(param *properties.SpecParam) string { + calculatedFunction := "OptionalStringsMatch" + if param.Type == "list" { + calculatedFunction = "OrderedListsMatch" + } + return calculatedFunction +} diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index a9eb00b6..88a5e255 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -6,17 +6,17 @@ import ( "testing" ) -func TestGenerateEntryXpath(t *testing.T) { +func TestAsEntryXpath(t *testing.T) { // given // when - asEntryXpath, _ := GenerateEntryXpathForLocation("DeviceGroup", "{{ Entry $panorama_device }}") + asEntryXpath, _ := AsEntryXpath("DeviceGroup", "{{ Entry $panorama_device }}") // then assert.Equal(t, "util.AsEntryXpath([]string{o.DeviceGroup.PanoramaDevice}),", asEntryXpath) } -func TestSpecifyEntryAssignmentForFlatStructure(t *testing.T) { +func TestSpecifyEntryAssignment(t *testing.T) { // given paramTypeString := properties.SpecParam{ Name: &properties.NameVariant{ @@ -45,118 +45,12 @@ func TestSpecifyEntryAssignmentForFlatStructure(t *testing.T) { } // when - calculatedAssignmentString := SpecifyEntryAssignment("entry", ¶mTypeString, "") - calculatedAssignmentListString := SpecifyEntryAssignment("entry", ¶mTypeListString, "") + calculatedAssignmentString := SpecifyEntryAssignment(¶mTypeString) + calculatedAssignmentListString := SpecifyEntryAssignment(¶mTypeListString) // then - assert.Equal(t, "entry.Description = o.Description", calculatedAssignmentString) - assert.Equal(t, "entry.Tags = util.StrToMem(o.Tags)", calculatedAssignmentListString) -} - -func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - expectedAssignmentStreing := `nestedA := &specAXml{} -if o.A != nil { -if o.A.B != nil { -nestedA.B = &specABXml{} -if _, ok := o.Misc["AB"]; ok { -nestedA.B.Misc = o.Misc["AB"] -} -if o.A.B.C != nil { -nestedA.B.C = o.A.B.C -} -} -} -entry.A = nestedA -` - // when - calculatedAssignmentString := SpecifyEntryAssignment("entry", spec.Params["a"], "") - - // then - assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) -} - -func TestNormalizeAssignmentForNestedObject(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - expectedAssignmentStreing := `nestedA := &SpecA{} -if o.A != nil { -if o.A.B != nil { -nestedA.B = &SpecAB{} -if o.A.B.Misc != nil { -entry.Misc["AB"] = o.A.B.Misc -} -if o.A.B.C != nil { -nestedA.B.C = o.A.B.C -} -} -} -entry.A = nestedA -` - // when - calculatedAssignmentString := NormalizeAssignment("entry", spec.Params["a"], "") - - // then - assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) + assert.Equal(t, "o.Description", calculatedAssignmentString) + assert.Equal(t, "util.StrToMem(o.Tags)", calculatedAssignmentListString) } func TestSpecMatchesFunction(t *testing.T) { @@ -173,74 +67,6 @@ func TestSpecMatchesFunction(t *testing.T) { calculatedSpecMatchFunctionListString := SpecMatchesFunction(¶mTypeListString) // then - assert.Equal(t, "util.OptionalStringsMatch", calculatedSpecMatchFunctionString) - assert.Equal(t, "util.OrderedListsMatch", calculatedSpecMatchFunctionListString) -} - -func TestNestedSpecMatchesFunction(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - expectedNestedSpec := `func specMatchABC(a *SpecABC, b *SpecABC) bool {if a == nil && b != nil || a != nil && b == nil { - return false -} else if a == nil && b == nil { - return true -} -return *a == *b -} -func specMatchAB(a *SpecAB, b *SpecAB) bool {if a == nil && b != nil || a != nil && b == nil { - return false -} else if a == nil && b == nil { - return true -} -if !specMatchABC(a.C, b.C) { - return false -} -return true -} -func specMatchA(a *SpecA, b *SpecA) bool {if a == nil && b != nil || a != nil && b == nil { - return false -} else if a == nil && b == nil { - return true -} -if !specMatchAB(a.B, b.B) { - return false -} -return true -} -` - - // when - renderedNestedSpecMatches := NestedSpecMatchesFunction(&spec) - - // then - assert.Equal(t, expectedNestedSpec, renderedNestedSpecMatches) + assert.Equal(t, "OptionalStringsMatch", calculatedSpecMatchFunctionString) + assert.Equal(t, "OrderedListsMatch", calculatedSpecMatchFunctionListString) } diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index b3639ce1..78b441ca 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -1,131 +1,69 @@ package translate import ( - "fmt" - "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" - "github.com/paloaltonetworks/pan-os-codegen/pkg/version" - "strings" ) -// LocationType function used in template location.tmpl to generate location type name. func LocationType(location *properties.Location, pointer bool) string { prefix := "" if pointer { prefix = "*" } if location.Vars != nil { - return fmt.Sprintf("%s%sLocation", prefix, location.Name.CamelCase) + return prefix + location.Name.CamelCase + "Location" } else { return "bool" } } -// NestedSpecs go through all params and one of (recursively) and return map of all nested specs. -func NestedSpecs(spec *properties.Spec) (map[string]*properties.Spec, error) { - nestedSpecs := make(map[string]*properties.Spec) - - checkNestedSpecs([]string{}, spec, nestedSpecs) - - return nestedSpecs, nil -} - -func checkNestedSpecs(parent []string, spec *properties.Spec, nestedSpecs map[string]*properties.Spec) { - for _, param := range spec.Params { - updateNestedSpecs(append(parent, param.Name.CamelCase), param, nestedSpecs) +func SpecParamType(param *properties.SpecParam) string { + prefix := "" + if !param.Required { + prefix = "*" } - for _, param := range spec.OneOf { - updateNestedSpecs(append(parent, param.Name.CamelCase), param, nestedSpecs) + if param.Type == "list" { + prefix = "[]" } -} - -func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs map[string]*properties.Spec) { - if param.Spec != nil { - nestedSpecs[strings.Join(parent, "")] = param.Spec - checkNestedSpecs(parent, param.Spec, nestedSpecs) - } -} - -// SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func SpecParamType(parent string, param *properties.SpecParam) string { - prefix := determinePrefix(param, false) calculatedType := "" if param.Type == "list" && param.Items != nil { - calculatedType = determineListType(param) - } else if param.Spec != nil { - calculatedType = calculateNestedSpecType(parent, param) + calculatedType = param.Items.Type } else { calculatedType = param.Type } - return fmt.Sprintf("%s%s", prefix, calculatedType) + return prefix + calculatedType } -// XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func XmlParamType(parent string, param *properties.SpecParam) string { - prefix := determinePrefix(param, true) +func XmlParamType(param *properties.SpecParam) string { + prefix := "" + if !param.Required { + prefix = "*" + } calculatedType := "" - if isParamListAndProfileTypeIsMember(param) { + if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { calculatedType = "util.MemberType" - } else if param.Spec != nil { - calculatedType = calculateNestedXmlSpecType(parent, param) } else { calculatedType = param.Type } - return fmt.Sprintf("%s%s", prefix, calculatedType) -} - -func determinePrefix(param *properties.SpecParam, useMemberTypeStruct bool) string { - if param.Type == "list" && !(useMemberTypeStruct && isParamListAndProfileTypeIsMember(param)) { - return "[]" - } else if !param.Required { - return "*" - } - return "" -} - -func determineListType(param *properties.SpecParam) string { - if param.Items.Type == "object" && param.Items.Ref != nil { - return "string" - } - return param.Items.Type -} - -func calculateNestedSpecType(parent string, param *properties.SpecParam) string { - return fmt.Sprintf("Spec%s%s", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) + return prefix + calculatedType } -func calculateNestedXmlSpecType(parent string, param *properties.SpecParam) string { - return fmt.Sprintf("spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) -} - -// XmlName creates a string with xml name (e.g. `description`). -func XmlName(param *properties.SpecParam) string { - if param.Profiles != nil && len(param.Profiles) > 0 { - return param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1] +func XmlTag(param *properties.SpecParam) string { + suffix := "" + if !param.Required { + suffix = ",omitempty" } - return "" -} - -// XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`). -func XmlTag(param *properties.SpecParam) string { + calculatedTag := "" if param.Profiles != nil && len(param.Profiles) > 0 { - suffix := "" - if !param.Required { - suffix = ",omitempty" - } - - return fmt.Sprintf("`xml:\"%s%s\"`", XmlName(param), suffix) + calculatedTag = "`xml:\"" + param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1] + suffix + "\"`" } - - return "" + return calculatedTag } -// OmitEmpty return omitempty in XML tag for location, if there are variables defined. func OmitEmpty(location *properties.Location) string { if location.Vars != nil { return ",omitempty" @@ -133,71 +71,3 @@ func OmitEmpty(location *properties.Location) string { return "" } } - -// CreateGoSuffixFromVersion convert version into Go suffix e.g. 10.1.1 into _10_1_1 -func CreateGoSuffixFromVersion(version string) string { - if len(version) > 0 { - return fmt.Sprintf("_%s", strings.ReplaceAll(version, ".", "_")) - } else { - return version - } -} - -// ParamSupportedInVersion checks if param is supported in specific PAN-OS version -func ParamSupportedInVersion(param *properties.SpecParam, deviceVersionStr string) bool { - var supported []bool - if deviceVersionStr == "" { - supported = listOfProfileSupportForNotDefinedDeviceVersion(param, supported) - } else { - deviceVersion, err := version.New(deviceVersionStr) - if err != nil { - return false - } - - supported, err = listOfProfileSupportForDefinedDeviceVersion(param, supported, deviceVersion) - if err != nil { - return false - } - } - return allTrue(supported) -} - -func listOfProfileSupportForNotDefinedDeviceVersion(param *properties.SpecParam, supported []bool) []bool { - for _, profile := range param.Profiles { - if profile.FromVersion != "" { - supported = append(supported, profile.NotPresent) - } else { - supported = append(supported, true) - } - } - return supported -} - -func listOfProfileSupportForDefinedDeviceVersion(param *properties.SpecParam, supported []bool, deviceVersion version.Version) ([]bool, error) { - for _, profile := range param.Profiles { - if profile.FromVersion != "" { - paramProfileVersion, err := version.New(profile.FromVersion) - if err != nil { - return nil, err - } - - if deviceVersion.Gte(paramProfileVersion) { - supported = append(supported, !profile.NotPresent) - } else { - supported = append(supported, profile.NotPresent) - } - } else { - supported = append(supported, !profile.NotPresent) - } - } - return supported, nil -} - -func allTrue(values []bool) bool { - for _, value := range values { - if !value { - return false - } - } - return true -} diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 14a43c08..bf438e45 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -85,9 +85,9 @@ func TestSpecParamType(t *testing.T) { } // when - calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString) - calculatedTypeListString := SpecParamType("", ¶mTypeListString) - calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString) + calculatedTypeRequiredString := SpecParamType(¶mTypeRequiredString) + calculatedTypeListString := SpecParamType(¶mTypeListString) + calculatedTypeOptionalString := SpecParamType(¶mTypeOptionalString) // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -139,8 +139,8 @@ func TestXmlParamType(t *testing.T) { } // when - calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString) - calculatedTypeListString := XmlParamType("", ¶mTypeListString) + calculatedTypeRequiredString := XmlParamType(¶mTypeRequiredString) + calculatedTypeListString := XmlParamType(¶mTypeListString) // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -180,121 +180,3 @@ func TestXmlTag(t *testing.T) { assert.Equal(t, "`xml:\"description,omitempty\"`", calculatedXmlTagRequiredString) assert.Equal(t, "`xml:\"tag,omitempty\"`", calculatedXmlTagListString) } - -func TestNestedSpecs(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - // when - nestedSpecs, _ := NestedSpecs(&spec) - - // then - assert.NotNil(t, nestedSpecs) - assert.Contains(t, nestedSpecs, "A") - assert.Contains(t, nestedSpecs, "AB") -} - -func TestCreateGoSuffixFromVersion(t *testing.T) { - // given - - // when - suffix := CreateGoSuffixFromVersion("10.1.1") - - // then - assert.Equal(t, "_10_1_1", suffix) -} - -func TestParamSupportedInVersion(t *testing.T) { - // given - deviceVersion101 := "10.1.1" - deviceVersion90 := "9.0.0" - - paramName := properties.NameVariant{ - CamelCase: "test", - Underscore: "test", - } - - profileAlwaysPresent := properties.SpecParamProfile{ - Xpath: []string{"test"}, - } - profilePresentFrom10 := properties.SpecParamProfile{ - Xpath: []string{"test"}, - FromVersion: "10.0.0", - } - profileNotPresentFrom10 := properties.SpecParamProfile{ - Xpath: []string{"test"}, - FromVersion: "10.0.0", - NotPresent: true, - } - - paramPresentFrom10 := &properties.SpecParam{ - Type: "string", - Name: ¶mName, - Profiles: []*properties.SpecParamProfile{ - &profilePresentFrom10, - }, - } - paramAlwaysPresent := &properties.SpecParam{ - Type: "string", - Name: ¶mName, - Profiles: []*properties.SpecParamProfile{ - &profileAlwaysPresent, - }, - } - paramNotPresentFrom10 := &properties.SpecParam{ - Type: "string", - Name: ¶mName, - Profiles: []*properties.SpecParamProfile{ - &profileNotPresentFrom10, - }, - } - - // when - noVersionAndParamAlwaysPresent := ParamSupportedInVersion(paramAlwaysPresent, "") - noVersionAndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, "") - device10AndParamPresentFrom10 := ParamSupportedInVersion(paramPresentFrom10, deviceVersion101) - device10AndParamAlwaysPresent := ParamSupportedInVersion(paramAlwaysPresent, deviceVersion101) - device10AndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, deviceVersion101) - device9AndParamPresentFrom10 := ParamSupportedInVersion(paramPresentFrom10, deviceVersion90) - device9AndParamAlwaysPresent := ParamSupportedInVersion(paramAlwaysPresent, deviceVersion90) - device9AndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, deviceVersion90) - - // then - assert.True(t, noVersionAndParamAlwaysPresent) - assert.True(t, noVersionAndParamNotPresentFrom10) - assert.True(t, device10AndParamPresentFrom10) - assert.True(t, device10AndParamAlwaysPresent) - assert.False(t, device10AndParamNotPresentFrom10) - assert.False(t, device9AndParamPresentFrom10) - assert.True(t, device9AndParamAlwaysPresent) - assert.True(t, device9AndParamNotPresentFrom10) -} diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index acea588e..9bcd2b8c 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -1,228 +1,161 @@ -{{- if .Entry}} - package {{packageName .GoSdkPath}} - - import ( - "encoding/xml" - "fmt" - - "github.com/PaloAltoNetworks/pango/filtering" - "github.com/PaloAltoNetworks/pango/generic" - "github.com/PaloAltoNetworks/pango/util" - "github.com/PaloAltoNetworks/pango/version" - ) - - var ( - _ filtering.Fielder = &Entry{} - ) - - var ( - Suffix = []string{ - {{- $length := subtract (len .XpathSuffix) 1 }} - {{- range $index, $suffix := .XpathSuffix}}" - {{- $suffix}}"{{- if lt $index $length}},{{- end}} - {{- end}}} - ) - - type Entry{{createGoSuffixFromVersion ""}} struct { +package {{packageName .GoSdkPath}} + +import ( + "encoding/xml" + "fmt" + + "github.com/PaloAltoNetworks/pango/filtering" + "github.com/PaloAltoNetworks/pango/generic" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/version" +) + +var ( + _ filtering.Fielder = &Entry{} +) + +var ( + Suffix = []string{ + {{- $length := subtract (len .XpathSuffix) 1 }} + {{- range $index, $suffix := .XpathSuffix}}" + {{- $suffix}}"{{- if lt $index $length}},{{- end}} + {{- end}}} +) + +type Entry struct { + {{- if .Entry}} Name string - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- range $_, $param := .Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $param}} {{- end}} - - Misc map[string][]generic.Xml - } - - {{- range $name, $spec := nestedSpecs $.Spec }} - type Spec{{$name}}{{createGoSuffixFromVersion ""}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - } + {{- range $_, $param := .Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $param}} {{- end}} - {{- range $version := .SupportedVersions }} - type entryXmlContainer{{createGoSuffixFromVersion $version}} struct { - Answer []entryXml{{createGoSuffixFromVersion $version}} `xml:"entry"` - } - {{- end}} + Misc map[string][]generic.Xml +} - {{- range $version := .SupportedVersions }} - type entryXml{{createGoSuffixFromVersion $version}} struct { - XMLName xml.Name `xml:"entry"` - Name string `xml:"name,attr"` - {{- range $_, $param := $.Spec.Params}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} +type EntryXmlContainer struct { + Answer []EntryXml `xml:"entry"` +} - Misc []generic.Xml `xml:",any"` - } +type EntryXml struct { + {{- if .Entry}} + XMLName xml.Name `xml:"entry"` + Name string `xml:"name,attr"` {{- end}} - - {{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} - type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { - {{- range $_, $param := $spec.Params}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - Misc []generic.Xml `xml:",any"` - } - {{- end}} + {{- range $_, $param := .Spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{- end}} + {{- range $_, $param := .Spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} {{- end}} - func (e *Entry) Field(v string) (any, error) { - if v == "name" || v == "Name" { - return e.Name, nil - } + Misc []generic.Xml `xml:",any"` +} + +func (e *Entry) CopyMiscFrom(v *Entry) { + if v == nil || len(v.Misc) == 0 { + return + } + + e.Misc = make(map[string][]generic.Xml) + for key := range v.Misc { + e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) + } +} + +func (e *Entry) Field(v string) (any, error) { + {{- if .Entry}} + if v == "name" || v == "Name" { + return e.Name, nil + } + {{- end}} {{- range $_, $param := .Spec.Params}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { return e.{{$param.Name.CamelCase}}, nil - } - {{- if eq $param.Type "list"}} - if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { - return int64(len(e.{{$param.Name.CamelCase}})), nil - } - {{- end}} + } + {{- if eq $param.Type "list"}} + if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { + return int64(len(e.{{$param.Name.CamelCase}})), nil + } + {{- end}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { return e.{{$param.Name.CamelCase}}, nil - } - {{- end}} - - return nil, fmt.Errorf("unknown field") } - - func Versioning(vn version.Number) (Specifier, Normalizer, error) { - {{- $numberOfVersions := len .SupportedVersions }} - {{- if gt $numberOfVersions 1}} - {{- range $index, $version := .SupportedVersions }} - {{- if ne $version ""}} - version{{createGoSuffixFromVersion $version}}, err := version.New("{{$version}}") - if err != nil { - return nil, nil, err - } - {{- end}} - {{- end}} - {{- range $index, $version := .SupportedVersions }} - {{- if ne $version ""}} - {{- if eq $index 1}} - if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return specifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil - {{- else}} - } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return specifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil - {{- end}} - {{- end}} - {{- end}} - } else { - {{- end}} - return specifyEntry, &entryXmlContainer{}, nil - {{- if gt $numberOfVersions 1}} - } {{- end}} - } - {{- range $version := .SupportedVersions }} - func specifyEntry{{createGoSuffixFromVersion $version}}(o Entry) (any, error) { - entry := entryXml{{createGoSuffixFromVersion $version}}{} + return nil, fmt.Errorf("unknown field") +} - entry.Name = o.Name - {{- range $_, $param := $.Spec.Params}} - {{specifyEntryAssignment "entry" $param $version}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{specifyEntryAssignment "entry" $param $version}} - {{- end}} +func Versioning(vn version.Number) (Specifier, Normalizer, error) { + return SpecifyEntry, &EntryXmlContainer{}, nil +} - entry.Misc = o.Misc["Entry"] +func SpecifyEntry(o Entry) (any, error) { + ans := EntryXml{} - return entry, nil - } + {{- if .Entry}} + ans.Name = o.Name {{- end}} + {{- range $_, $param := .Spec.Params}} + ans.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} + {{- end}} + {{- range $_, $param := .Spec.OneOf}} + ans.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} + {{- end}} + + ans.Misc = o.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] + + return ans, nil +} - {{- range $version := .SupportedVersions }} - func (c *entryXmlContainer{{createGoSuffixFromVersion $version}}) Normalize() ([]Entry, error) { - entryList := make([]Entry, 0, len(c.Answer)) - for _, o := range c.Answer { - entry := Entry{ - Misc: make(map[string][]generic.Xml), - } - entry.Name = o.Name - {{- range $_, $param := $.Spec.Params}} - {{normalizeAssignment "entry" $param $version}} +func (c *EntryXmlContainer) Normalize() ([]Entry, error) { + ans := make([]Entry, 0, len(c.Answer)) + for _, var0 := range c.Answer { + var1 := Entry{ + Misc: make(map[string][]generic.Xml), + } + {{- if .Entry}} + var1.Name = var0.Name {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{normalizeAssignment "entry" $param $version}} + {{- range $_, $param := .Spec.Params}} + var1.{{$param.Name.CamelCase}} = var0.{{$param.Name.CamelCase}} + {{- end}} + {{- range $_, $param := .Spec.OneOf}} + var1.{{$param.Name.CamelCase}} = var0.{{$param.Name.CamelCase}} {{- end}} - entry.Misc["Entry"] = o.Misc + var1.Misc[fmt.Sprintf("%s\n%s", "Entry", var0.Name)] = var0.Misc - entryList = append(entryList, entry) - } + ans = append(ans, var1) + } - return entryList, nil - } - {{- end}} + return ans, nil +} - func SpecMatches(a, b *Entry) bool { - if a == nil && b != nil || a != nil && b == nil { - return false - } else if a == nil && b == nil { - return true - } +func SpecMatches(a, b *Entry) bool { + if a == nil && b != nil || a != nil && b == nil { + return false + } else if a == nil && b == nil { + return true + } - // Don't compare Name. + // Don't compare Name. {{- range $_, $param := .Spec.Params}} - if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { return false - } + } {{- end}} {{- range $_, $param := .Spec.OneOf}} - if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { return false - } - {{- end}} - - return true } + {{- end}} - {{nestedSpecMatchesFunction $.Spec}} -{{- end}} \ No newline at end of file + return true +} From 948fe2456b8593524adc0284b92c4d87dc98458d Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 13:47:01 +0100 Subject: [PATCH 02/66] start work on service.go --- templates/sdk/service.tmpl | 443 ++++++++++++++++++++++++++++++++++++- 1 file changed, 442 insertions(+), 1 deletion(-) diff --git a/templates/sdk/service.tmpl b/templates/sdk/service.tmpl index 3b3d1518..677fb936 100644 --- a/templates/sdk/service.tmpl +++ b/templates/sdk/service.tmpl @@ -1 +1,442 @@ -package {{packageName .GoSdkPath}} \ No newline at end of file +package {{packageName .GoSdkPath}} + +import ( + "context" + "fmt" + + "github.com/PaloAltoNetworks/pango/errors" + "github.com/PaloAltoNetworks/pango/filtering" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/xmlapi" +) + +type Service struct { + client util.PangoClient +} + +func NewService(client util.PangoClient) *Service { + return &Service{ + client: client, + } +} + +// Create creates the given config object. +func (s *Service) Create(ctx context.Context, loc Location, entry Entry) (*Entry, error) { + if entry.Name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + + specifier, _, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + createSpec, err := specifier(entry) + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: "set", + Xpath: util.AsXpath(path[:len(path)-1]), + Element: createSpec, + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, false, nil); err != nil { + return nil, err + } + + return s.Read(ctx, loc, entry.Name, "get") +} + +// Read returns the given config object, using the specified action. +// Param action should be either "get" or "show". +func (s *Service) Read(ctx context.Context, loc Location, name, action string) (*Entry, error) { + if name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, name) + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + // action=show returns empty config like this + if err.Error() == "No such node" && action == "show" { + return nil, errors.ObjectNotFound() + } + return nil, err + } + + list, err := normalizer.Normalize() + if err != nil { + return nil, err + } else if len(list) != 1 { + return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) + } + + return &list[0], nil +} + +// ReadFromConfig returns the given config object from the loaded XML config. +// Requires that client.LoadPanosConfig() has been invoked. +func (s *Service) ReadFromConfig(ctx context.Context, loc Location, name string) (*Entry, error) { + if name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, name) + if err != nil { + return nil, err + } + + if _, err = s.client.ReadFromConfig(ctx, path, true, normalizer); err != nil { + return nil, err + } + + list, err := normalizer.Normalize() + if err != nil { + return nil, err + } else if len(list) != 1 { + return nil, fmt.Errorf("expected to find 1 entry, got %d", len(list)) + } + + return &list[0], nil +} + +// Update updates the given config object, then returns the result. +func (s *Service) Update(ctx context.Context, loc Location, entry Entry, oldName string) (*Entry, error) { + if entry.Name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + updates := xmlapi.NewMultiConfig(2) + specifier, _, err := Versioning(vn) + if err != nil { + return nil, err + } + + var old *Entry + if oldName != "" && oldName != entry.Name { + path, err := loc.Xpath(vn, oldName) + if err != nil { + return nil, err + } + + old, err = s.Read(ctx, loc, oldName, "get") + + updates.Add(&xmlapi.Config{ + Action: "rename", + Xpath: util.AsXpath(path), + NewName: entry.Name, + }) + } else { + old, err = s.Read(ctx, loc, entry.Name, "get") + } + if err != nil { + return nil, err + } + + if !SpecMatches(&entry, old) { + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + entry.CopyMiscFrom(old) + + updateSpec, err := specifier(entry) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "edit", + Xpath: util.AsXpath(path), + Element: updateSpec, + }) + } + + if len(updates.Operations) != 0 { + if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { + return nil, err + } + } + + return s.Read(ctx, loc, entry.Name, "get") +} + +// Delete deletes the given item. +func (s *Service) Delete(ctx context.Context, loc Location, name string) error { + if name == "" { + return errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + + path, err := loc.Xpath(vn, name) + if err != nil { + return err + } + + cmd := &xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + _, _, err = s.client.Communicate(ctx, cmd, false, nil) + + return err +} + +// List returns a list of service objects using the given action. +// Param action should be either "get" or "show". +// Params filter and quote are for client side filtering. +func (s *Service) List(ctx context.Context, loc Location, action, filter, quote string) ([]Entry, error) { + var err error + + var logic *filtering.Group + if filter != "" { + logic, err = filtering.Parse(filter, quote) + if err != nil { + return nil, err + } + } + + vn := s.client.Versioning() + + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, "") + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + // action=show returns empty config like this, it is not an error. + if err.Error() == "No such node" && action == "show" { + return nil, nil + } + return nil, err + } + + listing, err := normalizer.Normalize() + if err != nil || logic == nil { + return listing, err + } + + filtered := make([]Entry, 0, len(listing)) + for _, x := range listing { + ok, err := logic.Matches(&x) + if err != nil { + return nil, err + } + if ok { + filtered = append(filtered, x) + } + } + + return filtered, nil +} + +// ListFromConfig returns a list of objects at the given location. +// Requires that client.LoadPanosConfig() has been invoked. +// Params filter and quote are for client side filtering. +func (s *Service) ListFromConfig(ctx context.Context, loc Location, filter, quote string) ([]Entry, error) { + var err error + + var logic *filtering.Group + if filter != "" { + logic, err = filtering.Parse(filter, quote) + if err != nil { + return nil, err + } + } + + vn := s.client.Versioning() + + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, "") + if err != nil { + return nil, err + } + path = path[:len(path)-1] + + if _, err = s.client.ReadFromConfig(ctx, path, false, normalizer); err != nil { + return nil, err + } + + listing, err := normalizer.Normalize() + if err != nil || logic == nil { + return listing, err + } + + filtered := make([]Entry, 0, len(listing)) + for _, x := range listing { + ok, err := logic.Matches(&x) + if err != nil { + return nil, err + } + if ok { + filtered = append(filtered, x) + } + } + + return filtered, nil +} + +// ConfigureGroup performs all necessary set / edit / delete commands to ensure +// that the objects are configured as specified. +func (s *Service) ConfigureGroup(ctx context.Context, loc Location, entries []Entry, prevNames []string) ([]Entry, error) { + var err error + + vn := s.client.Versioning() + updates := xmlapi.NewMultiConfig(len(prevNames) + len(entries)) + specifier, _, err := Versioning(vn) + if err != nil { + return nil, err + } + + curObjs, err := s.List(ctx, loc, "get", "", "") + if err != nil { + return nil, err + } + + // Determine set vs edit for desired objects. + for _, entry := range entries { + var found bool + for _, live := range curObjs { + if entry.Name == live.Name { + found = true + if !SpecMatches(&entry, &live) { + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + entry.CopyMiscFrom(&live) + + elm, err := specifier(entry) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "edit", + Xpath: util.AsXpath(path), + Element: elm, + }) + } + break + } + } + + if !found { + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + elm, err := specifier(entry) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "set", + Xpath: util.AsXpath(path), + Element: elm, + }) + } + } + + // Determine which old objects need to be removed. + if len(prevNames) != 0 { + for _, name := range prevNames { + var found bool + for _, entry := range entries { + if entry.Name == name { + found = true + break + } + } + + if !found { + path, err := loc.Xpath(vn, name) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(path), + }) + } + } + } + + if len(updates.Operations) != 0 { + if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { + return nil, err + } + } + + curObjs, err = s.List(ctx, loc, "get", "", "") + if err != nil { + return nil, err + } + + ans := make([]Entry, 0, len(entries)) + for _, entry := range entries { + for _, live := range curObjs { + if entry.Name == live.Name { + ans = append(ans, live) + break + } + } + } + + return ans, nil +} From 024e86b53e0166438d548202b9e77ba38ede12ac Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 13:58:16 +0100 Subject: [PATCH 03/66] refactor names in template for entry.go --- templates/sdk/entry.tmpl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 9bcd2b8c..9941a7ad 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -97,45 +97,45 @@ func Versioning(vn version.Number) (Specifier, Normalizer, error) { } func SpecifyEntry(o Entry) (any, error) { - ans := EntryXml{} + entry := EntryXml{} {{- if .Entry}} - ans.Name = o.Name + entry.Name = o.Name {{- end}} {{- range $_, $param := .Spec.Params}} - ans.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} + entry.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - ans.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} + entry.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} {{- end}} - ans.Misc = o.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] + entry.Misc = o.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] - return ans, nil + return entry, nil } func (c *EntryXmlContainer) Normalize() ([]Entry, error) { - ans := make([]Entry, 0, len(c.Answer)) - for _, var0 := range c.Answer { - var1 := Entry{ + entryList := make([]Entry, 0, len(c.Answer)) + for _, entryXml := range c.Answer { + entry := Entry{ Misc: make(map[string][]generic.Xml), } {{- if .Entry}} - var1.Name = var0.Name + entry.Name = entryXml.Name {{- end}} {{- range $_, $param := .Spec.Params}} - var1.{{$param.Name.CamelCase}} = var0.{{$param.Name.CamelCase}} + entry.{{$param.Name.CamelCase}} = entryXml.{{$param.Name.CamelCase}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - var1.{{$param.Name.CamelCase}} = var0.{{$param.Name.CamelCase}} + entry.{{$param.Name.CamelCase}} = entryXml.{{$param.Name.CamelCase}} {{- end}} - var1.Misc[fmt.Sprintf("%s\n%s", "Entry", var0.Name)] = var0.Misc + entry.Misc[fmt.Sprintf("%s\n%s", "Entry", entryXml.Name)] = entryXml.Misc - ans = append(ans, var1) + entryList = append(entryList, entry) } - return ans, nil + return entryList, nil } func SpecMatches(a, b *Entry) bool { From 9b1a666d9af362f176c0b7e9aed116ce0d58f54d Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 15:05:53 +0100 Subject: [PATCH 04/66] add not_present and from_version to SpecParamProfile --- pkg/properties/normalized_test.go | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index bd0aeab8..767d3898 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -85,7 +85,6 @@ spec: profiles: - xpath: ["description"] - from_version: "10.1.1" tags: description: 'The administrative tags.' type: 'list' @@ -124,7 +123,6 @@ spec: profiles: - xpath: ["ip-wildcard"] - from_version: "11.1.2" ` func TestUnmarshallAddressSpecFile(t *testing.T) { @@ -268,7 +266,7 @@ spec: - xpath: - description not_present: false - from_version: 10.1.1 + from_version: "" spec: null tags: name: @@ -285,7 +283,6 @@ spec: length: min: null max: 127 - ref: [] profiles: - xpath: - tag @@ -348,13 +345,14 @@ spec: - xpath: - ip-wildcard not_present: false - from_version: 11.1.2 + from_version: "" spec: null ` // when yamlParsedData, _ := ParseSpec([]byte(sampleSpec)) yamlDump, _ := yaml.Marshal(&yamlParsedData) + //fmt.Printf("%s", string(yamlDump)) // then assert.NotNilf(t, yamlDump, "Marshalled data cannot be nil") @@ -414,15 +412,3 @@ xpath_suffix: // then assert.Len(t, problems, 2, "Not all expected validation checks failed") } - -func TestGettingListOfSupportedVersions(t *testing.T) { - // given - yamlParsedData, _ := ParseSpec([]byte(sampleSpec)) - - // when - versions := yamlParsedData.SupportedVersions() - - // then - assert.NotNilf(t, yamlParsedData, "Unmarshalled data cannot be nil") - assert.Contains(t, versions, "10.1.1") -} From 5c148fce0379f1c643a9f9b9d29baa9c73787272 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 15:40:33 +0100 Subject: [PATCH 05/66] current service is only prepared for entry --- templates/sdk/service.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/sdk/service.tmpl b/templates/sdk/service.tmpl index 677fb936..b142799d 100644 --- a/templates/sdk/service.tmpl +++ b/templates/sdk/service.tmpl @@ -20,6 +20,7 @@ func NewService(client util.PangoClient) *Service { } } +{{- if .Entry}} // Create creates the given config object. func (s *Service) Create(ctx context.Context, loc Location, entry Entry) (*Entry, error) { if entry.Name == "" { @@ -440,3 +441,4 @@ func (s *Service) ConfigureGroup(ctx context.Context, loc Location, entries []En return ans, nil } +{{- end}} \ No newline at end of file From dd3d1db57f64231e9326d863271180e5be339cf7 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 15:41:12 +0100 Subject: [PATCH 06/66] clear service template --- templates/sdk/service.tmpl | 445 +------------------------------------ 1 file changed, 1 insertion(+), 444 deletions(-) diff --git a/templates/sdk/service.tmpl b/templates/sdk/service.tmpl index b142799d..3b3d1518 100644 --- a/templates/sdk/service.tmpl +++ b/templates/sdk/service.tmpl @@ -1,444 +1 @@ -package {{packageName .GoSdkPath}} - -import ( - "context" - "fmt" - - "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/filtering" - "github.com/PaloAltoNetworks/pango/util" - "github.com/PaloAltoNetworks/pango/xmlapi" -) - -type Service struct { - client util.PangoClient -} - -func NewService(client util.PangoClient) *Service { - return &Service{ - client: client, - } -} - -{{- if .Entry}} -// Create creates the given config object. -func (s *Service) Create(ctx context.Context, loc Location, entry Entry) (*Entry, error) { - if entry.Name == "" { - return nil, errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - - specifier, _, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, entry.Name) - if err != nil { - return nil, err - } - - createSpec, err := specifier(entry) - if err != nil { - return nil, err - } - - cmd := &xmlapi.Config{ - Action: "set", - Xpath: util.AsXpath(path[:len(path)-1]), - Element: createSpec, - Target: s.client.GetTarget(), - } - - if _, _, err = s.client.Communicate(ctx, cmd, false, nil); err != nil { - return nil, err - } - - return s.Read(ctx, loc, entry.Name, "get") -} - -// Read returns the given config object, using the specified action. -// Param action should be either "get" or "show". -func (s *Service) Read(ctx context.Context, loc Location, name, action string) (*Entry, error) { - if name == "" { - return nil, errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, name) - if err != nil { - return nil, err - } - - cmd := &xmlapi.Config{ - Action: action, - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } - - if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { - // action=show returns empty config like this - if err.Error() == "No such node" && action == "show" { - return nil, errors.ObjectNotFound() - } - return nil, err - } - - list, err := normalizer.Normalize() - if err != nil { - return nil, err - } else if len(list) != 1 { - return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) - } - - return &list[0], nil -} - -// ReadFromConfig returns the given config object from the loaded XML config. -// Requires that client.LoadPanosConfig() has been invoked. -func (s *Service) ReadFromConfig(ctx context.Context, loc Location, name string) (*Entry, error) { - if name == "" { - return nil, errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, name) - if err != nil { - return nil, err - } - - if _, err = s.client.ReadFromConfig(ctx, path, true, normalizer); err != nil { - return nil, err - } - - list, err := normalizer.Normalize() - if err != nil { - return nil, err - } else if len(list) != 1 { - return nil, fmt.Errorf("expected to find 1 entry, got %d", len(list)) - } - - return &list[0], nil -} - -// Update updates the given config object, then returns the result. -func (s *Service) Update(ctx context.Context, loc Location, entry Entry, oldName string) (*Entry, error) { - if entry.Name == "" { - return nil, errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - updates := xmlapi.NewMultiConfig(2) - specifier, _, err := Versioning(vn) - if err != nil { - return nil, err - } - - var old *Entry - if oldName != "" && oldName != entry.Name { - path, err := loc.Xpath(vn, oldName) - if err != nil { - return nil, err - } - - old, err = s.Read(ctx, loc, oldName, "get") - - updates.Add(&xmlapi.Config{ - Action: "rename", - Xpath: util.AsXpath(path), - NewName: entry.Name, - }) - } else { - old, err = s.Read(ctx, loc, entry.Name, "get") - } - if err != nil { - return nil, err - } - - if !SpecMatches(&entry, old) { - path, err := loc.Xpath(vn, entry.Name) - if err != nil { - return nil, err - } - - entry.CopyMiscFrom(old) - - updateSpec, err := specifier(entry) - if err != nil { - return nil, err - } - - updates.Add(&xmlapi.Config{ - Action: "edit", - Xpath: util.AsXpath(path), - Element: updateSpec, - }) - } - - if len(updates.Operations) != 0 { - if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { - return nil, err - } - } - - return s.Read(ctx, loc, entry.Name, "get") -} - -// Delete deletes the given item. -func (s *Service) Delete(ctx context.Context, loc Location, name string) error { - if name == "" { - return errors.NameNotSpecifiedError - } - - vn := s.client.Versioning() - - path, err := loc.Xpath(vn, name) - if err != nil { - return err - } - - cmd := &xmlapi.Config{ - Action: "delete", - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } - - _, _, err = s.client.Communicate(ctx, cmd, false, nil) - - return err -} - -// List returns a list of service objects using the given action. -// Param action should be either "get" or "show". -// Params filter and quote are for client side filtering. -func (s *Service) List(ctx context.Context, loc Location, action, filter, quote string) ([]Entry, error) { - var err error - - var logic *filtering.Group - if filter != "" { - logic, err = filtering.Parse(filter, quote) - if err != nil { - return nil, err - } - } - - vn := s.client.Versioning() - - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, "") - if err != nil { - return nil, err - } - - cmd := &xmlapi.Config{ - Action: action, - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } - - if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { - // action=show returns empty config like this, it is not an error. - if err.Error() == "No such node" && action == "show" { - return nil, nil - } - return nil, err - } - - listing, err := normalizer.Normalize() - if err != nil || logic == nil { - return listing, err - } - - filtered := make([]Entry, 0, len(listing)) - for _, x := range listing { - ok, err := logic.Matches(&x) - if err != nil { - return nil, err - } - if ok { - filtered = append(filtered, x) - } - } - - return filtered, nil -} - -// ListFromConfig returns a list of objects at the given location. -// Requires that client.LoadPanosConfig() has been invoked. -// Params filter and quote are for client side filtering. -func (s *Service) ListFromConfig(ctx context.Context, loc Location, filter, quote string) ([]Entry, error) { - var err error - - var logic *filtering.Group - if filter != "" { - logic, err = filtering.Parse(filter, quote) - if err != nil { - return nil, err - } - } - - vn := s.client.Versioning() - - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } - - path, err := loc.Xpath(vn, "") - if err != nil { - return nil, err - } - path = path[:len(path)-1] - - if _, err = s.client.ReadFromConfig(ctx, path, false, normalizer); err != nil { - return nil, err - } - - listing, err := normalizer.Normalize() - if err != nil || logic == nil { - return listing, err - } - - filtered := make([]Entry, 0, len(listing)) - for _, x := range listing { - ok, err := logic.Matches(&x) - if err != nil { - return nil, err - } - if ok { - filtered = append(filtered, x) - } - } - - return filtered, nil -} - -// ConfigureGroup performs all necessary set / edit / delete commands to ensure -// that the objects are configured as specified. -func (s *Service) ConfigureGroup(ctx context.Context, loc Location, entries []Entry, prevNames []string) ([]Entry, error) { - var err error - - vn := s.client.Versioning() - updates := xmlapi.NewMultiConfig(len(prevNames) + len(entries)) - specifier, _, err := Versioning(vn) - if err != nil { - return nil, err - } - - curObjs, err := s.List(ctx, loc, "get", "", "") - if err != nil { - return nil, err - } - - // Determine set vs edit for desired objects. - for _, entry := range entries { - var found bool - for _, live := range curObjs { - if entry.Name == live.Name { - found = true - if !SpecMatches(&entry, &live) { - path, err := loc.Xpath(vn, entry.Name) - if err != nil { - return nil, err - } - - entry.CopyMiscFrom(&live) - - elm, err := specifier(entry) - if err != nil { - return nil, err - } - - updates.Add(&xmlapi.Config{ - Action: "edit", - Xpath: util.AsXpath(path), - Element: elm, - }) - } - break - } - } - - if !found { - path, err := loc.Xpath(vn, entry.Name) - if err != nil { - return nil, err - } - - elm, err := specifier(entry) - if err != nil { - return nil, err - } - - updates.Add(&xmlapi.Config{ - Action: "set", - Xpath: util.AsXpath(path), - Element: elm, - }) - } - } - - // Determine which old objects need to be removed. - if len(prevNames) != 0 { - for _, name := range prevNames { - var found bool - for _, entry := range entries { - if entry.Name == name { - found = true - break - } - } - - if !found { - path, err := loc.Xpath(vn, name) - if err != nil { - return nil, err - } - - updates.Add(&xmlapi.Config{ - Action: "delete", - Xpath: util.AsXpath(path), - }) - } - } - } - - if len(updates.Operations) != 0 { - if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { - return nil, err - } - } - - curObjs, err = s.List(ctx, loc, "get", "", "") - if err != nil { - return nil, err - } - - ans := make([]Entry, 0, len(entries)) - for _, entry := range entries { - for _, live := range curObjs { - if entry.Name == live.Name { - ans = append(ans, live) - break - } - } - } - - return ans, nil -} -{{- end}} \ No newline at end of file +package {{packageName .GoSdkPath}} \ No newline at end of file From 2fb79ed1e7d9e0870db694dca1b827babc371f72 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 16:24:19 +0100 Subject: [PATCH 07/66] render entry.go only for specs with entry --- pkg/generate/generator.go | 23 ++++++++++++++++------- templates/sdk/entry.tmpl | 12 ++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 734f763a..66737241 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -1,7 +1,9 @@ package generate import ( + "bytes" "fmt" + "io" "log" "os" "path/filepath" @@ -42,20 +44,27 @@ func (c *Creator) RenderTemplate() error { return fmt.Errorf("error creating directories for %s: %w", filePath, err) } - outputFile, err := os.Create(filePath) - if err != nil { - return fmt.Errorf("error creating file %s: %w", filePath, err) - } - defer outputFile.Close() - tmpl, err := c.parseTemplate(templateName) if err != nil { return fmt.Errorf("error parsing template %s: %w", templateName, err) } - if err := tmpl.Execute(outputFile, c.Spec); err != nil { + var data bytes.Buffer + if err := tmpl.Execute(&data, c.Spec); err != nil { return fmt.Errorf("error executing template %s: %w", templateName, err) } + if data.Len() > 0 { + outputFile, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("error creating file %s: %w", filePath, err) + } + defer outputFile.Close() + + _, err = io.Copy(outputFile, &data) + if err != nil { + return err + } + } } return nil } diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 9941a7ad..5fe723cd 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -1,3 +1,4 @@ +{{- if .Entry}} package {{packageName .GoSdkPath}} import ( @@ -23,9 +24,7 @@ var ( ) type Entry struct { - {{- if .Entry}} Name string - {{- end}} {{- range $_, $param := .Spec.Params}} {{$param.Name.CamelCase}} {{specParamType $param}} {{- end}} @@ -41,10 +40,8 @@ type EntryXmlContainer struct { } type EntryXml struct { - {{- if .Entry}} XMLName xml.Name `xml:"entry"` Name string `xml:"name,attr"` - {{- end}} {{- range $_, $param := .Spec.Params}} {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} {{- end}} @@ -67,11 +64,9 @@ func (e *Entry) CopyMiscFrom(v *Entry) { } func (e *Entry) Field(v string) (any, error) { - {{- if .Entry}} if v == "name" || v == "Name" { return e.Name, nil } - {{- end}} {{- range $_, $param := .Spec.Params}} if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { @@ -99,9 +94,7 @@ func Versioning(vn version.Number) (Specifier, Normalizer, error) { func SpecifyEntry(o Entry) (any, error) { entry := EntryXml{} - {{- if .Entry}} entry.Name = o.Name - {{- end}} {{- range $_, $param := .Spec.Params}} entry.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} {{- end}} @@ -120,9 +113,7 @@ func (c *EntryXmlContainer) Normalize() ([]Entry, error) { entry := Entry{ Misc: make(map[string][]generic.Xml), } - {{- if .Entry}} entry.Name = entryXml.Name - {{- end}} {{- range $_, $param := .Spec.Params}} entry.{{$param.Name.CamelCase}} = entryXml.{{$param.Name.CamelCase}} {{- end}} @@ -159,3 +150,4 @@ func SpecMatches(a, b *Entry) bool { return true } +{{- end}} \ No newline at end of file From 8372889a278ccbd7752f2c977475503cb5ff9f16 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 11 Mar 2024 23:02:05 +0100 Subject: [PATCH 08/66] create structs for nested specs --- pkg/generate/generator.go | 1 + pkg/properties/normalized.go | 97 +++++++++++------------------------ pkg/translate/structs.go | 29 +++++++++++ pkg/translate/structs_test.go | 42 +++++++++++++++ templates/sdk/entry.tmpl | 22 ++++++++ 5 files changed, 124 insertions(+), 67 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 66737241..23368199 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -125,6 +125,7 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) return a - b }, "asEntryXpath": translate.AsEntryXpath, + "nestedSpecs": translate.NestedSpecs, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index f6a0137c..b19999aa 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -1,6 +1,7 @@ package properties import ( + "errors" "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/content" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" @@ -96,7 +97,6 @@ type SpecParamCount struct { type SpecParamItems struct { Type string `json:"type" yaml:"type"` Length *SpecParamItemsLength `json:"length" yaml:"length"` - Ref []*string `json:"ref" yaml:"ref"` } type SpecParamItemsLength struct { @@ -111,7 +111,6 @@ type SpecParamProfile struct { FromVersion string `json:"from_version" yaml:"from_version"` } -// GetNormalizations get list of all specs (normalizations). func GetNormalizations() ([]string, error) { _, loc, _, ok := runtime.Caller(0) if !ok { @@ -140,7 +139,6 @@ func GetNormalizations() ([]string, error) { return files, nil } -// ParseSpec parse single spec (unmarshal file), add name variants for locations and params, add default types for params. func ParseSpec(input []byte) (*Normalization, error) { var spec Normalization @@ -167,7 +165,6 @@ func ParseSpec(input []byte) (*Normalization, error) { return &spec, err } -// AddNameVariantsForLocation add name variants for location (under_score and CamelCase). func (spec *Normalization) AddNameVariantsForLocation() error { for key, location := range spec.Locations { location.Name = &NameVariant{ @@ -186,7 +183,6 @@ func (spec *Normalization) AddNameVariantsForLocation() error { return nil } -// AddNameVariantsForParams recursively add name variants for params for nested specs. func AddNameVariantsForParams(name string, param *SpecParam) error { param.Name = &NameVariant{ Underscore: name, @@ -207,7 +203,6 @@ func AddNameVariantsForParams(name string, param *SpecParam) error { return nil } -// AddNameVariantsForParams add name variants for params (under_score and CamelCase). func (spec *Normalization) AddNameVariantsForParams() error { if spec.Spec != nil { for key, param := range spec.Spec.Params { @@ -224,34 +219,40 @@ func (spec *Normalization) AddNameVariantsForParams() error { return nil } -// addDefaultTypesForParams recursively add default types for params for nested specs. -func addDefaultTypesForParams(params map[string]*SpecParam) error { - for _, param := range params { - if param.Type == "" && param.Spec == nil { - param.Type = "string" - } +func AddDefaultTypesForParams(param *SpecParam) error { + if param.Type == "" { + param.Type = "string" + } - if param.Spec != nil { - if err := addDefaultTypesForParams(param.Spec.Params); err != nil { + if param.Spec != nil { + for _, childParam := range param.Spec.Params { + if err := AddDefaultTypesForParams(childParam); err != nil { return err } - if err := addDefaultTypesForParams(param.Spec.OneOf); err != nil { + } + for _, childParam := range param.Spec.OneOf { + if err := AddDefaultTypesForParams(childParam); err != nil { return err } } + return nil + } else { + return nil } - - return nil } -// AddDefaultTypesForParams ensures all params within Spec have a default type if not specified. +// AddDefaultTypesForParams ensures all SpecParams within Spec have a default type if not specified. func (spec *Normalization) AddDefaultTypesForParams() error { if spec.Spec != nil { - if err := addDefaultTypesForParams(spec.Spec.Params); err != nil { - return err + for _, childParam := range spec.Spec.Params { + if err := AddDefaultTypesForParams(childParam); err != nil { + return err + } } - if err := addDefaultTypesForParams(spec.Spec.OneOf); err != nil { - return err + for _, childParam := range spec.Spec.OneOf { + if err := AddDefaultTypesForParams(childParam); err != nil { + return err + } } return nil } else { @@ -259,75 +260,37 @@ func (spec *Normalization) AddDefaultTypesForParams() error { } } -// Sanity basic checks for specification (normalization) e.g. check if at least 1 location is defined. func (spec *Normalization) Sanity() error { if spec.Name == "" { - return fmt.Errorf("name is required") + return errors.New("name is required") } if spec.Locations == nil { - return fmt.Errorf("at least 1 location is required") + return errors.New("at least 1 location is required") } if spec.GoSdkPath == nil { - return fmt.Errorf("golang SDK path is required") + return errors.New("golang SDK path is required") } return nil } -// Validate validations for specification (normalization) e.g. check if XPath contain /. func (spec *Normalization) Validate() []error { var checks []error if strings.Contains(spec.TerraformProviderSuffix, "panos_") { - checks = append(checks, fmt.Errorf("suffix for Terraform provider cannot contain `panos_`")) + checks = append(checks, errors.New("suffix for Terraform provider cannot contain `panos_`")) } for _, suffix := range spec.XpathSuffix { if strings.Contains(suffix, "/") { - checks = append(checks, fmt.Errorf("XPath cannot contain /")) + checks = append(checks, errors.New("XPath cannot contain /")) } } if len(spec.Locations) < 1 { - checks = append(checks, fmt.Errorf("at least 1 location is required")) + checks = append(checks, errors.New("at least 1 location is required")) } if len(spec.GoSdkPath) < 2 { - checks = append(checks, fmt.Errorf("golang SDK path should contain at least 2 elements of the path")) + checks = append(checks, errors.New("golang SDK path should contain at least 2 elements of the path")) } return checks } - -// SupportedVersions provides list of all supported versions in format MAJOR.MINOR.PATCH -func (spec *Normalization) SupportedVersions() []string { - if spec.Spec != nil { - versions := supportedVersions(spec.Spec.Params, []string{""}) - versions = supportedVersions(spec.Spec.OneOf, versions) - return versions - } - return nil -} - -func supportedVersions(params map[string]*SpecParam, versions []string) []string { - for _, param := range params { - for _, profile := range param.Profiles { - if profile.FromVersion != "" { - if notExist := listContains(versions, profile.FromVersion); notExist { - versions = append(versions, profile.FromVersion) - } - } - } - if param.Spec != nil { - versions = supportedVersions(param.Spec.Params, versions) - versions = supportedVersions(param.Spec.OneOf, versions) - } - } - return versions -} - -func listContains(versions []string, checkedVersion string) bool { - for _, version := range versions { - if version == checkedVersion { - return false - } - } - return true -} diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 78b441ca..0d418955 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -1,6 +1,7 @@ package translate import ( + "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" ) @@ -16,6 +17,30 @@ func LocationType(location *properties.Location, pointer bool) string { } } +func NestedSpecs(spec *properties.Spec) (map[string]*properties.Spec, error) { + nestedSpecs := make(map[string]*properties.Spec) + + checkNestedSpecs(spec, nestedSpecs) + + return nestedSpecs, nil +} + +func checkNestedSpecs(spec *properties.Spec, nestedSpecs map[string]*properties.Spec) { + for _, param := range spec.Params { + updateNestedSpecs(param, nestedSpecs) + } + for _, param := range spec.OneOf { + updateNestedSpecs(param, nestedSpecs) + } +} + +func updateNestedSpecs(param *properties.SpecParam, nestedSpecs map[string]*properties.Spec) { + if param.Spec != nil { + nestedSpecs[naming.CamelCase("", param.Name.CamelCase, "", true)] = param.Spec + checkNestedSpecs(param.Spec, nestedSpecs) + } +} + func SpecParamType(param *properties.SpecParam) string { prefix := "" if !param.Required { @@ -28,6 +53,8 @@ func SpecParamType(param *properties.SpecParam) string { calculatedType := "" if param.Type == "list" && param.Items != nil { calculatedType = param.Items.Type + } else if param.Spec != nil { + calculatedType = "Spec" + naming.CamelCase("", param.Name.CamelCase, "", true) } else { calculatedType = param.Type } @@ -44,6 +71,8 @@ func XmlParamType(param *properties.SpecParam) string { calculatedType := "" if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { calculatedType = "util.MemberType" + } else if param.Spec != nil { + calculatedType = "Spec" + naming.CamelCase("", param.Name.CamelCase, "", true) + "Xml" } else { calculatedType = param.Type } diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index bf438e45..468e358a 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -180,3 +180,45 @@ func TestXmlTag(t *testing.T) { assert.Equal(t, "`xml:\"description,omitempty\"`", calculatedXmlTagRequiredString) assert.Equal(t, "`xml:\"tag,omitempty\"`", calculatedXmlTagListString) } + +func TestNestedSpecs(t *testing.T) { + // given + spec := properties.Spec{ + Params: map[string]*properties.SpecParam{ + "a": { + Name: &properties.NameVariant{ + Underscore: "a", + CamelCase: "a", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "b": { + Name: &properties.NameVariant{ + Underscore: "b", + CamelCase: "b", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "c": { + Name: &properties.NameVariant{ + Underscore: "c", + CamelCase: "c", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + // when + nestedSpecs, _ := NestedSpecs(&spec) + + // then + assert.NotNil(t, nestedSpecs) + assert.Contains(t, nestedSpecs, "A") + assert.Contains(t, nestedSpecs, "B") +} diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 5fe723cd..924a7902 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -35,6 +35,17 @@ type Entry struct { Misc map[string][]generic.Xml } +{{- range $name, $spec := nestedSpecs .Spec }} +type Spec{{$name}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $param}} + {{- end}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $param}} + {{- end}} +} +{{- end}} + type EntryXmlContainer struct { Answer []EntryXml `xml:"entry"` } @@ -52,6 +63,17 @@ type EntryXml struct { Misc []generic.Xml `xml:",any"` } +{{- range $name, $spec := nestedSpecs .Spec }} +type Spec{{$name}}Xml struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{- end}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{- end}} +} +{{- end}} + func (e *Entry) CopyMiscFrom(v *Entry) { if v == nil || len(v.Misc) == 0 { return From d305093e5a50e00b7478b7810ab1fdd4851ba01e Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 12 Mar 2024 13:15:43 +0100 Subject: [PATCH 09/66] generate content of function SpecifyEntry for specification with nested specs --- pkg/translate/funcs.go | 84 +++++++++++++++++++++++++++++++++++-- pkg/translate/funcs_test.go | 52 +++++++++++++++++++++-- templates/sdk/entry.tmpl | 4 +- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index c3a05b5f..e2d36cda 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -225,14 +225,90 @@ func argumentTypeForSpecMatchesFunction(parent []string, param *properties.SpecP } func SpecifyEntryAssignment(param *properties.SpecParam) string { - calculatedAssignment := "" + var builder strings.Builder + if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - calculatedAssignment = "util.StrToMem(o." + param.Name.CamelCase + ")" + builder.WriteString("entry." + param.Name.CamelCase + " = util.StrToMem(o." + param.Name.CamelCase + ")") + } else if param.Spec != nil { + for _, subParam := range param.Spec.Params { + builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) + } + builder.WriteString("entry." + param.Name.CamelCase + " = &Spec" + param.Name.CamelCase + "Xml{\n") + for _, subParam := range param.Spec.Params { + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, subParam)) + } + for _, subParam := range param.Spec.OneOf { + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, subParam)) + } + builder.WriteString("}\n") } else { - calculatedAssignment = "o." + param.Name.CamelCase + builder.WriteString("entry." + param.Name.CamelCase + " = o." + param.Name.CamelCase) + } + + return builder.String() +} + +func nestedObjectDeclaration(parent []string, param *properties.SpecParam) string { + var builder strings.Builder + + if param.Spec != nil { + for _, subParam := range param.Spec.Params { + builder.WriteString(declareVariableForNestedObject(parent, param, subParam)) + builder.WriteString(nestedObjectDeclaration(append(parent, param.Name.CamelCase), subParam)) + } + for _, subParam := range param.Spec.OneOf { + builder.WriteString(declareVariableForNestedObject(parent, param, subParam)) + builder.WriteString(nestedObjectDeclaration(append(parent, param.Name.CamelCase), subParam)) + } } - return calculatedAssignment + return builder.String() +} + +func declareVariableForNestedObject(parent []string, param *properties.SpecParam, subParam *properties.SpecParam) string { + if subParam.Spec == nil && parent != nil { + return "nested" + + strings.Join(parent, "") + + param.Name.CamelCase + + subParam.Name.CamelCase + + " := o." + + strings.Join(parent, ".") + + "." + param.Name.CamelCase + + "." + subParam.Name.CamelCase + + "\n" + } else { + return "" + } +} + +func nestedObjectAssignment(parent []string, param *properties.SpecParam) string { + var builder strings.Builder + + if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { + builder.WriteString(param.Name.CamelCase + + " : util.StrToMem(o." + + param.Name.CamelCase + + "),\n") + } else if param.Spec != nil { + builder.WriteString(param.Name.CamelCase + + " : &Spec" + + param.Name.CamelCase + + "Xml{\n") + for _, subParam := range param.Spec.Params { + builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), subParam)) + } + for _, subParam := range param.Spec.OneOf { + builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), subParam)) + } + builder.WriteString("},\n") + } else { + builder.WriteString(param.Name.CamelCase + + " : nested" + + strings.Join(parent, "") + + param.Name.CamelCase + ",\n") + } + + return builder.String() } func SpecMatchesFunction(param *properties.SpecParam) string { diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 88a5e255..bbd2629d 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -16,7 +16,7 @@ func TestAsEntryXpath(t *testing.T) { assert.Equal(t, "util.AsEntryXpath([]string{o.DeviceGroup.PanoramaDevice}),", asEntryXpath) } -func TestSpecifyEntryAssignment(t *testing.T) { +func TestSpecifyEntryAssignmentForFlatStructure(t *testing.T) { // given paramTypeString := properties.SpecParam{ Name: &properties.NameVariant{ @@ -49,8 +49,54 @@ func TestSpecifyEntryAssignment(t *testing.T) { calculatedAssignmentListString := SpecifyEntryAssignment(¶mTypeListString) // then - assert.Equal(t, "o.Description", calculatedAssignmentString) - assert.Equal(t, "util.StrToMem(o.Tags)", calculatedAssignmentListString) + assert.Equal(t, "entry.Description = o.Description", calculatedAssignmentString) + assert.Equal(t, "entry.Tags = util.StrToMem(o.Tags)", calculatedAssignmentListString) +} + +func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { + // given + spec := properties.Spec{ + Params: map[string]*properties.SpecParam{ + "a": { + Name: &properties.NameVariant{ + Underscore: "a", + CamelCase: "A", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "b": { + Name: &properties.NameVariant{ + Underscore: "b", + CamelCase: "B", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "c": { + Name: &properties.NameVariant{ + Underscore: "c", + CamelCase: "C", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + expectedAssignmentStreing := `nestedABC := o.A.B.C +entry.A = &SpecAXml{ +B : &SpecBXml{ +C : nestedABC, +}, +} +` + // when + calculatedAssignmentString := SpecifyEntryAssignment(spec.Params["a"]) + + // then + assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) } func TestSpecMatchesFunction(t *testing.T) { diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 924a7902..cca7f2dd 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -118,10 +118,10 @@ func SpecifyEntry(o Entry) (any, error) { entry.Name = o.Name {{- range $_, $param := .Spec.Params}} - entry.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} + {{specifyEntryAssignment $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - entry.{{$param.Name.CamelCase}} = {{specifyEntryAssignment $param}} + {{specifyEntryAssignment $param}} {{- end}} entry.Misc = o.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] From 9bbe3e1f6f707e699f129f6c004ca0b7a2cadf19 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 12 Mar 2024 13:30:45 +0100 Subject: [PATCH 10/66] generate content of function Normalize for specification with nested specs --- pkg/generate/generator.go | 1 + pkg/translate/funcs.go | 36 ++++++++++++++++++++++----- pkg/translate/funcs_test.go | 46 +++++++++++++++++++++++++++++++++++ pkg/translate/structs_test.go | 6 ++--- templates/sdk/entry.tmpl | 10 ++++---- 5 files changed, 85 insertions(+), 14 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 23368199..cb27a643 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -116,6 +116,7 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "xmlParamType": translate.XmlParamType, "xmlTag": translate.XmlTag, "specifyEntryAssignment": translate.SpecifyEntryAssignment, + "normalizeAssignment": translate.NormalizeAssignment, "specMatchesFunction": translate.SpecMatchesFunction, "omitEmpty": translate.OmitEmpty, "contains": func(full, part string) bool { diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index e2d36cda..637262d0 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -235,10 +235,10 @@ func SpecifyEntryAssignment(param *properties.SpecParam) string { } builder.WriteString("entry." + param.Name.CamelCase + " = &Spec" + param.Name.CamelCase + "Xml{\n") for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, subParam)) + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "Xml", subParam)) } for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, subParam)) + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "Xml", subParam)) } builder.WriteString("}\n") } else { @@ -281,7 +281,7 @@ func declareVariableForNestedObject(parent []string, param *properties.SpecParam } } -func nestedObjectAssignment(parent []string, param *properties.SpecParam) string { +func nestedObjectAssignment(parent []string, suffix string, param *properties.SpecParam) string { var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { @@ -293,12 +293,12 @@ func nestedObjectAssignment(parent []string, param *properties.SpecParam) string builder.WriteString(param.Name.CamelCase + " : &Spec" + param.Name.CamelCase + - "Xml{\n") + suffix + "{\n") for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), subParam)) + builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), suffix, subParam)) } for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), subParam)) + builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), suffix, subParam)) } builder.WriteString("},\n") } else { @@ -311,6 +311,30 @@ func nestedObjectAssignment(parent []string, param *properties.SpecParam) string return builder.String() } +func NormalizeAssignment(param *properties.SpecParam) string { + var builder strings.Builder + + if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { + builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) + } else if param.Spec != nil { + for _, subParam := range param.Spec.Params { + builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) + } + builder.WriteString("entry." + param.Name.CamelCase + " = &Spec" + param.Name.CamelCase + "{\n") + for _, subParam := range param.Spec.Params { + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) + } + for _, subParam := range param.Spec.OneOf { + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) + } + builder.WriteString("}\n") + } else { + builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) + } + + return builder.String() +} + func SpecMatchesFunction(param *properties.SpecParam) string { calculatedFunction := "OptionalStringsMatch" if param.Type == "list" { diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index bbd2629d..93a7edcc 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -99,6 +99,52 @@ C : nestedABC, assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) } +func TestNormalizeAssignmentForNestedObject(t *testing.T) { + // given + spec := properties.Spec{ + Params: map[string]*properties.SpecParam{ + "a": { + Name: &properties.NameVariant{ + Underscore: "a", + CamelCase: "A", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "b": { + Name: &properties.NameVariant{ + Underscore: "b", + CamelCase: "B", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "c": { + Name: &properties.NameVariant{ + Underscore: "c", + CamelCase: "C", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + expectedAssignmentStreing := `nestedABC := o.A.B.C +entry.A = &SpecA{ +B : &SpecB{ +C : nestedABC, +}, +} +` + // when + calculatedAssignmentString := NormalizeAssignment(spec.Params["a"]) + + // then + assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) +} + func TestSpecMatchesFunction(t *testing.T) { // given paramTypeString := properties.SpecParam{ diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 468e358a..27085549 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -188,21 +188,21 @@ func TestNestedSpecs(t *testing.T) { "a": { Name: &properties.NameVariant{ Underscore: "a", - CamelCase: "a", + CamelCase: "A", }, Spec: &properties.Spec{ Params: map[string]*properties.SpecParam{ "b": { Name: &properties.NameVariant{ Underscore: "b", - CamelCase: "b", + CamelCase: "B", }, Spec: &properties.Spec{ Params: map[string]*properties.SpecParam{ "c": { Name: &properties.NameVariant{ Underscore: "c", - CamelCase: "c", + CamelCase: "C", }, }, }, diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index cca7f2dd..a21b27d7 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -131,19 +131,19 @@ func SpecifyEntry(o Entry) (any, error) { func (c *EntryXmlContainer) Normalize() ([]Entry, error) { entryList := make([]Entry, 0, len(c.Answer)) - for _, entryXml := range c.Answer { + for _, o := range c.Answer { entry := Entry{ Misc: make(map[string][]generic.Xml), } - entry.Name = entryXml.Name + entry.Name = o.Name {{- range $_, $param := .Spec.Params}} - entry.{{$param.Name.CamelCase}} = entryXml.{{$param.Name.CamelCase}} + {{normalizeAssignment $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - entry.{{$param.Name.CamelCase}} = entryXml.{{$param.Name.CamelCase}} + {{normalizeAssignment $param}} {{- end}} - entry.Misc[fmt.Sprintf("%s\n%s", "Entry", entryXml.Name)] = entryXml.Misc + entry.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] = o.Misc entryList = append(entryList, entry) } From 2180a57a2780ee469daa7f4359469af8ca42e978 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 12 Mar 2024 14:03:46 +0100 Subject: [PATCH 11/66] add function comments --- cmd/codegen/main.go | 6 +- pkg/generate/assets.go | 9 +- pkg/generate/generator.go | 9 ++ pkg/properties/config.go | 2 +- pkg/properties/normalized.go | 10 +- pkg/translate/funcs.go | 255 ++++------------------------------- pkg/translate/names.go | 2 +- pkg/translate/structs.go | 6 + 8 files changed, 62 insertions(+), 237 deletions(-) diff --git a/cmd/codegen/main.go b/cmd/codegen/main.go index e2003efe..bf1dde56 100644 --- a/cmd/codegen/main.go +++ b/cmd/codegen/main.go @@ -8,13 +8,13 @@ import ( "github.com/paloaltonetworks/pan-os-codegen/pkg/commands/codegen" ) -// Config holds the configuration values for the application. +// Config holds the configuration values for the application type Config struct { ConfigFile string OpType string } -// parseFlags parses the command line flags. +// parseFlags parses the command line flags func parseFlags() Config { var cfg Config flag.StringVar(&cfg.ConfigFile, "config", "./cmd/codegen/config.yaml", "Path to the configuration file") @@ -24,7 +24,7 @@ func parseFlags() Config { return cfg } -// runCommand executed command to generate code for SDK or Terraform. +// runCommand executed command to generate code for SDK or Terraform func runCommand(ctx context.Context, cmdType codegen.CommandType, cfg string) { cmd, err := codegen.NewCommand(ctx, cmdType, cfg) if err != nil { diff --git a/pkg/generate/assets.go b/pkg/generate/assets.go index dabb50d9..7e20fa66 100644 --- a/pkg/generate/assets.go +++ b/pkg/generate/assets.go @@ -2,7 +2,6 @@ package generate import ( "bytes" - "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" "io" "io/fs" @@ -11,7 +10,7 @@ import ( "path/filepath" ) -// CopyAssets copy assets (static files) according to configuration. +// CopyAssets copy assets (static files) according to configuration func CopyAssets(config *properties.Config) error { for _, asset := range config.Assets { files, err := listAssets(asset) @@ -34,7 +33,7 @@ func CopyAssets(config *properties.Config) error { return nil } -// listAssets walk through directory and get list of all assets (static files). +// listAssets walk through directory and get list of all assets (static files) func listAssets(asset *properties.Asset) ([]string, error) { var files []string @@ -54,10 +53,10 @@ func listAssets(asset *properties.Asset) ([]string, error) { return files, nil } -// copyAsset copy single asset, which may contain multiple files. +// copyAsset copy single asset, which may contain multiple files func copyAsset(target string, asset *properties.Asset, files []string) error { // Prepare destination path - destinationDir := fmt.Sprintf("%s/%s", target, asset.Destination) + destinationDir := target + "/" + asset.Destination // Create the destination directory if it doesn't exist if err := os.MkdirAll(destinationDir, os.ModePerm); err != nil { diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index cb27a643..d401ddc4 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -20,6 +20,7 @@ type Creator struct { Spec *properties.Normalization } +// NewCreator initialize Creator instance func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization) *Creator { return &Creator{ GoOutputDir: goOutputDir, @@ -28,6 +29,7 @@ func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization } } +// RenderTemplate loop through all templates, parse them and render content, which is saved to output file func (c *Creator) RenderTemplate() error { log.Println("Start rendering templates") @@ -53,6 +55,8 @@ func (c *Creator) RenderTemplate() error { if err := tmpl.Execute(&data, c.Spec); err != nil { return fmt.Errorf("error executing template %s: %w", templateName, err) } + // If from template no data was rendered (e.g. for DNS spec entry should not be created), + // then we don't need to create empty file (e.g. `entry.go`) with no content if data.Len() > 0 { outputFile, err := os.Create(filePath) if err != nil { @@ -69,11 +73,13 @@ func (c *Creator) RenderTemplate() error { return nil } +// createFullFilePath returns a full path for output file generated from template passed as argument to function func (c *Creator) createFullFilePath(templateName string) string { fileBaseName := strings.TrimSuffix(templateName, filepath.Ext(templateName)) return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fileBaseName+".go") } +// listOfTemplates return list of templates defined in TemplatesDir func (c *Creator) listOfTemplates() ([]string, error) { var files []string err := filepath.WalkDir(c.TemplatesDir, func(path string, entry os.DirEntry, err error) error { @@ -94,11 +100,13 @@ func (c *Creator) listOfTemplates() ([]string, error) { return files, nil } +// makeAllDirs creates all required directories, which are in the file path func (c *Creator) makeAllDirs(filePath string) error { dirPath := filepath.Dir(filePath) return os.MkdirAll(dirPath, os.ModePerm) } +// createFile just create a file and return it func (c *Creator) createFile(filePath string) (*os.File, error) { outputFile, err := os.Create(filePath) if err != nil { @@ -107,6 +115,7 @@ func (c *Creator) createFile(filePath string) (*os.File, error) { return outputFile, nil } +// parseTemplate parse template passed as argument and with function map defined below func (c *Creator) parseTemplate(templateName string) (*template.Template, error) { templatePath := filepath.Join(c.TemplatesDir, templateName) funcMap := template.FuncMap{ diff --git a/pkg/properties/config.go b/pkg/properties/config.go index f40fe96e..6939d1bc 100644 --- a/pkg/properties/config.go +++ b/pkg/properties/config.go @@ -23,7 +23,7 @@ type Target struct { TerraformProvider bool `json:"terraform_provider" yaml:"terraform_provider"` } -// ParseConfig initialize Config instance using input data from YAML file. +// ParseConfig initialize Config instance using input data from YAML file func ParseConfig(input []byte) (*Config, error) { var ans Config err := content.Unmarshal(input, &ans) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index b19999aa..999f7019 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -111,6 +111,7 @@ type SpecParamProfile struct { FromVersion string `json:"from_version" yaml:"from_version"` } +// GetNormalizations get list of all specs (normalizations) func GetNormalizations() ([]string, error) { _, loc, _, ok := runtime.Caller(0) if !ok { @@ -139,6 +140,7 @@ func GetNormalizations() ([]string, error) { return files, nil } +// ParseSpec parse single spec (unmarshal file), add name variants for locations and params, add default types for params func ParseSpec(input []byte) (*Normalization, error) { var spec Normalization @@ -165,6 +167,7 @@ func ParseSpec(input []byte) (*Normalization, error) { return &spec, err } +// AddNameVariantsForLocation add name variants for location (under_score and CamelCase) func (spec *Normalization) AddNameVariantsForLocation() error { for key, location := range spec.Locations { location.Name = &NameVariant{ @@ -183,6 +186,7 @@ func (spec *Normalization) AddNameVariantsForLocation() error { return nil } +// AddNameVariantsForParams recursively add name variants for params for nested specs func AddNameVariantsForParams(name string, param *SpecParam) error { param.Name = &NameVariant{ Underscore: name, @@ -203,6 +207,7 @@ func AddNameVariantsForParams(name string, param *SpecParam) error { return nil } +// AddNameVariantsForParams add name variants for params (under_score and CamelCase) func (spec *Normalization) AddNameVariantsForParams() error { if spec.Spec != nil { for key, param := range spec.Spec.Params { @@ -219,6 +224,7 @@ func (spec *Normalization) AddNameVariantsForParams() error { return nil } +// AddDefaultTypesForParams recursively add default types for params for nested specs func AddDefaultTypesForParams(param *SpecParam) error { if param.Type == "" { param.Type = "string" @@ -241,7 +247,7 @@ func AddDefaultTypesForParams(param *SpecParam) error { } } -// AddDefaultTypesForParams ensures all SpecParams within Spec have a default type if not specified. +// AddDefaultTypesForParams ensures all params within Spec have a default type if not specified. func (spec *Normalization) AddDefaultTypesForParams() error { if spec.Spec != nil { for _, childParam := range spec.Spec.Params { @@ -260,6 +266,7 @@ func (spec *Normalization) AddDefaultTypesForParams() error { } } +// Sanity basic checks for specification (normalization) e.g. check if at least 1 location is defined func (spec *Normalization) Sanity() error { if spec.Name == "" { return errors.New("name is required") @@ -274,6 +281,7 @@ func (spec *Normalization) Sanity() error { return nil } +// Validate validations for specification (normalization) e.g. check if XPath contain / func (spec *Normalization) Validate() []error { var checks []error diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 637262d0..7087af7d 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -1,229 +1,54 @@ package translate import ( - "fmt" + "errors" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" "strings" ) -// GenerateEntryXpathForLocation functions used in location.tmpl to generate XPath for location. -func GenerateEntryXpathForLocation(location, xpath string) (string, error) { +// AsEntryXpath functions used in location.tmpl to generate XPath for location +func AsEntryXpath(location, xpath string) (string, error) { if !strings.Contains(xpath, "$") || !strings.Contains(xpath, "}") { - return "", fmt.Errorf("xpath '%s' is missing '$' followed by '}'", xpath) + return "", errors.New("$ followed by } should exists in xpath'") } - asEntryXpath := generateEntryXpathForLocation(location, xpath) - return asEntryXpath, nil + xpath = strings.TrimSpace(strings.Split(strings.Split(xpath, "$")[1], "}")[0]) + xpath = naming.CamelCase("", xpath, "", true) + return "util.AsEntryXpath([]string{o." + location + "." + xpath + "}),", nil } -func generateEntryXpathForLocation(location string, xpath string) string { - xpathPartWithoutDollar := strings.SplitAfter(xpath, "$") - xpathPartWithoutBrackets := strings.TrimSpace(strings.Trim(xpathPartWithoutDollar[1], "${}")) - xpathPartCamelCase := naming.CamelCase("", xpathPartWithoutBrackets, "", true) - asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s.%s}),", location, xpathPartCamelCase) - return asEntryXpath -} - -// NormalizeAssignment generates a string, which contains entry/config assignment in Normalize() function -// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed -// internal functions, which are creating entry assignment. -func NormalizeAssignment(objectType string, param *properties.SpecParam, version string) string { - return prepareAssignment(objectType, param, "util.MemToStr", "Spec", "", version) -} - -// SpecifyEntryAssignment generates a string, which contains entry/config assignment in SpecifyEntry() function -// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed -// internal functions, which are creating entry assignment. -func SpecifyEntryAssignment(objectType string, param *properties.SpecParam, version string) string { - return prepareAssignment(objectType, param, "util.StrToMem", "spec", "Xml", version) -} - -func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specPrefix, specSuffix string, version string) string { +// NormalizeAssignment generates a string, which contains entry assignment in Normalize() function +// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, +// which are declaring additional variables (function nestedObjectDeclaration()) and use them in +// entry assignment (function nestedObjectAssignment()) +func NormalizeAssignment(param *properties.SpecParam) string { var builder strings.Builder - if ParamSupportedInVersion(param, version) { - if param.Spec != nil { - if specSuffix == "Xml" { - appendSpecObjectAssignment(param, objectType, version, specPrefix, specSuffix, &builder) - } else { - appendSpecObjectAssignment(param, objectType, "", specPrefix, specSuffix, &builder) - } - } else if isParamListAndProfileTypeIsMember(param) { - appendListFunctionAssignment(param, objectType, listFunction, &builder) - } else { - appendSimpleAssignment(param, objectType, &builder) + if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { + builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) + } else if param.Spec != nil { + for _, subParam := range param.Spec.Params { + builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) } - } - - return builder.String() -} - -func isParamListAndProfileTypeIsMember(param *properties.SpecParam) bool { - return param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" -} - -func appendSimpleAssignment(param *properties.SpecParam, objectType string, builder *strings.Builder) { - builder.WriteString(fmt.Sprintf("%s.%s = o.%s", objectType, param.Name.CamelCase, param.Name.CamelCase)) -} - -func appendListFunctionAssignment(param *properties.SpecParam, objectType string, listFunction string, builder *strings.Builder) { - builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s)", objectType, param.Name.CamelCase, listFunction, param.Name.CamelCase)) -} - -func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, version, prefix, suffix string, builder *strings.Builder) { - defineNestedObject([]string{param.Name.CamelCase}, param, objectType, version, prefix, suffix, builder) - builder.WriteString(fmt.Sprintf("%s.%s = nested%s\n", objectType, param.Name.CamelCase, param.Name.CamelCase)) -} - -func defineNestedObject(parent []string, param *properties.SpecParam, objectType string, version, prefix, suffix string, builder *strings.Builder) { - declareRootOfNestedObject(parent, builder, version, prefix, suffix) - - if ParamSupportedInVersion(param, version) { - builder.WriteString(fmt.Sprintf("if o.%s != nil {\n", strings.Join(parent, "."))) - if param.Spec != nil { - assignEmptyStructForNestedObject(parent, builder, objectType, version, prefix, suffix) - defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, version, prefix, suffix, builder) - defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, version, prefix, suffix, builder) - } else { - assignValueForNestedObject(parent, builder) + builder.WriteString("entry." + param.Name.CamelCase + " = &Spec" + param.Name.CamelCase + "{\n") + for _, subParam := range param.Spec.Params { + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) } - builder.WriteString("}\n") - } -} - -func declareRootOfNestedObject(parent []string, builder *strings.Builder, version, prefix, suffix string) { - if len(parent) == 1 { - builder.WriteString(fmt.Sprintf("nested%s := &%s%s%s%s{}\n", - strings.Join(parent, "."), prefix, - strings.Join(parent, ""), suffix, - CreateGoSuffixFromVersion(version))) - } -} - -func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, objectType, version, prefix, suffix string) { - if len(parent) > 1 { - builder.WriteString(fmt.Sprintf("nested%s = &%s%s%s%s{}\n", - strings.Join(parent, "."), prefix, strings.Join(parent, ""), suffix, - CreateGoSuffixFromVersion(version))) - - if suffix == "Xml" { - builder.WriteString(fmt.Sprintf("if _, ok := o.Misc[\"%s\"]; ok {\n", - strings.Join(parent, ""))) - builder.WriteString(fmt.Sprintf("nested%s.Misc = o.Misc[\"%s\"]\n", - strings.Join(parent, "."), strings.Join(parent, ""), - )) - } else { - builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", - strings.Join(parent, "."))) - builder.WriteString(fmt.Sprintf("%s.Misc[\"%s\"] = o.%s.Misc\n", - objectType, strings.Join(parent, ""), strings.Join(parent, "."), - )) + for _, subParam := range param.Spec.OneOf { + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) } builder.WriteString("}\n") - } -} - -func assignValueForNestedObject(parent []string, builder *strings.Builder) { - builder.WriteString(fmt.Sprintf("nested%s = o.%s\n", - strings.Join(parent, "."), - strings.Join(parent, "."))) -} - -func defineNestedObjectForChildParams(parent []string, params map[string]*properties.SpecParam, objectType string, version, prefix, suffix string, builder *strings.Builder) { - for _, param := range params { - defineNestedObject(append(parent, param.Name.CamelCase), param, objectType, version, prefix, suffix, builder) - } -} - -// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl/config.tmpl -// to compare all items of generated entry. -func SpecMatchesFunction(param *properties.SpecParam) string { - return specMatchFunctionName([]string{}, param) -} - -func specMatchFunctionName(parent []string, param *properties.SpecParam) string { - if param.Type == "list" { - return "util.OrderedListsMatch" - } else if param.Type == "string" { - return "util.OptionalStringsMatch" } else { - return fmt.Sprintf("specMatch%s%s", strings.Join(parent, ""), param.Name.CamelCase) + builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) } -} - -// NestedSpecMatchesFunction return a string with body of specMach* functions required for nested params -func NestedSpecMatchesFunction(spec *properties.Spec) string { - var builder strings.Builder - - defineSpecMatchesFunction([]string{}, spec.Params, &builder) - defineSpecMatchesFunction([]string{}, spec.OneOf, &builder) return builder.String() } -func defineSpecMatchesFunction(parent []string, params map[string]*properties.SpecParam, builder *strings.Builder) { - for _, param := range params { - if param.Spec != nil { - defineSpecMatchesFunction(append(parent, param.Name.CamelCase), param.Spec.Params, builder) - defineSpecMatchesFunction(append(parent, param.Name.CamelCase), param.Spec.OneOf, builder) - - renderSpecMatchesFunctionNameWithArguments(parent, builder, param) - checkInSpecMatchesFunctionIfVariablesAreNil(builder) - - for _, subParam := range param.Spec.Params { - renderInSpecMatchesFunctionIfToCheckIfVariablesMatches(parent, builder, param, subParam) - } - for _, subParam := range param.Spec.OneOf { - renderInSpecMatchesFunctionIfToCheckIfVariablesMatches(parent, builder, param, subParam) - } - - builder.WriteString("return true\n") - builder.WriteString("}\n") - } else if param.Type != "list" && param.Type != "string" { - // whole section should be removed, when there will be dedicated function to compare integers - // in file https://github.com/PaloAltoNetworks/pango/blob/develop/util/comparison.go - renderSpecMatchesFunctionNameWithArguments(parent, builder, param) - checkInSpecMatchesFunctionIfVariablesAreNil(builder) - - builder.WriteString("return *a == *b\n") - builder.WriteString("}\n") - } - } -} - -func renderSpecMatchesFunctionNameWithArguments(parent []string, builder *strings.Builder, param *properties.SpecParam) { - builder.WriteString(fmt.Sprintf("func specMatch%s%s(a *%s, b *%s) bool {", - strings.Join(parent, ""), param.Name.CamelCase, - argumentTypeForSpecMatchesFunction(parent, param), - argumentTypeForSpecMatchesFunction(parent, param))) -} - -func checkInSpecMatchesFunctionIfVariablesAreNil(builder *strings.Builder) { - builder.WriteString("if a == nil && b != nil || a != nil && b == nil {\n") - builder.WriteString(" return false\n") - builder.WriteString("} else if a == nil && b == nil {\n") - builder.WriteString(" return true\n") - builder.WriteString("}\n") -} - -func renderInSpecMatchesFunctionIfToCheckIfVariablesMatches(parent []string, builder *strings.Builder, param *properties.SpecParam, subparam *properties.SpecParam) { - builder.WriteString(fmt.Sprintf("if !%s(a.%s, b.%s) {\n", - specMatchFunctionName(append(parent, param.Name.CamelCase), subparam), subparam.Name.CamelCase, subparam.Name.CamelCase)) - builder.WriteString(" return false\n") - builder.WriteString("}\n") -} - -func argumentTypeForSpecMatchesFunction(parent []string, param *properties.SpecParam) string { - if param.Type == "bool" { - return "bool" - } else if param.Type == "int" { - return "int" - } else { - return fmt.Sprintf("Spec%s%s", - strings.Join(parent, ""), param.Name.CamelCase) - } -} - +// SpecifyEntryAssignment generates a string, which contains entry assignment in SpecifyEntry() function +// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, +// which are declaring additional variables (function nestedObjectDeclaration()) and use them in +// entry assignment (function nestedObjectAssignment()) func SpecifyEntryAssignment(param *properties.SpecParam) string { var builder strings.Builder @@ -311,30 +136,8 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp return builder.String() } -func NormalizeAssignment(param *properties.SpecParam) string { - var builder strings.Builder - - if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) - } else if param.Spec != nil { - for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) - } - builder.WriteString("entry." + param.Name.CamelCase + " = &Spec" + param.Name.CamelCase + "{\n") - for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) - } - for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) - } - builder.WriteString("}\n") - } else { - builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) - } - - return builder.String() -} - +// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl +// to compare all items of generated entry func SpecMatchesFunction(param *properties.SpecParam) string { calculatedFunction := "OptionalStringsMatch" if param.Type == "list" { diff --git a/pkg/translate/names.go b/pkg/translate/names.go index 3b73be51..d10a57dd 100644 --- a/pkg/translate/names.go +++ b/pkg/translate/names.go @@ -1,6 +1,6 @@ package translate -// PackageName get package name from Go SDK path. +// PackageName get package name from Go SDK path func PackageName(list []string) string { if len(list) == 0 { return "" diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 0d418955..14d2958b 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -5,6 +5,7 @@ import ( "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" ) +// LocationType function used in template location.tmpl to generate location type name func LocationType(location *properties.Location, pointer bool) string { prefix := "" if pointer { @@ -17,6 +18,7 @@ func LocationType(location *properties.Location, pointer bool) string { } } +// NestedSpecs go through all params and one of (recursively) and return map of all nested specs func NestedSpecs(spec *properties.Spec) (map[string]*properties.Spec, error) { nestedSpecs := make(map[string]*properties.Spec) @@ -41,6 +43,7 @@ func updateNestedSpecs(param *properties.SpecParam, nestedSpecs map[string]*prop } } +// SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files) func SpecParamType(param *properties.SpecParam) string { prefix := "" if !param.Required { @@ -62,6 +65,7 @@ func SpecParamType(param *properties.SpecParam) string { return prefix + calculatedType } +// XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files) func XmlParamType(param *properties.SpecParam) string { prefix := "" if !param.Required { @@ -80,6 +84,7 @@ func XmlParamType(param *properties.SpecParam) string { return prefix + calculatedType } +// XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`) func XmlTag(param *properties.SpecParam) string { suffix := "" if !param.Required { @@ -93,6 +98,7 @@ func XmlTag(param *properties.SpecParam) string { return calculatedTag } +// OmitEmpty return omitempty in XML tag for location, if there are variables defined func OmitEmpty(location *properties.Location) string { if location.Vars != nil { return ",omitempty" From f1f901556ae488dbb4403566d7bb1a259abdfd6e Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 09:48:27 +0100 Subject: [PATCH 12/66] replace all string concatenation using "+" by function fmt.Sprintf() --- pkg/generate/assets.go | 3 ++- pkg/generate/generator.go | 2 +- pkg/translate/funcs.go | 50 +++++++++++++++++---------------------- pkg/translate/structs.go | 9 +++---- 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/pkg/generate/assets.go b/pkg/generate/assets.go index 7e20fa66..9cabbb4b 100644 --- a/pkg/generate/assets.go +++ b/pkg/generate/assets.go @@ -2,6 +2,7 @@ package generate import ( "bytes" + "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" "io" "io/fs" @@ -56,7 +57,7 @@ func listAssets(asset *properties.Asset) ([]string, error) { // copyAsset copy single asset, which may contain multiple files func copyAsset(target string, asset *properties.Asset, files []string) error { // Prepare destination path - destinationDir := target + "/" + asset.Destination + destinationDir := fmt.Sprintf("%s/%s", target, asset.Destination) // Create the destination directory if it doesn't exist if err := os.MkdirAll(destinationDir, os.ModePerm); err != nil { diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index d401ddc4..ca3dbf8d 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -76,7 +76,7 @@ func (c *Creator) RenderTemplate() error { // createFullFilePath returns a full path for output file generated from template passed as argument to function func (c *Creator) createFullFilePath(templateName string) string { fileBaseName := strings.TrimSuffix(templateName, filepath.Ext(templateName)) - return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fileBaseName+".go") + return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fmt.Sprintf("%s.go", fileBaseName)) } // listOfTemplates return list of templates defined in TemplatesDir diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 7087af7d..1fa3e50f 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -2,6 +2,7 @@ package translate import ( "errors" + "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" "strings" @@ -14,7 +15,8 @@ func AsEntryXpath(location, xpath string) (string, error) { } xpath = strings.TrimSpace(strings.Split(strings.Split(xpath, "$")[1], "}")[0]) xpath = naming.CamelCase("", xpath, "", true) - return "util.AsEntryXpath([]string{o." + location + "." + xpath + "}),", nil + asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s.%s}),", location, xpath) + return asEntryXpath, nil } // NormalizeAssignment generates a string, which contains entry assignment in Normalize() function @@ -25,12 +27,12 @@ func NormalizeAssignment(param *properties.SpecParam) string { var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) + builder.WriteString(fmt.Sprintf("entry.%s = entryXml.%s", param.Name.CamelCase, param.Name.CamelCase)) } else if param.Spec != nil { for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) } - builder.WriteString("entry." + param.Name.CamelCase + " = &Spec" + param.Name.CamelCase + "{\n") + builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s{\n", param.Name.CamelCase, param.Name.CamelCase)) for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) } @@ -39,7 +41,7 @@ func NormalizeAssignment(param *properties.SpecParam) string { } builder.WriteString("}\n") } else { - builder.WriteString("entry." + param.Name.CamelCase + " = entryXml." + param.Name.CamelCase) + builder.WriteString(fmt.Sprintf("entry.%s = entryXml.%s", param.Name.CamelCase, param.Name.CamelCase)) } return builder.String() @@ -53,12 +55,12 @@ func SpecifyEntryAssignment(param *properties.SpecParam) string { var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString("entry." + param.Name.CamelCase + " = util.StrToMem(o." + param.Name.CamelCase + ")") + builder.WriteString(fmt.Sprintf("entry.%s = util.StrToMem(o.%s)", param.Name.CamelCase, param.Name.CamelCase)) } else if param.Spec != nil { for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) } - builder.WriteString("entry." + param.Name.CamelCase + " = &Spec" + param.Name.CamelCase + "Xml{\n") + builder.WriteString(fmt.Sprintf("entry.%s = &Spec%sXml{\n", param.Name.CamelCase, param.Name.CamelCase)) for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "Xml", subParam)) } @@ -67,7 +69,7 @@ func SpecifyEntryAssignment(param *properties.SpecParam) string { } builder.WriteString("}\n") } else { - builder.WriteString("entry." + param.Name.CamelCase + " = o." + param.Name.CamelCase) + builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) } return builder.String() @@ -92,15 +94,13 @@ func nestedObjectDeclaration(parent []string, param *properties.SpecParam) strin func declareVariableForNestedObject(parent []string, param *properties.SpecParam, subParam *properties.SpecParam) string { if subParam.Spec == nil && parent != nil { - return "nested" + - strings.Join(parent, "") + - param.Name.CamelCase + - subParam.Name.CamelCase + - " := o." + - strings.Join(parent, ".") + - "." + param.Name.CamelCase + - "." + subParam.Name.CamelCase + - "\n" + return fmt.Sprintf("nested%s%s%s := o.%s.%s.%s\n", + strings.Join(parent, ""), + param.Name.CamelCase, + subParam.Name.CamelCase, + strings.Join(parent, "."), + param.Name.CamelCase, + subParam.Name.CamelCase) } else { return "" } @@ -110,15 +110,11 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString(param.Name.CamelCase + - " : util.StrToMem(o." + - param.Name.CamelCase + - "),\n") + builder.WriteString(fmt.Sprintf("%s : util.StrToMem(o.%s),\n", + param.Name.CamelCase, param.Name.CamelCase)) } else if param.Spec != nil { - builder.WriteString(param.Name.CamelCase + - " : &Spec" + - param.Name.CamelCase + - suffix + "{\n") + builder.WriteString(fmt.Sprintf("%s : &Spec%s%s{\n", + param.Name.CamelCase, param.Name.CamelCase, suffix)) for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), suffix, subParam)) } @@ -127,10 +123,8 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp } builder.WriteString("},\n") } else { - builder.WriteString(param.Name.CamelCase + - " : nested" + - strings.Join(parent, "") + - param.Name.CamelCase + ",\n") + builder.WriteString(fmt.Sprintf("%s : nested%s%s,\n", + param.Name.CamelCase, strings.Join(parent, ""), param.Name.CamelCase)) } return builder.String() diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 14d2958b..bdf99de8 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -1,6 +1,7 @@ package translate import ( + "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" ) @@ -12,7 +13,7 @@ func LocationType(location *properties.Location, pointer bool) string { prefix = "*" } if location.Vars != nil { - return prefix + location.Name.CamelCase + "Location" + return fmt.Sprintf("%s%sLocation", prefix, location.Name.CamelCase) } else { return "bool" } @@ -57,7 +58,7 @@ func SpecParamType(param *properties.SpecParam) string { if param.Type == "list" && param.Items != nil { calculatedType = param.Items.Type } else if param.Spec != nil { - calculatedType = "Spec" + naming.CamelCase("", param.Name.CamelCase, "", true) + calculatedType = fmt.Sprintf("Spec%s", naming.CamelCase("", param.Name.CamelCase, "", true)) } else { calculatedType = param.Type } @@ -76,7 +77,7 @@ func XmlParamType(param *properties.SpecParam) string { if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { calculatedType = "util.MemberType" } else if param.Spec != nil { - calculatedType = "Spec" + naming.CamelCase("", param.Name.CamelCase, "", true) + "Xml" + calculatedType = fmt.Sprintf("Spec%sXml", naming.CamelCase("", param.Name.CamelCase, "", true)) } else { calculatedType = param.Type } @@ -93,7 +94,7 @@ func XmlTag(param *properties.SpecParam) string { calculatedTag := "" if param.Profiles != nil && len(param.Profiles) > 0 { - calculatedTag = "`xml:\"" + param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1] + suffix + "\"`" + calculatedTag = fmt.Sprintf("`xml:\"%s%s\"`", param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1], suffix) } return calculatedTag } From 5966f1b85a5831bc541fa5e81e3d0ba9a9058904 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 11:21:38 +0100 Subject: [PATCH 13/66] for item with type object, render []string, which will contain all members --- pkg/properties/normalized.go | 1 + pkg/properties/normalized_test.go | 1 + pkg/translate/funcs.go | 4 ++-- pkg/translate/structs.go | 6 +++++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index 999f7019..c3ccd6d6 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -97,6 +97,7 @@ type SpecParamCount struct { type SpecParamItems struct { Type string `json:"type" yaml:"type"` Length *SpecParamItemsLength `json:"length" yaml:"length"` + Ref []*string `json:"ref" yaml:"ref"` } type SpecParamItemsLength struct { diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index 767d3898..9fc3a67c 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -283,6 +283,7 @@ spec: length: min: null max: 127 + ref: [] profiles: - xpath: - tag diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 1fa3e50f..362d85c3 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -27,7 +27,7 @@ func NormalizeAssignment(param *properties.SpecParam) string { var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString(fmt.Sprintf("entry.%s = entryXml.%s", param.Name.CamelCase, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) } else if param.Spec != nil { for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) @@ -41,7 +41,7 @@ func NormalizeAssignment(param *properties.SpecParam) string { } builder.WriteString("}\n") } else { - builder.WriteString(fmt.Sprintf("entry.%s = entryXml.%s", param.Name.CamelCase, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) } return builder.String() diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index bdf99de8..8f4a427f 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -56,7 +56,11 @@ func SpecParamType(param *properties.SpecParam) string { calculatedType := "" if param.Type == "list" && param.Items != nil { - calculatedType = param.Items.Type + if param.Items.Type == "object" && param.Items.Ref != nil { + calculatedType = "string" + } else { + calculatedType = param.Items.Type + } } else if param.Spec != nil { calculatedType = fmt.Sprintf("Spec%s", naming.CamelCase("", param.Name.CamelCase, "", true)) } else { From f7bb7547638022f7577ca00e928a85c3211886b7 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 11:53:15 +0100 Subject: [PATCH 14/66] refactor NormalizeAssignment(), SpecifyEntryAssignment(), fix some of the issues from `golangci-lint run --enable-all` --- cmd/codegen/main.go | 6 ++--- pkg/generate/assets.go | 6 ++--- pkg/generate/generator.go | 18 ++++++------- pkg/properties/config.go | 2 +- pkg/properties/normalized.go | 16 ++++++------ pkg/properties/normalized_test.go | 2 +- pkg/translate/funcs.go | 42 ++++++++++--------------------- pkg/translate/names.go | 2 +- pkg/translate/structs.go | 12 ++++----- 9 files changed, 44 insertions(+), 62 deletions(-) diff --git a/cmd/codegen/main.go b/cmd/codegen/main.go index bf1dde56..e2003efe 100644 --- a/cmd/codegen/main.go +++ b/cmd/codegen/main.go @@ -8,13 +8,13 @@ import ( "github.com/paloaltonetworks/pan-os-codegen/pkg/commands/codegen" ) -// Config holds the configuration values for the application +// Config holds the configuration values for the application. type Config struct { ConfigFile string OpType string } -// parseFlags parses the command line flags +// parseFlags parses the command line flags. func parseFlags() Config { var cfg Config flag.StringVar(&cfg.ConfigFile, "config", "./cmd/codegen/config.yaml", "Path to the configuration file") @@ -24,7 +24,7 @@ func parseFlags() Config { return cfg } -// runCommand executed command to generate code for SDK or Terraform +// runCommand executed command to generate code for SDK or Terraform. func runCommand(ctx context.Context, cmdType codegen.CommandType, cfg string) { cmd, err := codegen.NewCommand(ctx, cmdType, cfg) if err != nil { diff --git a/pkg/generate/assets.go b/pkg/generate/assets.go index 9cabbb4b..dabb50d9 100644 --- a/pkg/generate/assets.go +++ b/pkg/generate/assets.go @@ -11,7 +11,7 @@ import ( "path/filepath" ) -// CopyAssets copy assets (static files) according to configuration +// CopyAssets copy assets (static files) according to configuration. func CopyAssets(config *properties.Config) error { for _, asset := range config.Assets { files, err := listAssets(asset) @@ -34,7 +34,7 @@ func CopyAssets(config *properties.Config) error { return nil } -// listAssets walk through directory and get list of all assets (static files) +// listAssets walk through directory and get list of all assets (static files). func listAssets(asset *properties.Asset) ([]string, error) { var files []string @@ -54,7 +54,7 @@ func listAssets(asset *properties.Asset) ([]string, error) { return files, nil } -// copyAsset copy single asset, which may contain multiple files +// copyAsset copy single asset, which may contain multiple files. func copyAsset(target string, asset *properties.Asset, files []string) error { // Prepare destination path destinationDir := fmt.Sprintf("%s/%s", target, asset.Destination) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index ca3dbf8d..6e671302 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -20,7 +20,7 @@ type Creator struct { Spec *properties.Normalization } -// NewCreator initialize Creator instance +// NewCreator initialize Creator instance. func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization) *Creator { return &Creator{ GoOutputDir: goOutputDir, @@ -29,7 +29,7 @@ func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization } } -// RenderTemplate loop through all templates, parse them and render content, which is saved to output file +// RenderTemplate loop through all templates, parse them and render content, which is saved to output file. func (c *Creator) RenderTemplate() error { log.Println("Start rendering templates") @@ -73,13 +73,13 @@ func (c *Creator) RenderTemplate() error { return nil } -// createFullFilePath returns a full path for output file generated from template passed as argument to function +// createFullFilePath returns a full path for output file generated from template passed as argument to function. func (c *Creator) createFullFilePath(templateName string) string { fileBaseName := strings.TrimSuffix(templateName, filepath.Ext(templateName)) return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fmt.Sprintf("%s.go", fileBaseName)) } -// listOfTemplates return list of templates defined in TemplatesDir +// listOfTemplates return list of templates defined in TemplatesDir. func (c *Creator) listOfTemplates() ([]string, error) { var files []string err := filepath.WalkDir(c.TemplatesDir, func(path string, entry os.DirEntry, err error) error { @@ -100,13 +100,13 @@ func (c *Creator) listOfTemplates() ([]string, error) { return files, nil } -// makeAllDirs creates all required directories, which are in the file path +// makeAllDirs creates all required directories, which are in the file path. func (c *Creator) makeAllDirs(filePath string) error { dirPath := filepath.Dir(filePath) return os.MkdirAll(dirPath, os.ModePerm) } -// createFile just create a file and return it +// createFile just create a file and return it. func (c *Creator) createFile(filePath string) (*os.File, error) { outputFile, err := os.Create(filePath) if err != nil { @@ -115,7 +115,7 @@ func (c *Creator) createFile(filePath string) (*os.File, error) { return outputFile, nil } -// parseTemplate parse template passed as argument and with function map defined below +// parseTemplate parse template passed as argument and with function map defined below. func (c *Creator) parseTemplate(templateName string) (*template.Template, error) { templatePath := filepath.Join(c.TemplatesDir, templateName) funcMap := template.FuncMap{ @@ -128,9 +128,7 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "normalizeAssignment": translate.NormalizeAssignment, "specMatchesFunction": translate.SpecMatchesFunction, "omitEmpty": translate.OmitEmpty, - "contains": func(full, part string) bool { - return strings.Contains(full, part) - }, + "contains": strings.Contains, "subtract": func(a, b int) int { return a - b }, diff --git a/pkg/properties/config.go b/pkg/properties/config.go index 6939d1bc..f40fe96e 100644 --- a/pkg/properties/config.go +++ b/pkg/properties/config.go @@ -23,7 +23,7 @@ type Target struct { TerraformProvider bool `json:"terraform_provider" yaml:"terraform_provider"` } -// ParseConfig initialize Config instance using input data from YAML file +// ParseConfig initialize Config instance using input data from YAML file. func ParseConfig(input []byte) (*Config, error) { var ans Config err := content.Unmarshal(input, &ans) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index c3ccd6d6..073466fc 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -112,7 +112,7 @@ type SpecParamProfile struct { FromVersion string `json:"from_version" yaml:"from_version"` } -// GetNormalizations get list of all specs (normalizations) +// GetNormalizations get list of all specs (normalizations). func GetNormalizations() ([]string, error) { _, loc, _, ok := runtime.Caller(0) if !ok { @@ -141,7 +141,7 @@ func GetNormalizations() ([]string, error) { return files, nil } -// ParseSpec parse single spec (unmarshal file), add name variants for locations and params, add default types for params +// ParseSpec parse single spec (unmarshal file), add name variants for locations and params, add default types for params. func ParseSpec(input []byte) (*Normalization, error) { var spec Normalization @@ -168,7 +168,7 @@ func ParseSpec(input []byte) (*Normalization, error) { return &spec, err } -// AddNameVariantsForLocation add name variants for location (under_score and CamelCase) +// AddNameVariantsForLocation add name variants for location (under_score and CamelCase). func (spec *Normalization) AddNameVariantsForLocation() error { for key, location := range spec.Locations { location.Name = &NameVariant{ @@ -187,7 +187,7 @@ func (spec *Normalization) AddNameVariantsForLocation() error { return nil } -// AddNameVariantsForParams recursively add name variants for params for nested specs +// AddNameVariantsForParams recursively add name variants for params for nested specs. func AddNameVariantsForParams(name string, param *SpecParam) error { param.Name = &NameVariant{ Underscore: name, @@ -208,7 +208,7 @@ func AddNameVariantsForParams(name string, param *SpecParam) error { return nil } -// AddNameVariantsForParams add name variants for params (under_score and CamelCase) +// AddNameVariantsForParams add name variants for params (under_score and CamelCase). func (spec *Normalization) AddNameVariantsForParams() error { if spec.Spec != nil { for key, param := range spec.Spec.Params { @@ -225,7 +225,7 @@ func (spec *Normalization) AddNameVariantsForParams() error { return nil } -// AddDefaultTypesForParams recursively add default types for params for nested specs +// AddDefaultTypesForParams recursively add default types for params for nested specs. func AddDefaultTypesForParams(param *SpecParam) error { if param.Type == "" { param.Type = "string" @@ -267,7 +267,7 @@ func (spec *Normalization) AddDefaultTypesForParams() error { } } -// Sanity basic checks for specification (normalization) e.g. check if at least 1 location is defined +// Sanity basic checks for specification (normalization) e.g. check if at least 1 location is defined. func (spec *Normalization) Sanity() error { if spec.Name == "" { return errors.New("name is required") @@ -282,7 +282,7 @@ func (spec *Normalization) Sanity() error { return nil } -// Validate validations for specification (normalization) e.g. check if XPath contain / +// Validate validations for specification (normalization) e.g. check if XPath contain /. func (spec *Normalization) Validate() []error { var checks []error diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index 9fc3a67c..98a49bd4 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -353,7 +353,7 @@ spec: // when yamlParsedData, _ := ParseSpec([]byte(sampleSpec)) yamlDump, _ := yaml.Marshal(&yamlParsedData) - //fmt.Printf("%s", string(yamlDump)) + // fmt.Printf("%s", string(yamlDump)) // then assert.NotNilf(t, yamlDump, "Marshalled data cannot be nil") diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 362d85c3..c946543c 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -8,7 +8,7 @@ import ( "strings" ) -// AsEntryXpath functions used in location.tmpl to generate XPath for location +// AsEntryXpath functions used in location.tmpl to generate XPath for location. func AsEntryXpath(location, xpath string) (string, error) { if !strings.Contains(xpath, "$") || !strings.Contains(xpath, "}") { return "", errors.New("$ followed by } should exists in xpath'") @@ -22,50 +22,34 @@ func AsEntryXpath(location, xpath string) (string, error) { // NormalizeAssignment generates a string, which contains entry assignment in Normalize() function // in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, // which are declaring additional variables (function nestedObjectDeclaration()) and use them in -// entry assignment (function nestedObjectAssignment()) +// entry assignment (function nestedObjectAssignment()). func NormalizeAssignment(param *properties.SpecParam) string { - var builder strings.Builder - - if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) - } else if param.Spec != nil { - for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) - } - builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s{\n", param.Name.CamelCase, param.Name.CamelCase)) - for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) - } - for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "", subParam)) - } - builder.WriteString("}\n") - } else { - builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) - } - - return builder.String() + return prepareAssignment(param, "util.MemToStr", "") } // SpecifyEntryAssignment generates a string, which contains entry assignment in SpecifyEntry() function // in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, // which are declaring additional variables (function nestedObjectDeclaration()) and use them in -// entry assignment (function nestedObjectAssignment()) +// entry assignment (function nestedObjectAssignment()). func SpecifyEntryAssignment(param *properties.SpecParam) string { + return prepareAssignment(param, "util.StrToMem", "Xml") +} + +func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix string) string { var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString(fmt.Sprintf("entry.%s = util.StrToMem(o.%s)", param.Name.CamelCase, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("entry.%s = %s(o.%s)", param.Name.CamelCase, listFunction, param.Name.CamelCase)) } else if param.Spec != nil { for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) } - builder.WriteString(fmt.Sprintf("entry.%s = &Spec%sXml{\n", param.Name.CamelCase, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s%s{\n", param.Name.CamelCase, param.Name.CamelCase, specSuffix)) for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "Xml", subParam)) + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, specSuffix, subParam)) } for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, "Xml", subParam)) + builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, specSuffix, subParam)) } builder.WriteString("}\n") } else { @@ -131,7 +115,7 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp } // SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl -// to compare all items of generated entry +// to compare all items of generated entry. func SpecMatchesFunction(param *properties.SpecParam) string { calculatedFunction := "OptionalStringsMatch" if param.Type == "list" { diff --git a/pkg/translate/names.go b/pkg/translate/names.go index d10a57dd..3b73be51 100644 --- a/pkg/translate/names.go +++ b/pkg/translate/names.go @@ -1,6 +1,6 @@ package translate -// PackageName get package name from Go SDK path +// PackageName get package name from Go SDK path. func PackageName(list []string) string { if len(list) == 0 { return "" diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 8f4a427f..2d180ad2 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -6,7 +6,7 @@ import ( "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" ) -// LocationType function used in template location.tmpl to generate location type name +// LocationType function used in template location.tmpl to generate location type name. func LocationType(location *properties.Location, pointer bool) string { prefix := "" if pointer { @@ -19,7 +19,7 @@ func LocationType(location *properties.Location, pointer bool) string { } } -// NestedSpecs go through all params and one of (recursively) and return map of all nested specs +// NestedSpecs go through all params and one of (recursively) and return map of all nested specs. func NestedSpecs(spec *properties.Spec) (map[string]*properties.Spec, error) { nestedSpecs := make(map[string]*properties.Spec) @@ -44,7 +44,7 @@ func updateNestedSpecs(param *properties.SpecParam, nestedSpecs map[string]*prop } } -// SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files) +// SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). func SpecParamType(param *properties.SpecParam) string { prefix := "" if !param.Required { @@ -70,7 +70,7 @@ func SpecParamType(param *properties.SpecParam) string { return prefix + calculatedType } -// XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files) +// XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). func XmlParamType(param *properties.SpecParam) string { prefix := "" if !param.Required { @@ -89,7 +89,7 @@ func XmlParamType(param *properties.SpecParam) string { return prefix + calculatedType } -// XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`) +// XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`). func XmlTag(param *properties.SpecParam) string { suffix := "" if !param.Required { @@ -103,7 +103,7 @@ func XmlTag(param *properties.SpecParam) string { return calculatedTag } -// OmitEmpty return omitempty in XML tag for location, if there are variables defined +// OmitEmpty return omitempty in XML tag for location, if there are variables defined. func OmitEmpty(location *properties.Location) string { if location.Vars != nil { return ",omitempty" From 3949bb7f1ae0c5c2cdb922135f0e9d33b8730538 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 13:07:27 +0100 Subject: [PATCH 15/66] new template to generate config.go --- templates/sdk/config.tmpl | 274 +++++++++++++++++--------------------- 1 file changed, 120 insertions(+), 154 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index e959a9a2..a9c37f75 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -1,188 +1,154 @@ {{- if not .Entry}} - package {{packageName .GoSdkPath}} +package {{packageName .GoSdkPath}} - import ( - "encoding/xml" - "github.com/PaloAltoNetworks/pango/generic" - "github.com/PaloAltoNetworks/pango/version" - ) +import ( + "encoding/xml" + "fmt" - type Config{{createGoSuffixFromVersion ""}} struct { - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + "github.com/PaloAltoNetworks/pango/filtering" + "github.com/PaloAltoNetworks/pango/generic" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/version" +) + +type Config struct { + {{- range $_, $param := .Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $param}} {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- range $_, $param := .Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $param}} {{- end}} - Misc map[string][]generic.Xml - } + Misc map[string][]generic.Xml +} - {{- range $name, $spec := nestedSpecs $.Spec }} - type Spec{{$name}}{{createGoSuffixFromVersion ""}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - } +{{- range $name, $spec := nestedSpecs .Spec }} +type Spec{{$name}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $param}} {{- end}} - - {{- range $version := .SupportedVersions }} - type configXmlContainer{{createGoSuffixFromVersion $version}} struct { - XMLName xml.Name `xml:"result"` - Answer []configXml{{createGoSuffixFromVersion $version}} `xml:"system"` - } + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $param}} {{- end}} +} +{{- end}} - {{- range $version := .SupportedVersions }} - type configXml{{createGoSuffixFromVersion $version}} struct { - {{- range $_, $param := $.Spec.Params}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} +type ConfigXmlContainer struct { + Answer []ConfigXml `xml:"config"` +} - Misc []generic.Xml `xml:",any"` - } +type ConfigXml struct { + {{- range $_, $param := .Spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} {{- end}} - - {{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} - type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { - {{- range $_, $param := $spec.Params}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{- if paramSupportedInVersion $param $version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - Misc []generic.Xml `xml:",any"` - } - {{- end}} + {{- range $_, $param := .Spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} {{- end}} - func Versioning(vn version.Number) (Specifier, Normalizer, error) { - {{- $numberOfVersions := len .SupportedVersions }} - {{- if gt $numberOfVersions 1}} - {{- range $index, $version := .SupportedVersions }} - {{- if ne $version ""}} - version{{createGoSuffixFromVersion $version}}, err := version.New("{{$version}}") - if err != nil { - return nil, nil, err - } - {{- end}} - {{- end}} - {{- range $index, $version := .SupportedVersions }} - {{- if ne $version ""}} - {{- if eq $index 1}} - if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return specifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil - {{- else}} - } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return specifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil - {{- end}} - {{- end}} - {{- end}} - } else { + Misc []generic.Xml `xml:",any"` +} + +{{- range $name, $spec := nestedSpecs .Spec }} +type Spec{{$name}}Xml struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{- end}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{- end}} +} +{{- end}} + +func (e *Config) CopyMiscFrom(v *Config) { + if v == nil || len(v.Misc) == 0 { + return + } + + e.Misc = make(map[string][]generic.Xml) + for key := range v.Misc { + e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) + } +} + +func (e *Config) Field(v string) (any, error) { + {{- range $_, $param := .Spec.Params}} + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + return e.{{$param.Name.CamelCase}}, nil + } + {{- if eq $param.Type "list"}} + if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { + return int64(len(e.{{$param.Name.CamelCase}})), nil + } {{- end}} - return specifyConfig, &configXmlContainer{}, nil - {{- if gt $numberOfVersions 1}} - } {{- end}} + {{- range $_, $param := .Spec.OneOf}} + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + return e.{{$param.Name.CamelCase}}, nil } + {{- end}} - {{- range $version := .SupportedVersions }} - func specifyConfig{{createGoSuffixFromVersion $version}}(o Config) (any, error) { - config := configXml{{createGoSuffixFromVersion $version}}{} + return nil, fmt.Errorf("unknown field") +} - {{- range $_, $param := $.Spec.Params}} - {{specifyEntryAssignment "config" $param $version}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{specifyEntryAssignment "config" $param $version}} - {{- end}} +func Versioning(vn version.Number) (Specifier, Normalizer, error) { + return SpecifyConfig, &ConfigXmlContainer{}, nil +} - config.Misc = o.Misc["Config"] +func SpecifyConfig(o Config) (any, error) { + config := ConfigXml{} - return config, nil - } + {{- range $_, $param := .Spec.Params}} + {{specifyEntryAssignment "config" $param}} + {{- end}} + {{- range $_, $param := .Spec.OneOf}} + {{specifyEntryAssignment "config" $param}} {{- end}} - {{- range $version := .SupportedVersions }} - func (c *configXmlContainer{{createGoSuffixFromVersion $version}}) Normalize() ([]Config, error) { - configList := make([]Config, 0, len(c.Answer)) - for _, o := range c.Answer { - config := Config{ - Misc: make(map[string][]generic.Xml), - } - {{- range $_, $param := $.Spec.Params}} - {{normalizeAssignment "config" $param $version}} + config.Misc = o.Misc[fmt.Sprintf("%s", "Config")] + + return config, nil +} + +func (c *ConfigXmlContainer) Normalize() ([]Config, error) { + configList := make([]Config, 0, len(c.Answer)) + for _, o := range c.Answer { + config := Config{ + Misc: make(map[string][]generic.Xml), + } + {{- range $_, $param := .Spec.Params}} + {{normalizeAssignment "config" $param}} {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{normalizeAssignment "config" $param $version}} + {{- range $_, $param := .Spec.OneOf}} + {{normalizeAssignment "config" $param}} {{- end}} - config.Misc["Config"] = o.Misc + config.Misc[fmt.Sprintf("%s", "Config")] = o.Misc - configList = append(configList, config) - } + configList = append(configList, config) + } - return configList, nil - } - {{- end}} + return configList, nil +} - // MarshalXML customized implementation of XML marshal due to requirement to skip 'system' as root for configuration settings - func (c configXml) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - {{- range $_, $param := $.Spec.Params}} - if c.{{$param.Name.CamelCase}} != nil { - start.Name = xml.Name{Local: "{{xmlName $param}}"} - if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { - return err - } - } - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - if c.{{$param.Name.CamelCase}} != nil { - start.Name = xml.Name{Local: "{{xmlName $param}}"} - if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { - return err - } - } - {{- end}} +func SpecMatches(a, b *Config) bool { + if a == nil && b != nil || a != nil && b == nil { + return false + } else if a == nil && b == nil { + return true + } - for _, v := range c.Misc { - if err := e.Encode(v); err != nil { - return err + // Don't compare Name. + {{- range $_, $param := .Spec.Params}} + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false } + {{- end}} + {{- range $_, $param := .Spec.OneOf}} + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false } + {{- end}} - return nil - } + return true +} {{- end}} \ No newline at end of file From 6ae47fc6b3842dd224b176dcdfa1e1bf7ba593fa Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 13:08:47 +0100 Subject: [PATCH 16/66] extend function NormalizeAssignment() and SpecifyEntryAssignment(), which from now is used also in config.tmpl --- pkg/translate/funcs.go | 26 +++++++++++++------------- templates/sdk/entry.tmpl | 8 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index c946543c..e8c2ccf3 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -19,32 +19,32 @@ func AsEntryXpath(location, xpath string) (string, error) { return asEntryXpath, nil } -// NormalizeAssignment generates a string, which contains entry assignment in Normalize() function -// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, +// NormalizeAssignment generates a string, which contains entry/config assignment in Normalize() function +// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed internal functions, // which are declaring additional variables (function nestedObjectDeclaration()) and use them in // entry assignment (function nestedObjectAssignment()). -func NormalizeAssignment(param *properties.SpecParam) string { - return prepareAssignment(param, "util.MemToStr", "") +func NormalizeAssignment(objectType string, param *properties.SpecParam) string { + return prepareAssignment(objectType, param, "util.MemToStr", "") } -// SpecifyEntryAssignment generates a string, which contains entry assignment in SpecifyEntry() function -// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, +// SpecifyEntryAssignment generates a string, which contains entry/config assignment in SpecifyEntry() function +// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed internal functions, // which are declaring additional variables (function nestedObjectDeclaration()) and use them in // entry assignment (function nestedObjectAssignment()). -func SpecifyEntryAssignment(param *properties.SpecParam) string { - return prepareAssignment(param, "util.StrToMem", "Xml") +func SpecifyEntryAssignment(objectType string, param *properties.SpecParam) string { + return prepareAssignment(objectType, param, "util.StrToMem", "Xml") } -func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix string) string { +func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specSuffix string) string { var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString(fmt.Sprintf("entry.%s = %s(o.%s)", param.Name.CamelCase, listFunction, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s)", objectType, param.Name.CamelCase, listFunction, param.Name.CamelCase)) } else if param.Spec != nil { for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) } - builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s%s{\n", param.Name.CamelCase, param.Name.CamelCase, specSuffix)) + builder.WriteString(fmt.Sprintf("%s.%s = &Spec%s%s{\n", objectType, param.Name.CamelCase, param.Name.CamelCase, specSuffix)) for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, specSuffix, subParam)) } @@ -53,7 +53,7 @@ func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix str } builder.WriteString("}\n") } else { - builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("%s.%s = o.%s", objectType, param.Name.CamelCase, param.Name.CamelCase)) } return builder.String() @@ -114,7 +114,7 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp return builder.String() } -// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl +// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl/config.tmpl // to compare all items of generated entry. func SpecMatchesFunction(param *properties.SpecParam) string { calculatedFunction := "OptionalStringsMatch" diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index a21b27d7..759692f8 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -118,10 +118,10 @@ func SpecifyEntry(o Entry) (any, error) { entry.Name = o.Name {{- range $_, $param := .Spec.Params}} - {{specifyEntryAssignment $param}} + {{specifyEntryAssignment "entry" $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{specifyEntryAssignment $param}} + {{specifyEntryAssignment "entry" $param}} {{- end}} entry.Misc = o.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] @@ -137,10 +137,10 @@ func (c *EntryXmlContainer) Normalize() ([]Entry, error) { } entry.Name = o.Name {{- range $_, $param := .Spec.Params}} - {{normalizeAssignment $param}} + {{normalizeAssignment "entry" $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{normalizeAssignment $param}} + {{normalizeAssignment "entry" $param}} {{- end}} entry.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] = o.Misc From 802698c26b1f8d83f41e06befbbcabec3a10bbcd Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 13:09:38 +0100 Subject: [PATCH 17/66] fix tests --- pkg/translate/funcs_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 93a7edcc..650cd9a7 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -45,8 +45,8 @@ func TestSpecifyEntryAssignmentForFlatStructure(t *testing.T) { } // when - calculatedAssignmentString := SpecifyEntryAssignment(¶mTypeString) - calculatedAssignmentListString := SpecifyEntryAssignment(¶mTypeListString) + calculatedAssignmentString := SpecifyEntryAssignment("entry", ¶mTypeString) + calculatedAssignmentListString := SpecifyEntryAssignment("entry", ¶mTypeListString) // then assert.Equal(t, "entry.Description = o.Description", calculatedAssignmentString) @@ -93,7 +93,7 @@ C : nestedABC, } ` // when - calculatedAssignmentString := SpecifyEntryAssignment(spec.Params["a"]) + calculatedAssignmentString := SpecifyEntryAssignment("entry", spec.Params["a"]) // then assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) @@ -139,7 +139,7 @@ C : nestedABC, } ` // when - calculatedAssignmentString := NormalizeAssignment(spec.Params["a"]) + calculatedAssignmentString := NormalizeAssignment("entry", spec.Params["a"]) // then assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) From 1468446c7f2f1536cb9c905636c46e37c906ae0e Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 14:50:40 +0100 Subject: [PATCH 18/66] extend template functions after tests for service.yaml --- pkg/translate/funcs.go | 33 ++++++++++++++++++--------------- pkg/translate/funcs_test.go | 4 ++-- pkg/translate/structs.go | 23 ++++++++++++----------- pkg/translate/structs_test.go | 12 ++++++------ templates/sdk/entry.tmpl | 16 ++++++++-------- 5 files changed, 46 insertions(+), 42 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index e8c2ccf3..cf2ecd9a 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -19,32 +19,35 @@ func AsEntryXpath(location, xpath string) (string, error) { return asEntryXpath, nil } -// NormalizeAssignment generates a string, which contains entry/config assignment in Normalize() function -// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed internal functions, +// NormalizeAssignment generates a string, which contains entry assignment in Normalize() function +// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, // which are declaring additional variables (function nestedObjectDeclaration()) and use them in // entry assignment (function nestedObjectAssignment()). -func NormalizeAssignment(objectType string, param *properties.SpecParam) string { - return prepareAssignment(objectType, param, "util.MemToStr", "") +func NormalizeAssignment(param *properties.SpecParam) string { + return prepareAssignment(param, "util.MemToStr", "") } -// SpecifyEntryAssignment generates a string, which contains entry/config assignment in SpecifyEntry() function -// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed internal functions, +// SpecifyEntryAssignment generates a string, which contains entry assignment in SpecifyEntry() function +// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, // which are declaring additional variables (function nestedObjectDeclaration()) and use them in // entry assignment (function nestedObjectAssignment()). -func SpecifyEntryAssignment(objectType string, param *properties.SpecParam) string { - return prepareAssignment(objectType, param, "util.StrToMem", "Xml") +func SpecifyEntryAssignment(param *properties.SpecParam) string { + return prepareAssignment(param, "util.StrToMem", "Xml") } -func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specSuffix string) string { +func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix string) string { var builder strings.Builder if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { - builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s)", objectType, param.Name.CamelCase, listFunction, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("entry.%s = %s(o.%s)", param.Name.CamelCase, listFunction, param.Name.CamelCase)) } else if param.Spec != nil { for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) } - builder.WriteString(fmt.Sprintf("%s.%s = &Spec%s%s{\n", objectType, param.Name.CamelCase, param.Name.CamelCase, specSuffix)) + for _, subParam := range param.Spec.OneOf { + builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) + } + builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s%s{\n", param.Name.CamelCase, param.Name.CamelCase, specSuffix)) for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, specSuffix, subParam)) } @@ -53,7 +56,7 @@ func prepareAssignment(objectType string, param *properties.SpecParam, listFunct } builder.WriteString("}\n") } else { - builder.WriteString(fmt.Sprintf("%s.%s = o.%s", objectType, param.Name.CamelCase, param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) } return builder.String() @@ -97,8 +100,8 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp builder.WriteString(fmt.Sprintf("%s : util.StrToMem(o.%s),\n", param.Name.CamelCase, param.Name.CamelCase)) } else if param.Spec != nil { - builder.WriteString(fmt.Sprintf("%s : &Spec%s%s{\n", - param.Name.CamelCase, param.Name.CamelCase, suffix)) + builder.WriteString(fmt.Sprintf("%s : &Spec%s%s%s{\n", + param.Name.CamelCase, strings.Join(parent, ""), param.Name.CamelCase, suffix)) for _, subParam := range param.Spec.Params { builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), suffix, subParam)) } @@ -114,7 +117,7 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp return builder.String() } -// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl/config.tmpl +// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl // to compare all items of generated entry. func SpecMatchesFunction(param *properties.SpecParam) string { calculatedFunction := "OptionalStringsMatch" diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 650cd9a7..e650e454 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -87,7 +87,7 @@ func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { } expectedAssignmentStreing := `nestedABC := o.A.B.C entry.A = &SpecAXml{ -B : &SpecBXml{ +B : &SpecABXml{ C : nestedABC, }, } @@ -133,7 +133,7 @@ func TestNormalizeAssignmentForNestedObject(t *testing.T) { } expectedAssignmentStreing := `nestedABC := o.A.B.C entry.A = &SpecA{ -B : &SpecB{ +B : &SpecAB{ C : nestedABC, }, } diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 2d180ad2..cfc15670 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" + "strings" ) // LocationType function used in template location.tmpl to generate location type name. @@ -23,29 +24,29 @@ func LocationType(location *properties.Location, pointer bool) string { func NestedSpecs(spec *properties.Spec) (map[string]*properties.Spec, error) { nestedSpecs := make(map[string]*properties.Spec) - checkNestedSpecs(spec, nestedSpecs) + checkNestedSpecs([]string{}, spec, nestedSpecs) return nestedSpecs, nil } -func checkNestedSpecs(spec *properties.Spec, nestedSpecs map[string]*properties.Spec) { +func checkNestedSpecs(parent []string, spec *properties.Spec, nestedSpecs map[string]*properties.Spec) { for _, param := range spec.Params { - updateNestedSpecs(param, nestedSpecs) + updateNestedSpecs(append(parent, param.Name.CamelCase), param, nestedSpecs) } for _, param := range spec.OneOf { - updateNestedSpecs(param, nestedSpecs) + updateNestedSpecs(append(parent, param.Name.CamelCase), param, nestedSpecs) } } -func updateNestedSpecs(param *properties.SpecParam, nestedSpecs map[string]*properties.Spec) { +func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs map[string]*properties.Spec) { if param.Spec != nil { - nestedSpecs[naming.CamelCase("", param.Name.CamelCase, "", true)] = param.Spec - checkNestedSpecs(param.Spec, nestedSpecs) + nestedSpecs[strings.Join(parent, "")] = param.Spec + checkNestedSpecs(parent, param.Spec, nestedSpecs) } } // SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func SpecParamType(param *properties.SpecParam) string { +func SpecParamType(parent string, param *properties.SpecParam) string { prefix := "" if !param.Required { prefix = "*" @@ -62,7 +63,7 @@ func SpecParamType(param *properties.SpecParam) string { calculatedType = param.Items.Type } } else if param.Spec != nil { - calculatedType = fmt.Sprintf("Spec%s", naming.CamelCase("", param.Name.CamelCase, "", true)) + calculatedType = fmt.Sprintf("Spec%s%s", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) } else { calculatedType = param.Type } @@ -71,7 +72,7 @@ func SpecParamType(param *properties.SpecParam) string { } // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func XmlParamType(param *properties.SpecParam) string { +func XmlParamType(parent string, param *properties.SpecParam) string { prefix := "" if !param.Required { prefix = "*" @@ -81,7 +82,7 @@ func XmlParamType(param *properties.SpecParam) string { if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { calculatedType = "util.MemberType" } else if param.Spec != nil { - calculatedType = fmt.Sprintf("Spec%sXml", naming.CamelCase("", param.Name.CamelCase, "", true)) + calculatedType = fmt.Sprintf("Spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) } else { calculatedType = param.Type } diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 27085549..415be26d 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -85,9 +85,9 @@ func TestSpecParamType(t *testing.T) { } // when - calculatedTypeRequiredString := SpecParamType(¶mTypeRequiredString) - calculatedTypeListString := SpecParamType(¶mTypeListString) - calculatedTypeOptionalString := SpecParamType(¶mTypeOptionalString) + calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString) + calculatedTypeListString := SpecParamType("", ¶mTypeListString) + calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString) // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -139,8 +139,8 @@ func TestXmlParamType(t *testing.T) { } // when - calculatedTypeRequiredString := XmlParamType(¶mTypeRequiredString) - calculatedTypeListString := XmlParamType(¶mTypeListString) + calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString) + calculatedTypeListString := XmlParamType("", ¶mTypeListString) // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -220,5 +220,5 @@ func TestNestedSpecs(t *testing.T) { // then assert.NotNil(t, nestedSpecs) assert.Contains(t, nestedSpecs, "A") - assert.Contains(t, nestedSpecs, "B") + assert.Contains(t, nestedSpecs, "AB") } diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 759692f8..f90e4ac9 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -26,10 +26,10 @@ var ( type Entry struct { Name string {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} {{- end}} Misc map[string][]generic.Xml @@ -38,10 +38,10 @@ type Entry struct { {{- range $name, $spec := nestedSpecs .Spec }} type Spec{{$name}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} } {{- end}} @@ -54,10 +54,10 @@ type EntryXml struct { XMLName xml.Name `xml:"entry"` Name string `xml:"name,attr"` {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} {{- end}} Misc []generic.Xml `xml:",any"` @@ -66,10 +66,10 @@ type EntryXml struct { {{- range $name, $spec := nestedSpecs .Spec }} type Spec{{$name}}Xml struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} } {{- end}} From dbeedb45e60e0f92e9e8e2d3996de260ab8e7ff4 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 13 Mar 2024 14:56:34 +0100 Subject: [PATCH 19/66] extend template functions --- templates/sdk/config.tmpl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index a9c37f75..d63104ae 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -13,10 +13,10 @@ import ( type Config struct { {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} {{- end}} Misc map[string][]generic.Xml @@ -25,10 +25,10 @@ type Config struct { {{- range $name, $spec := nestedSpecs .Spec }} type Spec{{$name}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} } {{- end}} @@ -39,10 +39,10 @@ type ConfigXmlContainer struct { type ConfigXml struct { {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} {{- end}} Misc []generic.Xml `xml:",any"` @@ -51,10 +51,10 @@ type ConfigXml struct { {{- range $name, $spec := nestedSpecs .Spec }} type Spec{{$name}}Xml struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} } {{- end}} From 9ccdce12314c1bde47fe1c64e5d8aad7e9cea8aa Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Thu, 14 Mar 2024 13:28:07 +0100 Subject: [PATCH 20/66] apply changes after review (#1) --- pkg/properties/normalized.go | 39 +++++++++++++------------------ pkg/properties/normalized_test.go | 1 - pkg/translate/funcs.go | 15 +++++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index 073466fc..8d599391 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -225,41 +225,34 @@ func (spec *Normalization) AddNameVariantsForParams() error { return nil } -// AddDefaultTypesForParams recursively add default types for params for nested specs. -func AddDefaultTypesForParams(param *SpecParam) error { - if param.Type == "" { - param.Type = "string" - } +// addDefaultTypesForParams recursively add default types for params for nested specs. +func addDefaultTypesForParams(params map[string]*SpecParam) error { + for _, param := range params { + if param.Type == "" { + param.Type = "string" + } - if param.Spec != nil { - for _, childParam := range param.Spec.Params { - if err := AddDefaultTypesForParams(childParam); err != nil { + if param.Spec != nil { + if err := addDefaultTypesForParams(param.Spec.Params); err != nil { return err } - } - for _, childParam := range param.Spec.OneOf { - if err := AddDefaultTypesForParams(childParam); err != nil { + if err := addDefaultTypesForParams(param.Spec.OneOf); err != nil { return err } } - return nil - } else { - return nil } + + return nil } -// AddDefaultTypesForParams ensures all params within Spec have a default type if not specified. +// addDefaultTypesForParams ensures all params within Spec have a default type if not specified. func (spec *Normalization) AddDefaultTypesForParams() error { if spec.Spec != nil { - for _, childParam := range spec.Spec.Params { - if err := AddDefaultTypesForParams(childParam); err != nil { - return err - } + if err := addDefaultTypesForParams(spec.Spec.Params); err != nil { + return err } - for _, childParam := range spec.Spec.OneOf { - if err := AddDefaultTypesForParams(childParam); err != nil { - return err - } + if err := addDefaultTypesForParams(spec.Spec.OneOf); err != nil { + return err } return nil } else { diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index 98a49bd4..e722344a 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -353,7 +353,6 @@ spec: // when yamlParsedData, _ := ParseSpec([]byte(sampleSpec)) yamlDump, _ := yaml.Marshal(&yamlParsedData) - // fmt.Printf("%s", string(yamlDump)) // then assert.NotNilf(t, yamlDump, "Marshalled data cannot be nil") diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index cf2ecd9a..8a5a1ec2 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -13,12 +13,21 @@ func AsEntryXpath(location, xpath string) (string, error) { if !strings.Contains(xpath, "$") || !strings.Contains(xpath, "}") { return "", errors.New("$ followed by } should exists in xpath'") } - xpath = strings.TrimSpace(strings.Split(strings.Split(xpath, "$")[1], "}")[0]) - xpath = naming.CamelCase("", xpath, "", true) - asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s.%s}),", location, xpath) + asEntryXpath := generateXpathForLocation(location, xpath) return asEntryXpath, nil } +func generateXpathForLocation(location string, xpath string) string { + xpathPartWithoutDollar := strings.SplitAfter(xpath, "$") + xpathPartWithoutBrackets := strings.TrimSpace(strings.Trim(xpathPartWithoutDollar[1], "${}")) + xpathPartCamelCase := naming.CamelCase("", xpathPartWithoutBrackets, "", true) + asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s.%s}),", location, xpathPartCamelCase) + //xpath = strings.TrimSpace(strings.Split(strings.Split(xpath, "$")[1], "}")[0]) + //xpath = naming.CamelCase("", xpath, "", true) + //asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s.%s}),", location, xpath) + return asEntryXpath +} + // NormalizeAssignment generates a string, which contains entry assignment in Normalize() function // in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, // which are declaring additional variables (function nestedObjectDeclaration()) and use them in From c40fbfd78df2a9a17096604017bd4ed7f5014e3f Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Thu, 14 Mar 2024 13:28:32 +0100 Subject: [PATCH 21/66] remove comments --- pkg/translate/funcs.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 8a5a1ec2..7c7d8b83 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -22,9 +22,6 @@ func generateXpathForLocation(location string, xpath string) string { xpathPartWithoutBrackets := strings.TrimSpace(strings.Trim(xpathPartWithoutDollar[1], "${}")) xpathPartCamelCase := naming.CamelCase("", xpathPartWithoutBrackets, "", true) asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s.%s}),", location, xpathPartCamelCase) - //xpath = strings.TrimSpace(strings.Split(strings.Split(xpath, "$")[1], "}")[0]) - //xpath = naming.CamelCase("", xpath, "", true) - //asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s.%s}),", location, xpath) return asEntryXpath } From f3e7d5623d0236d887473201a41fa879e62a45be Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Thu, 14 Mar 2024 13:32:25 +0100 Subject: [PATCH 22/66] use fmt.Errorf to return errors --- pkg/properties/normalized.go | 15 +++++++-------- pkg/translate/funcs.go | 3 +-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index 8d599391..a665b277 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -1,7 +1,6 @@ package properties import ( - "errors" "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/content" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" @@ -263,13 +262,13 @@ func (spec *Normalization) AddDefaultTypesForParams() error { // Sanity basic checks for specification (normalization) e.g. check if at least 1 location is defined. func (spec *Normalization) Sanity() error { if spec.Name == "" { - return errors.New("name is required") + return fmt.Errorf("name is required") } if spec.Locations == nil { - return errors.New("at least 1 location is required") + return fmt.Errorf("at least 1 location is required") } if spec.GoSdkPath == nil { - return errors.New("golang SDK path is required") + return fmt.Errorf("golang SDK path is required") } return nil @@ -280,18 +279,18 @@ func (spec *Normalization) Validate() []error { var checks []error if strings.Contains(spec.TerraformProviderSuffix, "panos_") { - checks = append(checks, errors.New("suffix for Terraform provider cannot contain `panos_`")) + checks = append(checks, fmt.Errorf("suffix for Terraform provider cannot contain `panos_`")) } for _, suffix := range spec.XpathSuffix { if strings.Contains(suffix, "/") { - checks = append(checks, errors.New("XPath cannot contain /")) + checks = append(checks, fmt.Errorf("XPath cannot contain /")) } } if len(spec.Locations) < 1 { - checks = append(checks, errors.New("at least 1 location is required")) + checks = append(checks, fmt.Errorf("at least 1 location is required")) } if len(spec.GoSdkPath) < 2 { - checks = append(checks, errors.New("golang SDK path should contain at least 2 elements of the path")) + checks = append(checks, fmt.Errorf("golang SDK path should contain at least 2 elements of the path")) } return checks diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 7c7d8b83..1328d8fe 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -1,7 +1,6 @@ package translate import ( - "errors" "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" @@ -11,7 +10,7 @@ import ( // AsEntryXpath functions used in location.tmpl to generate XPath for location. func AsEntryXpath(location, xpath string) (string, error) { if !strings.Contains(xpath, "$") || !strings.Contains(xpath, "}") { - return "", errors.New("$ followed by } should exists in xpath'") + return "", fmt.Errorf("xpath '%s' is missing '$' followed by '}'", xpath) } asEntryXpath := generateXpathForLocation(location, xpath) return asEntryXpath, nil From e7fa93a641c629db6c630cc1b5d10ff7d51497a7 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Thu, 14 Mar 2024 13:42:47 +0100 Subject: [PATCH 23/66] apply changes after review (#2) --- pkg/generate/generator.go | 4 +- pkg/translate/funcs.go | 16 +++--- pkg/translate/funcs_test.go | 4 +- pkg/translate/structs.go | 22 ++++---- templates/sdk/location.tmpl | 101 +++++++++++++++++------------------- 5 files changed, 72 insertions(+), 75 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 6e671302..d6c35bb1 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -132,8 +132,8 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "subtract": func(a, b int) int { return a - b }, - "asEntryXpath": translate.AsEntryXpath, - "nestedSpecs": translate.NestedSpecs, + "generateEntryXpath": translate.GenerateEntryXpathForLocation, + "nestedSpecs": translate.NestedSpecs, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 1328d8fe..dc9cf49f 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -7,16 +7,16 @@ import ( "strings" ) -// AsEntryXpath functions used in location.tmpl to generate XPath for location. -func AsEntryXpath(location, xpath string) (string, error) { +// GenerateEntryXpathForLocation functions used in location.tmpl to generate XPath for location. +func GenerateEntryXpathForLocation(location, xpath string) (string, error) { if !strings.Contains(xpath, "$") || !strings.Contains(xpath, "}") { return "", fmt.Errorf("xpath '%s' is missing '$' followed by '}'", xpath) } - asEntryXpath := generateXpathForLocation(location, xpath) + asEntryXpath := generateEntryXpathForLocation(location, xpath) return asEntryXpath, nil } -func generateXpathForLocation(location string, xpath string) string { +func generateEntryXpathForLocation(location string, xpath string) string { xpathPartWithoutDollar := strings.SplitAfter(xpath, "$") xpathPartWithoutBrackets := strings.TrimSpace(strings.Trim(xpathPartWithoutDollar[1], "${}")) xpathPartCamelCase := naming.CamelCase("", xpathPartWithoutBrackets, "", true) @@ -43,7 +43,7 @@ func SpecifyEntryAssignment(param *properties.SpecParam) string { func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix string) string { var builder strings.Builder - if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { + if isParamListAndProfileTypeIsMember(param) { builder.WriteString(fmt.Sprintf("entry.%s = %s(o.%s)", param.Name.CamelCase, listFunction, param.Name.CamelCase)) } else if param.Spec != nil { for _, subParam := range param.Spec.Params { @@ -67,6 +67,10 @@ func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix str return builder.String() } +func isParamListAndProfileTypeIsMember(param *properties.SpecParam) bool { + return param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" +} + func nestedObjectDeclaration(parent []string, param *properties.SpecParam) string { var builder strings.Builder @@ -101,7 +105,7 @@ func declareVariableForNestedObject(parent []string, param *properties.SpecParam func nestedObjectAssignment(parent []string, suffix string, param *properties.SpecParam) string { var builder strings.Builder - if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { + if isParamListAndProfileTypeIsMember(param) { builder.WriteString(fmt.Sprintf("%s : util.StrToMem(o.%s),\n", param.Name.CamelCase, param.Name.CamelCase)) } else if param.Spec != nil { diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index e650e454..2c62b19a 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -6,11 +6,11 @@ import ( "testing" ) -func TestAsEntryXpath(t *testing.T) { +func TestGenerateEntryXpath(t *testing.T) { // given // when - asEntryXpath, _ := AsEntryXpath("DeviceGroup", "{{ Entry $panorama_device }}") + asEntryXpath, _ := GenerateEntryXpathForLocation("DeviceGroup", "{{ Entry $panorama_device }}") // then assert.Equal(t, "util.AsEntryXpath([]string{o.DeviceGroup.PanoramaDevice}),", asEntryXpath) diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index cfc15670..6bdbb13e 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -68,7 +68,7 @@ func SpecParamType(parent string, param *properties.SpecParam) string { calculatedType = param.Type } - return prefix + calculatedType + return fmt.Sprintf("%s%s", prefix, calculatedType) } // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). @@ -79,7 +79,7 @@ func XmlParamType(parent string, param *properties.SpecParam) string { } calculatedType := "" - if param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" { + if isParamListAndProfileTypeIsMember(param) { calculatedType = "util.MemberType" } else if param.Spec != nil { calculatedType = fmt.Sprintf("Spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) @@ -87,21 +87,21 @@ func XmlParamType(parent string, param *properties.SpecParam) string { calculatedType = param.Type } - return prefix + calculatedType + return fmt.Sprintf("%s%s", prefix, calculatedType) } // XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`). func XmlTag(param *properties.SpecParam) string { - suffix := "" - if !param.Required { - suffix = ",omitempty" - } - - calculatedTag := "" if param.Profiles != nil && len(param.Profiles) > 0 { - calculatedTag = fmt.Sprintf("`xml:\"%s%s\"`", param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1], suffix) + suffix := "" + if !param.Required { + suffix = ",omitempty" + } + + return fmt.Sprintf("`xml:\"%s%s\"`", param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1], suffix) } - return calculatedTag + + return "" } // OmitEmpty return omitempty in XML tag for location, if there are variables defined. diff --git a/templates/sdk/location.tmpl b/templates/sdk/location.tmpl index e23bebde..121125cf 100644 --- a/templates/sdk/location.tmpl +++ b/templates/sdk/location.tmpl @@ -1,87 +1,80 @@ package {{packageName .GoSdkPath}} import ( -"fmt" + "fmt" -"github.com/PaloAltoNetworks/pango/errors" -"github.com/PaloAltoNetworks/pango/util" -"github.com/PaloAltoNetworks/pango/version" + "github.com/PaloAltoNetworks/pango/errors" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/version" ) type Location struct { -{{range $key, $location := .Locations}} + {{range $key, $location := .Locations}} {{- $location.Name.CamelCase }} {{locationType $location true}} `json:"{{$location.Name.Underscore}}{{omitEmpty $location}}"` -{{end}} + {{end}} } {{range $key, $location := .Locations}} - {{- if $location.Vars}} - type {{locationType $location false}} struct { - {{- range $key, $var := $location.Vars}} - {{$var.Name.CamelCase}} string `json:"{{$var.Name.Underscore}}"` - {{- end}} - } - {{end}} +{{- if $location.Vars}} +type {{locationType $location false}} struct { +{{- range $key, $var := $location.Vars}} + {{$var.Name.CamelCase}} string `json:"{{$var.Name.Underscore}}"` +{{- end}} +} +{{end}} {{- end}} func (o Location) IsValid() error { -count := 0 + count := 0 -switch { -{{- range $key, $location := .Locations}} + switch { + {{- range $key, $location := .Locations}} case o.{{- $location.Name.CamelCase}}{{if ne (locationType $location true) "bool"}} != nil{{end}}: - {{- range $name, $var := $location.Vars}} + {{- range $name, $var := $location.Vars}} if o.{{$location.Name.CamelCase}}.{{$var.Name.CamelCase}} == "" { - return fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") + return fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") } + {{- end}} + count++ {{- end}} - count++ -{{- end}} -} + } -if count == 0 { -return fmt.Errorf("no path specified") -} + if count == 0 { + return fmt.Errorf("no path specified") + } -if count > 1 { -return fmt.Errorf("multiple paths specified: only one should be specified") -} + if count > 1 { + return fmt.Errorf("multiple paths specified: only one should be specified") + } -return nil + return nil } -{{- if .Entry}} - func (o Location) Xpath(vn version.Number, name string) ([]string, error) { -{{- else}} - func (o Location) Xpath(vn version.Number) ([]string, error) { -{{- end}} +func (o Location) Xpath(vn version.Number, name string) ([]string, error) { -var ans []string + var ans []string -switch { -{{- range $key, $location := .Locations}} + switch { + {{- range $key, $location := .Locations}} case o.{{- $location.Name.CamelCase}}{{if ne (locationType $location true) "bool"}} != nil{{end}}: - {{- range $name, $var := $location.Vars}} + {{- range $name, $var := $location.Vars}} if o.{{$location.Name.CamelCase}}.{{$var.Name.CamelCase}} == "" { - return nil, fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") + return nil, fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") } - {{- end}} - ans = []string{ - {{- range $name, $xpath := $location.Xpath}} - {{- if contains $xpath "Entry"}} + {{- end}} + ans = []string{ + {{- range $name, $xpath := $location.Xpath}} + {{- if contains $xpath "Entry"}} {{generateEntryXpath $location.Name.CamelCase $xpath}} - {{- else}} + {{- else}} "{{$xpath}}", - {{- end}} + {{- end}} + {{- end}} + } {{- end}} - } -{{- end}} -default: -return nil, errors.NoLocationSpecifiedError -} + default: + return nil, errors.NoLocationSpecifiedError + } -{{- if .Entry}} - ans = append(ans, Suffix...) - ans = append(ans, util.AsEntryXpath([]string{name})) -{{- end}} + ans = append(ans, util.AsEntryXpath([]string{name})) -return ans, nil + return ans, nil } \ No newline at end of file From b5e271ee4a8992c653a7ac976963b650a21a3bcc Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Thu, 14 Mar 2024 13:53:22 +0100 Subject: [PATCH 24/66] refactor SpecMatchesFunction() --- pkg/translate/funcs.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index dc9cf49f..1ec81668 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -129,9 +129,8 @@ func nestedObjectAssignment(parent []string, suffix string, param *properties.Sp // SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl // to compare all items of generated entry. func SpecMatchesFunction(param *properties.SpecParam) string { - calculatedFunction := "OptionalStringsMatch" if param.Type == "list" { - calculatedFunction = "OrderedListsMatch" + return "OrderedListsMatch" } - return calculatedFunction + return "OptionalStringsMatch" } From 633940cf1cf4154f4f85b063bcbf856f0bbe021f Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 15 Mar 2024 09:12:38 +0100 Subject: [PATCH 25/66] refactor funcs.go --- pkg/properties/normalized.go | 2 +- pkg/translate/funcs.go | 103 ++++++++++++++++------------------- 2 files changed, 48 insertions(+), 57 deletions(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index a665b277..a24dd4e8 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -244,7 +244,7 @@ func addDefaultTypesForParams(params map[string]*SpecParam) error { return nil } -// addDefaultTypesForParams ensures all params within Spec have a default type if not specified. +// AddDefaultTypesForParams ensures all params within Spec have a default type if not specified. func (spec *Normalization) AddDefaultTypesForParams() error { if spec.Spec != nil { if err := addDefaultTypesForParams(spec.Spec.Params); err != nil { diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 1ec81668..251e4a14 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -43,25 +43,12 @@ func SpecifyEntryAssignment(param *properties.SpecParam) string { func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix string) string { var builder strings.Builder - if isParamListAndProfileTypeIsMember(param) { - builder.WriteString(fmt.Sprintf("entry.%s = %s(o.%s)", param.Name.CamelCase, listFunction, param.Name.CamelCase)) - } else if param.Spec != nil { - for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) - } - for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectDeclaration([]string{param.Name.CamelCase}, subParam)) - } - builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s%s{\n", param.Name.CamelCase, param.Name.CamelCase, specSuffix)) - for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, specSuffix, subParam)) - } - for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectAssignment([]string{param.Name.CamelCase}, specSuffix, subParam)) - } - builder.WriteString("}\n") + if param.Spec != nil { + appendSpecObjectAssignment(param, specSuffix, &builder) + } else if isParamListAndProfileTypeIsMember(param) { + appendListFunctionAssignment(param, listFunction, &builder) } else { - builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) + appendSimpleAssignment(param, &builder) } return builder.String() @@ -71,59 +58,63 @@ func isParamListAndProfileTypeIsMember(param *properties.SpecParam) bool { return param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" } -func nestedObjectDeclaration(parent []string, param *properties.SpecParam) string { - var builder strings.Builder +func appendSimpleAssignment(param *properties.SpecParam, builder *strings.Builder) { + builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) +} - if param.Spec != nil { - for _, subParam := range param.Spec.Params { - builder.WriteString(declareVariableForNestedObject(parent, param, subParam)) - builder.WriteString(nestedObjectDeclaration(append(parent, param.Name.CamelCase), subParam)) - } - for _, subParam := range param.Spec.OneOf { - builder.WriteString(declareVariableForNestedObject(parent, param, subParam)) - builder.WriteString(nestedObjectDeclaration(append(parent, param.Name.CamelCase), subParam)) - } - } +func appendListFunctionAssignment(param *properties.SpecParam, listFunction string, builder *strings.Builder) { + builder.WriteString(fmt.Sprintf("entry.%s = %s(o.%s)", param.Name.CamelCase, listFunction, param.Name.CamelCase)) +} - return builder.String() +func appendSpecObjectAssignment(param *properties.SpecParam, suffix string, builder *strings.Builder) { + appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.Params, builder) + appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.OneOf, builder) + + builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s%s{\n", param.Name.CamelCase, param.Name.CamelCase, suffix)) + + appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.Params, suffix, builder) + appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.OneOf, suffix, builder) + + builder.WriteString("}\n") +} + +func appendNestedObjectDeclaration(parent []string, params map[string]*properties.SpecParam, builder *strings.Builder) { + for _, subParam := range params { + appendDeclarationForNestedObject(parent, subParam, builder) + } } -func declareVariableForNestedObject(parent []string, param *properties.SpecParam, subParam *properties.SpecParam) string { - if subParam.Spec == nil && parent != nil { - return fmt.Sprintf("nested%s%s%s := o.%s.%s.%s\n", - strings.Join(parent, ""), - param.Name.CamelCase, - subParam.Name.CamelCase, - strings.Join(parent, "."), - param.Name.CamelCase, - subParam.Name.CamelCase) +func appendDeclarationForNestedObject(parent []string, param *properties.SpecParam, builder *strings.Builder) { + if param.Spec != nil { + appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.Params, builder) + appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.OneOf, builder) } else { - return "" + builder.WriteString(fmt.Sprintf("nested%s%s := o.%s.%s\n", + strings.Join(parent, ""), param.Name.CamelCase, + strings.Join(parent, "."), param.Name.CamelCase)) } } -func nestedObjectAssignment(parent []string, suffix string, param *properties.SpecParam) string { - var builder strings.Builder +func appendNestedObjectAssignment(parent []string, params map[string]*properties.SpecParam, suffix string, builder *strings.Builder) { + for _, subParam := range params { + appendAssignmentForNestedObject(parent, subParam, suffix, builder) + } +} - if isParamListAndProfileTypeIsMember(param) { +func appendAssignmentForNestedObject(parent []string, param *properties.SpecParam, suffix string, builder *strings.Builder) { + if param.Spec != nil { + builder.WriteString(fmt.Sprintf("%s : &Spec%s%s%s{\n", param.Name.CamelCase, + strings.Join(parent, ""), param.Name.CamelCase, suffix)) + appendNestedObjectAssignment(append(parent, param.Name.CamelCase), param.Spec.Params, suffix, builder) + appendNestedObjectAssignment(append(parent, param.Name.CamelCase), param.Spec.OneOf, suffix, builder) + builder.WriteString("},\n") + } else if isParamListAndProfileTypeIsMember(param) { builder.WriteString(fmt.Sprintf("%s : util.StrToMem(o.%s),\n", param.Name.CamelCase, param.Name.CamelCase)) - } else if param.Spec != nil { - builder.WriteString(fmt.Sprintf("%s : &Spec%s%s%s{\n", - param.Name.CamelCase, strings.Join(parent, ""), param.Name.CamelCase, suffix)) - for _, subParam := range param.Spec.Params { - builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), suffix, subParam)) - } - for _, subParam := range param.Spec.OneOf { - builder.WriteString(nestedObjectAssignment(append(parent, param.Name.CamelCase), suffix, subParam)) - } - builder.WriteString("},\n") } else { builder.WriteString(fmt.Sprintf("%s : nested%s%s,\n", param.Name.CamelCase, strings.Join(parent, ""), param.Name.CamelCase)) } - - return builder.String() } // SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl From 57bec844cbccd4b6b6a8ec9bd8900a3f9ab6b5ad Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 15 Mar 2024 09:24:04 +0100 Subject: [PATCH 26/66] refactor structs.go --- pkg/translate/structs.go | 49 ++++++++++++++++++++++------------- pkg/translate/structs_test.go | 4 +-- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 6bdbb13e..d96638e6 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -47,23 +47,13 @@ func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs // SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). func SpecParamType(parent string, param *properties.SpecParam) string { - prefix := "" - if !param.Required { - prefix = "*" - } - if param.Type == "list" { - prefix = "[]" - } + prefix := determinePrefix(param) calculatedType := "" if param.Type == "list" && param.Items != nil { - if param.Items.Type == "object" && param.Items.Ref != nil { - calculatedType = "string" - } else { - calculatedType = param.Items.Type - } + calculatedType = determineListType(param) } else if param.Spec != nil { - calculatedType = fmt.Sprintf("Spec%s%s", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) + calculatedType = calculateNestedSpecType(parent, param) } else { calculatedType = param.Type } @@ -73,16 +63,13 @@ func SpecParamType(parent string, param *properties.SpecParam) string { // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). func XmlParamType(parent string, param *properties.SpecParam) string { - prefix := "" - if !param.Required { - prefix = "*" - } + prefix := determinePrefix(param) calculatedType := "" if isParamListAndProfileTypeIsMember(param) { calculatedType = "util.MemberType" } else if param.Spec != nil { - calculatedType = fmt.Sprintf("Spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) + calculatedType = calculateNestedXmlSpecType(parent, param) } else { calculatedType = param.Type } @@ -90,6 +77,32 @@ func XmlParamType(parent string, param *properties.SpecParam) string { return fmt.Sprintf("%s%s", prefix, calculatedType) } +func determinePrefix(param *properties.SpecParam) string { + prefix := "" + if param.Type == "list" { + prefix = prefix + "[]" + } + if !param.Required { + prefix = prefix + "*" + } + return prefix +} + +func determineListType(param *properties.SpecParam) string { + if param.Items.Type == "object" && param.Items.Ref != nil { + return "string" + } + return param.Items.Type +} + +func calculateNestedSpecType(parent string, param *properties.SpecParam) string { + return fmt.Sprintf("Spec%s%s", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) +} + +func calculateNestedXmlSpecType(parent string, param *properties.SpecParam) string { + return fmt.Sprintf("Spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) +} + // XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`). func XmlTag(param *properties.SpecParam) string { if param.Profiles != nil && len(param.Profiles) > 0 { diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 415be26d..4a3cb1bf 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -91,7 +91,7 @@ func TestSpecParamType(t *testing.T) { // then assert.Equal(t, "string", calculatedTypeRequiredString) - assert.Equal(t, "[]string", calculatedTypeListString) + assert.Equal(t, "[]*string", calculatedTypeListString) assert.Equal(t, "*string", calculatedTypeOptionalString) } @@ -144,7 +144,7 @@ func TestXmlParamType(t *testing.T) { // then assert.Equal(t, "string", calculatedTypeRequiredString) - assert.Equal(t, "*util.MemberType", calculatedTypeListString) + assert.Equal(t, "[]*util.MemberType", calculatedTypeListString) } func TestXmlTag(t *testing.T) { From c9640fecc09f541ec397f37789f0ff588322cd08 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 15 Mar 2024 10:01:25 +0100 Subject: [PATCH 27/66] merge changes from render-entry branch --- pkg/translate/funcs.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 251e4a14..122c5414 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -44,11 +44,11 @@ func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix str var builder strings.Builder if param.Spec != nil { - appendSpecObjectAssignment(param, specSuffix, &builder) + appendSpecObjectAssignment(param, objectType, specSuffix, &builder) } else if isParamListAndProfileTypeIsMember(param) { - appendListFunctionAssignment(param, listFunction, &builder) + appendListFunctionAssignment(param, objectType, listFunction, &builder) } else { - appendSimpleAssignment(param, &builder) + appendSimpleAssignment(param, objectType, &builder) } return builder.String() @@ -58,19 +58,19 @@ func isParamListAndProfileTypeIsMember(param *properties.SpecParam) bool { return param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" } -func appendSimpleAssignment(param *properties.SpecParam, builder *strings.Builder) { - builder.WriteString(fmt.Sprintf("entry.%s = o.%s", param.Name.CamelCase, param.Name.CamelCase)) +func appendSimpleAssignment(param *properties.SpecParam, objectType string, builder *strings.Builder) { + builder.WriteString(fmt.Sprintf("%s.%s = o.%s", objectType, param.Name.CamelCase, param.Name.CamelCase)) } -func appendListFunctionAssignment(param *properties.SpecParam, listFunction string, builder *strings.Builder) { - builder.WriteString(fmt.Sprintf("entry.%s = %s(o.%s)", param.Name.CamelCase, listFunction, param.Name.CamelCase)) +func appendListFunctionAssignment(param *properties.SpecParam, objectType string, listFunction string, builder *strings.Builder) { + builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s)", objectType, param.Name.CamelCase, listFunction, param.Name.CamelCase)) } -func appendSpecObjectAssignment(param *properties.SpecParam, suffix string, builder *strings.Builder) { +func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.Params, builder) appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.OneOf, builder) - builder.WriteString(fmt.Sprintf("entry.%s = &Spec%s%s{\n", param.Name.CamelCase, param.Name.CamelCase, suffix)) + builder.WriteString(fmt.Sprintf("%s.%s = &Spec%s%s{\n", objectType, param.Name.CamelCase, param.Name.CamelCase, suffix)) appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.Params, suffix, builder) appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.OneOf, suffix, builder) From ed7ed72ad5f5e5c783bc8305b264afa371123c9b Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 18 Mar 2024 14:12:53 +0100 Subject: [PATCH 28/66] remove nested variables declaration, use the directly in assignments --- pkg/translate/funcs.go | 26 ++++---------------------- pkg/translate/funcs_test.go | 10 ++++------ 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 122c5414..c34fb843 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -67,9 +67,6 @@ func appendListFunctionAssignment(param *properties.SpecParam, objectType string } func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { - appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.Params, builder) - appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.OneOf, builder) - builder.WriteString(fmt.Sprintf("%s.%s = &Spec%s%s{\n", objectType, param.Name.CamelCase, param.Name.CamelCase, suffix)) appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.Params, suffix, builder) @@ -78,24 +75,9 @@ func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, builder.WriteString("}\n") } -func appendNestedObjectDeclaration(parent []string, params map[string]*properties.SpecParam, builder *strings.Builder) { - for _, subParam := range params { - appendDeclarationForNestedObject(parent, subParam, builder) - } -} - -func appendDeclarationForNestedObject(parent []string, param *properties.SpecParam, builder *strings.Builder) { - if param.Spec != nil { - appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.Params, builder) - appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.OneOf, builder) - } else { - builder.WriteString(fmt.Sprintf("nested%s%s := o.%s.%s\n", - strings.Join(parent, ""), param.Name.CamelCase, - strings.Join(parent, "."), param.Name.CamelCase)) - } -} - func appendNestedObjectAssignment(parent []string, params map[string]*properties.SpecParam, suffix string, builder *strings.Builder) { + // TODO: for one_of e.g. UDP or TCP, we need to generate additional if, because we cannot specify both TCP and UDP + // TODO: above applies not only for one, but also for any pointers to structs !!! for _, subParam := range params { appendAssignmentForNestedObject(parent, subParam, suffix, builder) } @@ -112,8 +94,8 @@ func appendAssignmentForNestedObject(parent []string, param *properties.SpecPara builder.WriteString(fmt.Sprintf("%s : util.StrToMem(o.%s),\n", param.Name.CamelCase, param.Name.CamelCase)) } else { - builder.WriteString(fmt.Sprintf("%s : nested%s%s,\n", - param.Name.CamelCase, strings.Join(parent, ""), param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("%s : o.%s.%s,\n", + param.Name.CamelCase, strings.Join(parent, "."), param.Name.CamelCase)) } } diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 2c62b19a..d7de5583 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -85,10 +85,9 @@ func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { }, }, } - expectedAssignmentStreing := `nestedABC := o.A.B.C -entry.A = &SpecAXml{ + expectedAssignmentStreing := `entry.A = &SpecAXml{ B : &SpecABXml{ -C : nestedABC, +C : o.A.B.C, }, } ` @@ -131,10 +130,9 @@ func TestNormalizeAssignmentForNestedObject(t *testing.T) { }, }, } - expectedAssignmentStreing := `nestedABC := o.A.B.C -entry.A = &SpecA{ + expectedAssignmentStreing := `entry.A = &SpecA{ B : &SpecAB{ -C : nestedABC, +C : o.A.B.C, }, } ` From 24f1f513e06b0ee8618266f5f25c041b925e0ce4 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 19 Mar 2024 09:16:14 +0100 Subject: [PATCH 29/66] fix problems while using entry.go with E2E tests for address object --- pkg/translate/funcs.go | 28 ++++++++++++---------------- pkg/translate/structs.go | 18 ++++++++---------- pkg/translate/structs_test.go | 4 ++-- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index c34fb843..99e8f850 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -24,23 +24,21 @@ func generateEntryXpathForLocation(location string, xpath string) string { return asEntryXpath } -// NormalizeAssignment generates a string, which contains entry assignment in Normalize() function -// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, -// which are declaring additional variables (function nestedObjectDeclaration()) and use them in -// entry assignment (function nestedObjectAssignment()). -func NormalizeAssignment(param *properties.SpecParam) string { - return prepareAssignment(param, "util.MemToStr", "") +// NormalizeAssignment generates a string, which contains entry/config assignment in Normalize() function +// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed +// internal functions, which are creating entry assignment. +func NormalizeAssignment(objectType string, param *properties.SpecParam) string { + return prepareAssignment(objectType, param, "util.MemToStr", "") } -// SpecifyEntryAssignment generates a string, which contains entry assignment in SpecifyEntry() function -// in entry.tmpl template. If param contains nested specs, then recursively are executed internal functions, -// which are declaring additional variables (function nestedObjectDeclaration()) and use them in -// entry assignment (function nestedObjectAssignment()). -func SpecifyEntryAssignment(param *properties.SpecParam) string { - return prepareAssignment(param, "util.StrToMem", "Xml") +// SpecifyEntryAssignment generates a string, which contains entry/config assignment in SpecifyEntry() function +// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed +// internal functions, which are creating entry assignment. +func SpecifyEntryAssignment(objectType string, param *properties.SpecParam) string { + return prepareAssignment(objectType, param, "util.StrToMem", "Xml") } -func prepareAssignment(param *properties.SpecParam, listFunction, specSuffix string) string { +func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specSuffix string) string { var builder strings.Builder if param.Spec != nil { @@ -76,8 +74,6 @@ func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, } func appendNestedObjectAssignment(parent []string, params map[string]*properties.SpecParam, suffix string, builder *strings.Builder) { - // TODO: for one_of e.g. UDP or TCP, we need to generate additional if, because we cannot specify both TCP and UDP - // TODO: above applies not only for one, but also for any pointers to structs !!! for _, subParam := range params { appendAssignmentForNestedObject(parent, subParam, suffix, builder) } @@ -99,7 +95,7 @@ func appendAssignmentForNestedObject(parent []string, param *properties.SpecPara } } -// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl +// SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl/config.tmpl // to compare all items of generated entry. func SpecMatchesFunction(param *properties.SpecParam) string { if param.Type == "list" { diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index d96638e6..df6337d5 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -47,7 +47,7 @@ func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs // SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). func SpecParamType(parent string, param *properties.SpecParam) string { - prefix := determinePrefix(param) + prefix := determinePrefix(param, false) calculatedType := "" if param.Type == "list" && param.Items != nil { @@ -63,7 +63,7 @@ func SpecParamType(parent string, param *properties.SpecParam) string { // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). func XmlParamType(parent string, param *properties.SpecParam) string { - prefix := determinePrefix(param) + prefix := determinePrefix(param, true) calculatedType := "" if isParamListAndProfileTypeIsMember(param) { @@ -77,15 +77,13 @@ func XmlParamType(parent string, param *properties.SpecParam) string { return fmt.Sprintf("%s%s", prefix, calculatedType) } -func determinePrefix(param *properties.SpecParam) string { - prefix := "" - if param.Type == "list" { - prefix = prefix + "[]" - } - if !param.Required { - prefix = prefix + "*" +func determinePrefix(param *properties.SpecParam, useMemberTypeStruct bool) string { + if param.Type == "list" && !(useMemberTypeStruct && isParamListAndProfileTypeIsMember(param)) { + return "[]" + } else if !param.Required { + return "*" } - return prefix + return "" } func determineListType(param *properties.SpecParam) string { diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 4a3cb1bf..415be26d 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -91,7 +91,7 @@ func TestSpecParamType(t *testing.T) { // then assert.Equal(t, "string", calculatedTypeRequiredString) - assert.Equal(t, "[]*string", calculatedTypeListString) + assert.Equal(t, "[]string", calculatedTypeListString) assert.Equal(t, "*string", calculatedTypeOptionalString) } @@ -144,7 +144,7 @@ func TestXmlParamType(t *testing.T) { // then assert.Equal(t, "string", calculatedTypeRequiredString) - assert.Equal(t, "[]*util.MemberType", calculatedTypeListString) + assert.Equal(t, "*util.MemberType", calculatedTypeListString) } func TestXmlTag(t *testing.T) { From 44db76fabfe3d81429464d3905082c1ab8db7033 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 19 Mar 2024 15:58:35 +0100 Subject: [PATCH 30/66] fix problems with default type for param, which has spec --- pkg/properties/normalized.go | 2 +- templates/sdk/config.tmpl | 4 ++++ templates/sdk/entry.tmpl | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index a24dd4e8..8c28354e 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -227,7 +227,7 @@ func (spec *Normalization) AddNameVariantsForParams() error { // addDefaultTypesForParams recursively add default types for params for nested specs. func addDefaultTypesForParams(params map[string]*SpecParam) error { for _, param := range params { - if param.Type == "" { + if param.Type == "" && param.Spec == nil { param.Type = "string" } diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index d63104ae..54b831a0 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -139,14 +139,18 @@ func SpecMatches(a, b *Config) bool { // Don't compare Name. {{- range $_, $param := .Spec.Params}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { return false } {{- end}} + {{- end}} {{- range $_, $param := .Spec.OneOf}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { return false } + {{- end}} {{- end}} return true diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index f90e4ac9..c5fae4d2 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -160,14 +160,18 @@ func SpecMatches(a, b *Entry) bool { // Don't compare Name. {{- range $_, $param := .Spec.Params}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { return false } {{- end}} + {{- end}} {{- range $_, $param := .Spec.OneOf}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { return false } + {{- end}} {{- end}} return true From 5a6422091a4e4c5f85bf92c2ceaf7b2ca02be89d Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 20 Mar 2024 10:22:28 +0100 Subject: [PATCH 31/66] restore nested declaration (need to rebuild that mechanism) --- pkg/translate/funcs.go | 24 ++++++++++++++++++++++-- pkg/translate/funcs_test.go | 10 ++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 99e8f850..3ad24c99 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -65,6 +65,9 @@ func appendListFunctionAssignment(param *properties.SpecParam, objectType string } func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { + appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.Params, builder) + appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.OneOf, builder) + builder.WriteString(fmt.Sprintf("%s.%s = &Spec%s%s{\n", objectType, param.Name.CamelCase, param.Name.CamelCase, suffix)) appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.Params, suffix, builder) @@ -73,6 +76,23 @@ func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, builder.WriteString("}\n") } +func appendNestedObjectDeclaration(parent []string, params map[string]*properties.SpecParam, builder *strings.Builder) { + for _, subParam := range params { + appendDeclarationForNestedObject(parent, subParam, builder) + } +} + +func appendDeclarationForNestedObject(parent []string, param *properties.SpecParam, builder *strings.Builder) { + if param.Spec != nil { + appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.Params, builder) + appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.OneOf, builder) + } else { + builder.WriteString(fmt.Sprintf("nested%s%s := o.%s.%s\n", + strings.Join(parent, ""), param.Name.CamelCase, + strings.Join(parent, "."), param.Name.CamelCase)) + } +} + func appendNestedObjectAssignment(parent []string, params map[string]*properties.SpecParam, suffix string, builder *strings.Builder) { for _, subParam := range params { appendAssignmentForNestedObject(parent, subParam, suffix, builder) @@ -90,8 +110,8 @@ func appendAssignmentForNestedObject(parent []string, param *properties.SpecPara builder.WriteString(fmt.Sprintf("%s : util.StrToMem(o.%s),\n", param.Name.CamelCase, param.Name.CamelCase)) } else { - builder.WriteString(fmt.Sprintf("%s : o.%s.%s,\n", - param.Name.CamelCase, strings.Join(parent, "."), param.Name.CamelCase)) + builder.WriteString(fmt.Sprintf("%s : nested%s%s,\n", + param.Name.CamelCase, strings.Join(parent, ""), param.Name.CamelCase)) } } diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index d7de5583..2c62b19a 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -85,9 +85,10 @@ func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { }, }, } - expectedAssignmentStreing := `entry.A = &SpecAXml{ + expectedAssignmentStreing := `nestedABC := o.A.B.C +entry.A = &SpecAXml{ B : &SpecABXml{ -C : o.A.B.C, +C : nestedABC, }, } ` @@ -130,9 +131,10 @@ func TestNormalizeAssignmentForNestedObject(t *testing.T) { }, }, } - expectedAssignmentStreing := `entry.A = &SpecA{ + expectedAssignmentStreing := `nestedABC := o.A.B.C +entry.A = &SpecA{ B : &SpecAB{ -C : o.A.B.C, +C : nestedABC, }, } ` From 5ec29bb9ab032f17f111555b1ae68f114f07f82f Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 20 Mar 2024 12:04:33 +0100 Subject: [PATCH 32/66] rebuild mechanism of generating nested objects --- pkg/translate/funcs.go | 64 +++++++++++++++++-------------------- pkg/translate/funcs_test.go | 28 ++++++++++------ 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 3ad24c99..b2e6f6b4 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -65,53 +65,49 @@ func appendListFunctionAssignment(param *properties.SpecParam, objectType string } func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { - appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.Params, builder) - appendNestedObjectDeclaration([]string{param.Name.CamelCase}, param.Spec.OneOf, builder) - - builder.WriteString(fmt.Sprintf("%s.%s = &Spec%s%s{\n", objectType, param.Name.CamelCase, param.Name.CamelCase, suffix)) + defineNestedObject([]string{param.Name.CamelCase}, param, objectType, suffix, builder) + builder.WriteString(fmt.Sprintf("%s.%s = nested%s\n", objectType, param.Name.CamelCase, param.Name.CamelCase)) +} - appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.Params, suffix, builder) - appendNestedObjectAssignment([]string{param.Name.CamelCase}, param.Spec.OneOf, suffix, builder) +func defineNestedObject(parent []string, param *properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { + declareRootOfNestedObject(parent, builder, suffix) + builder.WriteString(fmt.Sprintf("if o.%s != nil {\n", strings.Join(parent, "."))) + if param.Spec != nil { + assignEmptyStructForNestedObject(parent, builder, suffix) + defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, suffix, builder) + defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, suffix, builder) + } else { + assignValueForNestedObject(parent, builder) + } builder.WriteString("}\n") } -func appendNestedObjectDeclaration(parent []string, params map[string]*properties.SpecParam, builder *strings.Builder) { - for _, subParam := range params { - appendDeclarationForNestedObject(parent, subParam, builder) +func declareRootOfNestedObject(parent []string, builder *strings.Builder, suffix string) { + if len(parent) == 1 { + builder.WriteString(fmt.Sprintf("nested%s := &Spec%s%s{}\n", + strings.Join(parent, "."), + strings.Join(parent, ""), suffix)) } } -func appendDeclarationForNestedObject(parent []string, param *properties.SpecParam, builder *strings.Builder) { - if param.Spec != nil { - appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.Params, builder) - appendNestedObjectDeclaration(append(parent, param.Name.CamelCase), param.Spec.OneOf, builder) - } else { - builder.WriteString(fmt.Sprintf("nested%s%s := o.%s.%s\n", - strings.Join(parent, ""), param.Name.CamelCase, - strings.Join(parent, "."), param.Name.CamelCase)) +func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, suffix string) { + if len(parent) > 1 { + builder.WriteString(fmt.Sprintf("nested%s = &Spec%s%s{}\n", + strings.Join(parent, "."), + strings.Join(parent, ""), suffix)) } } -func appendNestedObjectAssignment(parent []string, params map[string]*properties.SpecParam, suffix string, builder *strings.Builder) { - for _, subParam := range params { - appendAssignmentForNestedObject(parent, subParam, suffix, builder) - } +func assignValueForNestedObject(parent []string, builder *strings.Builder) { + builder.WriteString(fmt.Sprintf("nested%s = o.%s\n", + strings.Join(parent, "."), + strings.Join(parent, "."))) } -func appendAssignmentForNestedObject(parent []string, param *properties.SpecParam, suffix string, builder *strings.Builder) { - if param.Spec != nil { - builder.WriteString(fmt.Sprintf("%s : &Spec%s%s%s{\n", param.Name.CamelCase, - strings.Join(parent, ""), param.Name.CamelCase, suffix)) - appendNestedObjectAssignment(append(parent, param.Name.CamelCase), param.Spec.Params, suffix, builder) - appendNestedObjectAssignment(append(parent, param.Name.CamelCase), param.Spec.OneOf, suffix, builder) - builder.WriteString("},\n") - } else if isParamListAndProfileTypeIsMember(param) { - builder.WriteString(fmt.Sprintf("%s : util.StrToMem(o.%s),\n", - param.Name.CamelCase, param.Name.CamelCase)) - } else { - builder.WriteString(fmt.Sprintf("%s : nested%s%s,\n", - param.Name.CamelCase, strings.Join(parent, ""), param.Name.CamelCase)) +func defineNestedObjectForChildParams(parent []string, params map[string]*properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { + for _, param := range params { + defineNestedObject(append(parent, param.Name.CamelCase), param, objectType, suffix, builder) } } diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 2c62b19a..6160e0fb 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -85,12 +85,16 @@ func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { }, }, } - expectedAssignmentStreing := `nestedABC := o.A.B.C -entry.A = &SpecAXml{ -B : &SpecABXml{ -C : nestedABC, -}, + expectedAssignmentStreing := `nestedA := &SpecAXml{} +if o.A != nil { +if o.A.B != nil { +nestedA.B = &SpecABXml{} +if o.A.B.C != nil { +nestedA.B.C = o.A.B.C } +} +} +entry.A = nestedA ` // when calculatedAssignmentString := SpecifyEntryAssignment("entry", spec.Params["a"]) @@ -131,12 +135,16 @@ func TestNormalizeAssignmentForNestedObject(t *testing.T) { }, }, } - expectedAssignmentStreing := `nestedABC := o.A.B.C -entry.A = &SpecA{ -B : &SpecAB{ -C : nestedABC, -}, + expectedAssignmentStreing := `nestedA := &SpecA{} +if o.A != nil { +if o.A.B != nil { +nestedA.B = &SpecAB{} +if o.A.B.C != nil { +nestedA.B.C = o.A.B.C +} +} } +entry.A = nestedA ` // when calculatedAssignmentString := NormalizeAssignment("entry", spec.Params["a"]) From 4fd868a37687c97c6a9a1ad600ce8d3c87e0457e Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 20 Mar 2024 12:48:54 +0100 Subject: [PATCH 33/66] fix location template --- templates/sdk/location.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/sdk/location.tmpl b/templates/sdk/location.tmpl index 121125cf..5d898d08 100644 --- a/templates/sdk/location.tmpl +++ b/templates/sdk/location.tmpl @@ -74,6 +74,7 @@ func (o Location) Xpath(vn version.Number, name string) ([]string, error) { return nil, errors.NoLocationSpecifiedError } + ans = append(ans, Suffix...) ans = append(ans, util.AsEntryXpath([]string{name})) return ans, nil From 3fedad1ccd3902dbe665071b37351e437ec4b00f Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 11:44:22 +0100 Subject: [PATCH 34/66] render 'Misc []generic.Xml `xml:",any"' for child objects (not only for the root) --- templates/sdk/entry.tmpl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index c5fae4d2..c006d6f2 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -71,6 +71,8 @@ type Spec{{$name}}Xml struct { {{- range $_, $param := $spec.OneOf}} {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} + + Misc []generic.Xml `xml:",any"` } {{- end}} @@ -113,7 +115,10 @@ func Versioning(vn version.Number) (Specifier, Normalizer, error) { return SpecifyEntry, &EntryXmlContainer{}, nil } -func SpecifyEntry(o Entry) (any, error) { +// TODO: think if struct EntryXml1 / E_0_0 has to be public or not ??? +// maybe it's good to make it hidden and do not have in public documentation ... + +func SpecifyEntry(o Entry) (any, entry := EntryXml{} entry.Name = o.Name @@ -126,6 +131,8 @@ func SpecifyEntry(o Entry) (any, error) { entry.Misc = o.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] + // TODO: we use name of the struct for single objects, but if we have a list, we combine name of the struct + index + return entry, nil } @@ -145,6 +152,12 @@ func (c *EntryXmlContainer) Normalize() ([]Entry, error) { entry.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] = o.Misc + // TODO: similary as for SpecifyEntry(), it has to be name of the struct e.g. EntryXml for address + + // TODO: start thinking about versioning e.g: + // 1 approach: EntryXml1, EntryXml2, EntryXml3, ... + // 2 approach: EntryXml_10_0_0, EntryXml_10_1_0, EntryXml_11_0_0, ... + entryList = append(entryList, entry) } From e96487817d02b41b9481dbee3562473c80fba974 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 11:45:09 +0100 Subject: [PATCH 35/66] render 'Misc []generic.Xml `xml:",any"' for child objects (not only for the root) --- templates/sdk/config.tmpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 54b831a0..a927fd4c 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -56,6 +56,8 @@ type Spec{{$name}}Xml struct { {{- range $_, $param := $spec.OneOf}} {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} + + Misc []generic.Xml `xml:",any" } {{- end}} From b399cf190670ee547db936a1a84485c478c86117 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 13:08:00 +0100 Subject: [PATCH 36/66] set value for nested Misc --- pkg/translate/funcs.go | 18 ++++++++++++++++++ templates/sdk/config.tmpl | 8 +++++--- templates/sdk/entry.tmpl | 12 +++++------- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index b2e6f6b4..fd8b039f 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -96,6 +96,24 @@ func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, builder.WriteString(fmt.Sprintf("nested%s = &Spec%s%s{}\n", strings.Join(parent, "."), strings.Join(parent, ""), suffix)) + + if suffix == "Xml" { + builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", strings.Join(parent, "."))) + builder.WriteString(fmt.Sprintf("nested%s.Misc = o.%s.Misc[\"%s\"]\n", + strings.Join(parent, "."), + strings.Join(parent, "."), + strings.Join(parent, ""), + )) + builder.WriteString("}\n") + } else { + builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", strings.Join(parent, "."))) + builder.WriteString(fmt.Sprintf("nested%s.Misc[\"%s\"] = o.%s.Misc\n", + strings.Join(parent, "."), + strings.Join(parent, ""), + strings.Join(parent, "."), + )) + builder.WriteString("}\n") + } } } diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index a927fd4c..b941e47f 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -30,6 +30,8 @@ type Spec{{$name}} struct { {{- range $_, $param := $spec.OneOf}} {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} + + Misc map[string][]generic.Xml } {{- end}} @@ -57,7 +59,7 @@ type Spec{{$name}}Xml struct { {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} - Misc []generic.Xml `xml:",any" + Misc []generic.Xml `xml:",any"` } {{- end}} @@ -106,7 +108,7 @@ func SpecifyConfig(o Config) (any, error) { {{specifyEntryAssignment "config" $param}} {{- end}} - config.Misc = o.Misc[fmt.Sprintf("%s", "Config")] + config.Misc = o.Misc["Config"] return config, nil } @@ -124,7 +126,7 @@ func (c *ConfigXmlContainer) Normalize() ([]Config, error) { {{normalizeAssignment "config" $param}} {{- end}} - config.Misc[fmt.Sprintf("%s", "Config")] = o.Misc + config.Misc["Config"] = o.Misc configList = append(configList, config) } diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index c006d6f2..4f4b1d36 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -43,6 +43,8 @@ type Spec{{$name}} struct { {{- range $_, $param := $spec.OneOf}} {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} + + Misc map[string][]generic.Xml } {{- end}} @@ -118,7 +120,7 @@ func Versioning(vn version.Number) (Specifier, Normalizer, error) { // TODO: think if struct EntryXml1 / E_0_0 has to be public or not ??? // maybe it's good to make it hidden and do not have in public documentation ... -func SpecifyEntry(o Entry) (any, +func SpecifyEntry(o Entry) (any, error) { entry := EntryXml{} entry.Name = o.Name @@ -129,9 +131,7 @@ func SpecifyEntry(o Entry) (any, {{specifyEntryAssignment "entry" $param}} {{- end}} - entry.Misc = o.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] - - // TODO: we use name of the struct for single objects, but if we have a list, we combine name of the struct + index + entry.Misc = o.Misc["Entry"] return entry, nil } @@ -150,9 +150,7 @@ func (c *EntryXmlContainer) Normalize() ([]Entry, error) { {{normalizeAssignment "entry" $param}} {{- end}} - entry.Misc[fmt.Sprintf("%s\n%s", "Entry", o.Name)] = o.Misc - - // TODO: similary as for SpecifyEntry(), it has to be name of the struct e.g. EntryXml for address + entry.Misc["Entry"] = o.Misc // TODO: start thinking about versioning e.g: // 1 approach: EntryXml1, EntryXml2, EntryXml3, ... From c267fff590f41a5028c235f56f729bbe4121881a Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 13:09:53 +0100 Subject: [PATCH 37/66] fix tests after changes with Misc --- pkg/translate/funcs_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 6160e0fb..f547bfb5 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -89,6 +89,9 @@ func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { if o.A != nil { if o.A.B != nil { nestedA.B = &SpecABXml{} +if o.A.B.Misc != nil { +nestedA.B.Misc = o.A.B.Misc["AB"] +} if o.A.B.C != nil { nestedA.B.C = o.A.B.C } @@ -139,6 +142,9 @@ func TestNormalizeAssignmentForNestedObject(t *testing.T) { if o.A != nil { if o.A.B != nil { nestedA.B = &SpecAB{} +if o.A.B.Misc != nil { +nestedA.B.Misc["AB"] = o.A.B.Misc +} if o.A.B.C != nil { nestedA.B.C = o.A.B.C } From e11fdcaecbd4f69b032744b357266879484c2247 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 13:20:56 +0100 Subject: [PATCH 38/66] struct ending with Xml are no more public from now --- pkg/translate/funcs.go | 38 ++++++++++++++++++------------------- pkg/translate/funcs_test.go | 4 ++-- pkg/translate/structs.go | 2 +- templates/sdk/config.tmpl | 2 +- templates/sdk/entry.tmpl | 21 +++++++++----------- 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index fd8b039f..77f45c9a 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -28,21 +28,21 @@ func generateEntryXpathForLocation(location string, xpath string) string { // in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed // internal functions, which are creating entry assignment. func NormalizeAssignment(objectType string, param *properties.SpecParam) string { - return prepareAssignment(objectType, param, "util.MemToStr", "") + return prepareAssignment(objectType, param, "util.MemToStr", "Spec", "") } // SpecifyEntryAssignment generates a string, which contains entry/config assignment in SpecifyEntry() function // in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed // internal functions, which are creating entry assignment. func SpecifyEntryAssignment(objectType string, param *properties.SpecParam) string { - return prepareAssignment(objectType, param, "util.StrToMem", "Xml") + return prepareAssignment(objectType, param, "util.StrToMem", "spec", "Xml") } -func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specSuffix string) string { +func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specPrefix, specSuffix string) string { var builder strings.Builder if param.Spec != nil { - appendSpecObjectAssignment(param, objectType, specSuffix, &builder) + appendSpecObjectAssignment(param, objectType, specPrefix, specSuffix, &builder) } else if isParamListAndProfileTypeIsMember(param) { appendListFunctionAssignment(param, objectType, listFunction, &builder) } else { @@ -64,37 +64,37 @@ func appendListFunctionAssignment(param *properties.SpecParam, objectType string builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s)", objectType, param.Name.CamelCase, listFunction, param.Name.CamelCase)) } -func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { - defineNestedObject([]string{param.Name.CamelCase}, param, objectType, suffix, builder) +func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, prefix, suffix string, builder *strings.Builder) { + defineNestedObject([]string{param.Name.CamelCase}, param, objectType, prefix, suffix, builder) builder.WriteString(fmt.Sprintf("%s.%s = nested%s\n", objectType, param.Name.CamelCase, param.Name.CamelCase)) } -func defineNestedObject(parent []string, param *properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { - declareRootOfNestedObject(parent, builder, suffix) +func defineNestedObject(parent []string, param *properties.SpecParam, objectType string, prefix, suffix string, builder *strings.Builder) { + declareRootOfNestedObject(parent, builder, prefix, suffix) builder.WriteString(fmt.Sprintf("if o.%s != nil {\n", strings.Join(parent, "."))) if param.Spec != nil { - assignEmptyStructForNestedObject(parent, builder, suffix) - defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, suffix, builder) - defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, suffix, builder) + assignEmptyStructForNestedObject(parent, builder, prefix, suffix) + defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, prefix, suffix, builder) + defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, prefix, suffix, builder) } else { assignValueForNestedObject(parent, builder) } builder.WriteString("}\n") } -func declareRootOfNestedObject(parent []string, builder *strings.Builder, suffix string) { +func declareRootOfNestedObject(parent []string, builder *strings.Builder, prefix, suffix string) { if len(parent) == 1 { - builder.WriteString(fmt.Sprintf("nested%s := &Spec%s%s{}\n", - strings.Join(parent, "."), + builder.WriteString(fmt.Sprintf("nested%s := &%s%s%s{}\n", + strings.Join(parent, "."), prefix, strings.Join(parent, ""), suffix)) } } -func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, suffix string) { +func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, prefix, suffix string) { if len(parent) > 1 { - builder.WriteString(fmt.Sprintf("nested%s = &Spec%s%s{}\n", - strings.Join(parent, "."), + builder.WriteString(fmt.Sprintf("nested%s = &%s%s%s{}\n", + strings.Join(parent, "."), prefix, strings.Join(parent, ""), suffix)) if suffix == "Xml" { @@ -123,9 +123,9 @@ func assignValueForNestedObject(parent []string, builder *strings.Builder) { strings.Join(parent, "."))) } -func defineNestedObjectForChildParams(parent []string, params map[string]*properties.SpecParam, objectType string, suffix string, builder *strings.Builder) { +func defineNestedObjectForChildParams(parent []string, params map[string]*properties.SpecParam, objectType string, prefix, suffix string, builder *strings.Builder) { for _, param := range params { - defineNestedObject(append(parent, param.Name.CamelCase), param, objectType, suffix, builder) + defineNestedObject(append(parent, param.Name.CamelCase), param, objectType, prefix, suffix, builder) } } diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index f547bfb5..0f9ba43f 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -85,10 +85,10 @@ func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { }, }, } - expectedAssignmentStreing := `nestedA := &SpecAXml{} + expectedAssignmentStreing := `nestedA := &specAXml{} if o.A != nil { if o.A.B != nil { -nestedA.B = &SpecABXml{} +nestedA.B = &specABXml{} if o.A.B.Misc != nil { nestedA.B.Misc = o.A.B.Misc["AB"] } diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index df6337d5..f0774f4f 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -98,7 +98,7 @@ func calculateNestedSpecType(parent string, param *properties.SpecParam) string } func calculateNestedXmlSpecType(parent string, param *properties.SpecParam) string { - return fmt.Sprintf("Spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) + return fmt.Sprintf("spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) } // XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`). diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index b941e47f..32840482 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -51,7 +51,7 @@ type ConfigXml struct { } {{- range $name, $spec := nestedSpecs .Spec }} -type Spec{{$name}}Xml struct { +type spec{{$name}}Xml struct { {{- range $_, $param := $spec.Params}} {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 4f4b1d36..b8a14be0 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -48,11 +48,11 @@ type Spec{{$name}} struct { } {{- end}} -type EntryXmlContainer struct { - Answer []EntryXml `xml:"entry"` +type entryXmlContainer struct { + Answer []entryXml `xml:"entry"` } -type EntryXml struct { +type entryXml struct { XMLName xml.Name `xml:"entry"` Name string `xml:"name,attr"` {{- range $_, $param := .Spec.Params}} @@ -66,7 +66,7 @@ type EntryXml struct { } {{- range $name, $spec := nestedSpecs .Spec }} -type Spec{{$name}}Xml struct { +type spec{{$name}}Xml struct { {{- range $_, $param := $spec.Params}} {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} {{- end}} @@ -114,14 +114,11 @@ func (e *Entry) Field(v string) (any, error) { } func Versioning(vn version.Number) (Specifier, Normalizer, error) { - return SpecifyEntry, &EntryXmlContainer{}, nil + return SpecifyEntry, &entryXmlContainer{}, nil } -// TODO: think if struct EntryXml1 / E_0_0 has to be public or not ??? -// maybe it's good to make it hidden and do not have in public documentation ... - func SpecifyEntry(o Entry) (any, error) { - entry := EntryXml{} + entry := entryXml{} entry.Name = o.Name {{- range $_, $param := .Spec.Params}} @@ -136,7 +133,7 @@ func SpecifyEntry(o Entry) (any, error) { return entry, nil } -func (c *EntryXmlContainer) Normalize() ([]Entry, error) { +func (c *entryXmlContainer) Normalize() ([]Entry, error) { entryList := make([]Entry, 0, len(c.Answer)) for _, o := range c.Answer { entry := Entry{ @@ -153,8 +150,8 @@ func (c *EntryXmlContainer) Normalize() ([]Entry, error) { entry.Misc["Entry"] = o.Misc // TODO: start thinking about versioning e.g: - // 1 approach: EntryXml1, EntryXml2, EntryXml3, ... - // 2 approach: EntryXml_10_0_0, EntryXml_10_1_0, EntryXml_11_0_0, ... + // 1 approach: entryXml1, entryXml2, entryXml3, ... + // 2 approach: entryXml_10_0_0, entryXml_10_1_0, entryXml_11_0_0, ... entryList = append(entryList, entry) } From fe774945ff4fd9c7cb845ef2847650fcfcd69cd4 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 14:49:04 +0100 Subject: [PATCH 39/66] introduce function to detecting versions --- pkg/properties/normalized.go | 25 +++++++++++++++++++++++++ pkg/properties/normalized_test.go | 18 ++++++++++++++++-- templates/sdk/entry.tmpl | 4 ---- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index 8c28354e..a6ccece8 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -295,3 +295,28 @@ func (spec *Normalization) Validate() []error { return checks } + +// SupportedVersions provides list of all supported versions +func (spec *Normalization) SupportedVersions() []string { + if spec.Spec != nil { + versions := supportedVersions(spec.Spec.Params, []string{""}) + versions = supportedVersions(spec.Spec.OneOf, versions) + return versions + } + return nil +} + +func supportedVersions(params map[string]*SpecParam, versions []string) []string { + for _, param := range params { + for _, profile := range param.Profiles { + if profile.FromVersion != "" { + versions = append(versions, profile.FromVersion) + } + } + if param.Spec != nil { + versions = supportedVersions(param.Spec.Params, versions) + versions = supportedVersions(param.Spec.OneOf, versions) + } + } + return versions +} diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index e722344a..f5a1091e 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -85,6 +85,7 @@ spec: profiles: - xpath: ["description"] + from_version: "10.1.1" tags: description: 'The administrative tags.' type: 'list' @@ -123,6 +124,7 @@ spec: profiles: - xpath: ["ip-wildcard"] + from_version: "11.1.2" ` func TestUnmarshallAddressSpecFile(t *testing.T) { @@ -266,7 +268,7 @@ spec: - xpath: - description not_present: false - from_version: "" + from_version: 10.1.1 spec: null tags: name: @@ -346,7 +348,7 @@ spec: - xpath: - ip-wildcard not_present: false - from_version: "" + from_version: 11.1.2 spec: null ` @@ -412,3 +414,15 @@ xpath_suffix: // then assert.Len(t, problems, 2, "Not all expected validation checks failed") } + +func TestNamesOfStructsForVersioning(t *testing.T) { + // given + yamlParsedData, _ := ParseSpec([]byte(sampleSpec)) + + // when + versions := yamlParsedData.SupportedVersions() + + // then + assert.NotNilf(t, yamlParsedData, "Unmarshalled data cannot be nil") + assert.Contains(t, versions, "10.1.1") +} diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index b8a14be0..67e414e0 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -149,10 +149,6 @@ func (c *entryXmlContainer) Normalize() ([]Entry, error) { entry.Misc["Entry"] = o.Misc - // TODO: start thinking about versioning e.g: - // 1 approach: entryXml1, entryXml2, entryXml3, ... - // 2 approach: entryXml_10_0_0, entryXml_10_1_0, entryXml_11_0_0, ... - entryList = append(entryList, entry) } From 2f16fb879e12e678bc3bbe2bc93699bcc56e0788 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 15:37:51 +0100 Subject: [PATCH 40/66] return unique versions in format required in template --- pkg/properties/normalized.go | 12 ++++++++++-- pkg/properties/normalized_test.go | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index a6ccece8..371dd19a 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -296,7 +296,7 @@ func (spec *Normalization) Validate() []error { return checks } -// SupportedVersions provides list of all supported versions +// SupportedVersions provides list of all supported versions in format _MAJOR_MINOR_PATCH func (spec *Normalization) SupportedVersions() []string { if spec.Spec != nil { versions := supportedVersions(spec.Spec.Params, []string{""}) @@ -310,7 +310,15 @@ func supportedVersions(params map[string]*SpecParam, versions []string) []string for _, param := range params { for _, profile := range param.Profiles { if profile.FromVersion != "" { - versions = append(versions, profile.FromVersion) + notExist := true + for _, version := range versions { + if version == profile.FromVersion { + notExist = false + } + } + if notExist { + versions = append(versions, fmt.Sprintf("_%s", strings.ReplaceAll(profile.FromVersion, ".", "_"))) + } } } if param.Spec != nil { diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index f5a1091e..63455763 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -424,5 +424,5 @@ func TestNamesOfStructsForVersioning(t *testing.T) { // then assert.NotNilf(t, yamlParsedData, "Unmarshalled data cannot be nil") - assert.Contains(t, versions, "10.1.1") + assert.Contains(t, versions, "_10_1_1") } From a9331b00f5979836398f8fca4fa28bd9404acfd9 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 15:46:23 +0100 Subject: [PATCH 41/66] render different structs names for different versions --- pkg/translate/structs.go | 4 +-- pkg/translate/structs_test.go | 10 +++---- templates/sdk/config.tmpl | 52 +++++++++++++++++++++-------------- templates/sdk/entry.tmpl | 52 +++++++++++++++++++++-------------- 4 files changed, 69 insertions(+), 49 deletions(-) diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index f0774f4f..76ad7cc7 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -46,7 +46,7 @@ func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs } // SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func SpecParamType(parent string, param *properties.SpecParam) string { +func SpecParamType(parent string, param *properties.SpecParam, version string) string { prefix := determinePrefix(param, false) calculatedType := "" @@ -62,7 +62,7 @@ func SpecParamType(parent string, param *properties.SpecParam) string { } // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func XmlParamType(parent string, param *properties.SpecParam) string { +func XmlParamType(parent string, param *properties.SpecParam, version string) string { prefix := determinePrefix(param, true) calculatedType := "" diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 415be26d..78a51945 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -85,9 +85,9 @@ func TestSpecParamType(t *testing.T) { } // when - calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString) - calculatedTypeListString := SpecParamType("", ¶mTypeListString) - calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString) + calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString, "") + calculatedTypeListString := SpecParamType("", ¶mTypeListString, "") + calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString, "") // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -139,8 +139,8 @@ func TestXmlParamType(t *testing.T) { } // when - calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString) - calculatedTypeListString := XmlParamType("", ¶mTypeListString) + calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString, "") + calculatedTypeListString := XmlParamType("", ¶mTypeListString, "") // then assert.Equal(t, "string", calculatedTypeRequiredString) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 32840482..bf4a6c31 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -11,57 +11,67 @@ import ( "github.com/PaloAltoNetworks/pango/version" ) +{{- range $version := .SupportedVersions }} type Config struct { - {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} +{{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType "" $param $version}} {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} +{{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType "" $param $version}} {{- end}} Misc map[string][]generic.Xml } +{{- end}} -{{- range $name, $spec := nestedSpecs .Spec }} +{{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} type Spec{{$name}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param $version}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param $version}} {{- end}} Misc map[string][]generic.Xml } {{- end}} +{{- end}} -type ConfigXmlContainer struct { - Answer []ConfigXml `xml:"config"` +{{- range $version := .SupportedVersions }} + type configXmlContainer{{$version}} struct { + Answer []configXml{{$version}} `xml:"config"` } +{{- end}} -type ConfigXml struct { - {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} +{{- range $version := .SupportedVersions }} + type configXml{{$version}} struct { + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} {{- end}} Misc []generic.Xml `xml:",any"` } +{{- end}} -{{- range $name, $spec := nestedSpecs .Spec }} -type spec{{$name}}Xml struct { +{{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} + type spec{{$name}}Xml{{$version}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} {{- end}} Misc []generic.Xml `xml:",any"` } {{- end}} +{{- end}} func (e *Config) CopyMiscFrom(v *Config) { if v == nil || len(v.Misc) == 0 { @@ -95,11 +105,11 @@ func (e *Config) Field(v string) (any, error) { } func Versioning(vn version.Number) (Specifier, Normalizer, error) { - return SpecifyConfig, &ConfigXmlContainer{}, nil +return SpecifyConfig, &configXmlContainer{}, nil } func SpecifyConfig(o Config) (any, error) { - config := ConfigXml{} +config := configXml{} {{- range $_, $param := .Spec.Params}} {{specifyEntryAssignment "config" $param}} @@ -113,7 +123,7 @@ func SpecifyConfig(o Config) (any, error) { return config, nil } -func (c *ConfigXmlContainer) Normalize() ([]Config, error) { +func (c *configXmlContainer) Normalize() ([]Config, error) { configList := make([]Config, 0, len(c.Answer)) for _, o := range c.Answer { config := Config{ diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 67e414e0..00c20b1f 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -23,60 +23,70 @@ var ( {{- end}}} ) -type Entry struct { +{{- range $version := .SupportedVersions }} + type Entry{{$version}} struct { Name string - {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType "" $param $version}} {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType "" $param $version}} {{- end}} - Misc map[string][]generic.Xml + Misc map[string][]generic.Xml } +{{- end}} -{{- range $name, $spec := nestedSpecs .Spec }} -type Spec{{$name}} struct { +{{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} + type Spec{{$name}}{{$version}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param $version}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{$param.Name.CamelCase}} {{specParamType $name $param $version}} {{- end}} Misc map[string][]generic.Xml } {{- end}} +{{- end}} -type entryXmlContainer struct { - Answer []entryXml `xml:"entry"` +{{- range $version := .SupportedVersions }} + type entryXmlContainer{{$version}} struct { + Answer []entryXml{{$version}} `xml:"entry"` } +{{- end}} -type entryXml struct { +{{- range $version := .SupportedVersions }} + type entryXml{{$version}} struct { XMLName xml.Name `xml:"entry"` Name string `xml:"name,attr"` - {{- range $_, $param := .Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} {{- end}} Misc []generic.Xml `xml:",any"` } +{{- end}} -{{- range $name, $spec := nestedSpecs .Spec }} -type spec{{$name}}Xml struct { +{{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} + type spec{{$name}}Xml{{$version}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} {{- end}} Misc []generic.Xml `xml:",any"` } {{- end}} +{{- end}} func (e *Entry) CopyMiscFrom(v *Entry) { if v == nil || len(v.Misc) == 0 { From 3ab0edd88d05563fc88dadbce4498447c0a91540 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 22 Mar 2024 16:04:27 +0100 Subject: [PATCH 42/66] improve rendering different structs names for different versions --- pkg/generate/generator.go | 5 +- pkg/properties/normalized.go | 4 +- pkg/properties/normalized_test.go | 2 +- pkg/translate/structs.go | 13 +- pkg/translate/structs_test.go | 10 +- templates/sdk/config.tmpl | 256 +++++++++++++------------- templates/sdk/entry.tmpl | 296 +++++++++++++++--------------- templates/sdk/interfaces.tmpl | 12 +- templates/sdk/location.tmpl | 94 +++++----- 9 files changed, 347 insertions(+), 345 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index d6c35bb1..80b1808e 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -132,8 +132,9 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "subtract": func(a, b int) int { return a - b }, - "generateEntryXpath": translate.GenerateEntryXpathForLocation, - "nestedSpecs": translate.NestedSpecs, + "generateEntryXpath": translate.GenerateEntryXpathForLocation, + "nestedSpecs": translate.NestedSpecs, + "createGoSuffixFromVersion": translate.CreateGoSuffixFromVersion, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index 371dd19a..db18c8e6 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -296,7 +296,7 @@ func (spec *Normalization) Validate() []error { return checks } -// SupportedVersions provides list of all supported versions in format _MAJOR_MINOR_PATCH +// SupportedVersions provides list of all supported versions in format MAJOR.MINOR.PATCH func (spec *Normalization) SupportedVersions() []string { if spec.Spec != nil { versions := supportedVersions(spec.Spec.Params, []string{""}) @@ -317,7 +317,7 @@ func supportedVersions(params map[string]*SpecParam, versions []string) []string } } if notExist { - versions = append(versions, fmt.Sprintf("_%s", strings.ReplaceAll(profile.FromVersion, ".", "_"))) + versions = append(versions, profile.FromVersion) } } } diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index 63455763..f5a1091e 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -424,5 +424,5 @@ func TestNamesOfStructsForVersioning(t *testing.T) { // then assert.NotNilf(t, yamlParsedData, "Unmarshalled data cannot be nil") - assert.Contains(t, versions, "_10_1_1") + assert.Contains(t, versions, "10.1.1") } diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 76ad7cc7..e1bad63b 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -46,7 +46,7 @@ func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs } // SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func SpecParamType(parent string, param *properties.SpecParam, version string) string { +func SpecParamType(parent string, param *properties.SpecParam) string { prefix := determinePrefix(param, false) calculatedType := "" @@ -62,7 +62,7 @@ func SpecParamType(parent string, param *properties.SpecParam, version string) s } // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func XmlParamType(parent string, param *properties.SpecParam, version string) string { +func XmlParamType(parent string, param *properties.SpecParam) string { prefix := determinePrefix(param, true) calculatedType := "" @@ -123,3 +123,12 @@ func OmitEmpty(location *properties.Location) string { return "" } } + +// CreateGoSuffixFromVersion convert version into Go suffix e.g. 10.1.1 into _10_1_1 +func CreateGoSuffixFromVersion(version string) string { + if len(version) > 0 { + return fmt.Sprintf("_%s", strings.ReplaceAll(version, ".", "_")) + } else { + return version + } +} diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 78a51945..415be26d 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -85,9 +85,9 @@ func TestSpecParamType(t *testing.T) { } // when - calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString, "") - calculatedTypeListString := SpecParamType("", ¶mTypeListString, "") - calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString, "") + calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString) + calculatedTypeListString := SpecParamType("", ¶mTypeListString) + calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString) // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -139,8 +139,8 @@ func TestXmlParamType(t *testing.T) { } // when - calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString, "") - calculatedTypeListString := XmlParamType("", ¶mTypeListString, "") + calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString) + calculatedTypeListString := XmlParamType("", ¶mTypeListString) // then assert.Equal(t, "string", calculatedTypeRequiredString) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index bf4a6c31..d15d7ae7 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -1,172 +1,172 @@ {{- if not .Entry}} -package {{packageName .GoSdkPath}} - -import ( - "encoding/xml" - "fmt" - - "github.com/PaloAltoNetworks/pango/filtering" - "github.com/PaloAltoNetworks/pango/generic" - "github.com/PaloAltoNetworks/pango/util" - "github.com/PaloAltoNetworks/pango/version" -) - -{{- range $version := .SupportedVersions }} -type Config struct { -{{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param $version}} - {{- end}} -{{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param $version}} + package {{packageName .GoSdkPath}} + + import ( + "encoding/xml" + "fmt" + + "github.com/PaloAltoNetworks/pango/filtering" + "github.com/PaloAltoNetworks/pango/generic" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/version" + ) + + {{- range $version := .SupportedVersions }} + type Config{{createGoSuffixFromVersion $version}} struct { + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + + Misc map[string][]generic.Xml + } {{- end}} - Misc map[string][]generic.Xml -} -{{- end}} + {{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} + type Spec{{$name}}{{createGoSuffixFromVersion $version}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{- end}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{- end}} -{{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} -type Spec{{$name}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param $version}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param $version}} + Misc map[string][]generic.Xml + } + {{- end}} {{- end}} - Misc map[string][]generic.Xml -} -{{- end}} -{{- end}} - -{{- range $version := .SupportedVersions }} - type configXmlContainer{{$version}} struct { - Answer []configXml{{$version}} `xml:"config"` -} -{{- end}} - -{{- range $version := .SupportedVersions }} - type configXml{{$version}} struct { - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} + {{- range $version := .SupportedVersions }} + type configXmlContainer{{createGoSuffixFromVersion $version}} struct { + Answer []configXml{{createGoSuffixFromVersion $version}} `xml:"config"` + } {{- end}} - Misc []generic.Xml `xml:",any"` -} -{{- end}} + {{- range $version := .SupportedVersions }} + type configXml{{createGoSuffixFromVersion $version}} struct { + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} -{{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} - type spec{{$name}}Xml{{$version}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} + Misc []generic.Xml `xml:",any"` + } {{- end}} - Misc []generic.Xml `xml:",any"` -} -{{- end}} -{{- end}} + {{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} + type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} -func (e *Config) CopyMiscFrom(v *Config) { - if v == nil || len(v.Misc) == 0 { - return - } + Misc []generic.Xml `xml:",any"` + } + {{- end}} + {{- end}} - e.Misc = make(map[string][]generic.Xml) - for key := range v.Misc { - e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) - } -} + func (e *Config) CopyMiscFrom(v *Config) { + if v == nil || len(v.Misc) == 0 { + return + } -func (e *Config) Field(v string) (any, error) { - {{- range $_, $param := .Spec.Params}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { - return e.{{$param.Name.CamelCase}}, nil + e.Misc = make(map[string][]generic.Xml) + for key := range v.Misc { + e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) } - {{- if eq $param.Type "list"}} - if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { - return int64(len(e.{{$param.Name.CamelCase}})), nil } - {{- end}} + + func (e *Config) Field(v string) (any, error) { + {{- range $_, $param := .Spec.Params}} + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + return e.{{$param.Name.CamelCase}}, nil + } + {{- if eq $param.Type "list"}} + if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { + return int64(len(e.{{$param.Name.CamelCase}})), nil + } + {{- end}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { return e.{{$param.Name.CamelCase}}, nil - } + } {{- end}} - return nil, fmt.Errorf("unknown field") -} + return nil, fmt.Errorf("unknown field") + } -func Versioning(vn version.Number) (Specifier, Normalizer, error) { -return SpecifyConfig, &configXmlContainer{}, nil -} + func Versioning(vn version.Number) (Specifier, Normalizer, error) { + return SpecifyConfig, &configXmlContainer{}, nil + } -func SpecifyConfig(o Config) (any, error) { -config := configXml{} + func SpecifyConfig(o Config) (any, error) { + config := configXml{} {{- range $_, $param := .Spec.Params}} - {{specifyEntryAssignment "config" $param}} + {{specifyEntryAssignment "config" $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{specifyEntryAssignment "config" $param}} + {{specifyEntryAssignment "config" $param}} {{- end}} - config.Misc = o.Misc["Config"] + config.Misc = o.Misc["Config"] - return config, nil -} + return config, nil + } -func (c *configXmlContainer) Normalize() ([]Config, error) { - configList := make([]Config, 0, len(c.Answer)) - for _, o := range c.Answer { - config := Config{ - Misc: make(map[string][]generic.Xml), - } - {{- range $_, $param := .Spec.Params}} + func (c *configXmlContainer) Normalize() ([]Config, error) { + configList := make([]Config, 0, len(c.Answer)) + for _, o := range c.Answer { + config := Config{ + Misc: make(map[string][]generic.Xml), + } + {{- range $_, $param := .Spec.Params}} {{normalizeAssignment "config" $param}} - {{- end}} - {{- range $_, $param := .Spec.OneOf}} + {{- end}} + {{- range $_, $param := .Spec.OneOf}} {{normalizeAssignment "config" $param}} - {{- end}} + {{- end}} - config.Misc["Config"] = o.Misc + config.Misc["Config"] = o.Misc - configList = append(configList, config) - } + configList = append(configList, config) + } - return configList, nil -} + return configList, nil + } -func SpecMatches(a, b *Config) bool { - if a == nil && b != nil || a != nil && b == nil { - return false - } else if a == nil && b == nil { - return true - } + func SpecMatches(a, b *Config) bool { + if a == nil && b != nil || a != nil && b == nil { + return false + } else if a == nil && b == nil { + return true + } - // Don't compare Name. + // Don't compare Name. {{- range $_, $param := .Spec.Params}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false + } + {{- end}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false + } + {{- end}} {{- end}} - return true -} + return true + } {{- end}} \ No newline at end of file diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 00c20b1f..9b4ed6e4 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -1,193 +1,193 @@ {{- if .Entry}} -package {{packageName .GoSdkPath}} - -import ( - "encoding/xml" - "fmt" - - "github.com/PaloAltoNetworks/pango/filtering" - "github.com/PaloAltoNetworks/pango/generic" - "github.com/PaloAltoNetworks/pango/util" - "github.com/PaloAltoNetworks/pango/version" -) - -var ( - _ filtering.Fielder = &Entry{} -) - -var ( - Suffix = []string{ - {{- $length := subtract (len .XpathSuffix) 1 }} - {{- range $index, $suffix := .XpathSuffix}}" - {{- $suffix}}"{{- if lt $index $length}},{{- end}} - {{- end}}} -) - -{{- range $version := .SupportedVersions }} - type Entry{{$version}} struct { - Name string - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param $version}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param $version}} + package {{packageName .GoSdkPath}} + + import ( + "encoding/xml" + "fmt" + + "github.com/PaloAltoNetworks/pango/filtering" + "github.com/PaloAltoNetworks/pango/generic" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/version" + ) + + var ( + _ filtering.Fielder = &Entry{} + ) + + var ( + Suffix = []string{ + {{- $length := subtract (len .XpathSuffix) 1 }} + {{- range $index, $suffix := .XpathSuffix}}" + {{- $suffix}}"{{- if lt $index $length}},{{- end}} + {{- end}}} + ) + + {{- range $version := .SupportedVersions }} + type Entry{{createGoSuffixFromVersion $version}} struct { + Name string + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + + Misc map[string][]generic.Xml + } {{- end}} - Misc map[string][]generic.Xml -} -{{- end}} + {{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} + type Spec{{$name}}{{createGoSuffixFromVersion $version}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{- end}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} + {{- end}} -{{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} - type Spec{{$name}}{{$version}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param $version}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param $version}} + Misc map[string][]generic.Xml + } + {{- end}} {{- end}} - Misc map[string][]generic.Xml -} -{{- end}} -{{- end}} - -{{- range $version := .SupportedVersions }} - type entryXmlContainer{{$version}} struct { - Answer []entryXml{{$version}} `xml:"entry"` -} -{{- end}} - -{{- range $version := .SupportedVersions }} - type entryXml{{$version}} struct { - XMLName xml.Name `xml:"entry"` - Name string `xml:"name,attr"` - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param $version}} {{xmlTag $param}} + {{- range $version := .SupportedVersions }} + type entryXmlContainer{{createGoSuffixFromVersion $version}} struct { + Answer []entryXml{{createGoSuffixFromVersion $version}} `xml:"entry"` + } {{- end}} - Misc []generic.Xml `xml:",any"` -} -{{- end}} + {{- range $version := .SupportedVersions }} + type entryXml{{createGoSuffixFromVersion $version}} struct { + XMLName xml.Name `xml:"entry"` + Name string `xml:"name,attr"` + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} -{{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} - type spec{{$name}}Xml{{$version}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param $version}} {{xmlTag $param}} + Misc []generic.Xml `xml:",any"` + } {{- end}} - Misc []generic.Xml `xml:",any"` -} -{{- end}} -{{- end}} + {{- range $version := .SupportedVersions }} + {{- range $name, $spec := nestedSpecs $.Spec }} + type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} -func (e *Entry) CopyMiscFrom(v *Entry) { - if v == nil || len(v.Misc) == 0 { - return - } + Misc []generic.Xml `xml:",any"` + } + {{- end}} + {{- end}} + + func (e *Entry) CopyMiscFrom(v *Entry) { + if v == nil || len(v.Misc) == 0 { + return + } - e.Misc = make(map[string][]generic.Xml) - for key := range v.Misc { - e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) - } -} + e.Misc = make(map[string][]generic.Xml) + for key := range v.Misc { + e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) + } + } -func (e *Entry) Field(v string) (any, error) { - if v == "name" || v == "Name" { - return e.Name, nil - } + func (e *Entry) Field(v string) (any, error) { + if v == "name" || v == "Name" { + return e.Name, nil + } {{- range $_, $param := .Spec.Params}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { return e.{{$param.Name.CamelCase}}, nil - } - {{- if eq $param.Type "list"}} - if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { - return int64(len(e.{{$param.Name.CamelCase}})), nil - } - {{- end}} + } + {{- if eq $param.Type "list"}} + if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { + return int64(len(e.{{$param.Name.CamelCase}})), nil + } + {{- end}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { + if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { return e.{{$param.Name.CamelCase}}, nil - } + } {{- end}} - return nil, fmt.Errorf("unknown field") -} + return nil, fmt.Errorf("unknown field") + } -func Versioning(vn version.Number) (Specifier, Normalizer, error) { - return SpecifyEntry, &entryXmlContainer{}, nil -} + func Versioning(vn version.Number) (Specifier, Normalizer, error) { + return SpecifyEntry, &entryXmlContainer{}, nil + } -func SpecifyEntry(o Entry) (any, error) { - entry := entryXml{} + func SpecifyEntry(o Entry) (any, error) { + entry := entryXml{} - entry.Name = o.Name + entry.Name = o.Name {{- range $_, $param := .Spec.Params}} - {{specifyEntryAssignment "entry" $param}} + {{specifyEntryAssignment "entry" $param}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{specifyEntryAssignment "entry" $param}} + {{specifyEntryAssignment "entry" $param}} {{- end}} - entry.Misc = o.Misc["Entry"] + entry.Misc = o.Misc["Entry"] - return entry, nil -} + return entry, nil + } -func (c *entryXmlContainer) Normalize() ([]Entry, error) { - entryList := make([]Entry, 0, len(c.Answer)) - for _, o := range c.Answer { - entry := Entry{ - Misc: make(map[string][]generic.Xml), - } - entry.Name = o.Name - {{- range $_, $param := .Spec.Params}} + func (c *entryXmlContainer) Normalize() ([]Entry, error) { + entryList := make([]Entry, 0, len(c.Answer)) + for _, o := range c.Answer { + entry := Entry{ + Misc: make(map[string][]generic.Xml), + } + entry.Name = o.Name + {{- range $_, $param := .Spec.Params}} {{normalizeAssignment "entry" $param}} - {{- end}} - {{- range $_, $param := .Spec.OneOf}} + {{- end}} + {{- range $_, $param := .Spec.OneOf}} {{normalizeAssignment "entry" $param}} - {{- end}} + {{- end}} - entry.Misc["Entry"] = o.Misc + entry.Misc["Entry"] = o.Misc - entryList = append(entryList, entry) - } + entryList = append(entryList, entry) + } - return entryList, nil -} + return entryList, nil + } -func SpecMatches(a, b *Entry) bool { - if a == nil && b != nil || a != nil && b == nil { - return false - } else if a == nil && b == nil { - return true - } + func SpecMatches(a, b *Entry) bool { + if a == nil && b != nil || a != nil && b == nil { + return false + } else if a == nil && b == nil { + return true + } - // Don't compare Name. + // Don't compare Name. {{- range $_, $param := .Spec.Params}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false + } + {{- end}} {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} + {{- if or (eq $param.Type "list") (eq $param.Type "string")}} + if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false + } + {{- end}} {{- end}} - return true -} + return true + } {{- end}} \ No newline at end of file diff --git a/templates/sdk/interfaces.tmpl b/templates/sdk/interfaces.tmpl index f8a0175f..cac86e15 100644 --- a/templates/sdk/interfaces.tmpl +++ b/templates/sdk/interfaces.tmpl @@ -1,15 +1,7 @@ package {{packageName .GoSdkPath}} -{{- if .Entry}} - type Specifier func(Entry) (any, error) -{{- else}} - type Specifier func(Config) (any, error) -{{- end}} +type Specifier func(Entry) (any, error) type Normalizer interface { -{{- if .Entry}} - Normalize() ([]Entry, error) -{{- else}} - Normalize() ([]Config, error) -{{- end}} +Normalize() ([]Entry, error) } \ No newline at end of file diff --git a/templates/sdk/location.tmpl b/templates/sdk/location.tmpl index 5d898d08..13c0e854 100644 --- a/templates/sdk/location.tmpl +++ b/templates/sdk/location.tmpl @@ -1,81 +1,81 @@ package {{packageName .GoSdkPath}} import ( - "fmt" +"fmt" - "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/util" - "github.com/PaloAltoNetworks/pango/version" +"github.com/PaloAltoNetworks/pango/errors" +"github.com/PaloAltoNetworks/pango/util" +"github.com/PaloAltoNetworks/pango/version" ) type Location struct { - {{range $key, $location := .Locations}} +{{range $key, $location := .Locations}} {{- $location.Name.CamelCase }} {{locationType $location true}} `json:"{{$location.Name.Underscore}}{{omitEmpty $location}}"` - {{end}} +{{end}} } {{range $key, $location := .Locations}} -{{- if $location.Vars}} -type {{locationType $location false}} struct { -{{- range $key, $var := $location.Vars}} - {{$var.Name.CamelCase}} string `json:"{{$var.Name.Underscore}}"` -{{- end}} -} -{{end}} + {{- if $location.Vars}} + type {{locationType $location false}} struct { + {{- range $key, $var := $location.Vars}} + {{$var.Name.CamelCase}} string `json:"{{$var.Name.Underscore}}"` + {{- end}} + } + {{end}} {{- end}} func (o Location) IsValid() error { - count := 0 +count := 0 - switch { - {{- range $key, $location := .Locations}} +switch { +{{- range $key, $location := .Locations}} case o.{{- $location.Name.CamelCase}}{{if ne (locationType $location true) "bool"}} != nil{{end}}: - {{- range $name, $var := $location.Vars}} + {{- range $name, $var := $location.Vars}} if o.{{$location.Name.CamelCase}}.{{$var.Name.CamelCase}} == "" { - return fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") + return fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") } - {{- end}} - count++ {{- end}} - } + count++ +{{- end}} +} - if count == 0 { - return fmt.Errorf("no path specified") - } +if count == 0 { +return fmt.Errorf("no path specified") +} - if count > 1 { - return fmt.Errorf("multiple paths specified: only one should be specified") - } +if count > 1 { +return fmt.Errorf("multiple paths specified: only one should be specified") +} - return nil +return nil } func (o Location) Xpath(vn version.Number, name string) ([]string, error) { - var ans []string +var ans []string - switch { - {{- range $key, $location := .Locations}} +switch { +{{- range $key, $location := .Locations}} case o.{{- $location.Name.CamelCase}}{{if ne (locationType $location true) "bool"}} != nil{{end}}: - {{- range $name, $var := $location.Vars}} + {{- range $name, $var := $location.Vars}} if o.{{$location.Name.CamelCase}}.{{$var.Name.CamelCase}} == "" { - return nil, fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") + return nil, fmt.Errorf("{{$var.Name.CamelCase}} is unspecified") } - {{- end}} - ans = []string{ - {{- range $name, $xpath := $location.Xpath}} - {{- if contains $xpath "Entry"}} + {{- end}} + ans = []string{ + {{- range $name, $xpath := $location.Xpath}} + {{- if contains $xpath "Entry"}} {{generateEntryXpath $location.Name.CamelCase $xpath}} - {{- else}} + {{- else}} "{{$xpath}}", - {{- end}} - {{- end}} - } + {{- end}} {{- end}} - default: - return nil, errors.NoLocationSpecifiedError - } + } +{{- end}} +default: +return nil, errors.NoLocationSpecifiedError +} - ans = append(ans, Suffix...) - ans = append(ans, util.AsEntryXpath([]string{name})) +ans = append(ans, Suffix...) +ans = append(ans, util.AsEntryXpath([]string{name})) - return ans, nil +return ans, nil } \ No newline at end of file From 279b05a1605e4dddc5f1c5ebc23cd97552524698 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 12:13:15 +0100 Subject: [PATCH 43/66] Introduce version struct --- pkg/translate/structs_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 415be26d..743e804b 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -222,3 +222,13 @@ func TestNestedSpecs(t *testing.T) { assert.Contains(t, nestedSpecs, "A") assert.Contains(t, nestedSpecs, "AB") } + +func TestCreateGoSuffixFromVersion(t *testing.T) { + // given + + // when + suffix := CreateGoSuffixFromVersion("10.1.1") + + // then + assert.Equal(t, "_10_1_1", suffix) +} From 61a505db698b78a50b810010904d9b9f9d8192f7 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 14:05:42 +0100 Subject: [PATCH 44/66] Continue work on versioning --- templates/sdk/config.tmpl | 78 +++++++++++++++++++++------------------ templates/sdk/entry.tmpl | 64 +++++++++++++++++++++++--------- 2 files changed, 89 insertions(+), 53 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index d15d7ae7..0dee47e3 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -84,28 +84,34 @@ } } - func (e *Config) Field(v string) (any, error) { - {{- range $_, $param := .Spec.Params}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { - return e.{{$param.Name.CamelCase}}, nil - } - {{- if eq $param.Type "list"}} - if v == "{{$param.Name.Underscore}}|LENGTH" || v == "{{$param.Name.CamelCase}}|LENGTH" { - return int64(len(e.{{$param.Name.CamelCase}})), nil - } + func Versioning(vn version.Number) (Specifier, Normalizer, error) { + {{- $numberOfVersions := len .SupportedVersions }} + {{- if gt $numberOfVersions 1}} + {{- range $index, $version := .SupportedVersions }} + {{- if ne $version ""}} + version{{createGoSuffixFromVersion $version}}, err := version.New("{{$version}}") + if err != nil { + return nil, nil, err + } + {{- end}} + {{- end}} + {{- range $index, $version := .SupportedVersions }} + {{- if ne $version ""}} + {{- if eq $index 1}} + if vn.Gte(version{{createGoSuffixFromVersion $version}}) { + return SpecifyConfig, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + {{- else}} + } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { + return SpecifyConfig, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + {{- end}} + {{- end}} {{- end}} + } else { {{- end}} - {{- range $_, $param := .Spec.OneOf}} - if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { - return e.{{$param.Name.CamelCase}}, nil + return SpecifyConfig, &configXmlContainer{}, nil + {{- if gt $numberOfVersions 1}} } {{- end}} - - return nil, fmt.Errorf("unknown field") - } - - func Versioning(vn version.Number) (Specifier, Normalizer, error) { - return SpecifyConfig, &configXmlContainer{}, nil } func SpecifyConfig(o Config) (any, error) { @@ -123,26 +129,28 @@ return config, nil } - func (c *configXmlContainer) Normalize() ([]Config, error) { - configList := make([]Config, 0, len(c.Answer)) - for _, o := range c.Answer { - config := Config{ - Misc: make(map[string][]generic.Xml), - } - {{- range $_, $param := .Spec.Params}} - {{normalizeAssignment "config" $param}} - {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{normalizeAssignment "config" $param}} - {{- end}} + {{- range $version := .SupportedVersions }} + func (c *configXmlContainer{{createGoSuffixFromVersion $version}}) Normalize() ([]Config, error) { + configList := make([]Config, 0, len(c.Answer)) + for _, o := range c.Answer { + config := Config{ + Misc: make(map[string][]generic.Xml), + } + {{- range $_, $param := $.Spec.Params}} + {{normalizeAssignment "config" $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{normalizeAssignment "config" $param}} + {{- end}} - config.Misc["Config"] = o.Misc + config.Misc["Config"] = o.Misc - configList = append(configList, config) - } + configList = append(configList, config) + } - return configList, nil - } + return configList, nil + } + {{- end}} func SpecMatches(a, b *Config) bool { if a == nil && b != nil || a != nil && b == nil { diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 9b4ed6e4..d029c5e4 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -124,7 +124,33 @@ } func Versioning(vn version.Number) (Specifier, Normalizer, error) { + {{- $numberOfVersions := len .SupportedVersions }} + {{- if gt $numberOfVersions 1}} + {{- range $index, $version := .SupportedVersions }} + {{- if ne $version ""}} + version{{createGoSuffixFromVersion $version}}, err := version.New("{{$version}}") + if err != nil { + return nil, nil, err + } + {{- end}} + {{- end}} + {{- range $index, $version := .SupportedVersions }} + {{- if ne $version ""}} + {{- if eq $index 1}} + if vn.Gte(version{{createGoSuffixFromVersion $version}}) { + return SpecifyEntry, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + {{- else}} + } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { + return SpecifyEntry, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + {{- end}} + {{- end}} + {{- end}} + } else { + {{- end}} return SpecifyEntry, &entryXmlContainer{}, nil + {{- if gt $numberOfVersions 1}} + } + {{- end}} } func SpecifyEntry(o Entry) (any, error) { @@ -143,27 +169,29 @@ return entry, nil } - func (c *entryXmlContainer) Normalize() ([]Entry, error) { - entryList := make([]Entry, 0, len(c.Answer)) - for _, o := range c.Answer { - entry := Entry{ - Misc: make(map[string][]generic.Xml), - } - entry.Name = o.Name - {{- range $_, $param := .Spec.Params}} - {{normalizeAssignment "entry" $param}} - {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{normalizeAssignment "entry" $param}} - {{- end}} + {{- range $version := .SupportedVersions }} + func (c *entryXmlContainer{{createGoSuffixFromVersion $version}}) Normalize() ([]Entry, error) { + entryList := make([]Entry, 0, len(c.Answer)) + for _, o := range c.Answer { + entry := Entry{ + Misc: make(map[string][]generic.Xml), + } + entry.Name = o.Name + {{- range $_, $param := $.Spec.Params}} + {{normalizeAssignment "entry" $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{normalizeAssignment "entry" $param}} + {{- end}} - entry.Misc["Entry"] = o.Misc + entry.Misc["Entry"] = o.Misc - entryList = append(entryList, entry) - } + entryList = append(entryList, entry) + } - return entryList, nil - } + return entryList, nil + } + {{- end}} func SpecMatches(a, b *Entry) bool { if a == nil && b != nil || a != nil && b == nil { From 10fc1ecf678895b2a2efd3f72c7bb18504c6a244 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 14:09:24 +0100 Subject: [PATCH 45/66] Remove version package --- pkg/version/version.go | 72 ------------------------------------- pkg/version/version_test.go | 49 ------------------------- 2 files changed, 121 deletions(-) delete mode 100644 pkg/version/version.go delete mode 100644 pkg/version/version_test.go diff --git a/pkg/version/version.go b/pkg/version/version.go deleted file mode 100644 index df8175eb..00000000 --- a/pkg/version/version.go +++ /dev/null @@ -1,72 +0,0 @@ -package version - -import ( - "errors" - "fmt" - "strconv" - "strings" -) - -// Version is the version number struct. -type Version struct { - Major, Minor, Patch int - Hotfix string -} - -// Gte tests if this version number is greater than or equal to the argument. -func (v Version) Gte(o Version) bool { - if v.Major != o.Major { - return v.Major > o.Major - } - - if v.Minor != o.Minor { - return v.Minor > o.Minor - } - - return v.Patch >= o.Patch -} - -// String returns the version number as a string. -func (v Version) String() string { - if v.Hotfix == "" { - return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) - } else { - return fmt.Sprintf("%d.%d.%d-%s", v.Major, v.Minor, v.Patch, v.Hotfix) - } -} - -// New returns a version number from the given string. -func New(version string) (Version, error) { - parts := strings.Split(version, ".") - if len(parts) != 3 { - return Version{}, errors.New("invalid version") - } - - major, err := strconv.Atoi(parts[0]) - if err != nil { - return Version{}, fmt.Errorf("major %s is not a number: %s", parts[0], err) - } - - minor, err := strconv.Atoi(parts[1]) - if err != nil { - return Version{}, fmt.Errorf("minor %s is not a number: %s", parts[0], err) - } - - patchWithHotfix := strings.Split(parts[2], "-") - - var hotfix string - if len(patchWithHotfix) == 1 { - hotfix = "" - } else if len(patchWithHotfix) == 2 { - hotfix = patchWithHotfix[1] - } else { - return Version{}, fmt.Errorf("patch %s is not formatted as expected", parts[2]) - } - - patch, err := strconv.Atoi(patchWithHotfix[0]) - if err != nil { - return Version{}, fmt.Errorf("patch %s is not a number: %s", parts[2], err) - } - - return Version{major, minor, patch, hotfix}, nil -} diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go deleted file mode 100644 index b0a0f2f4..00000000 --- a/pkg/version/version_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package version - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestCorrectVersion(t *testing.T) { - // given - - // when - version1011, err1011 := New("10.1.1") - version1011h2, err1011h2 := New("10.1.1-h2") - - // then - assert.NoError(t, err1011) - assert.Equal(t, 10, version1011.Major) - assert.Equal(t, 1, version1011.Minor) - assert.Equal(t, 1, version1011.Patch) - assert.Equal(t, "", version1011.Hotfix) - assert.NoError(t, err1011h2) - assert.Equal(t, 10, version1011h2.Major) - assert.Equal(t, 1, version1011h2.Minor) - assert.Equal(t, 1, version1011h2.Patch) - assert.Equal(t, "h2", version1011h2.Hotfix) -} - -func TestIncorrectVersion(t *testing.T) { - // given - - // when - _, err101 := New("10.1") - _, err1011h2h2 := New("10.1.1-h2-h2") - - // then - assert.Error(t, err101) - assert.Error(t, err1011h2h2) -} - -func TestVersionComparison(t *testing.T) { - // given - - // when - v1, _ := New("10.1.1") - v2, _ := New("10.2.1-h2") - - // then - assert.True(t, v2.Gte(v1)) -} From 9a9099b2da3d6af0ef485874e0c4510ae1765644 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 14:22:58 +0100 Subject: [PATCH 46/66] Restore version package --- pkg/version/version.go | 72 +++++++++++++++++++++++++++++++++++++ pkg/version/version_test.go | 49 +++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 pkg/version/version.go create mode 100644 pkg/version/version_test.go diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 00000000..df8175eb --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,72 @@ +package version + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +// Version is the version number struct. +type Version struct { + Major, Minor, Patch int + Hotfix string +} + +// Gte tests if this version number is greater than or equal to the argument. +func (v Version) Gte(o Version) bool { + if v.Major != o.Major { + return v.Major > o.Major + } + + if v.Minor != o.Minor { + return v.Minor > o.Minor + } + + return v.Patch >= o.Patch +} + +// String returns the version number as a string. +func (v Version) String() string { + if v.Hotfix == "" { + return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) + } else { + return fmt.Sprintf("%d.%d.%d-%s", v.Major, v.Minor, v.Patch, v.Hotfix) + } +} + +// New returns a version number from the given string. +func New(version string) (Version, error) { + parts := strings.Split(version, ".") + if len(parts) != 3 { + return Version{}, errors.New("invalid version") + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return Version{}, fmt.Errorf("major %s is not a number: %s", parts[0], err) + } + + minor, err := strconv.Atoi(parts[1]) + if err != nil { + return Version{}, fmt.Errorf("minor %s is not a number: %s", parts[0], err) + } + + patchWithHotfix := strings.Split(parts[2], "-") + + var hotfix string + if len(patchWithHotfix) == 1 { + hotfix = "" + } else if len(patchWithHotfix) == 2 { + hotfix = patchWithHotfix[1] + } else { + return Version{}, fmt.Errorf("patch %s is not formatted as expected", parts[2]) + } + + patch, err := strconv.Atoi(patchWithHotfix[0]) + if err != nil { + return Version{}, fmt.Errorf("patch %s is not a number: %s", parts[2], err) + } + + return Version{major, minor, patch, hotfix}, nil +} diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go new file mode 100644 index 00000000..b0a0f2f4 --- /dev/null +++ b/pkg/version/version_test.go @@ -0,0 +1,49 @@ +package version + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestCorrectVersion(t *testing.T) { + // given + + // when + version1011, err1011 := New("10.1.1") + version1011h2, err1011h2 := New("10.1.1-h2") + + // then + assert.NoError(t, err1011) + assert.Equal(t, 10, version1011.Major) + assert.Equal(t, 1, version1011.Minor) + assert.Equal(t, 1, version1011.Patch) + assert.Equal(t, "", version1011.Hotfix) + assert.NoError(t, err1011h2) + assert.Equal(t, 10, version1011h2.Major) + assert.Equal(t, 1, version1011h2.Minor) + assert.Equal(t, 1, version1011h2.Patch) + assert.Equal(t, "h2", version1011h2.Hotfix) +} + +func TestIncorrectVersion(t *testing.T) { + // given + + // when + _, err101 := New("10.1") + _, err1011h2h2 := New("10.1.1-h2-h2") + + // then + assert.Error(t, err101) + assert.Error(t, err1011h2h2) +} + +func TestVersionComparison(t *testing.T) { + // given + + // when + v1, _ := New("10.1.1") + v2, _ := New("10.2.1-h2") + + // then + assert.True(t, v2.Gte(v1)) +} From 68cb7c5a4849bb2c39a249c1bf018ae0fcbc143d Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 15:23:30 +0100 Subject: [PATCH 47/66] Check if param is supported in specific PAN-OS version --- pkg/translate/structs.go | 32 +++++++++++++++- pkg/translate/structs_test.go | 72 ++++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 7 deletions(-) diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index e1bad63b..b6d47913 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" + "github.com/paloaltonetworks/pan-os-codegen/pkg/version" "strings" ) @@ -46,7 +47,7 @@ func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs } // SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func SpecParamType(parent string, param *properties.SpecParam) string { +func SpecParamType(parent string, param *properties.SpecParam, version string) string { prefix := determinePrefix(param, false) calculatedType := "" @@ -62,7 +63,7 @@ func SpecParamType(parent string, param *properties.SpecParam) string { } // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func XmlParamType(parent string, param *properties.SpecParam) string { +func XmlParamType(parent string, param *properties.SpecParam, version string) string { prefix := determinePrefix(param, true) calculatedType := "" @@ -132,3 +133,30 @@ func CreateGoSuffixFromVersion(version string) string { return version } } + +// ParamSupportedInVersion checks if param is supported in specific PAN-OS version +func ParamSupportedInVersion(param *properties.SpecParam, deviceVersionStr string) bool { + deviceVersion, err := version.New(deviceVersionStr) + if err != nil { + return false + } + + for _, profile := range param.Profiles { + if profile.FromVersion != "" { + paramProfileVersion, err := version.New(profile.FromVersion) + if err != nil { + return false + } + + if deviceVersion.Gte(paramProfileVersion) { + return !profile.NotPresent + } else { + return profile.NotPresent + } + } else { + return !profile.NotPresent + } + } + + return false +} diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index 743e804b..a709453a 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -85,9 +85,9 @@ func TestSpecParamType(t *testing.T) { } // when - calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString) - calculatedTypeListString := SpecParamType("", ¶mTypeListString) - calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString) + calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString, "") + calculatedTypeListString := SpecParamType("", ¶mTypeListString, "") + calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString, "") // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -139,8 +139,8 @@ func TestXmlParamType(t *testing.T) { } // when - calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString) - calculatedTypeListString := XmlParamType("", ¶mTypeListString) + calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString, "") + calculatedTypeListString := XmlParamType("", ¶mTypeListString, "") // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -232,3 +232,65 @@ func TestCreateGoSuffixFromVersion(t *testing.T) { // then assert.Equal(t, "_10_1_1", suffix) } + +func TestParamSupportedInVersion(t *testing.T) { + // given + deviceVersion101 := "10.1.1" + deviceVersion90 := "9.0.0" + + paramName := properties.NameVariant{ + CamelCase: "test", + Underscore: "test", + } + + profileAlwaysPresent := properties.SpecParamProfile{ + Xpath: []string{"test"}, + } + profilePresentFrom10 := properties.SpecParamProfile{ + Xpath: []string{"test"}, + FromVersion: "10.0.0", + } + profileNotPresentFrom10 := properties.SpecParamProfile{ + Xpath: []string{"test"}, + FromVersion: "10.0.0", + NotPresent: true, + } + + paramPresentFrom10 := &properties.SpecParam{ + Type: "string", + Name: ¶mName, + Profiles: []*properties.SpecParamProfile{ + &profilePresentFrom10, + }, + } + paramAlwaysPresent := &properties.SpecParam{ + Type: "string", + Name: ¶mName, + Profiles: []*properties.SpecParamProfile{ + &profileAlwaysPresent, + }, + } + paramNotPresentFrom10 := &properties.SpecParam{ + Type: "string", + Name: ¶mName, + Profiles: []*properties.SpecParamProfile{ + &profileNotPresentFrom10, + }, + } + + // when + device10AndParamPresentFrom10 := ParamSupportedInVersion(paramPresentFrom10, deviceVersion101) + device10AndParamAlwaysPresent := ParamSupportedInVersion(paramAlwaysPresent, deviceVersion101) + device10AndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, deviceVersion101) + device9AndParamPresentFrom10 := ParamSupportedInVersion(paramPresentFrom10, deviceVersion90) + device9AndParamAlwaysPresent := ParamSupportedInVersion(paramAlwaysPresent, deviceVersion90) + device9AndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, deviceVersion90) + + // then + assert.True(t, device10AndParamPresentFrom10) + assert.True(t, device10AndParamAlwaysPresent) + assert.False(t, device10AndParamNotPresentFrom10) + assert.False(t, device9AndParamPresentFrom10) + assert.True(t, device9AndParamAlwaysPresent) + assert.True(t, device9AndParamNotPresentFrom10) +} From 2f86bd6faf7a8352ee5425734efffb56df0320ff Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 16:13:49 +0100 Subject: [PATCH 48/66] Extend function for checking if param is supported in specific PAN-OS version --- pkg/generate/generator.go | 1 + pkg/translate/structs.go | 55 +++++++++++++++++++++++------------ pkg/translate/structs_test.go | 14 +++++---- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 80b1808e..d74cdad0 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -135,6 +135,7 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "generateEntryXpath": translate.GenerateEntryXpathForLocation, "nestedSpecs": translate.NestedSpecs, "createGoSuffixFromVersion": translate.CreateGoSuffixFromVersion, + "paramSupportedInVersion": translate.ParamSupportedInVersion, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index b6d47913..953cf950 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -47,7 +47,7 @@ func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs } // SpecParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func SpecParamType(parent string, param *properties.SpecParam, version string) string { +func SpecParamType(parent string, param *properties.SpecParam) string { prefix := determinePrefix(param, false) calculatedType := "" @@ -63,7 +63,7 @@ func SpecParamType(parent string, param *properties.SpecParam, version string) s } // XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func XmlParamType(parent string, param *properties.SpecParam, version string) string { +func XmlParamType(parent string, param *properties.SpecParam) string { prefix := determinePrefix(param, true) calculatedType := "" @@ -136,27 +136,46 @@ func CreateGoSuffixFromVersion(version string) string { // ParamSupportedInVersion checks if param is supported in specific PAN-OS version func ParamSupportedInVersion(param *properties.SpecParam, deviceVersionStr string) bool { - deviceVersion, err := version.New(deviceVersionStr) - if err != nil { - return false - } - - for _, profile := range param.Profiles { - if profile.FromVersion != "" { - paramProfileVersion, err := version.New(profile.FromVersion) - if err != nil { - return false + var supported []bool + if deviceVersionStr == "" { + for _, profile := range param.Profiles { + if profile.FromVersion != "" { + supported = append(supported, profile.NotPresent) + } else { + supported = append(supported, true) } + } + } else { + deviceVersion, err := version.New(deviceVersionStr) + if err != nil { + return false + } - if deviceVersion.Gte(paramProfileVersion) { - return !profile.NotPresent + for _, profile := range param.Profiles { + if profile.FromVersion != "" { + paramProfileVersion, err := version.New(profile.FromVersion) + if err != nil { + return false + } + + if deviceVersion.Gte(paramProfileVersion) { + supported = append(supported, !profile.NotPresent) + } else { + supported = append(supported, profile.NotPresent) + } } else { - return profile.NotPresent + supported = append(supported, !profile.NotPresent) } - } else { - return !profile.NotPresent } } + return allTrue(supported) +} - return false +func allTrue(values []bool) bool { + for _, value := range values { + if !value { + return false + } + } + return true } diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index a709453a..14a43c08 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -85,9 +85,9 @@ func TestSpecParamType(t *testing.T) { } // when - calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString, "") - calculatedTypeListString := SpecParamType("", ¶mTypeListString, "") - calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString, "") + calculatedTypeRequiredString := SpecParamType("", ¶mTypeRequiredString) + calculatedTypeListString := SpecParamType("", ¶mTypeListString) + calculatedTypeOptionalString := SpecParamType("", ¶mTypeOptionalString) // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -139,8 +139,8 @@ func TestXmlParamType(t *testing.T) { } // when - calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString, "") - calculatedTypeListString := XmlParamType("", ¶mTypeListString, "") + calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString) + calculatedTypeListString := XmlParamType("", ¶mTypeListString) // then assert.Equal(t, "string", calculatedTypeRequiredString) @@ -279,6 +279,8 @@ func TestParamSupportedInVersion(t *testing.T) { } // when + noVersionAndParamAlwaysPresent := ParamSupportedInVersion(paramAlwaysPresent, "") + noVersionAndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, "") device10AndParamPresentFrom10 := ParamSupportedInVersion(paramPresentFrom10, deviceVersion101) device10AndParamAlwaysPresent := ParamSupportedInVersion(paramAlwaysPresent, deviceVersion101) device10AndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, deviceVersion101) @@ -287,6 +289,8 @@ func TestParamSupportedInVersion(t *testing.T) { device9AndParamNotPresentFrom10 := ParamSupportedInVersion(paramNotPresentFrom10, deviceVersion90) // then + assert.True(t, noVersionAndParamAlwaysPresent) + assert.True(t, noVersionAndParamNotPresentFrom10) assert.True(t, device10AndParamPresentFrom10) assert.True(t, device10AndParamAlwaysPresent) assert.False(t, device10AndParamNotPresentFrom10) From 6ca7bbf316bd04e135e4f397f83dde3fe558e202 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 16:21:38 +0100 Subject: [PATCH 49/66] Use function paramSupportedInVersion in templates --- templates/sdk/config.tmpl | 54 ++++++++++++++++++++----------------- templates/sdk/entry.tmpl | 56 +++++++++++++++++++++------------------ 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 0dee47e3..79d7627e 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -11,34 +11,30 @@ "github.com/PaloAltoNetworks/pango/version" ) - {{- range $version := .SupportedVersions }} - type Config{{createGoSuffixFromVersion $version}} struct { - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + type Config{{createGoSuffixFromVersion ""}} struct { + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + + Misc map[string][]generic.Xml + } + + {{- range $name, $spec := nestedSpecs $.Spec }} + type Spec{{$name}}{{createGoSuffixFromVersion ""}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} Misc map[string][]generic.Xml } {{- end}} - {{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} - type Spec{{$name}}{{createGoSuffixFromVersion $version}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - - Misc map[string][]generic.Xml - } - {{- end}} - {{- end}} - {{- range $version := .SupportedVersions }} type configXmlContainer{{createGoSuffixFromVersion $version}} struct { Answer []configXml{{createGoSuffixFromVersion $version}} `xml:"config"` @@ -48,10 +44,14 @@ {{- range $version := .SupportedVersions }} type configXml{{createGoSuffixFromVersion $version}} struct { {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} Misc []generic.Xml `xml:",any"` @@ -62,10 +62,14 @@ {{- range $name, $spec := nestedSpecs $.Spec }} type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} Misc []generic.Xml `xml:",any"` diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index d029c5e4..07a28030 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -23,35 +23,31 @@ {{- end}}} ) - {{- range $version := .SupportedVersions }} - type Entry{{createGoSuffixFromVersion $version}} struct { - Name string - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + type Entry{{createGoSuffixFromVersion ""}} struct { + Name string + {{- range $_, $param := $.Spec.Params}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- end}} + + Misc map[string][]generic.Xml + } + + {{- range $name, $spec := nestedSpecs $.Spec }} + type Spec{{$name}}{{createGoSuffixFromVersion ""}} struct { + {{- range $_, $param := $spec.Params}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} + {{- range $_, $param := $spec.OneOf}} + {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} Misc map[string][]generic.Xml } {{- end}} - {{- range $version := .SupportedVersions }} - {{- range $name, $spec := nestedSpecs $.Spec }} - type Spec{{$name}}{{createGoSuffixFromVersion $version}} struct { - {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - - Misc map[string][]generic.Xml - } - {{- end}} - {{- end}} - {{- range $version := .SupportedVersions }} type entryXmlContainer{{createGoSuffixFromVersion $version}} struct { Answer []entryXml{{createGoSuffixFromVersion $version}} `xml:"entry"` @@ -63,10 +59,14 @@ XMLName xml.Name `xml:"entry"` Name string `xml:"name,attr"` {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} Misc []generic.Xml `xml:",any"` @@ -77,10 +77,14 @@ {{- range $name, $spec := nestedSpecs $.Spec }} type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { {{- range $_, $param := $spec.Params}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- range $_, $param := $spec.OneOf}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if paramSupportedInVersion $param $version}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} Misc []generic.Xml `xml:",any"` From 2423da767ee8cb17b8ef496bf43bb7be40191c52 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 16:35:44 +0100 Subject: [PATCH 50/66] Do not render assignment for not supported param in specific version --- pkg/translate/funcs.go | 24 +++++++++++++----------- pkg/translate/funcs_test.go | 8 ++++---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 77f45c9a..a651118b 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -27,26 +27,28 @@ func generateEntryXpathForLocation(location string, xpath string) string { // NormalizeAssignment generates a string, which contains entry/config assignment in Normalize() function // in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed // internal functions, which are creating entry assignment. -func NormalizeAssignment(objectType string, param *properties.SpecParam) string { - return prepareAssignment(objectType, param, "util.MemToStr", "Spec", "") +func NormalizeAssignment(objectType string, param *properties.SpecParam, version string) string { + return prepareAssignment(objectType, param, "util.MemToStr", "Spec", "", version) } // SpecifyEntryAssignment generates a string, which contains entry/config assignment in SpecifyEntry() function // in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed // internal functions, which are creating entry assignment. -func SpecifyEntryAssignment(objectType string, param *properties.SpecParam) string { - return prepareAssignment(objectType, param, "util.StrToMem", "spec", "Xml") +func SpecifyEntryAssignment(objectType string, param *properties.SpecParam, version string) string { + return prepareAssignment(objectType, param, "util.StrToMem", "spec", "Xml", version) } -func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specPrefix, specSuffix string) string { +func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, specPrefix, specSuffix string, version string) string { var builder strings.Builder - if param.Spec != nil { - appendSpecObjectAssignment(param, objectType, specPrefix, specSuffix, &builder) - } else if isParamListAndProfileTypeIsMember(param) { - appendListFunctionAssignment(param, objectType, listFunction, &builder) - } else { - appendSimpleAssignment(param, objectType, &builder) + if ParamSupportedInVersion(param, version) { + if param.Spec != nil { + appendSpecObjectAssignment(param, objectType, specPrefix, specSuffix, &builder) + } else if isParamListAndProfileTypeIsMember(param) { + appendListFunctionAssignment(param, objectType, listFunction, &builder) + } else { + appendSimpleAssignment(param, objectType, &builder) + } } return builder.String() diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 0f9ba43f..4bdbae8f 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -45,8 +45,8 @@ func TestSpecifyEntryAssignmentForFlatStructure(t *testing.T) { } // when - calculatedAssignmentString := SpecifyEntryAssignment("entry", ¶mTypeString) - calculatedAssignmentListString := SpecifyEntryAssignment("entry", ¶mTypeListString) + calculatedAssignmentString := SpecifyEntryAssignment("entry", ¶mTypeString, "") + calculatedAssignmentListString := SpecifyEntryAssignment("entry", ¶mTypeListString, "") // then assert.Equal(t, "entry.Description = o.Description", calculatedAssignmentString) @@ -100,7 +100,7 @@ nestedA.B.C = o.A.B.C entry.A = nestedA ` // when - calculatedAssignmentString := SpecifyEntryAssignment("entry", spec.Params["a"]) + calculatedAssignmentString := SpecifyEntryAssignment("entry", spec.Params["a"], "") // then assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) @@ -153,7 +153,7 @@ nestedA.B.C = o.A.B.C entry.A = nestedA ` // when - calculatedAssignmentString := NormalizeAssignment("entry", spec.Params["a"]) + calculatedAssignmentString := NormalizeAssignment("entry", spec.Params["a"], "") // then assert.Equal(t, expectedAssignmentStreing, calculatedAssignmentString) From e49158ab7aedffba33993cf671e595aac22c76cf Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 16:36:14 +0100 Subject: [PATCH 51/66] Update template to use newer versions of functions --- templates/sdk/config.tmpl | 32 +++++++++++++++++--------------- templates/sdk/entry.tmpl | 34 ++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 79d7627e..e136743c 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -103,10 +103,10 @@ {{- if ne $version ""}} {{- if eq $index 1}} if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyConfig, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return SpecifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- else}} } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyConfig, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return SpecifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- end}} {{- end}} {{- end}} @@ -118,20 +118,22 @@ {{- end}} } - func SpecifyConfig(o Config) (any, error) { - config := configXml{} + {{- range $version := .SupportedVersions }} + func SpecifyConfig{{createGoSuffixFromVersion $version}}(o Config) (any, error) { + config := configXml{{createGoSuffixFromVersion $version}}{} - {{- range $_, $param := .Spec.Params}} - {{specifyEntryAssignment "config" $param}} - {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{specifyEntryAssignment "config" $param}} - {{- end}} + {{- range $_, $param := $.Spec.Params}} + {{specifyEntryAssignment "config" $param $version}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{specifyEntryAssignment "config" $param $version}} + {{- end}} - config.Misc = o.Misc["Config"] + config.Misc = o.Misc["Config"] - return config, nil - } + return config, nil + } + {{- end}} {{- range $version := .SupportedVersions }} func (c *configXmlContainer{{createGoSuffixFromVersion $version}}) Normalize() ([]Config, error) { @@ -141,10 +143,10 @@ Misc: make(map[string][]generic.Xml), } {{- range $_, $param := $.Spec.Params}} - {{normalizeAssignment "config" $param}} + {{normalizeAssignment "config" $param $version}} {{- end}} {{- range $_, $param := $.Spec.OneOf}} - {{normalizeAssignment "config" $param}} + {{normalizeAssignment "config" $param $version}} {{- end}} config.Misc["Config"] = o.Misc diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 07a28030..6ad94404 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -142,10 +142,10 @@ {{- if ne $version ""}} {{- if eq $index 1}} if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyEntry, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return SpecifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- else}} } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyEntry, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return SpecifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- end}} {{- end}} {{- end}} @@ -157,21 +157,23 @@ {{- end}} } - func SpecifyEntry(o Entry) (any, error) { - entry := entryXml{} + {{- range $version := .SupportedVersions }} + func SpecifyEntry{{createGoSuffixFromVersion $version}}(o Entry) (any, error) { + entry := entryXml{{createGoSuffixFromVersion $version}}{} - entry.Name = o.Name - {{- range $_, $param := .Spec.Params}} - {{specifyEntryAssignment "entry" $param}} - {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{specifyEntryAssignment "entry" $param}} - {{- end}} + entry.Name = o.Name + {{- range $_, $param := $.Spec.Params}} + {{specifyEntryAssignment "entry" $param $version}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{specifyEntryAssignment "entry" $param $version}} + {{- end}} - entry.Misc = o.Misc["Entry"] + entry.Misc = o.Misc["Entry"] - return entry, nil - } + return entry, nil + } + {{- end}} {{- range $version := .SupportedVersions }} func (c *entryXmlContainer{{createGoSuffixFromVersion $version}}) Normalize() ([]Entry, error) { @@ -182,10 +184,10 @@ } entry.Name = o.Name {{- range $_, $param := $.Spec.Params}} - {{normalizeAssignment "entry" $param}} + {{normalizeAssignment "entry" $param $version}} {{- end}} {{- range $_, $param := $.Spec.OneOf}} - {{normalizeAssignment "entry" $param}} + {{normalizeAssignment "entry" $param $version}} {{- end}} entry.Misc["Entry"] = o.Misc From 9ac44707d471876f6921b0716b2969c0df7c9d04 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Mon, 25 Mar 2024 20:50:14 +0100 Subject: [PATCH 52/66] Refactor code --- pkg/properties/normalized.go | 17 ++++++---- pkg/properties/normalized_test.go | 2 +- pkg/translate/funcs.go | 18 ++++------ pkg/translate/structs.go | 55 +++++++++++++++++++------------ 4 files changed, 51 insertions(+), 41 deletions(-) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index db18c8e6..f6a0137c 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -310,13 +310,7 @@ func supportedVersions(params map[string]*SpecParam, versions []string) []string for _, param := range params { for _, profile := range param.Profiles { if profile.FromVersion != "" { - notExist := true - for _, version := range versions { - if version == profile.FromVersion { - notExist = false - } - } - if notExist { + if notExist := listContains(versions, profile.FromVersion); notExist { versions = append(versions, profile.FromVersion) } } @@ -328,3 +322,12 @@ func supportedVersions(params map[string]*SpecParam, versions []string) []string } return versions } + +func listContains(versions []string, checkedVersion string) bool { + for _, version := range versions { + if version == checkedVersion { + return false + } + } + return true +} diff --git a/pkg/properties/normalized_test.go b/pkg/properties/normalized_test.go index f5a1091e..bd0aeab8 100644 --- a/pkg/properties/normalized_test.go +++ b/pkg/properties/normalized_test.go @@ -415,7 +415,7 @@ xpath_suffix: assert.Len(t, problems, 2, "Not all expected validation checks failed") } -func TestNamesOfStructsForVersioning(t *testing.T) { +func TestGettingListOfSupportedVersions(t *testing.T) { // given yamlParsedData, _ := ParseSpec([]byte(sampleSpec)) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index a651118b..b370f51f 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -96,26 +96,20 @@ func declareRootOfNestedObject(parent []string, builder *strings.Builder, prefix func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, prefix, suffix string) { if len(parent) > 1 { builder.WriteString(fmt.Sprintf("nested%s = &%s%s%s{}\n", - strings.Join(parent, "."), prefix, - strings.Join(parent, ""), suffix)) + strings.Join(parent, "."), prefix, strings.Join(parent, ""), suffix)) + builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", + strings.Join(parent, "."))) if suffix == "Xml" { - builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", strings.Join(parent, "."))) builder.WriteString(fmt.Sprintf("nested%s.Misc = o.%s.Misc[\"%s\"]\n", - strings.Join(parent, "."), - strings.Join(parent, "."), - strings.Join(parent, ""), + strings.Join(parent, "."), strings.Join(parent, "."), strings.Join(parent, ""), )) - builder.WriteString("}\n") } else { - builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", strings.Join(parent, "."))) builder.WriteString(fmt.Sprintf("nested%s.Misc[\"%s\"] = o.%s.Misc\n", - strings.Join(parent, "."), - strings.Join(parent, ""), - strings.Join(parent, "."), + strings.Join(parent, "."), strings.Join(parent, ""), strings.Join(parent, "."), )) - builder.WriteString("}\n") } + builder.WriteString("}\n") } } diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 953cf950..df85ad52 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -138,37 +138,50 @@ func CreateGoSuffixFromVersion(version string) string { func ParamSupportedInVersion(param *properties.SpecParam, deviceVersionStr string) bool { var supported []bool if deviceVersionStr == "" { - for _, profile := range param.Profiles { - if profile.FromVersion != "" { - supported = append(supported, profile.NotPresent) - } else { - supported = append(supported, true) - } - } + supported = listOfProfileSupportForNotDefinedDeviceVersion(param, supported) } else { deviceVersion, err := version.New(deviceVersionStr) if err != nil { return false } - for _, profile := range param.Profiles { - if profile.FromVersion != "" { - paramProfileVersion, err := version.New(profile.FromVersion) - if err != nil { - return false - } - - if deviceVersion.Gte(paramProfileVersion) { - supported = append(supported, !profile.NotPresent) - } else { - supported = append(supported, profile.NotPresent) - } - } else { + supported, err = listOfProfileSupportForDefinedDeviceVersion(param, supported, deviceVersion) + if err != nil { + return false + } + } + return allTrue(supported) +} + +func listOfProfileSupportForNotDefinedDeviceVersion(param *properties.SpecParam, supported []bool) []bool { + for _, profile := range param.Profiles { + if profile.FromVersion != "" { + supported = append(supported, profile.NotPresent) + } else { + supported = append(supported, true) + } + } + return supported +} + +func listOfProfileSupportForDefinedDeviceVersion(param *properties.SpecParam, supported []bool, deviceVersion version.Version) ([]bool, error) { + for _, profile := range param.Profiles { + if profile.FromVersion != "" { + paramProfileVersion, err := version.New(profile.FromVersion) + if err != nil { + return nil, err + } + + if deviceVersion.Gte(paramProfileVersion) { supported = append(supported, !profile.NotPresent) + } else { + supported = append(supported, profile.NotPresent) } + } else { + supported = append(supported, !profile.NotPresent) } } - return allTrue(supported) + return supported, nil } func allTrue(values []bool) bool { From 862bd4ada7bd3e281101a9c7af484860e407376c Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 26 Mar 2024 14:05:21 +0100 Subject: [PATCH 53/66] Fix config template after tests for NTP --- templates/sdk/config.tmpl | 26 ++++++++++++++++++++------ templates/sdk/interfaces.tmpl | 12 ++++++++++-- templates/sdk/location.tmpl | 4 +++- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index e136743c..6ff63e36 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -3,11 +3,7 @@ import ( "encoding/xml" - "fmt" - - "github.com/PaloAltoNetworks/pango/filtering" "github.com/PaloAltoNetworks/pango/generic" - "github.com/PaloAltoNetworks/pango/util" "github.com/PaloAltoNetworks/pango/version" ) @@ -37,7 +33,8 @@ {{- range $version := .SupportedVersions }} type configXmlContainer{{createGoSuffixFromVersion $version}} struct { - Answer []configXml{{createGoSuffixFromVersion $version}} `xml:"config"` + XMLName xml.Name `xml:"result"` + Answer []configXml{{createGoSuffixFromVersion $version}} `xml:"system"` } {{- end}} @@ -183,4 +180,21 @@ return true } -{{- end}} \ No newline at end of file +{{- end}} + +func (c configXml) MarshalXML(e *xml.Encoder, start xml.StartElement) error { +if c.NtpServers != nil { +start.Name = xml.Name{Local: "ntp-servers"} +if err := e.EncodeElement(c.NtpServers, start); err != nil { +return err +} +} + +for _, v := range c.Misc { +if err := e.Encode(v); err != nil { +return err +} +} + +return nil +} \ No newline at end of file diff --git a/templates/sdk/interfaces.tmpl b/templates/sdk/interfaces.tmpl index cac86e15..f8a0175f 100644 --- a/templates/sdk/interfaces.tmpl +++ b/templates/sdk/interfaces.tmpl @@ -1,7 +1,15 @@ package {{packageName .GoSdkPath}} -type Specifier func(Entry) (any, error) +{{- if .Entry}} + type Specifier func(Entry) (any, error) +{{- else}} + type Specifier func(Config) (any, error) +{{- end}} type Normalizer interface { -Normalize() ([]Entry, error) +{{- if .Entry}} + Normalize() ([]Entry, error) +{{- else}} + Normalize() ([]Config, error) +{{- end}} } \ No newline at end of file diff --git a/templates/sdk/location.tmpl b/templates/sdk/location.tmpl index 13c0e854..32a4ae2c 100644 --- a/templates/sdk/location.tmpl +++ b/templates/sdk/location.tmpl @@ -74,7 +74,9 @@ default: return nil, errors.NoLocationSpecifiedError } -ans = append(ans, Suffix...) +{{- if .Entry}} + ans = append(ans, Suffix...) +{{- end}} ans = append(ans, util.AsEntryXpath([]string{name})) return ans, nil From f284c78489395cb3f19c27ec09980013bbe61731 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 26 Mar 2024 14:30:08 +0100 Subject: [PATCH 54/66] Fix config template after tests for DNS --- pkg/generate/generator.go | 1 + pkg/translate/structs.go | 11 ++++++++++- templates/sdk/config.tmpl | 22 ++++++++++++++++------ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index d74cdad0..8fde676d 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -123,6 +123,7 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "locationType": translate.LocationType, "specParamType": translate.SpecParamType, "xmlParamType": translate.XmlParamType, + "xmlName": translate.XmlName, "xmlTag": translate.XmlTag, "specifyEntryAssignment": translate.SpecifyEntryAssignment, "normalizeAssignment": translate.NormalizeAssignment, diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index df85ad52..b3639ce1 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -102,6 +102,15 @@ func calculateNestedXmlSpecType(parent string, param *properties.SpecParam) stri return fmt.Sprintf("spec%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) } +// XmlName creates a string with xml name (e.g. `description`). +func XmlName(param *properties.SpecParam) string { + if param.Profiles != nil && len(param.Profiles) > 0 { + return param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1] + } + + return "" +} + // XmlTag creates a string with xml tag (e.g. `xml:"description,omitempty"`). func XmlTag(param *properties.SpecParam) string { if param.Profiles != nil && len(param.Profiles) > 0 { @@ -110,7 +119,7 @@ func XmlTag(param *properties.SpecParam) string { suffix = ",omitempty" } - return fmt.Sprintf("`xml:\"%s%s\"`", param.Profiles[0].Xpath[len(param.Profiles[0].Xpath)-1], suffix) + return fmt.Sprintf("`xml:\"%s%s\"`", XmlName(param), suffix) } return "" diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 6ff63e36..4dad7748 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -183,12 +183,22 @@ {{- end}} func (c configXml) MarshalXML(e *xml.Encoder, start xml.StartElement) error { -if c.NtpServers != nil { -start.Name = xml.Name{Local: "ntp-servers"} -if err := e.EncodeElement(c.NtpServers, start); err != nil { -return err -} -} +{{- range $_, $param := $.Spec.Params}} + if c.{{$param.Name.CamelCase}} != nil { + start.Name = xml.Name{Local: "{{xmlName $param}}"} + if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { + return err + } + } +{{- end}} +{{- range $_, $param := $.Spec.OneOf}} + if c.{{$param.Name.CamelCase}} != nil { + start.Name = xml.Name{Local: "{{xmlName $param}}"} + if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { + return err + } + } +{{- end}} for _, v := range c.Misc { if err := e.Encode(v); err != nil { From 337c2ed9d80533d012038603d664215b7ea1e795 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 26 Mar 2024 15:53:03 +0100 Subject: [PATCH 55/66] Remove CopyMiscFrom, SpecMatches from config template --- templates/sdk/config.tmpl | 81 +++++++++++---------------------------- 1 file changed, 23 insertions(+), 58 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 4dad7748..cf077704 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -74,17 +74,6 @@ {{- end}} {{- end}} - func (e *Config) CopyMiscFrom(v *Config) { - if v == nil || len(v.Misc) == 0 { - return - } - - e.Misc = make(map[string][]generic.Xml) - for key := range v.Misc { - e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) - } - } - func Versioning(vn version.Number) (Specifier, Normalizer, error) { {{- $numberOfVersions := len .SupportedVersions }} {{- if gt $numberOfVersions 1}} @@ -155,56 +144,32 @@ } {{- end}} - func SpecMatches(a, b *Config) bool { - if a == nil && b != nil || a != nil && b == nil { - return false - } else if a == nil && b == nil { - return true - } - - // Don't compare Name. - {{- range $_, $param := .Spec.Params}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false + // MarshalXML customized implementation of XML marshal due to requirement to skip + as root for configuration settings + func (c configXml) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + {{- range $_, $param := $.Spec.Params}} + if c.{{$param.Name.CamelCase}} != nil { + start.Name = xml.Name{Local: "{{xmlName $param}}"} + if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { + return err + } } {{- end}} - {{- end}} - {{- range $_, $param := .Spec.OneOf}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false + {{- range $_, $param := $.Spec.OneOf}} + if c.{{$param.Name.CamelCase}} != nil { + start.Name = xml.Name{Local: "{{xmlName $param}}"} + if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { + return err + } } {{- end}} - {{- end}} - return true - } -{{- end}} - -func (c configXml) MarshalXML(e *xml.Encoder, start xml.StartElement) error { -{{- range $_, $param := $.Spec.Params}} - if c.{{$param.Name.CamelCase}} != nil { - start.Name = xml.Name{Local: "{{xmlName $param}}"} - if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { - return err - } - } -{{- end}} -{{- range $_, $param := $.Spec.OneOf}} - if c.{{$param.Name.CamelCase}} != nil { - start.Name = xml.Name{Local: "{{xmlName $param}}"} - if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { - return err - } - } -{{- end}} - -for _, v := range c.Misc { -if err := e.Encode(v); err != nil { -return err -} -} + for _, v := range c.Misc { + if err := e.Encode(v); err != nil { + return err + } + } -return nil -} \ No newline at end of file + return nil + } +{{- end}} \ No newline at end of file From 644db519f8ac0ecbe2795b8fb7bfeffb49d5ee34 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 26 Mar 2024 15:55:07 +0100 Subject: [PATCH 56/66] Add comment for MarshalXML in config template --- templates/sdk/config.tmpl | 47 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index cf077704..305d9336 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -144,32 +144,31 @@ } {{- end}} - // MarshalXML customized implementation of XML marshal due to requirement to skip - as root for configuration settings - func (c configXml) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - {{- range $_, $param := $.Spec.Params}} - if c.{{$param.Name.CamelCase}} != nil { - start.Name = xml.Name{Local: "{{xmlName $param}}"} - if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { - return err - } - } - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - if c.{{$param.Name.CamelCase}} != nil { - start.Name = xml.Name{Local: "{{xmlName $param}}"} - if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { - return err - } - } - {{- end}} - - for _, v := range c.Misc { - if err := e.Encode(v); err != nil { + // MarshalXML customized implementation of XML marshal due to requirement to skip 'system' as root for configuration settings + func (c configXml) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + {{- range $_, $param := $.Spec.Params}} + if c.{{$param.Name.CamelCase}} != nil { + start.Name = xml.Name{Local: "{{xmlName $param}}"} + if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { return err } } - - return nil + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + if c.{{$param.Name.CamelCase}} != nil { + start.Name = xml.Name{Local: "{{xmlName $param}}"} + if err := e.EncodeElement(c.{{$param.Name.CamelCase}}, start); err != nil { + return err } + } + {{- end}} + + for _, v := range c.Misc { + if err := e.Encode(v); err != nil { + return err + } + } + + return nil + } {{- end}} \ No newline at end of file From 9377a74d0a8ffd28f10c19f62d2a7fcb5c5e621a Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 27 Mar 2024 14:54:04 +0100 Subject: [PATCH 57/66] Working SpecMatches() (not refactored) --- pkg/generate/generator.go | 23 ++++++----- pkg/translate/funcs.go | 82 ++++++++++++++++++++++++++++++++++++- pkg/translate/funcs_test.go | 4 +- templates/sdk/entry.tmpl | 18 ++++---- 4 files changed, 102 insertions(+), 25 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 8fde676d..7b6d925a 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -119,17 +119,18 @@ func (c *Creator) createFile(filePath string) (*os.File, error) { func (c *Creator) parseTemplate(templateName string) (*template.Template, error) { templatePath := filepath.Join(c.TemplatesDir, templateName) funcMap := template.FuncMap{ - "packageName": translate.PackageName, - "locationType": translate.LocationType, - "specParamType": translate.SpecParamType, - "xmlParamType": translate.XmlParamType, - "xmlName": translate.XmlName, - "xmlTag": translate.XmlTag, - "specifyEntryAssignment": translate.SpecifyEntryAssignment, - "normalizeAssignment": translate.NormalizeAssignment, - "specMatchesFunction": translate.SpecMatchesFunction, - "omitEmpty": translate.OmitEmpty, - "contains": strings.Contains, + "packageName": translate.PackageName, + "locationType": translate.LocationType, + "specParamType": translate.SpecParamType, + "xmlParamType": translate.XmlParamType, + "xmlName": translate.XmlName, + "xmlTag": translate.XmlTag, + "specifyEntryAssignment": translate.SpecifyEntryAssignment, + "normalizeAssignment": translate.NormalizeAssignment, + "specMatchesFunction": translate.SpecMatchesFunction, + "nestedSpecMatchesFunction": translate.NestedSpecMatchesFunction, + "omitEmpty": translate.OmitEmpty, + "contains": strings.Contains, "subtract": func(a, b int) int { return a - b }, diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index b370f51f..9dd69da1 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -128,8 +128,86 @@ func defineNestedObjectForChildParams(parent []string, params map[string]*proper // SpecMatchesFunction return a string used in function SpecMatches() in entry.tmpl/config.tmpl // to compare all items of generated entry. func SpecMatchesFunction(param *properties.SpecParam) string { + return specMatchFunctionName([]string{}, param) +} + +func specMatchFunctionName(parent []string, param *properties.SpecParam) string { if param.Type == "list" { - return "OrderedListsMatch" + return "util.OrderedListsMatch" + } else if param.Type == "string" { + return "util.OptionalStringsMatch" + } else { + return fmt.Sprintf("specMatch%s%s", strings.Join(parent, ""), param.Name.CamelCase) + } +} + +// NestedSpecMatchesFunction .......... +func NestedSpecMatchesFunction(spec *properties.Spec) string { + var builder strings.Builder + + defineSpecMatchesFunction([]string{}, spec.Params, &builder) + defineSpecMatchesFunction([]string{}, spec.OneOf, &builder) + + return builder.String() +} + +func defineSpecMatchesFunction(parent []string, params map[string]*properties.SpecParam, builder *strings.Builder) { + for _, param := range params { + if param.Spec != nil { + defineSpecMatchesFunction(append(parent, param.Name.CamelCase), param.Spec.Params, builder) + defineSpecMatchesFunction(append(parent, param.Name.CamelCase), param.Spec.OneOf, builder) + + builder.WriteString(fmt.Sprintf("func specMatch%s%s(a *%s, b *%s) bool {", + strings.Join(parent, ""), param.Name.CamelCase, + argumentTypeForSpecMatchesFunction(parent, param), + argumentTypeForSpecMatchesFunction(parent, param))) + + builder.WriteString("if a == nil && b != nil || a != nil && b == nil {\n") + builder.WriteString(" return false\n") + builder.WriteString("} else if a == nil && b == nil {\n") + builder.WriteString(" return true\n") + builder.WriteString("}\n") + + for _, subparam := range param.Spec.Params { + builder.WriteString(fmt.Sprintf("if !%s(a.%s, b.%s) {\n", + specMatchFunctionName(append(parent, param.Name.CamelCase), subparam), subparam.Name.CamelCase, subparam.Name.CamelCase)) + builder.WriteString(" return false\n") + builder.WriteString("}\n") + } + for _, subparam := range param.Spec.OneOf { + builder.WriteString(fmt.Sprintf("if !%s(a.%s, b.%s) {\n", + specMatchFunctionName(append(parent, param.Name.CamelCase), subparam), subparam.Name.CamelCase, subparam.Name.CamelCase)) + builder.WriteString(" return false\n") + builder.WriteString("}\n") + } + + builder.WriteString("return true\n") + builder.WriteString("}\n") + } else if param.Type != "list" && param.Type != "string" { + builder.WriteString(fmt.Sprintf("func specMatch%s%s(a *%s, b *%s) bool {", + strings.Join(parent, ""), param.Name.CamelCase, + argumentTypeForSpecMatchesFunction(parent, param), + argumentTypeForSpecMatchesFunction(parent, param))) + + builder.WriteString("if a == nil && b != nil || a != nil && b == nil {\n") + builder.WriteString(" return false\n") + builder.WriteString("} else if a == nil && b == nil {\n") + builder.WriteString(" return true\n") + builder.WriteString("}\n") + + builder.WriteString("return *a == *b\n") + builder.WriteString("}\n") + } + } +} + +func argumentTypeForSpecMatchesFunction(parent []string, param *properties.SpecParam) string { + if param.Type == "bool" { + return "bool" + } else if param.Type == "int" { + return "int" + } else { + return fmt.Sprintf("Spec%s%s", + strings.Join(parent, ""), param.Name.CamelCase) } - return "OptionalStringsMatch" } diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index 4bdbae8f..db29c374 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -173,6 +173,6 @@ func TestSpecMatchesFunction(t *testing.T) { calculatedSpecMatchFunctionListString := SpecMatchesFunction(¶mTypeListString) // then - assert.Equal(t, "OptionalStringsMatch", calculatedSpecMatchFunctionString) - assert.Equal(t, "OrderedListsMatch", calculatedSpecMatchFunctionListString) + assert.Equal(t, "util.OptionalStringsMatch", calculatedSpecMatchFunctionString) + assert.Equal(t, "util.OrderedListsMatch", calculatedSpecMatchFunctionListString) } diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 6ad94404..6ff6a683 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -208,20 +208,18 @@ // Don't compare Name. {{- range $_, $param := .Spec.Params}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} + if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false + } {{- end}} {{- range $_, $param := .Spec.OneOf}} - {{- if or (eq $param.Type "list") (eq $param.Type "string")}} - if !util.{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} + if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { + return false + } {{- end}} return true } + + {{nestedSpecMatchesFunction $.Spec}} {{- end}} \ No newline at end of file From 0877c798d91996c8915dd5861ccd25d67c2e8107 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 27 Mar 2024 16:59:43 +0100 Subject: [PATCH 58/66] Refactor code to render SpecMatches() --- pkg/translate/funcs.go | 62 ++++++++++++++++----------------- pkg/translate/funcs_test.go | 68 +++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 31 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 9dd69da1..3fd2f3b1 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -141,7 +141,7 @@ func specMatchFunctionName(parent []string, param *properties.SpecParam) string } } -// NestedSpecMatchesFunction .......... +// NestedSpecMatchesFunction return a string with body of specMach* functions required for nested params func NestedSpecMatchesFunction(spec *properties.Spec) string { var builder strings.Builder @@ -157,43 +157,21 @@ func defineSpecMatchesFunction(parent []string, params map[string]*properties.Sp defineSpecMatchesFunction(append(parent, param.Name.CamelCase), param.Spec.Params, builder) defineSpecMatchesFunction(append(parent, param.Name.CamelCase), param.Spec.OneOf, builder) - builder.WriteString(fmt.Sprintf("func specMatch%s%s(a *%s, b *%s) bool {", - strings.Join(parent, ""), param.Name.CamelCase, - argumentTypeForSpecMatchesFunction(parent, param), - argumentTypeForSpecMatchesFunction(parent, param))) + renderSpecMatchesFunctionNameWithArguments(parent, builder, param) + checkInSpecMatchesFunctionIfVariablesAreNil(builder) - builder.WriteString("if a == nil && b != nil || a != nil && b == nil {\n") - builder.WriteString(" return false\n") - builder.WriteString("} else if a == nil && b == nil {\n") - builder.WriteString(" return true\n") - builder.WriteString("}\n") - - for _, subparam := range param.Spec.Params { - builder.WriteString(fmt.Sprintf("if !%s(a.%s, b.%s) {\n", - specMatchFunctionName(append(parent, param.Name.CamelCase), subparam), subparam.Name.CamelCase, subparam.Name.CamelCase)) - builder.WriteString(" return false\n") - builder.WriteString("}\n") + for _, subParam := range param.Spec.Params { + renderInSpecMatchesFunctionIfToCheckIfVariablesMatches(parent, builder, param, subParam) } - for _, subparam := range param.Spec.OneOf { - builder.WriteString(fmt.Sprintf("if !%s(a.%s, b.%s) {\n", - specMatchFunctionName(append(parent, param.Name.CamelCase), subparam), subparam.Name.CamelCase, subparam.Name.CamelCase)) - builder.WriteString(" return false\n") - builder.WriteString("}\n") + for _, subParam := range param.Spec.OneOf { + renderInSpecMatchesFunctionIfToCheckIfVariablesMatches(parent, builder, param, subParam) } builder.WriteString("return true\n") builder.WriteString("}\n") } else if param.Type != "list" && param.Type != "string" { - builder.WriteString(fmt.Sprintf("func specMatch%s%s(a *%s, b *%s) bool {", - strings.Join(parent, ""), param.Name.CamelCase, - argumentTypeForSpecMatchesFunction(parent, param), - argumentTypeForSpecMatchesFunction(parent, param))) - - builder.WriteString("if a == nil && b != nil || a != nil && b == nil {\n") - builder.WriteString(" return false\n") - builder.WriteString("} else if a == nil && b == nil {\n") - builder.WriteString(" return true\n") - builder.WriteString("}\n") + renderSpecMatchesFunctionNameWithArguments(parent, builder, param) + checkInSpecMatchesFunctionIfVariablesAreNil(builder) builder.WriteString("return *a == *b\n") builder.WriteString("}\n") @@ -201,6 +179,28 @@ func defineSpecMatchesFunction(parent []string, params map[string]*properties.Sp } } +func renderSpecMatchesFunctionNameWithArguments(parent []string, builder *strings.Builder, param *properties.SpecParam) { + builder.WriteString(fmt.Sprintf("func specMatch%s%s(a *%s, b *%s) bool {", + strings.Join(parent, ""), param.Name.CamelCase, + argumentTypeForSpecMatchesFunction(parent, param), + argumentTypeForSpecMatchesFunction(parent, param))) +} + +func checkInSpecMatchesFunctionIfVariablesAreNil(builder *strings.Builder) { + builder.WriteString("if a == nil && b != nil || a != nil && b == nil {\n") + builder.WriteString(" return false\n") + builder.WriteString("} else if a == nil && b == nil {\n") + builder.WriteString(" return true\n") + builder.WriteString("}\n") +} + +func renderInSpecMatchesFunctionIfToCheckIfVariablesMatches(parent []string, builder *strings.Builder, param *properties.SpecParam, subparam *properties.SpecParam) { + builder.WriteString(fmt.Sprintf("if !%s(a.%s, b.%s) {\n", + specMatchFunctionName(append(parent, param.Name.CamelCase), subparam), subparam.Name.CamelCase, subparam.Name.CamelCase)) + builder.WriteString(" return false\n") + builder.WriteString("}\n") +} + func argumentTypeForSpecMatchesFunction(parent []string, param *properties.SpecParam) string { if param.Type == "bool" { return "bool" diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index db29c374..e4b60ca5 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -176,3 +176,71 @@ func TestSpecMatchesFunction(t *testing.T) { assert.Equal(t, "util.OptionalStringsMatch", calculatedSpecMatchFunctionString) assert.Equal(t, "util.OrderedListsMatch", calculatedSpecMatchFunctionListString) } + +func TestNestedSpecMatchesFunction(t *testing.T) { + // given + spec := properties.Spec{ + Params: map[string]*properties.SpecParam{ + "a": { + Name: &properties.NameVariant{ + Underscore: "a", + CamelCase: "A", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "b": { + Name: &properties.NameVariant{ + Underscore: "b", + CamelCase: "B", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "c": { + Name: &properties.NameVariant{ + Underscore: "c", + CamelCase: "C", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + expectedNestedSpec := `func specMatchABC(a *SpecABC, b *SpecABC) bool {if a == nil && b != nil || a != nil && b == nil { + return false +} else if a == nil && b == nil { + return true +} +return *a == *b +} +func specMatchAB(a *SpecAB, b *SpecAB) bool {if a == nil && b != nil || a != nil && b == nil { + return false +} else if a == nil && b == nil { + return true +} +if !specMatchABC(a.C, b.C) { + return false +} +return true +} +func specMatchA(a *SpecA, b *SpecA) bool {if a == nil && b != nil || a != nil && b == nil { + return false +} else if a == nil && b == nil { + return true +} +if !specMatchAB(a.B, b.B) { + return false +} +return true +} +` + + // when + renderedNestedSpecMatches := NestedSpecMatchesFunction(&spec) + + // then + assert.Equal(t, expectedNestedSpec, renderedNestedSpecMatches) +} From b78ffd02819ab7771adb2182a58db5b557d67e76 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Wed, 27 Mar 2024 17:28:41 +0100 Subject: [PATCH 59/66] Fix versioning after more tests --- pkg/translate/funcs.go | 38 ++++++++++++++++++++++---------------- templates/sdk/entry.tmpl | 24 ++++++++++++++++++++---- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 3fd2f3b1..021951d4 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -43,7 +43,11 @@ func prepareAssignment(objectType string, param *properties.SpecParam, listFunct if ParamSupportedInVersion(param, version) { if param.Spec != nil { - appendSpecObjectAssignment(param, objectType, specPrefix, specSuffix, &builder) + if specSuffix == "Xml" { + appendSpecObjectAssignment(param, objectType, version, specPrefix, specSuffix, &builder) + } else { + appendSpecObjectAssignment(param, objectType, "", specPrefix, specSuffix, &builder) + } } else if isParamListAndProfileTypeIsMember(param) { appendListFunctionAssignment(param, objectType, listFunction, &builder) } else { @@ -66,37 +70,39 @@ func appendListFunctionAssignment(param *properties.SpecParam, objectType string builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s)", objectType, param.Name.CamelCase, listFunction, param.Name.CamelCase)) } -func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, prefix, suffix string, builder *strings.Builder) { - defineNestedObject([]string{param.Name.CamelCase}, param, objectType, prefix, suffix, builder) +func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, version, prefix, suffix string, builder *strings.Builder) { + defineNestedObject([]string{param.Name.CamelCase}, param, objectType, version, prefix, suffix, builder) builder.WriteString(fmt.Sprintf("%s.%s = nested%s\n", objectType, param.Name.CamelCase, param.Name.CamelCase)) } -func defineNestedObject(parent []string, param *properties.SpecParam, objectType string, prefix, suffix string, builder *strings.Builder) { - declareRootOfNestedObject(parent, builder, prefix, suffix) +func defineNestedObject(parent []string, param *properties.SpecParam, objectType string, version, prefix, suffix string, builder *strings.Builder) { + declareRootOfNestedObject(parent, builder, version, prefix, suffix) builder.WriteString(fmt.Sprintf("if o.%s != nil {\n", strings.Join(parent, "."))) if param.Spec != nil { - assignEmptyStructForNestedObject(parent, builder, prefix, suffix) - defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, prefix, suffix, builder) - defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, prefix, suffix, builder) + assignEmptyStructForNestedObject(parent, builder, version, prefix, suffix) + defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, version, prefix, suffix, builder) + defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, version, prefix, suffix, builder) } else { assignValueForNestedObject(parent, builder) } builder.WriteString("}\n") } -func declareRootOfNestedObject(parent []string, builder *strings.Builder, prefix, suffix string) { +func declareRootOfNestedObject(parent []string, builder *strings.Builder, version, prefix, suffix string) { if len(parent) == 1 { - builder.WriteString(fmt.Sprintf("nested%s := &%s%s%s{}\n", + builder.WriteString(fmt.Sprintf("nested%s := &%s%s%s%s{}\n", strings.Join(parent, "."), prefix, - strings.Join(parent, ""), suffix)) + strings.Join(parent, ""), suffix, + CreateGoSuffixFromVersion(version))) } } -func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, prefix, suffix string) { +func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, version, prefix, suffix string) { if len(parent) > 1 { - builder.WriteString(fmt.Sprintf("nested%s = &%s%s%s{}\n", - strings.Join(parent, "."), prefix, strings.Join(parent, ""), suffix)) + builder.WriteString(fmt.Sprintf("nested%s = &%s%s%s%s{}\n", + strings.Join(parent, "."), prefix, strings.Join(parent, ""), suffix, + CreateGoSuffixFromVersion(version))) builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", strings.Join(parent, "."))) @@ -119,9 +125,9 @@ func assignValueForNestedObject(parent []string, builder *strings.Builder) { strings.Join(parent, "."))) } -func defineNestedObjectForChildParams(parent []string, params map[string]*properties.SpecParam, objectType string, prefix, suffix string, builder *strings.Builder) { +func defineNestedObjectForChildParams(parent []string, params map[string]*properties.SpecParam, objectType string, version, prefix, suffix string, builder *strings.Builder) { for _, param := range params { - defineNestedObject(append(parent, param.Name.CamelCase), param, objectType, prefix, suffix, builder) + defineNestedObject(append(parent, param.Name.CamelCase), param, objectType, version, prefix, suffix, builder) } } diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 6ff6a683..3efbb0e4 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -60,12 +60,20 @@ Name string `xml:"name,attr"` {{- range $_, $param := $.Spec.Params}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}} {{- range $_, $param := $.Spec.OneOf}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}} @@ -78,12 +86,20 @@ type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { {{- range $_, $param := $spec.Params}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}} {{- range $_, $param := $spec.OneOf}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}} From 782d82907e7360f9d8e577a4315bcc27669b35ad Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Thu, 28 Mar 2024 16:02:41 +0100 Subject: [PATCH 60/66] Fix Misc map[string][]generic.Xml --- pkg/translate/funcs.go | 20 ++++++++++++-------- pkg/translate/funcs_test.go | 6 +++--- templates/sdk/config.tmpl | 2 -- templates/sdk/entry.tmpl | 2 -- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 021951d4..004cfe59 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -80,7 +80,7 @@ func defineNestedObject(parent []string, param *properties.SpecParam, objectType builder.WriteString(fmt.Sprintf("if o.%s != nil {\n", strings.Join(parent, "."))) if param.Spec != nil { - assignEmptyStructForNestedObject(parent, builder, version, prefix, suffix) + assignEmptyStructForNestedObject(parent, builder, objectType, version, prefix, suffix) defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, version, prefix, suffix, builder) defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, version, prefix, suffix, builder) } else { @@ -98,21 +98,23 @@ func declareRootOfNestedObject(parent []string, builder *strings.Builder, versio } } -func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, version, prefix, suffix string) { +func assignEmptyStructForNestedObject(parent []string, builder *strings.Builder, objectType, version, prefix, suffix string) { if len(parent) > 1 { builder.WriteString(fmt.Sprintf("nested%s = &%s%s%s%s{}\n", strings.Join(parent, "."), prefix, strings.Join(parent, ""), suffix, CreateGoSuffixFromVersion(version))) - builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", - strings.Join(parent, "."))) if suffix == "Xml" { - builder.WriteString(fmt.Sprintf("nested%s.Misc = o.%s.Misc[\"%s\"]\n", - strings.Join(parent, "."), strings.Join(parent, "."), strings.Join(parent, ""), + builder.WriteString(fmt.Sprintf("if _, ok := o.Misc[\"%s\"]; ok {\n", + strings.Join(parent, ""))) + builder.WriteString(fmt.Sprintf("nested%s.Misc = o.Misc[\"%s\"]\n", + strings.Join(parent, "."), strings.Join(parent, ""), )) } else { - builder.WriteString(fmt.Sprintf("nested%s.Misc[\"%s\"] = o.%s.Misc\n", - strings.Join(parent, "."), strings.Join(parent, ""), strings.Join(parent, "."), + builder.WriteString(fmt.Sprintf("if o.%s.Misc != nil {\n", + strings.Join(parent, "."))) + builder.WriteString(fmt.Sprintf("%s.Misc[\"%s\"] = o.%s.Misc\n", + objectType, strings.Join(parent, ""), strings.Join(parent, "."), )) } builder.WriteString("}\n") @@ -176,6 +178,8 @@ func defineSpecMatchesFunction(parent []string, params map[string]*properties.Sp builder.WriteString("return true\n") builder.WriteString("}\n") } else if param.Type != "list" && param.Type != "string" { + // whole section should be removed, when there will be dedicated function to compare integers + // in file https://github.com/PaloAltoNetworks/pango/blob/develop/util/comparison.go renderSpecMatchesFunctionNameWithArguments(parent, builder, param) checkInSpecMatchesFunctionIfVariablesAreNil(builder) diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index e4b60ca5..a9eb00b6 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -89,8 +89,8 @@ func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { if o.A != nil { if o.A.B != nil { nestedA.B = &specABXml{} -if o.A.B.Misc != nil { -nestedA.B.Misc = o.A.B.Misc["AB"] +if _, ok := o.Misc["AB"]; ok { +nestedA.B.Misc = o.Misc["AB"] } if o.A.B.C != nil { nestedA.B.C = o.A.B.C @@ -143,7 +143,7 @@ if o.A != nil { if o.A.B != nil { nestedA.B = &SpecAB{} if o.A.B.Misc != nil { -nestedA.B.Misc["AB"] = o.A.B.Misc +entry.Misc["AB"] = o.A.B.Misc } if o.A.B.C != nil { nestedA.B.C = o.A.B.C diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 305d9336..4df15e13 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -26,8 +26,6 @@ {{- range $_, $param := $spec.OneOf}} {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} - - Misc map[string][]generic.Xml } {{- end}} diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 3efbb0e4..f6d79a69 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -43,8 +43,6 @@ {{- range $_, $param := $spec.OneOf}} {{$param.Name.CamelCase}} {{specParamType $name $param}} {{- end}} - - Misc map[string][]generic.Xml } {{- end}} From 583b48df063c43819cc266ac9002558de9f0d161 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Thu, 28 Mar 2024 16:14:19 +0100 Subject: [PATCH 61/66] Make specify functions internal (we have multiple versions of specify functions) --- templates/sdk/config.tmpl | 8 ++++---- templates/sdk/entry.tmpl | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 4df15e13..ae0e67e5 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -87,23 +87,23 @@ {{- if ne $version ""}} {{- if eq $index 1}} if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return specifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- else}} } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return specifyConfig{{createGoSuffixFromVersion $version}}, &configXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- end}} {{- end}} {{- end}} } else { {{- end}} - return SpecifyConfig, &configXmlContainer{}, nil + return specifyConfig, &configXmlContainer{}, nil {{- if gt $numberOfVersions 1}} } {{- end}} } {{- range $version := .SupportedVersions }} - func SpecifyConfig{{createGoSuffixFromVersion $version}}(o Config) (any, error) { + func specifyConfig{{createGoSuffixFromVersion $version}}(o Config) (any, error) { config := configXml{{createGoSuffixFromVersion $version}}{} {{- range $_, $param := $.Spec.Params}} diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index f6d79a69..370923a6 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -156,23 +156,23 @@ {{- if ne $version ""}} {{- if eq $index 1}} if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return specifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- else}} } else if vn.Gte(version{{createGoSuffixFromVersion $version}}) { - return SpecifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil + return specifyEntry{{createGoSuffixFromVersion $version}}, &entryXmlContainer{{createGoSuffixFromVersion $version}}{}, nil {{- end}} {{- end}} {{- end}} } else { {{- end}} - return SpecifyEntry, &entryXmlContainer{}, nil + return specifyEntry, &entryXmlContainer{}, nil {{- if gt $numberOfVersions 1}} } {{- end}} } {{- range $version := .SupportedVersions }} - func SpecifyEntry{{createGoSuffixFromVersion $version}}(o Entry) (any, error) { + func specifyEntry{{createGoSuffixFromVersion $version}}(o Entry) (any, error) { entry := entryXml{{createGoSuffixFromVersion $version}}{} entry.Name = o.Name From b3318656078ee6932952898d03364be9e528897a Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Fri, 29 Mar 2024 11:47:53 +0100 Subject: [PATCH 62/66] Remove CopyMiscFrom(), fix issues with nested & versioned params --- pkg/translate/funcs.go | 18 ++++++++++-------- templates/sdk/entry.tmpl | 11 ----------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 004cfe59..902434bc 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -78,15 +78,17 @@ func appendSpecObjectAssignment(param *properties.SpecParam, objectType string, func defineNestedObject(parent []string, param *properties.SpecParam, objectType string, version, prefix, suffix string, builder *strings.Builder) { declareRootOfNestedObject(parent, builder, version, prefix, suffix) - builder.WriteString(fmt.Sprintf("if o.%s != nil {\n", strings.Join(parent, "."))) - if param.Spec != nil { - assignEmptyStructForNestedObject(parent, builder, objectType, version, prefix, suffix) - defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, version, prefix, suffix, builder) - defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, version, prefix, suffix, builder) - } else { - assignValueForNestedObject(parent, builder) + if ParamSupportedInVersion(param, version) { + builder.WriteString(fmt.Sprintf("if o.%s != nil {\n", strings.Join(parent, "."))) + if param.Spec != nil { + assignEmptyStructForNestedObject(parent, builder, objectType, version, prefix, suffix) + defineNestedObjectForChildParams(parent, param.Spec.Params, objectType, version, prefix, suffix, builder) + defineNestedObjectForChildParams(parent, param.Spec.OneOf, objectType, version, prefix, suffix, builder) + } else { + assignValueForNestedObject(parent, builder) + } + builder.WriteString("}\n") } - builder.WriteString("}\n") } func declareRootOfNestedObject(parent []string, builder *strings.Builder, version, prefix, suffix string) { diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 370923a6..acea588e 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -106,17 +106,6 @@ {{- end}} {{- end}} - func (e *Entry) CopyMiscFrom(v *Entry) { - if v == nil || len(v.Misc) == 0 { - return - } - - e.Misc = make(map[string][]generic.Xml) - for key := range v.Misc { - e.Misc[key] = append([]generic.Xml(nil), v.Misc[key]...) - } - } - func (e *Entry) Field(v string) (any, error) { if v == "name" || v == "Name" { return e.Name, nil From f86c6602e76715c7969c4241e9a2bee4c7052845 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 2 Apr 2024 10:32:07 +0200 Subject: [PATCH 63/66] Fixes in location.tmpl after tests while building service.go --- templates/sdk/location.tmpl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/templates/sdk/location.tmpl b/templates/sdk/location.tmpl index 32a4ae2c..e23bebde 100644 --- a/templates/sdk/location.tmpl +++ b/templates/sdk/location.tmpl @@ -48,7 +48,11 @@ return fmt.Errorf("multiple paths specified: only one should be specified") return nil } -func (o Location) Xpath(vn version.Number, name string) ([]string, error) { +{{- if .Entry}} + func (o Location) Xpath(vn version.Number, name string) ([]string, error) { +{{- else}} + func (o Location) Xpath(vn version.Number) ([]string, error) { +{{- end}} var ans []string @@ -76,8 +80,8 @@ return nil, errors.NoLocationSpecifiedError {{- if .Entry}} ans = append(ans, Suffix...) + ans = append(ans, util.AsEntryXpath([]string{name})) {{- end}} -ans = append(ans, util.AsEntryXpath([]string{name})) return ans, nil } \ No newline at end of file From 47f7d071caa6450e36bae87a06d50bb19d0587de Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 2 Apr 2024 13:36:41 +0200 Subject: [PATCH 64/66] Render service.go for entry and config --- pkg/generate/generator.go | 1 + pkg/translate/funcs.go | 11 + pkg/translate/funcs_test.go | 24 ++ templates/sdk/service.tmpl | 562 +++++++++++++++++++++++++++++++++++- 4 files changed, 597 insertions(+), 1 deletion(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 7b6d925a..bf7e8902 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -138,6 +138,7 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "nestedSpecs": translate.NestedSpecs, "createGoSuffixFromVersion": translate.CreateGoSuffixFromVersion, "paramSupportedInVersion": translate.ParamSupportedInVersion, + "xmlPathSuffix": translate.XmlPathSuffix, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 902434bc..24e1e6a8 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -223,3 +223,14 @@ func argumentTypeForSpecMatchesFunction(parent []string, param *properties.SpecP strings.Join(parent, ""), param.Name.CamelCase) } } + +// XmlPathSuffix return XML path suffixes created from profiles. +func XmlPathSuffix(param *properties.SpecParam) []string { + xmlPathSuffixes := []string{} + if param.Profiles != nil { + for _, profile := range param.Profiles { + xmlPathSuffixes = append(xmlPathSuffixes, strings.Join(profile.Xpath, "/")) + } + } + return xmlPathSuffixes +} diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index a9eb00b6..d5ed4bb6 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -244,3 +244,27 @@ return true // then assert.Equal(t, expectedNestedSpec, renderedNestedSpecMatches) } + +func TestXmlPathSuffix(t *testing.T) { + // given + spec := properties.Spec{ + Params: map[string]*properties.SpecParam{ + "a": { + Profiles: []*properties.SpecParamProfile{{ + Xpath: []string{"test", "a"}, + }}, + Name: &properties.NameVariant{ + Underscore: "a", + CamelCase: "A", + }, + }, + }, + } + expectedXpathSuffixes := []string{"test/a"} + + // when + actualXpathSuffixes := XmlPathSuffix(spec.Params["a"]) + + // then + assert.Equal(t, expectedXpathSuffixes, actualXpathSuffixes) +} diff --git a/templates/sdk/service.tmpl b/templates/sdk/service.tmpl index 3b3d1518..8152d967 100644 --- a/templates/sdk/service.tmpl +++ b/templates/sdk/service.tmpl @@ -1 +1,561 @@ -package {{packageName .GoSdkPath}} \ No newline at end of file +package {{packageName .GoSdkPath}} +{{- if .Entry}} + import ( + "context" + "fmt" + + "github.com/PaloAltoNetworks/pango/errors" + "github.com/PaloAltoNetworks/pango/filtering" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/xmlapi" + ) + + type Service struct { + client util.PangoClient + } + + func NewService(client util.PangoClient) *Service { + return &Service{ + client: client, + } + } + + // Create adds new entry, then returns the result. + func (s *Service) Create(ctx context.Context, loc Location, entry Entry) (*Entry, error) { + if entry.Name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + + specifier, _, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + createSpec, err := specifier(entry) + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: "set", + Xpath: util.AsXpath(path[:len(path)-1]), + Element: createSpec, + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, false, nil); err != nil { + return nil, err + } + + return s.Read(ctx, loc, entry.Name, "get") + } + + // Read returns the given config object, using the specified action ("get" or "show"). + func (s *Service) Read(ctx context.Context, loc Location, name, action string) (*Entry, error) { + if name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, name) + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + if err.Error() == "No such node" && action == "show" { + return nil, errors.ObjectNotFound() + } + return nil, err + } + + list, err := normalizer.Normalize() + if err != nil { + return nil, err + } else if len(list) != 1 { + return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) + } + + return &list[0], nil + } + + // ReadFromConfig returns the given config object from the loaded XML config. + // Requires that client.LoadPanosConfig() has been invoked. + func (s *Service) ReadFromConfig(ctx context.Context, loc Location, name string) (*Entry, error) { + if name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, name) + if err != nil { + return nil, err + } + + if _, err = s.client.ReadFromConfig(ctx, path, true, normalizer); err != nil { + return nil, err + } + + list, err := normalizer.Normalize() + if err != nil { + return nil, err + } else if len(list) != 1 { + return nil, fmt.Errorf("expected to find 1 entry, got %d", len(list)) + } + + return &list[0], nil + } + + // Update updates the given config object, then returns the result. + func (s *Service) Update(ctx context.Context, loc Location, entry Entry, oldName string) (*Entry, error) { + if entry.Name == "" { + return nil, errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + updates := xmlapi.NewMultiConfig(2) + specifier, _, err := Versioning(vn) + if err != nil { + return nil, err + } + + var old *Entry + if oldName != "" && oldName != entry.Name { + path, err := loc.Xpath(vn, oldName) + if err != nil { + return nil, err + } + + old, err = s.Read(ctx, loc, oldName, "get") + + updates.Add(&xmlapi.Config{ + Action: "rename", + Xpath: util.AsXpath(path), + NewName: entry.Name, + }) + } else { + old, err = s.Read(ctx, loc, entry.Name, "get") + } + if err != nil { + return nil, err + } + + if !SpecMatches(&entry, old) { + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + updateSpec, err := specifier(entry) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "edit", + Xpath: util.AsXpath(path), + Element: updateSpec, + }) + } + + if len(updates.Operations) != 0 { + if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { + return nil, err + } + } + + return s.Read(ctx, loc, entry.Name, "get") + } + + // Delete deletes the given item. + func (s *Service) Delete(ctx context.Context, loc Location, name string) error { + if name == "" { + return errors.NameNotSpecifiedError + } + + vn := s.client.Versioning() + + path, err := loc.Xpath(vn, name) + if err != nil { + return err + } + + cmd := &xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + _, _, err = s.client.Communicate(ctx, cmd, false, nil) + + return err + } + + // List returns a list of objects using the given action ("get" or "show"). + // Params filter and quote are for client side filtering. + func (s *Service) List(ctx context.Context, loc Location, action, filter, quote string) ([]Entry, error) { + var err error + + var logic *filtering.Group + if filter != "" { + logic, err = filtering.Parse(filter, quote) + if err != nil { + return nil, err + } + } + + vn := s.client.Versioning() + + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, "") + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + if err.Error() == "No such node" && action == "show" { + return nil, nil + } + return nil, err + } + + listing, err := normalizer.Normalize() + if err != nil || logic == nil { + return listing, err + } + + filtered := make([]Entry, 0, len(listing)) + for _, x := range listing { + ok, err := logic.Matches(&x) + if err != nil { + return nil, err + } + if ok { + filtered = append(filtered, x) + } + } + + return filtered, nil + } + + // ListFromConfig returns a list of objects at the given location. + // Requires that client.LoadPanosConfig() has been invoked. + // Params filter and quote are for client side filtering. + func (s *Service) ListFromConfig(ctx context.Context, loc Location, filter, quote string) ([]Entry, error) { + var err error + + var logic *filtering.Group + if filter != "" { + logic, err = filtering.Parse(filter, quote) + if err != nil { + return nil, err + } + } + + vn := s.client.Versioning() + + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn, "") + if err != nil { + return nil, err + } + path = path[:len(path)-1] + + if _, err = s.client.ReadFromConfig(ctx, path, false, normalizer); err != nil { + return nil, err + } + + listing, err := normalizer.Normalize() + if err != nil || logic == nil { + return listing, err + } + + filtered := make([]Entry, 0, len(listing)) + for _, x := range listing { + ok, err := logic.Matches(&x) + if err != nil { + return nil, err + } + if ok { + filtered = append(filtered, x) + } + } + + return filtered, nil + } + + // ConfigureGroup performs all necessary set / edit / delete commands to ensure that the objects are configured as specified. + func (s *Service) ConfigureGroup(ctx context.Context, loc Location, entries []Entry, prevNames []string) ([]Entry, error) { + var err error + + vn := s.client.Versioning() + updates := xmlapi.NewMultiConfig(len(prevNames) + len(entries)) + specifier, _, err := Versioning(vn) + if err != nil { + return nil, err + } + + curObjs, err := s.List(ctx, loc, "get", "", "") + if err != nil { + return nil, err + } + + for _, entry := range entries { + var found bool + for _, live := range curObjs { + if entry.Name == live.Name { + found = true + if !SpecMatches(&entry, &live) { + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + elm, err := specifier(entry) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "edit", + Xpath: util.AsXpath(path), + Element: elm, + }) + } + break + } + } + + if !found { + path, err := loc.Xpath(vn, entry.Name) + if err != nil { + return nil, err + } + + elm, err := specifier(entry) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "set", + Xpath: util.AsXpath(path), + Element: elm, + }) + } + } + + if len(prevNames) != 0 { + for _, name := range prevNames { + var found bool + for _, entry := range entries { + if entry.Name == name { + found = true + break + } + } + + if !found { + path, err := loc.Xpath(vn, name) + if err != nil { + return nil, err + } + + updates.Add(&xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(path), + }) + } + } + } + + if len(updates.Operations) != 0 { + if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { + return nil, err + } + } + + curObjs, err = s.List(ctx, loc, "get", "", "") + if err != nil { + return nil, err + } + + ans := make([]Entry, 0, len(entries)) + for _, entry := range entries { + for _, live := range curObjs { + if entry.Name == live.Name { + ans = append(ans, live) + break + } + } + } + + return ans, nil + } +{{- else}} + import ( + "context" + "fmt" + + "github.com/PaloAltoNetworks/pango/errors" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/xmlapi" + ) + + type Service struct { + client util.PangoClient + } + + func NewService(client util.PangoClient) *Service { + return &Service{ + client: client, + } + } + + // Create creates the given config object. + func (s *Service) Create(ctx context.Context, loc Location, config Config) (*Config, error) { + vn := s.client.Versioning() + + specifier, _, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn) + if err != nil { + return nil, err + } + + createSpec, err := specifier(config) + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: "set", + Xpath: util.AsXpath(path), + Element: createSpec, + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, false, nil); err != nil { + return nil, err + } + + return s.Read(ctx, loc, "get") + } + + // Read returns the given config object, using the specified action ("get" or "show"). + func (s *Service) Read(ctx context.Context, loc Location, action string) (*Config, error) { + vn := s.client.Versioning() + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + path, err := loc.Xpath(vn) + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(path), + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + if err.Error() == "No such node" && action == "show" { + return nil, errors.ObjectNotFound() + } + return nil, err + } + + list, err := normalizer.Normalize() + if err != nil { + return nil, err + } else if len(list) != 1 { + return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) + } + + return &list[0], nil + } + + // Delete deletes the given item. + func (s *Service) Delete(ctx context.Context, loc Location, config Config) error { + vn := s.client.Versioning() + + path, err := loc.Xpath(vn) + if err != nil { + return err + } + + deleteSuffixes := []string{ + {{- range $version := .SupportedVersions }} + {{- range $_, $param := $.Spec.Params}} + {{- if paramSupportedInVersion $param $version}} + {{- range $_, $profile := $param.Profiles}} + {{- range $_, $xpath := xmlPathSuffix $param}} + "{{$xpath}}", + {{- end}} + {{- end}} + {{- end}} + {{- end}} + {{- range $_, $param := $.Spec.OneOf}} + {{- end}} + {{- end}} + } + + for _, suffix := range deleteSuffixes { + cmd := &xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(append(path, suffix)), + Target: s.client.GetTarget(), + } + + _, _, err = s.client.Communicate(ctx, cmd, false, nil) + + if err != nil { + return err + } + } + return nil + } +{{- end}} \ No newline at end of file From b272b016c0d57578a3240f691a27811a664f8e09 Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 2 Apr 2024 14:46:17 +0200 Subject: [PATCH 65/66] Rename function XmlPathSuffixes() --- pkg/generate/generator.go | 2 +- pkg/translate/funcs.go | 4 ++-- pkg/translate/funcs_test.go | 4 ++-- templates/sdk/service.tmpl | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index bf7e8902..99a2aba3 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -138,7 +138,7 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "nestedSpecs": translate.NestedSpecs, "createGoSuffixFromVersion": translate.CreateGoSuffixFromVersion, "paramSupportedInVersion": translate.ParamSupportedInVersion, - "xmlPathSuffix": translate.XmlPathSuffix, + "xmlPathSuffixes": translate.XmlPathSuffixes, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/translate/funcs.go b/pkg/translate/funcs.go index 24e1e6a8..1d8fb9ab 100644 --- a/pkg/translate/funcs.go +++ b/pkg/translate/funcs.go @@ -224,8 +224,8 @@ func argumentTypeForSpecMatchesFunction(parent []string, param *properties.SpecP } } -// XmlPathSuffix return XML path suffixes created from profiles. -func XmlPathSuffix(param *properties.SpecParam) []string { +// XmlPathSuffixes return XML path suffixes created from profiles. +func XmlPathSuffixes(param *properties.SpecParam) []string { xmlPathSuffixes := []string{} if param.Profiles != nil { for _, profile := range param.Profiles { diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index d5ed4bb6..328bb066 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -245,7 +245,7 @@ return true assert.Equal(t, expectedNestedSpec, renderedNestedSpecMatches) } -func TestXmlPathSuffix(t *testing.T) { +func TestXmlPathSuffixes(t *testing.T) { // given spec := properties.Spec{ Params: map[string]*properties.SpecParam{ @@ -263,7 +263,7 @@ func TestXmlPathSuffix(t *testing.T) { expectedXpathSuffixes := []string{"test/a"} // when - actualXpathSuffixes := XmlPathSuffix(spec.Params["a"]) + actualXpathSuffixes := XmlPathSuffixes(spec.Params["a"]) // then assert.Equal(t, expectedXpathSuffixes, actualXpathSuffixes) diff --git a/templates/sdk/service.tmpl b/templates/sdk/service.tmpl index 8152d967..a81358bc 100644 --- a/templates/sdk/service.tmpl +++ b/templates/sdk/service.tmpl @@ -532,7 +532,7 @@ package {{packageName .GoSdkPath}} {{- range $_, $param := $.Spec.Params}} {{- if paramSupportedInVersion $param $version}} {{- range $_, $profile := $param.Profiles}} - {{- range $_, $xpath := xmlPathSuffix $param}} + {{- range $_, $xpath := xmlPathSuffixes $param}} "{{$xpath}}", {{- end}} {{- end}} From ec3c02ed388b74f4d4bd6fb5bbe4bd22c4de2c5c Mon Sep 17 00:00:00 2001 From: Sebastian Czech Date: Tue, 2 Apr 2024 15:11:34 +0200 Subject: [PATCH 66/66] Fix problem with nested specs and versions --- templates/sdk/config.tmpl | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index ae0e67e5..e959a9a2 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -40,12 +40,20 @@ type configXml{{createGoSuffixFromVersion $version}} struct { {{- range $_, $param := $.Spec.Params}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}} {{- range $_, $param := $.Spec.OneOf}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}} @@ -58,12 +66,20 @@ type spec{{$name}}Xml{{createGoSuffixFromVersion $version}} struct { {{- range $_, $param := $spec.Params}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}} {{- range $_, $param := $spec.OneOf}} {{- if paramSupportedInVersion $param $version}} - {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- if $param.Spec}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}}{{createGoSuffixFromVersion $version}} {{xmlTag $param}} + {{- else}} + {{$param.Name.CamelCase}} {{xmlParamType $name $param}} {{xmlTag $param}} + {{- end}} {{- end}} {{- end}}