From e31e167e03c7156c39b67eac7f6e4aac2d8363f8 Mon Sep 17 00:00:00 2001 From: Peefy Date: Mon, 15 Jan 2024 17:48:11 +0800 Subject: [PATCH] refactor: enhance kcl file generation with imports and JSON/YAML data (#213) * refactor: enhance kcl file generation with imports and JSON/YAML data Signed-off-by: peefy * fix: TestGenKclFromJsonAndImports tests on windows Signed-off-by: peefy --------- Signed-off-by: peefy --- pkg/tools/gen/genkcl_jsonschema.go | 3 +- pkg/tools/gen/genkcl_terraform.go | 6 +- pkg/tools/gen/genkcl_test.go | 69 ++++++++++++++++++++++- pkg/tools/gen/genkcl_yaml.go | 24 +++++--- pkg/tools/gen/templates/kcl/config.gotmpl | 2 +- pkg/tools/gen/templates/kcl/header.gotmpl | 2 +- pkg/tools/gen/types.go | 20 ++++++- 7 files changed, 108 insertions(+), 18 deletions(-) diff --git a/pkg/tools/gen/genkcl_jsonschema.go b/pkg/tools/gen/genkcl_jsonschema.go index dda95cbc..78e3a3f6 100644 --- a/pkg/tools/gen/genkcl_jsonschema.go +++ b/pkg/tools/gen/genkcl_jsonschema.go @@ -52,11 +52,10 @@ func (k *kclGenerator) genSchemaFromJsonSchema(w io.Writer, filename string, src panic("result is not schema") } kclSch := kclFile{ - Imports: []string{}, Schemas: []schema{result.schema}, } for _, imp := range getSortedKeys(ctx.imports) { - kclSch.Imports = append(kclSch.Imports, imp) + kclSch.Imports = append(kclSch.Imports, kImport{PkgPath: imp}) } for _, key := range getSortedKeys(ctx.resultMap) { if ctx.resultMap[key].IsSchema { diff --git a/pkg/tools/gen/genkcl_terraform.go b/pkg/tools/gen/genkcl_terraform.go index af9611a0..5e355c7b 100644 --- a/pkg/tools/gen/genkcl_terraform.go +++ b/pkg/tools/gen/genkcl_terraform.go @@ -2,12 +2,13 @@ package gen import ( "encoding/json" - "github.com/iancoleman/strcase" "io" - "kcl-lang.io/kcl-go/pkg/logger" "reflect" "sort" "strconv" + + "github.com/iancoleman/strcase" + "kcl-lang.io/kcl-go/pkg/logger" ) type tfSchema struct { @@ -74,7 +75,6 @@ func (k *kclGenerator) genSchemaFromTerraformSchema(w io.Writer, filename string // generate kcl schema code kclSch := kclFile{ - Imports: []string{}, Schemas: result, } return k.genKcl(w, kclSch) diff --git a/pkg/tools/gen/genkcl_test.go b/pkg/tools/gen/genkcl_test.go index d65a41e9..5b810203 100644 --- a/pkg/tools/gen/genkcl_test.go +++ b/pkg/tools/gen/genkcl_test.go @@ -3,11 +3,13 @@ package gen import ( "bytes" "fmt" - assert2 "github.com/stretchr/testify/assert" "log" "os" "path/filepath" + "strings" "testing" + + assert2 "github.com/stretchr/testify/assert" ) func TestGenKcl(t *testing.T) { @@ -217,6 +219,71 @@ func TestGenKclFromYaml(t *testing.T) { } } +func TestGenKclFromJsonAndImports(t *testing.T) { + file := kclFile{} + g := &kclGenerator{} + b := bytes.NewBuffer(nil) + // Get a KCL config from JSON or YAML string + data, err := convertKclFromYamlString([]byte(`workload: + containers: + nginx: + image: nginx:v2 + replicas: 2 +`)) + if err != nil { + t.Fatal(err) + } + // Add import statements + importStmt := kImport{ + PkgPath: "models.schema.v1", + Alias: "ac", + } + file.Imports = append(file.Imports, importStmt) + configSchemaName := strings.Join([]string{importStmt.PkgName(), "AppConfiguration"}, ".") + // Add configurations + file.Config = append(file.Config, config{ + Data: data, + IsUnion: true, + Var: "app1", + Name: configSchemaName, + }) + file.Config = append(file.Config, config{ + Data: data, + IsUnion: true, + Var: "app2", + Name: configSchemaName, + }) + // Generate KCL code. + g.genKcl(b, file) + assert2.Equal(t, strings.ReplaceAll(b.String(), "\r\n", "\n"), `""" +This file was generated by the KCL auto-gen tool. DO NOT EDIT. +Editing this file might prove futile when you re-run the KCL auto-gen generate command. +""" +import models.schema.v1 as ac + +app1: ac.AppConfiguration { + workload = { + containers = { + nginx = { + image = "nginx:v2" + } + } + replicas = 2 + } +} +app2: ac.AppConfiguration { + workload = { + containers = { + nginx = { + image = "nginx:v2" + } + } + replicas = 2 + } +} +`) +} + func readFileString(t testing.TB, p string) (content string) { data, err := os.ReadFile(p) if err != nil { diff --git a/pkg/tools/gen/genkcl_yaml.go b/pkg/tools/gen/genkcl_yaml.go index 031f01e8..3e9960af 100644 --- a/pkg/tools/gen/genkcl_yaml.go +++ b/pkg/tools/gen/genkcl_yaml.go @@ -12,17 +12,11 @@ func (k *kclGenerator) genKclFromYaml(w io.Writer, filename string, src interfac if err != nil { return err } - - code = bytes.ReplaceAll(code, []byte("\r\n"), []byte("\n")) - - yamlData := &yaml.MapSlice{} - if err = yaml.UnmarshalWithOptions(code, yamlData, yaml.UseOrderedMap()); err != nil { + // convert yaml data to kcl + result, err := convertKclFromYamlString(code) + if err != nil { return err } - - // convert yaml data to kcl - result := convertKclFromYaml(yamlData) - // generate kcl code return k.genKcl(w, kclFile{Config: []config{ {Data: result}, @@ -59,3 +53,15 @@ func convertKclFromYaml(yamlData *yaml.MapSlice) []data { } return result } + +func convertKclFromYamlString(data []byte) ([]data, error) { + data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n")) + + yamlData := &yaml.MapSlice{} + if err := yaml.UnmarshalWithOptions(data, yamlData, yaml.UseOrderedMap()); err != nil { + return nil, err + } + + // convert yaml data to kcl + return convertKclFromYaml(yamlData), nil +} diff --git a/pkg/tools/gen/templates/kcl/config.gotmpl b/pkg/tools/gen/templates/kcl/config.gotmpl index 45e2dd85..29c312b5 100644 --- a/pkg/tools/gen/templates/kcl/config.gotmpl +++ b/pkg/tools/gen/templates/kcl/config.gotmpl @@ -1,4 +1,4 @@ -{{- if .Var }}{{ formatName .Var }}{{- if .IsUnion }}{{ " : " }}{{- else }}{{ " = " }}{{- end }}{{- end }}{{- if .Name }}{{ formatName .Name }}{{- end }}{{- if .Data }}{{- "{\n" }} +{{- if .Var }}{{ formatName .Var }}{{- if .IsUnion }}{{ ": " }}{{- else }}{{ " = " }}{{- end }}{{- end }}{{- if .Name }}{{ .Name }}{{ " " }}{{- end }}{{- if .Data }}{{- "{\n" }} {{- range .Data -}} {{- indentLines (include "data" .) " " }} {{- end -}} diff --git a/pkg/tools/gen/templates/kcl/header.gotmpl b/pkg/tools/gen/templates/kcl/header.gotmpl index 74824ee2..f56f59b1 100644 --- a/pkg/tools/gen/templates/kcl/header.gotmpl +++ b/pkg/tools/gen/templates/kcl/header.gotmpl @@ -5,6 +5,6 @@ Editing this file might prove futile when you re-run the KCL auto-gen generate c {{- if .Imports }} {{- range .Imports }} -import {{ . }} +import {{ .PkgPath }}{{- if .Alias }}{{ " as " }}{{ .Alias }}{{- end }} {{- end }} {{- end -}} diff --git a/pkg/tools/gen/types.go b/pkg/tools/gen/types.go index 34372211..a259b799 100644 --- a/pkg/tools/gen/types.go +++ b/pkg/tools/gen/types.go @@ -125,7 +125,7 @@ func getSortedFieldNames(fields map[string]*pb.KclType) []string { // It contains all the imports, schemas and data in this file. type kclFile struct { // Import statements. - Imports []string + Imports []kImport // Schema definitions. Schemas []schema // Top Level data definitions. @@ -134,6 +134,23 @@ type kclFile struct { Config []config } +type kImport struct { + PkgPath string + Alias string +} + +func (i *kImport) PkgName() string { + if len(i.Alias) > 0 { + return i.Alias + } + pkgNames := strings.Split(i.PkgPath, ".") + return pkgNames[len(pkgNames)-1] +} + +func (i *kImport) Validate() bool { + return len(i.PkgPath) > 0 +} + // schema is a kcl schema definition. type schema struct { Name string @@ -166,6 +183,7 @@ type validation struct { Regex *regexp.Regexp MultiplyOf *int Unique bool + AllOf []*validation } // indexSignature is a kcl schema index signature definition.