Skip to content

Commit

Permalink
feat: impl kcl openapi schema generation with kcl decorators (#206)
Browse files Browse the repository at this point in the history
* feat: impl kcl openapi schema generation with kcl decorators

Signed-off-by: peefy <xpf6677@163.com>

* chore: bump go version to 1.21+

Signed-off-by: peefy <xpf6677@163.com>

---------

Signed-off-by: peefy <xpf6677@163.com>
  • Loading branch information
Peefy committed Jan 8, 2024
1 parent 8d23b3b commit 6bb63b7
Show file tree
Hide file tree
Showing 25 changed files with 1,101 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main_darwin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
go-version: 1.21

# Parallel tests
- run: go test ./...
2 changes: 1 addition & 1 deletion .github/workflows/main_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
go-version: 1.21

# Parallel tests
- run: go test -v -coverprofile=profile.cov ./...
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
go-version: 1.21

# Parallel tests
- run: go test ./...
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ You can use KCL to

## Building & Testing

- [Install Go 1.19+](https://go.dev/dl/)
- [Install Go 1.21+](https://go.dev/dl/)

```bash
go test ./...
Expand Down
10 changes: 9 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module kcl-lang.io/kcl-go

go 1.19
go 1.21

require (
github.com/chai2010/jsonv v1.1.3
github.com/chai2010/protorpc v1.1.4
github.com/getkin/kin-openapi v0.122.0
github.com/goccy/go-yaml v1.11.0
github.com/gofrs/flock v0.8.1
github.com/golang/protobuf v1.5.3
Expand All @@ -16,6 +17,7 @@ require (
github.com/qri-io/jsonpointer v0.1.1
github.com/stretchr/testify v1.8.4
github.com/wk8/go-ordered-map/v2 v2.1.8
github.com/yuin/goldmark v1.4.13
google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.30.0
gopkg.in/yaml.v3 v3.0.1
Expand Down Expand Up @@ -52,13 +54,17 @@ require (
github.com/go-git/go-git/v5 v5.11.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
Expand All @@ -67,10 +73,12 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/otiai10/copy v1.9.0 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
68 changes: 68 additions & 0 deletions go.sum

Large diffs are not rendered by default.

99 changes: 71 additions & 28 deletions pkg/tools/gen/gendoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"sort"
"strings"
"text/template"

"github.com/yuin/goldmark"
)

//go:embed templates/doc/schemaDoc.gotmpl
Expand Down Expand Up @@ -71,6 +73,7 @@ type Format string
const (
Html Format = "html"
Markdown Format = "md"
OpenAPI Format = "openapi"
)

// KclPackage contains package information of package metadata(such as name, version, description, ...) and exported models(such as schemas)
Expand All @@ -90,12 +93,8 @@ func (g *GenContext) render(spec *SwaggerV2Spec) error {
if err != nil {
return fmt.Errorf("failed to create docs/ directory under the target directory: %s", err)
}
// extract kcl package from swaggerV2 spec
rootPkg := spec.toKclPackage()
// sort schemas and subpackages by their names
rootPkg.sortSchemasAndPkgs()
// render the package
err = g.renderPackage(rootPkg, g.Target)
err = g.renderPackage(spec, g.Target)
if err != nil {
return err
}
Expand Down Expand Up @@ -231,28 +230,72 @@ func (pkg *KclPackage) getIndexContent(level int, indentation string) string {
return content
}

func (g *GenContext) renderPackage(pkg *KclPackage, parentDir string) error {
func (g *GenContext) renderPackage(spec *SwaggerV2Spec, parentDir string) error {
// extract kcl package from swaggerV2 spec
pkg := spec.toKclPackage()
// sort schemas and subpackages by their names
pkg.sortSchemasAndPkgs()
pkgName := pkg.Name
if pkg.Name == "" {
pkgName = "main"
}
fmt.Println(fmt.Sprintf("generating doc for package %s", pkgName))
docFileName := fmt.Sprintf("%s.%s", pkgName, g.Format)
var contentBuf bytes.Buffer
err := g.Template.ExecuteTemplate(&contentBuf, "packageDoc", struct {
EscapeHtml bool
Data *KclPackage
}{
EscapeHtml: g.EscapeHtml,
Data: pkg,
})
if err != nil {
return fmt.Errorf("failed to render package %s with template, err: %s", pkg.Name, err)
}
// write content to file
err = os.WriteFile(filepath.Join(parentDir, docFileName), contentBuf.Bytes(), 0644)
if err != nil {
return fmt.Errorf("failed to write file %s in %s: %v", docFileName, parentDir, err)
fmt.Printf("generating doc for package %s\n", pkgName)
// --- format ---
switch strings.ToLower(string(g.Format)) {
case string(Markdown):
docFileName := fmt.Sprintf("%s.%s", pkgName, g.Format)
var buf bytes.Buffer
err := g.Template.ExecuteTemplate(&buf, "packageDoc", struct {
EscapeHtml bool
Data *KclPackage
}{
EscapeHtml: g.EscapeHtml,
Data: pkg,
})
if err != nil {
return fmt.Errorf("failed to render package %s with template, err: %s", pkg.Name, err)
}
// write content to file
err = os.WriteFile(filepath.Join(parentDir, docFileName), buf.Bytes(), 0644)
if err != nil {
return fmt.Errorf("failed to write file %s in %s: %v", docFileName, parentDir, err)
}
case string(Html):
var mdBuf bytes.Buffer
err := g.Template.ExecuteTemplate(&mdBuf, "packageDoc", struct {
EscapeHtml bool
Data *KclPackage
}{
EscapeHtml: g.EscapeHtml,
Data: pkg,
})
if err != nil {
return fmt.Errorf("failed to render package %s with template, err: %s", pkg.Name, err)
}
var htmlBuf bytes.Buffer
if err := goldmark.Convert(mdBuf.Bytes(), &htmlBuf); err != nil {
panic(err)
}
docFileName := fmt.Sprintf("%s.%s", pkgName, g.Format)
// write content to file
err = os.WriteFile(filepath.Join(parentDir, docFileName), htmlBuf.Bytes(), 0644)
if err != nil {
return fmt.Errorf("failed to write file %s in %s: %v", docFileName, parentDir, err)
}
case string(OpenAPI):
docFileName := fmt.Sprintf("%s.%s", pkgName, "json")
spec := SwaggerV2TotOpenAPIV3Spec(spec)
json, err := spec.MarshalJSON()
if err != nil {
return err
}
// write content to file
err = os.WriteFile(filepath.Join(parentDir, docFileName), json, 0644)
if err != nil {
return fmt.Errorf("failed to write file %s in %s: %v", docFileName, parentDir, err)
}
default:
return fmt.Errorf("invalid generate format. Allow values: %s", []Format{Markdown, Html, OpenAPI})
}
return nil
}
Expand All @@ -263,12 +306,12 @@ func (opts *GenOpts) ValidateComplete() (*GenContext, error) {
switch strings.ToLower(opts.Format) {
case string(Markdown):
g.Format = Markdown
break
case string(Html):
g.Format = Html
break
case string(OpenAPI):
g.Format = OpenAPI
default:
return nil, fmt.Errorf("invalid generate format. Allow values: %s", []Format{Markdown, Html})
return nil, fmt.Errorf("invalid generate format. Allow values: %s", []Format{Markdown, Html, OpenAPI})
}

// --- package path ---
Expand Down Expand Up @@ -374,7 +417,7 @@ func (opts *GenOpts) ValidateComplete() (*GenContext, error) {
g.Target = path.Join(g.Target, "docs")
if _, err := os.Stat(g.Target); err == nil {
// check and warn if the docs directory already exists
fmt.Println(fmt.Sprintf("[Warn] path %s exists, all the content will be overwritten", g.Target))
fmt.Printf("[Warn] path %s exists, all the content will be overwritten\n", g.Target)
if err := os.RemoveAll(g.Target); err != nil {
return nil, fmt.Errorf("failed to remove existing content in %s:%s", g.Target, err)
}
Expand All @@ -385,7 +428,7 @@ func (opts *GenOpts) ValidateComplete() (*GenContext, error) {

// GenDoc generate document files from KCL source files
func (g *GenContext) GenDoc() error {
spec, err := KclPackageToSwaggerV2Spec(g.PackagePath)
spec, err := ExportSwaggerV2Spec(g.PackagePath)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 6bb63b7

Please sign in to comment.