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: add export openapi API #160

Merged
merged 1 commit into from
Sep 15, 2023
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
36 changes: 2 additions & 34 deletions pkg/tools/gen/gendoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"sort"
"strings"
"text/template"

kpm "kcl-lang.io/kpm/pkg/api"
)

//go:embed templates/doc/schemaDoc.gotmpl
Expand Down Expand Up @@ -244,12 +242,11 @@ func (pkg *KclPackage) getIndexContent(level int, indentation string, pkgPath st
}

func (g *GenContext) renderPackage(pkg *KclPackage, parentDir string) error {
// render the package's index.md page
//fmt.Println(fmt.Sprintf("creating %s/index.md", parentDir))
pkgName := pkg.Name
if pkg.Name == "" {
pkgName = "main"
}
fmt.Println(fmt.Sprintf("generating doc for package %s", pkgName))
indexFileName := fmt.Sprintf("%s.%s", pkgName, g.Format)
var contentBuf bytes.Buffer
err := g.Template.ExecuteTemplate(&contentBuf, "packageDoc", struct {
Expand Down Expand Up @@ -398,11 +395,7 @@ func (opts *GenOpts) ValidateComplete() (*GenContext, error) {

// GenDoc generate document files from KCL source files
func (g *GenContext) GenDoc() error {
pkg, err := kpm.GetKclPackage(g.PackagePath)
if err != nil {
return fmt.Errorf("filePath is not a KCL package: %s", err)
}
spec, err := g.getSwagger2Spec(pkg)
spec, err := KclPackageToSwaggerV2Spec(g.PackagePath)
if err != nil {
return err
}
Expand All @@ -412,28 +405,3 @@ func (g *GenContext) GenDoc() error {
}
return nil
}

func (g *GenContext) getSwagger2Spec(pkg *kpm.KclPackage) (*SwaggerV2Spec, error) {
spec := &SwaggerV2Spec{
Swagger: "2.0",
Definitions: make(map[string]*KclOpenAPIType),
Info: SpecInfo{
Title: pkg.GetPkgName(),
Version: pkg.GetVersion(),
},
}
pkgMapping, err := pkg.GetAllSchemaTypeMapping()
if err != nil {
return spec, err
}
// package path -> package
for packagePath, p := range pkgMapping {
// schema name -> schema type
for _, t := range p {
id := SchemaId(packagePath, t.KclType)
spec.Definitions[id] = GetKclOpenAPIType(packagePath, t.KclType, false)
fmt.Println(fmt.Sprintf("generate docs for schema %s", id))
}
}
return spec, nil
}
73 changes: 58 additions & 15 deletions pkg/tools/gen/genopenapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,56 @@ package gen

import (
"fmt"
kpm "kcl-lang.io/kpm/pkg/api"

"os"
"path/filepath"
"strings"

kcl "kcl-lang.io/kcl-go"
)

// ExportSwaggerV2Spec export swagger v2 spec of a kcl package
func ExportSwaggerV2Spec(pkgPath string) (string, error) {
spec, err := KclPackageToSwaggerV2Spec(pkgPath)
if err != nil {
return "", err
}
return jsonString(spec), nil
}

// KclPackageToSwaggerV2Spec extracts the swagger v2 representation of a kcl package
func KclPackageToSwaggerV2Spec(pkgPath string) (*SwaggerV2Spec, error) {
pkg, err := kpm.GetKclPackage(pkgPath)
if err != nil {
return nil, fmt.Errorf("filePath is not a KCL package: %s", err)
}

spec := &SwaggerV2Spec{
Swagger: "2.0",
Definitions: make(map[string]*KclOpenAPIType),
Paths: map[string]interface{}{},
Info: SpecInfo{
Title: pkg.GetPkgName(),
Version: pkg.GetVersion(),
},
}
pkgMapping, err := pkg.GetAllSchemaTypeMapping()
if err != nil {
return spec, err
}
// package path -> package
for packagePath, p := range pkgMapping {
// schema name -> schema type
for _, t := range p {
id := SchemaId(packagePath, t.KclType)
spec.Definitions[id] = GetKclOpenAPIType(packagePath, t.KclType, false)
fmt.Println(fmt.Sprintf("exporting openAPI spec from schema %s", id))
}
}
return spec, nil
}

// SwaggerV2Spec defines KCL OpenAPI Spec based on Swagger v2.0
type SwaggerV2Spec struct {
Definitions map[string]*KclOpenAPIType `json:"definitions"`
Expand All @@ -21,7 +64,7 @@ type SwaggerV2Spec struct {
type SpecInfo struct {
Title string `json:"title"`
Version string `json:"version"`
Description string `json:"description"`
Description string `json:"description,omitempty"`
}

// KclOpenAPIType defines the KCL representation of SchemaObject field in Swagger v2.0.
Expand Down Expand Up @@ -58,20 +101,20 @@ type SpecInfo struct {
└───────────────────────┴───────────────────────────────────────────────────────────────────────────────┘
*/
type KclOpenAPIType struct {
Type SwaggerTypeName // object, string, array, integer, number, bool
Format TypeFormat // type format
Default string // default value
Enum []string // enum values
ReadOnly bool // readonly
Description string // description
Properties map[string]*KclOpenAPIType // schema properties
Required []string // list of required schema property names
Items *KclOpenAPIType // list item type
AdditionalProperties *KclOpenAPIType // dict value type
Examples map[string]KclExample // examples
ExternalDocs string // externalDocs
KclExtensions *KclExtensions // x-kcl- extensions
Ref string // reference to schema path
Type SwaggerTypeName `json:"type,omitempty"` // object, string, array, integer, number, bool
Format TypeFormat `json:"format,omitempty"` // type format
Default string `json:"default,omitempty"` // default value
Enum []string `json:"enum,omitempty"` // enum values
ReadOnly bool `json:"readOnly,omitempty"` // readonly
Description string `json:"description,omitempty"` // description
Properties map[string]*KclOpenAPIType `json:"properties,omitempty"` // schema properties
Required []string `json:"required,omitempty"` // list of required schema property names
Items *KclOpenAPIType `json:"items,omitempty"` // list item type
AdditionalProperties *KclOpenAPIType `json:"additionalProperties,omitempty"` // dict value type
Examples map[string]KclExample `json:"examples,omitempty"` // examples
ExternalDocs string `json:"externalDocs,omitempty"` // externalDocs
Ref string `json:"ref,omitempty"` // reference to schema path
*KclExtensions // x-kcl- extensions
}

// SwaggerTypeName defines possible values of "type" field in Swagger v2.0 spec
Expand Down
73 changes: 73 additions & 0 deletions pkg/tools/gen/genopenapi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package gen

import (
assert2 "github.com/stretchr/testify/assert"
"os"
"path/filepath"
"testing"
)

func TestExportSwaggerV2Spec(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
t.Fatal("get work directory failed")
}
pkgPath := filepath.Join(cwd, "testdata", "doc", "k8s")
got, err := ExportSwaggerV2Spec(pkgPath)
if err != nil {
t.Fatal(err)
}

expect := `{
"definitions": {
"apps.Deployment": {
"type": "object",
"properties": {
"metadata": {
"type": "string"
},
"podSpec": {
"type": "object"
}
},
"required": [
"metadata",
"podSpec"
],
"x-kcl-type": {
"type": "Deployment",
"import": {
"package": "apps",
"alias": "deployment.k"
}
}
},
"core.PodSpec": {
"type": "object",
"properties": {
"image": {
"type": "string"
}
},
"required": [
"image"
],
"x-kcl-type": {
"type": "PodSpec",
"import": {
"package": "core",
"alias": "podSpec.k"
}
}
}
},
"paths": {},
"swagger": "2.0",
"info": {
"title": "",
"version": ""
}
}`

assert2.Equal(t, expect, got)
}
5 changes: 5 additions & 0 deletions pkg/tools/gen/testdata/oai/k8s/apps/deployment.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import k8s.core

schema Deployment:
metadata: str
podSpec: core.PodSpec
2 changes: 2 additions & 0 deletions pkg/tools/gen/testdata/oai/k8s/core/podSpec.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema PodSpec:
image: str
Empty file.
Loading