Skip to content

Commit

Permalink
feat: add export openapi API (#160)
Browse files Browse the repository at this point in the history
add API: export openapi from kcl package
  • Loading branch information
amyXia1994 authored Sep 15, 2023
1 parent 5d0d984 commit 7f880c1
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 49 deletions.
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.

0 comments on commit 7f880c1

Please sign in to comment.