Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Render entry.go (second stage) #34

Merged
merged 23 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6ea5f9d
add EntryXml struct, render all missing functions for entry.go, exten…
sebastianczech Mar 11, 2024
4d70add
start work on service.go
sebastianczech Mar 11, 2024
401a470
refactor names in template for entry.go
sebastianczech Mar 11, 2024
53084e3
add not_present and from_version to SpecParamProfile
sebastianczech Mar 11, 2024
c5ff1b4
current service is only prepared for entry
sebastianczech Mar 11, 2024
5da312c
clear service template
sebastianczech Mar 11, 2024
2e011a7
render entry.go only for specs with entry
sebastianczech Mar 11, 2024
8e301c0
Merge branch 'main' into render-entry
sebastianczech Mar 11, 2024
48db70c
create structs for nested specs
sebastianczech Mar 11, 2024
07ccc14
generate content of function SpecifyEntry for specification with nest…
sebastianczech Mar 12, 2024
60af14e
generate content of function Normalize for specification with nested …
sebastianczech Mar 12, 2024
917a301
add function comments
sebastianczech Mar 12, 2024
2939d86
replace all string concatenation using "+" by function fmt.Sprintf()
sebastianczech Mar 13, 2024
b51801b
for item with type object, render []string, which will contain all me…
sebastianczech Mar 13, 2024
6461db6
refactor NormalizeAssignment(), SpecifyEntryAssignment(), fix some of…
sebastianczech Mar 13, 2024
30bcb0d
extend template functions after tests for service.yaml
sebastianczech Mar 13, 2024
743ac40
apply changes after review (#1)
sebastianczech Mar 14, 2024
f0e47db
remove comments
sebastianczech Mar 14, 2024
a0edfa2
use fmt.Errorf to return errors
sebastianczech Mar 14, 2024
e55a5be
apply changes after review (#2)
sebastianczech Mar 14, 2024
f6e4f92
refactor SpecMatchesFunction()
sebastianczech Mar 14, 2024
4df52fa
refactor funcs.go
sebastianczech Mar 15, 2024
0ff2d59
refactor structs.go
sebastianczech Mar 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/codegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -24,6 +24,7 @@ func parseFlags() Config {
return cfg
}

// 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 {
Expand Down
7 changes: 5 additions & 2 deletions pkg/generate/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package generate

import (
"bytes"
"fmt"
"github.com/paloaltonetworks/pan-os-codegen/pkg/properties"
"io"
"io/fs"
Expand All @@ -10,6 +11,7 @@ import (
"path/filepath"
)

// CopyAssets copy assets (static files) according to configuration.
func CopyAssets(config *properties.Config) error {
for _, asset := range config.Assets {
files, err := listAssets(asset)
Expand All @@ -32,10 +34,10 @@ func CopyAssets(config *properties.Config) error {
return nil
}

// listAssets walk through directory and get list of all assets (static files).
func listAssets(asset *properties.Asset) ([]string, error) {
var files []string

// Walk through directory and get list of all files
err := filepath.WalkDir(asset.Source, func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
Expand All @@ -52,9 +54,10 @@ func listAssets(asset *properties.Asset) ([]string, error) {
return files, nil
}

// 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 {
Expand Down
56 changes: 38 additions & 18 deletions pkg/generate/generator.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package generate

import (
"bytes"
"fmt"
"io"
"log"
"os"
"path/filepath"
Expand All @@ -18,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,
Expand All @@ -26,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")

Expand All @@ -42,30 +46,40 @@ 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 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 {
pimielowski marked this conversation as resolved.
Show resolved Hide resolved
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...), 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.
func (c *Creator) listOfTemplates() ([]string, error) {
var files []string
err := filepath.WalkDir(c.TemplatesDir, func(path string, entry os.DirEntry, err error) error {
Expand All @@ -86,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 {
Expand All @@ -99,21 +115,25 @@ 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,
"omitEmpty": translate.OmitEmpty,
"contains": func(full, part string) bool {
return strings.Contains(full, part)
},
"packageName": translate.PackageName,
"locationType": translate.LocationType,
"specParamType": translate.SpecParamType,
"xmlParamType": translate.XmlParamType,
"xmlTag": translate.XmlTag,
"specifyEntryAssignment": translate.SpecifyEntryAssignment,
"normalizeAssignment": translate.NormalizeAssignment,
"specMatchesFunction": translate.SpecMatchesFunction,
"omitEmpty": translate.OmitEmpty,
"contains": strings.Contains,
sebastianczech marked this conversation as resolved.
Show resolved Hide resolved
"subtract": func(a, b int) int {
return a - b
},
"asEntryXpath": translate.AsEntryXpath,
"generateEntryXpath": translate.GenerateEntryXpathForLocation,
"nestedSpecs": translate.NestedSpecs,
}
return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath)
}
1 change: 1 addition & 0 deletions pkg/load/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ func File(path string) ([]byte, error) {
if err != nil {
return nil, err
}

return content, err
}
1 change: 1 addition & 0 deletions pkg/properties/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Target struct {
TerraformProvider bool `json:"terraform_provider" yaml:"terraform_provider"`
}

// ParseConfig initialize Config instance using input data from YAML file.
func ParseConfig(input []byte) (*Config, error) {
var ans Config
err := content.Unmarshal(input, &ans)
Expand Down
4 changes: 2 additions & 2 deletions pkg/properties/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ assets:
assert.NotEmptyf(t, config.Output.GoSdk, "Config Go SDK path cannot be empty")
assert.NotEmptyf(t, config.Output.TerraformProvider, "Config Terraform provider path cannot be empty")
assert.NotEmpty(t, config.Assets)
assert.Equal(t, 1, len(config.Assets))
assert.Equal(t, 1, len(config.Assets))
assert.Len(t, config.Assets, 1)
assert.Len(t, config.Assets, 1)
assert.Equal(t, "assets/util", config.Assets["util_package"].Source)
assert.True(t, config.Assets["util_package"].Target.GoSdk)
assert.False(t, config.Assets["util_package"].Target.TerraformProvider)
Expand Down
81 changes: 59 additions & 22 deletions pkg/properties/normalized.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -97,6 +96,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"`
pimielowski marked this conversation as resolved.
Show resolved Hide resolved
}

type SpecParamItemsLength struct {
Expand All @@ -105,10 +105,13 @@ type SpecParamItemsLength struct {
}

type SpecParamProfile struct {
Xpath []string `json:"xpath" yaml:"xpath"`
Type string `json:"type" yaml:"type,omitempty"`
Xpath []string `json:"xpath" yaml:"xpath"`
Type string `json:"type" yaml:"type,omitempty"`
NotPresent bool `json:"not_present" yaml:"not_present"`
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 {
Expand Down Expand Up @@ -137,18 +140,34 @@ 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

err := content.Unmarshal(input, &spec)
if err != nil {
sebastianczech marked this conversation as resolved.
Show resolved Hide resolved
return nil, err
}

err = spec.AddNameVariantsForLocation()
if err != nil {
return nil, err
}

err = spec.AddNameVariantsForParams()
if err != nil {
return nil, err
}

err = spec.AddDefaultTypesForParams()
if err != nil {
return nil, err
}

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{
Expand All @@ -167,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,
Expand All @@ -187,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 {
Expand All @@ -203,57 +224,73 @@ func (spec *Normalization) AddNameVariantsForParams() error {
return nil
}

// AddDefaultTypesForParams ensures all SpecParams within Spec have a default type if not specified.
func (spec *Normalization) AddDefaultTypesForParams() error {
if spec.Spec == nil {
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.Type = "string"
}

setDefaultParamTypeForMap(spec.Spec.Params)
setDefaultParamTypeForMap(spec.Spec.OneOf)
if param.Spec != nil {
if err := addDefaultTypesForParams(param.Spec.Params); err != nil {
return err
}
if err := addDefaultTypesForParams(param.Spec.OneOf); err != nil {
return err
}
}
}

return nil
}

// setDefaultParamTypeForMap iterates over a map of SpecParam pointers, setting their Type to "string" if not specified.
func setDefaultParamTypeForMap(params map[string]*SpecParam) {
for _, param := range params {
if param.Type == "" {
param.Type = "string"
// AddDefaultTypesForParams ensures all params within Spec have a default type if not specified.
func (spec *Normalization) AddDefaultTypesForParams() error {
sebastianczech marked this conversation as resolved.
Show resolved Hide resolved
if spec.Spec != nil {
if err := addDefaultTypesForParams(spec.Spec.Params); err != nil {
return err
}
if err := addDefaultTypesForParams(spec.Spec.OneOf); err != nil {
return err
}
return nil
} else {
return nil
}
}

// 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
}

// 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, 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
Expand Down
Loading
Loading