Generate a local site to host all reference docs

By Cli `vela reference` to generate reference docs for all workload
types and traits, and host them in a local website.

To fix #880
This commit is contained in:
zzxwill
2021-01-18 12:18:15 +08:00
parent 40aa97363e
commit 3546001bf6
6 changed files with 570 additions and 246 deletions
+2 -224
View File
@@ -2,236 +2,14 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"cuelang.org/go/cue"
"github.com/oam-dev/kubevela/apis/types"
mycue "github.com/oam-dev/kubevela/pkg/cue"
"github.com/oam-dev/kubevela/pkg/plugins"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
const (
BaseRefPath = "docs/en/developers/references"
ReferencePath = "hack/references"
)
type ReferenceMarkdown struct {
CapabilityName string `json:"capabilityName"`
CapabilityType string `json:"capabilityType"`
}
type Parameter struct {
types.Parameter `json:",inline,omitempty"`
// PrintableType is same to `parameter.Type` which could be printable
PrintableType string `json:"printableType"`
// Depth marks the depth for calling of function `parseParameters`
Depth *int `json:"depth"`
}
var refContent string
var recurseDepth *int
func main() {
var capabilityType string
var specificationType string
caps, err := plugins.LoadAllInstalledCapability()
if err != nil {
fmt.Printf("failed to generate reference docs for all capabilities: %s", err)
if err := plugins.GenerateReferenceDocs(); err != nil {
fmt.Println(err)
os.Exit(1)
}
for _, c := range caps {
switch c.Type {
case "workload":
capabilityType = "workload-types"
specificationType = "workload type"
case "trait":
capabilityType = "traits"
specificationType = "trait"
default:
fmt.Printf("the type of the capability is not right")
os.Exit(1)
}
fileName := fmt.Sprintf("%s.md", c.Name)
filePath := filepath.Join(BaseRefPath, capabilityType, fileName)
f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
fmt.Printf("failed to open file %s: %s", filePath, err)
os.Exit(1)
}
if err = os.Truncate(filePath, 0); err != nil {
fmt.Printf("failed to truncate file %s: %s", filePath, err)
os.Exit(1)
}
capName := c.Name
ref := ReferenceMarkdown{
CapabilityName: capName,
CapabilityType: capabilityType,
}
cueValue, err := common.GetCUEParameterValue(c.CueTemplate)
if err != nil {
fmt.Printf("failed to retrieve `parameters` value from %s with err: %s", c.Name, err)
os.Exit(1)
}
refContent = ""
var defaultDepth = 0
recurseDepth = &defaultDepth
capNameInTitle := strings.Title(capName)
if err := ref.parseParameters(cueValue, "Properties", defaultDepth); err != nil {
fmt.Printf(err.Error())
os.Exit(1)
}
title := fmt.Sprintf("# %s", capNameInTitle)
description := fmt.Sprintf("\n\n## Description\n\n%s", c.Description)
specificationIntro := fmt.Sprintf("List of all configuration options for a `%s` %s.", capNameInTitle, specificationType)
specificationContent, err := generateSpecification(capName)
if err != nil {
fmt.Printf(err.Error())
os.Exit(1)
}
specification := fmt.Sprintf("\n\n## Specification\n\n%s\n\n%s", specificationIntro, specificationContent)
conflictWithAndMoreSection, err := generateConflictWithAndMore(capName)
if err != nil {
fmt.Printf(err.Error())
os.Exit(1)
}
refContent = title + description + specification + refContent + conflictWithAndMoreSection
f.WriteString(refContent)
f.Close()
}
}
// prepareParameterTable prepares the table content for each property
func (ref *ReferenceMarkdown) prepareParameterTable(tableName string, parameterList []Parameter) string {
refContent := fmt.Sprintf("\n\n%s\n\n", tableName)
refContent += "Name | Description | Type | Required | Default \n"
refContent += "------------ | ------------- | ------------- | ------------- | ------------- \n"
for _, p := range parameterList {
//defaultValue := p.Default
//if defaultValue == nil {
// defaultValue = ""
//}
printableDefaultValue := getPrintableDefaultValue(p.Default)
refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, p.Usage, p.PrintableType, p.Required, printableDefaultValue)
}
return refContent
}
// parseParameters parses every parameter
func (ref *ReferenceMarkdown) parseParameters(paraValue cue.Value, paramKey string, depth int) error {
var params []Parameter
*recurseDepth++
switch paraValue.Kind() {
case cue.StructKind:
arguments, err := paraValue.Struct()
if err != nil {
return fmt.Errorf("arguments not defined as struct %w", err)
}
for i := 0; i < arguments.Len(); i++ {
var param Parameter
fi := arguments.Field(i)
if fi.IsDefinition {
continue
}
val := fi.Value
name := fi.Name
param.Name = name
param.Required = !fi.IsOptional
if def, ok := val.Default(); ok && def.IsConcrete() {
param.Default = mycue.GetDefault(def)
}
param.Short, param.Usage, param.Alias = mycue.RetrieveComments(val)
param.Type = val.IncompleteKind()
switch val.IncompleteKind() {
case cue.StructKind:
depth := *recurseDepth
// TODO(zzxwill) this case not processed `selector?: [string]: string`
if name == "selector" {
param.PrintableType = "map[string]string"
} else {
if err := ref.parseParameters(val, name, depth); err != nil {
return err
}
param.PrintableType = fmt.Sprintf("[%s](#%s)", name, name)
}
case cue.ListKind:
elem, success := val.Elem()
if !success {
return fmt.Errorf("failed to get elements from %s", val)
}
switch elem.Kind() {
case cue.StructKind:
param.PrintableType = fmt.Sprintf("[[]%s](#%s)", name, name)
depth := *recurseDepth
if err := ref.parseParameters(elem, name, depth); err != nil {
return err
}
default:
param.Type = elem.Kind()
param.PrintableType = fmt.Sprintf("[]%s", elem.IncompleteKind().String())
}
default:
param.PrintableType = param.Type.String()
}
params = append(params, param)
}
}
tableName := fmt.Sprintf("%s %s", strings.Repeat("#", depth+2), paramKey)
refContent = ref.prepareParameterTable(tableName, params) + refContent
return nil
}
// getPrintableDefaultValue converts the value in `interface{}` type to be printable
func getPrintableDefaultValue(v interface{}) string {
if v == nil {
return ""
}
switch v.(type) {
case int64:
return strconv.FormatInt(v.(int64), 10)
case string:
if v == "" {
return "empty"
}
return v.(string)
case bool:
return strconv.FormatBool(v.(bool))
}
return ""
}
// generateSpecification generates Specification part for reference docs
func generateSpecification(capability string) (string, error) {
configurationPath, err := filepath.Abs(filepath.Join(ReferencePath, "configurations", fmt.Sprintf("%s.yaml", capability)))
if err != nil {
return "", fmt.Errorf("failed to get configuration path: %w", err)
}
spec, err := ioutil.ReadFile(configurationPath)
// skip if Configuration usage of a capability doesn't exist.
if err != nil {
spec = nil
}
return fmt.Sprintf("```yaml\n%s```", string(spec)), nil
}
// generateConflictWithAndMore generates Section `Conflicts With` and more like `How xxx works` in reference docs
func generateConflictWithAndMore(capabilityName string) (string, error) {
conflictWithFile, err := filepath.Abs(filepath.Join(ReferencePath, "conflictsWithAndMore", fmt.Sprintf("%s.md", capabilityName)))
if err != nil {
return "", fmt.Errorf("failed to locate conflictWith file: %w", err)
}
data, err := ioutil.ReadFile(conflictWithFile)
if err != nil {
return "", nil
}
return "\n" + string(data), nil
}
+1
View File
@@ -58,6 +58,7 @@ func NewCommand() *cobra.Command {
NewInitCommand(commandArgs, ioStream),
NewUpCommand(commandArgs, ioStream),
NewExportCommand(commandArgs, ioStream),
NewReferencesCommand(commandArgs, ioStream),
// Apps
NewListCommand(commandArgs, ioStream),
+271
View File
@@ -0,0 +1,271 @@
package commands
import (
"context"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"github.com/spf13/cobra"
"github.com/oam-dev/kubevela/apis/types"
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
"github.com/oam-dev/kubevela/pkg/plugins"
"github.com/oam-dev/kubevela/pkg/utils/system"
)
const (
// SideBar file name for docsify
SideBar = "_sidebar.md"
// NavBar file name for docsify
NavBar = "_navbar.md"
// IndexHTML file name for docsify
IndexHTML = "index.html"
// README file name for docsify
README = "README.md"
)
// Port is the port for reference docs website
const Port = ":18081"
// NewReferencesCommand shows the website which hosts the reference docs for workload types and trait types
func NewReferencesCommand(c types.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "reference",
Aliases: []string{"ref", "ref-docs"},
Short: "Opens up the reference docs website for workload types and trait types",
Long: "Opens up the reference docs website for workload types and trait types",
Example: `vela reference`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return c.SetConfig()
},
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
return startReferenceDocsSite(ctx, c, ioStreams)
},
Annotations: map[string]string{
types.TagCommandType: types.TypeStart,
},
}
cmd.Flags().StringP("svc", "s", "", "service name")
cmd.SetOut(ioStreams.Out)
return cmd
}
func startReferenceDocsSite(ctx context.Context, c types.Args, ioStreams cmdutil.IOStreams) error {
home, err := system.GetVelaHomeDir()
if err != nil {
return err
}
referenceHome := filepath.Join(home, "reference")
definitionPath := filepath.Join(referenceHome, "capabilities")
if _, err := os.Stat(definitionPath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(definitionPath, 0750); err != nil {
return err
}
}
docsPath := filepath.Join(referenceHome, "docs")
if _, err := os.Stat(docsPath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(docsPath, 0750); err != nil {
return err
}
}
capabilities, _, err := plugins.SyncDefinitionToLocal(ctx, c, definitionPath)
if err != nil {
return err
}
if err := plugins.CreateMarkdown(capabilities, docsPath, plugins.ReferenceSourcePath); err != nil {
return err
}
if err := generateSideBar(capabilities, docsPath); err != nil {
return err
}
if err := generateNavBar(docsPath); err != nil {
return err
}
if err := generateIndexHTML(docsPath); err != nil {
return err
}
if err := generateREADME(capabilities, docsPath); err != nil {
return err
}
server := &http.Server{
Addr: Port,
Handler: http.FileServer(http.Dir(docsPath)),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server.SetKeepAlivesEnabled(true)
errCh := make(chan error, 1)
launch(server, errCh)
select {
case err = <-errCh:
return err
case <-time.After(time.Second):
var url = "http://127.0.0.1" + Port
if err := OpenBrowser(url); err != nil {
ioStreams.Infof("automatically invoking browser failed: %v\nPlease visit %s for reference docs", err, url)
}
}
// handle signal: SIGTERM(15)
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGTERM)
<-sc
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
return server.Shutdown(ctx)
}
func launch(server *http.Server, errChan chan<- error) {
go func() {
// http.Handle("/", http.FileServer(http.Dir(docsPath)))
err := server.ListenAndServe()
if err != nil && errors.Is(err, http.ErrServerClosed) {
errChan <- err
}
}()
}
func generateSideBar(capabilities []types.Capability, docsPath string) error {
sideBar := filepath.Join(docsPath, SideBar)
workloads, traits := getWorkloadsAndTraits(capabilities)
f, err := os.Create(sideBar)
if err != nil {
return err
}
if _, err := f.WriteString("- Workload Types\n"); err != nil {
return nil
}
workloadFolderName := "workload-types"
for _, w := range workloads {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", w, workloadFolderName, w)); err != nil {
return nil
}
}
if _, err := f.WriteString("- Traits\n"); err != nil {
return nil
}
traitFolderName := "traits"
for _, t := range traits {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, traitFolderName, t)); err != nil {
return nil
}
}
return nil
}
func generateNavBar(docsPath string) error {
sideBar := filepath.Join(docsPath, NavBar)
_, err := os.Create(sideBar)
if err != nil {
return err
}
return nil
}
func generateIndexHTML(docsPath string) error {
indexHTML := `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>KubeVela Reference Docs</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify-sidebar-collapse/dist/sidebar.min.css" />
<link rel="stylesheet" href="./resources/css/custom.css">
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'KubeVela Reference Docs',
loadSidebar: true,
loadNavbar: true,
subMaxLevel: 1,
alias: {
'/_sidebar.md': '/_sidebar.md',
'/_navbar.md': '/_navbar.md'
},
formatUpdated: '{MM}/{DD}/{YYYY} {HH}:{mm}:{ss}',
}
</script>
<!-- Docsify v4 -->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<!-- plugins -->
<script src="//cdn.jsdelivr.net/npm/docsify-sidebar-collapse/dist/docsify-sidebar-collapse.min.js"></script>
</body>
</html>
`
return ioutil.WriteFile(filepath.Join(docsPath, IndexHTML), []byte(indexHTML), 0600)
}
func generateREADME(capabilities []types.Capability, docsPath string) error {
readmeMD := filepath.Join(docsPath, README)
f, err := os.Create(readmeMD)
if err != nil {
return err
}
if _, err := f.WriteString("# KubeVela Reference Docs for Workload Types and Traits\n" +
"Click the navigation bar on the left or the links below to look into the detailed referennce of a Workload type or a Trait.\n"); err != nil {
return err
}
workloads, traits := getWorkloadsAndTraits(capabilities)
if _, err := f.WriteString("## Workload Types\n"); err != nil {
return err
}
workloadFolderName := "workload-types"
for _, w := range workloads {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", w, workloadFolderName, w)); err != nil {
return err
}
}
if _, err := f.WriteString("## Traits\n"); err != nil {
return err
}
traitFolderName := "traits"
for _, t := range traits {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, traitFolderName, t)); err != nil {
return err
}
}
return nil
}
func getWorkloadsAndTraits(capabilities []types.Capability) ([]string, []string) {
var workloads, traits []string
for _, c := range capabilities {
switch c.Type {
case types.TypeWorkload:
workloads = append(workloads, c.Name)
case types.TypeTrait:
traits = append(traits, c.Name)
case types.TypeScope:
}
}
return workloads, traits
}
+3 -22
View File
@@ -49,33 +49,14 @@ func RefreshDefinitions(ctx context.Context, c types.Args, ioStreams cmdutil.IOS
return nil
}
var syncedTemplates []types.Capability
templates, templateErrors, err := plugins.GetWorkloadsFromCluster(ctx, types.DefaultKubeVelaNS, c, dir, nil)
syncedTemplates, warnings, err := plugins.SyncDefinitionToLocal(ctx, c, dir)
if err != nil {
return err
}
if len(templateErrors) > 0 {
for _, e := range templateErrors {
ioStreams.Infof("WARN: %v, you will unable to use this workload capability\n", e)
}
for _, w := range warnings {
ioStreams.Infof(w)
}
syncedTemplates = append(syncedTemplates, templates...)
plugins.SinkTemp2Local(templates, dir)
templates, templateErrors, err = plugins.GetTraitsFromCluster(ctx, types.DefaultKubeVelaNS, c, dir, nil)
if err != nil {
return err
}
if len(templateErrors) > 0 {
for _, e := range templateErrors {
ioStreams.Infof("WARN: %v, you will unable to use this trait capability\n", e)
}
}
syncedTemplates = append(syncedTemplates, templates...)
plugins.SinkTemp2Local(templates, dir)
plugins.RemoveLegacyTemps(syncedTemplates, dir)
printRefreshReport(syncedTemplates, oldCaps, ioStreams, silentOutput, false)
return nil
}
+31
View File
@@ -198,3 +198,34 @@ func HandleTemplate(in *runtime.RawExtension, name, syncDir string) (types.Capab
}
return tmp, nil
}
// SyncDefinitionToLocal sync definitions to local
func SyncDefinitionToLocal(ctx context.Context, c types.Args, localDefinitionDir string) ([]types.Capability, []string, error) {
var syncedTemplates []types.Capability
var warnings []string
templates, templateErrors, err := GetWorkloadsFromCluster(ctx, types.DefaultKubeVelaNS, c, localDefinitionDir, nil)
if err != nil {
return nil, nil, err
}
if len(templateErrors) > 0 {
for _, e := range templateErrors {
warnings = append(warnings, fmt.Sprintf("WARN: %v, you will unable to use this workload capability\n", e))
}
}
syncedTemplates = append(syncedTemplates, templates...)
SinkTemp2Local(templates, localDefinitionDir)
templates, templateErrors, err = GetTraitsFromCluster(ctx, types.DefaultKubeVelaNS, c, localDefinitionDir, nil)
if err != nil {
return nil, warnings, err
}
if len(templateErrors) > 0 {
for _, e := range templateErrors {
warnings = append(warnings, fmt.Sprintf("WARN: %v, you will unable to use this trait capability\n", e))
}
}
syncedTemplates = append(syncedTemplates, templates...)
SinkTemp2Local(templates, localDefinitionDir)
return syncedTemplates, warnings, nil
}
+262
View File
@@ -0,0 +1,262 @@
package plugins
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"cuelang.org/go/cue"
"github.com/oam-dev/kubevela/apis/types"
mycue "github.com/oam-dev/kubevela/pkg/cue"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
const (
// BaseRefPath is the target path for reference docs
BaseRefPath = "docs/en/developers/references"
// ReferenceSourcePath is the location for source reference
ReferenceSourcePath = "hack/references"
)
// Int64Type is int64 type
type Int64Type int64
// StringType is string type
type StringType string
// BoolType is bool type
type BoolType bool
// ReferenceMarkdown is the struct for capability information
type ReferenceMarkdown struct {
// CapabilityName is the name of a capability
CapabilityName string `json:"capabilityName"`
// CapabilityType is the type of a capability
CapabilityType string `json:"capabilityType"`
}
// Parameter is the parameter section of CUE template
type Parameter struct {
types.Parameter `json:",inline,omitempty"`
// PrintableType is same to `parameter.Type` which could be printable
PrintableType string `json:"printableType"`
// Depth marks the depth for calling of function `parseParameters`
Depth *int `json:"depth"`
}
var refContent string
var recurseDepth *int
// GenerateReferenceDocs generates reference docs
func GenerateReferenceDocs() error {
caps, err := LoadAllInstalledCapability()
if err != nil {
return fmt.Errorf("failed to generate reference docs for all capabilities: %s", err)
}
return CreateMarkdown(caps, BaseRefPath, ReferenceSourcePath)
}
// CreateMarkdown creates markdown based on capabilities
func CreateMarkdown(caps []types.Capability, baseRefPath, referenceSourcePath string) error {
var capabilityType string
var specificationType string
for _, c := range caps {
switch c.Type {
case types.TypeWorkload:
capabilityType = "workload-types"
specificationType = "workload type"
case types.TypeTrait:
capabilityType = "traits"
specificationType = "trait"
default:
return fmt.Errorf("the type of the capability is not right")
}
fileName := fmt.Sprintf("%s.md", c.Name)
filePath := filepath.Join(baseRefPath, capabilityType)
if _, err := os.Stat(filePath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(filePath, 0750); err != nil {
return err
}
}
markdownFile := filepath.Join(baseRefPath, capabilityType, fileName)
f, err := os.OpenFile(filepath.Clean(markdownFile), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("failed to open file %s: %s", markdownFile, err)
}
if err = os.Truncate(markdownFile, 0); err != nil {
return fmt.Errorf("failed to truncate file %s: %s", markdownFile, err)
}
capName := c.Name
ref := ReferenceMarkdown{
CapabilityName: capName,
CapabilityType: capabilityType,
}
cueValue, err := common.GetCUEParameterValue(c.CueTemplate)
if err != nil {
return fmt.Errorf("failed to retrieve `parameters` value from %s with err: %s", c.Name, err)
}
refContent = ""
var defaultDepth = 0
recurseDepth = &defaultDepth
capNameInTitle := strings.Title(capName)
if err := ref.parseParameters(cueValue, "Properties", defaultDepth); err != nil {
return err
}
title := fmt.Sprintf("# %s", capNameInTitle)
description := fmt.Sprintf("\n\n## Description\n\n%s", c.Description)
specificationIntro := fmt.Sprintf("List of all configuration options for a `%s` %s.", capNameInTitle, specificationType)
specificationContent, err := generateSpecification(capName, referenceSourcePath)
if err != nil {
return err
}
specification := fmt.Sprintf("\n\n## Specification\n\n%s\n\n%s", specificationIntro, specificationContent)
conflictWithAndMoreSection, err := generateConflictWithAndMore(capName, referenceSourcePath)
if err != nil {
return err
}
refContent = title + description + specification + refContent + conflictWithAndMoreSection
if _, err := f.WriteString(refContent); err != nil {
return nil
}
if err := f.Close(); err != nil {
return err
}
}
return nil
}
// prepareParameterTable prepares the table content for each property
func (ref *ReferenceMarkdown) prepareParameterTable(tableName string, parameterList []Parameter) string {
refContent := fmt.Sprintf("\n\n%s\n\n", tableName)
refContent += "Name | Description | Type | Required | Default \n"
refContent += "------------ | ------------- | ------------- | ------------- | ------------- \n"
for _, p := range parameterList {
printableDefaultValue := getPrintableDefaultValue(p.Default)
refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, p.Usage, p.PrintableType, p.Required, printableDefaultValue)
}
return refContent
}
// parseParameters parses every parameter
func (ref *ReferenceMarkdown) parseParameters(paraValue cue.Value, paramKey string, depth int) error {
var params []Parameter
*recurseDepth++
switch paraValue.Kind() {
case cue.StructKind:
arguments, err := paraValue.Struct()
if err != nil {
return fmt.Errorf("arguments not defined as struct %w", err)
}
for i := 0; i < arguments.Len(); i++ {
var param Parameter
fi := arguments.Field(i)
if fi.IsDefinition {
continue
}
val := fi.Value
name := fi.Name
param.Name = name
param.Required = !fi.IsOptional
if def, ok := val.Default(); ok && def.IsConcrete() {
param.Default = mycue.GetDefault(def)
}
param.Short, param.Usage, param.Alias = mycue.RetrieveComments(val)
param.Type = val.IncompleteKind()
switch val.IncompleteKind() {
case cue.StructKind:
depth := *recurseDepth
// TODO(zzxwill) this case not processed `selector?: [string]: string`
if name == "selector" {
param.PrintableType = "map[string]string"
} else {
if err := ref.parseParameters(val, name, depth); err != nil {
return err
}
param.PrintableType = fmt.Sprintf("[%s](#%s)", name, name)
}
case cue.ListKind:
elem, success := val.Elem()
if !success {
return fmt.Errorf("failed to get elements from %s", val)
}
switch elem.Kind() {
case cue.StructKind:
param.PrintableType = fmt.Sprintf("[[]%s](#%s)", name, name)
depth := *recurseDepth
if err := ref.parseParameters(elem, name, depth); err != nil {
return err
}
default:
param.Type = elem.Kind()
param.PrintableType = fmt.Sprintf("[]%s", elem.IncompleteKind().String())
}
default:
param.PrintableType = param.Type.String()
}
params = append(params, param)
}
default:
//
}
tableName := fmt.Sprintf("%s %s", strings.Repeat("#", depth+2), paramKey)
refContent = ref.prepareParameterTable(tableName, params) + refContent
return nil
}
// getPrintableDefaultValue converts the value in `interface{}` type to be printable
func getPrintableDefaultValue(v interface{}) string {
if v == nil {
return ""
}
switch v.(type) {
case Int64Type:
return strconv.FormatInt(v.(int64), 10)
case StringType:
if v == "" {
return "empty"
}
return v.(string)
case BoolType:
return strconv.FormatBool(v.(bool))
}
return ""
}
// generateSpecification generates Specification part for reference docs
func generateSpecification(capability string, referenceSourcePath string) (string, error) {
configurationPath, err := filepath.Abs(filepath.Join(referenceSourcePath, "configurations", fmt.Sprintf("%s.yaml", capability)))
if err != nil {
return "", fmt.Errorf("failed to get configuration path: %w", err)
}
spec, err := ioutil.ReadFile(filepath.Clean(configurationPath))
// skip if Configuration usage of a capability doesn't exist.
if err != nil {
spec = nil
}
return fmt.Sprintf("```yaml\n%s```", string(spec)), nil
}
// generateConflictWithAndMore generates Section `Conflicts With` and more like `How xxx works` in reference docs
func generateConflictWithAndMore(capabilityName string, referenceSourcePath string) (string, error) {
conflictWithFile, err := filepath.Abs(filepath.Join(referenceSourcePath, "conflictsWithAndMore", fmt.Sprintf("%s.md", capabilityName)))
if err != nil {
return "", fmt.Errorf("failed to locate conflictWith file: %w", err)
}
data, err := ioutil.ReadFile(filepath.Clean(conflictWithFile))
if err != nil {
return "", nil
}
return "\n" + string(data), nil
}