Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 22 additions & 6 deletions pkg/swagger/generator/language.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"reflect"
"regexp"
"sort"
"strconv"
"strings"

"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -158,6 +159,14 @@ func (l *LanguageOpts) ManglePackagePath(name string, suffix string) string {
}

func (l *LanguageOpts) ToKclValue(data interface{}) string {
return l.toKclValue(data, true)
}

func (l *LanguageOpts) ToKclDocValue(data interface{}) string {
return l.toKclValue(data, false)
}

func (l *LanguageOpts) toKclValue(data interface{}, allowMultiline bool) string {
if data == nil {
return "None"
}
Expand All @@ -167,7 +176,7 @@ func (l *LanguageOpts) ToKclValue(data interface{}) string {
var mapContents []string
iter := value.MapRange()
for iter.Next() {
mapContents = append(mapContents, fmt.Sprintf("%s: %s", l.ToKclValue(iter.Key()), l.ToKclValue(iter.Value())))
mapContents = append(mapContents, fmt.Sprintf("%s: %s", l.toKclValue(iter.Key(), allowMultiline), l.toKclValue(iter.Value(), allowMultiline)))
}
content := strings.Join(mapContents, ", ")
return fmt.Sprintf("{%s}", content)
Expand All @@ -178,20 +187,20 @@ func (l *LanguageOpts) ToKclValue(data interface{}) string {
for _, v := range dataSlice {
k := v.Key
v := v.Value
dictContents = append(dictContents, fmt.Sprintf("%s: %s", l.ToKclValue(k), l.ToKclValue(v)))
dictContents = append(dictContents, fmt.Sprintf("%s: %s", l.toKclValue(k, allowMultiline), l.toKclValue(v, allowMultiline)))
}
content := strings.Join(dictContents, ", ")
return fmt.Sprintf("{%s}", content)
}
// if is a normal slice
var sliceContents []string
for i := 0; i < value.Len(); i++ {
sliceContents = append(sliceContents, l.ToKclValue(value.Index(i).Interface()))
sliceContents = append(sliceContents, l.toKclValue(value.Index(i).Interface(), allowMultiline))
}
content := strings.Join(sliceContents, ", ")
return fmt.Sprintf("[%s]", content)
case reflect.String:
return fmt.Sprintf("\"%s\"", data)
return quoteKclString(data.(string), allowMultiline)
case reflect.Int,
reflect.Int8,
reflect.Int16,
Expand All @@ -213,14 +222,14 @@ func (l *LanguageOpts) ToKclValue(data interface{}) string {
default:
// Reflect value
if dataValue, ok := data.(reflect.Value); ok {
return l.ToKclValue(dataValue.Interface())
return l.toKclValue(dataValue.Interface(), allowMultiline)
} else if dataSlice, ok := data.(yaml.MapSlice); ok {
// If is a MapSlice
var dictContents []string
for _, v := range dataSlice {
k := v.Key
v := v.Value
dictContents = append(dictContents, fmt.Sprintf("%s: %s", l.ToKclValue(k), l.ToKclValue(v)))
dictContents = append(dictContents, fmt.Sprintf("%s: %s", l.toKclValue(k, allowMultiline), l.toKclValue(v, allowMultiline)))
}
content := strings.Join(dictContents, ", ")
return fmt.Sprintf("{%s}", content)
Expand All @@ -236,6 +245,13 @@ func (l *LanguageOpts) ToKclValue(data interface{}) string {
}
}

func quoteKclString(data string, allowMultiline bool) string {
if allowMultiline && strings.ContainsAny(data, "\r\n") && !strings.Contains(data, `"""`) {
return fmt.Sprintf("\"\"\"%s\"\"\"", data)
}
return strconv.Quote(data)
}

// FormatContent formats a file with a language specific formatter
func (l *LanguageOpts) FormatContent(name string, content []byte) ([]byte, error) {
if l.formatFunc != nil {
Expand Down
67 changes: 67 additions & 0 deletions pkg/swagger/generator/support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

crdGen "kcl-lang.io/kcl-openapi/pkg/kube_resource/generator"
Expand Down Expand Up @@ -34,6 +35,72 @@ func TestGenerate_CRD2KCL(t *testing.T) {
utils.DoTestDirs(t, utils.KubeTestDirs, apiConvertModel, true)
}

func TestGenerate_CRD2KCL_MultilineStringDefault(t *testing.T) {
tempDir := t.TempDir()
specPath := filepath.Join(tempDir, "crd.yaml")
if err := os.WriteFile(specPath, []byte(`apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: examples.example.com
spec:
group: example.com
names:
kind: Example
plural: examples
singular: example
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
singleLine:
type: string
default: "hello world"
multiLine:
type: string
default: |
#!/bin/bash
set -e
echo "line one"
echo "line two"
`), 0o644); err != nil {
t.Fatalf("write CRD spec failed: %v", err)
}

if err := apiConvertModel(utils.IntegrationGenOpts{
SpecPath: specPath,
TargetDir: tempDir,
IsCrd: true,
ModelPackage: "models",
}); err != nil {
t.Fatalf("generate failed: %v", err)
}

generatedPath := filepath.Join(tempDir, "models", "example_com_v1alpha1_example.k")
generated, err := os.ReadFile(generatedPath)
if err != nil {
t.Fatalf("read generated model failed: %v", err)
}
content := string(generated)
if !strings.Contains(content, `multiLine?: str = """#!/bin/bash
set -e
echo "line one"
echo "line two"
"""`) {
t.Fatalf("missing multiline default in generated model:\n%s", content)
}
if !strings.Contains(content, `multiLine : str, default is "#!/bin/bash\nset -e\necho \"line one\"\necho \"line two\"\n", optional`) {
t.Fatalf("missing escaped multiline doc default in generated model:\n%s", content)
}
}

func apiConvertModel(integrationGenOpts utils.IntegrationGenOpts) error {
opts := new(GenOpts)
opts.Spec = integrationGenOpts.SpecPath
Expand Down
1 change: 1 addition & 0 deletions pkg/swagger/generator/template_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ func DefaultFuncMap(lang *LanguageOpts) template.FuncMap {
return properties
},
"toKCLValue": lang.ToKclValue,
"toKCLDocValue": lang.ToKclDocValue,
"nonEmptyValue": lang.NonEmptyValue,
"dict": func(values ...interface{}) (map[string]interface{}, error) {
if len(values)%2 != 0 {
Expand Down
19 changes: 19 additions & 0 deletions pkg/swagger/generator/template_repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ func TestToKCLValue(t *testing.T) {
value: "hello",
expect: "\"hello\"",
},
{
name: "string-with-quote",
value: "hello \"world\"",
expect: "\"hello \\\"world\\\"\"",
},
{
name: "multiline-string",
value: "#!/bin/bash\nset -e\necho \"line one\"\n",
expect: "\"\"\"#!/bin/bash\nset -e\necho \"line one\"\n\"\"\"",
},
{
name: "map-string-int",
value: yaml.MapSlice{
Expand Down Expand Up @@ -100,6 +110,15 @@ func TestToKCLValue(t *testing.T) {
}
}

func TestToKCLDocValue(t *testing.T) {
opts := LanguageOpts{}
got := opts.ToKclDocValue("#!/bin/bash\nset -e\necho \"line one\"\n")
expect := "\"#!/bin/bash\\nset -e\\necho \\\"line one\\\"\\n\""
if got != expect {
t.Fatalf("unexpected output, expect:\n%s\ngot:\n%s\n", expect, got)
}
}

func TestPadDocument(t *testing.T) {
cases := []struct {
doc string
Expand Down
2 changes: 1 addition & 1 deletion pkg/swagger/generator/templates/docstring.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@

Examples
--------
demo = {{ toKCLValue .Example }}
demo = {{ toKCLDocValue .Example }}
{{- end -}}
{{- end }}
2 changes: 1 addition & 1 deletion pkg/swagger/generator/templates/propertydoc.gotmpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{ define "propertydoc" }}
{{ .EscapedName }} : {{ .KclType }}, default is {{ if .Default }}{{ toKCLValue .Default }}{{ else }}Undefined{{ end }}, {{ if not .Required }}optional{{else}}required{{ end }}
{{ .EscapedName }} : {{ .KclType }}, default is {{ if .Default }}{{ toKCLDocValue .Default }}{{ else }}Undefined{{ end }}, {{ if not .Required }}optional{{else}}required{{ end }}
{{ template "introduction" . }}
{{- end }}
Loading