feat(P2+P3): docs-generator — убрать ID, value_list читаемый, двойной пример, _nav_fragment.yml, mkdocs breadcrumbs
- renderParamTable/renderModifyTable/renderNestedParams: убрать колонку ID - collectConstraints: value_list → Допустимые значения - buildExamplePage: минимальный пример + полный в <details> - minimalExampleBlock: только required без default - WriteNavFragment: генерация _nav_fragment.yml с категориями - mkdocs.yml: navigation.path, navigation.footer, navigation.indexes
This commit is contained in:
@@ -46,6 +46,21 @@ func ResourceDocs(docsDir string, spec types.ServiceSpec, version string, apiEnd
|
||||
}
|
||||
}
|
||||
|
||||
// CollectSubresources возвращает уникальные subresource-имена.
|
||||
func CollectSubresources(spec types.ServiceSpec) []string {
|
||||
seen := map[string]bool{}
|
||||
var out []string
|
||||
for _, op := range spec.Operations {
|
||||
if op.Kind == "subresource" && op.Subresource != "" {
|
||||
if !seen[op.Subresource] {
|
||||
seen[op.Subresource] = true
|
||||
out = append(out, op.Subresource)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// WriteFile пишет контент в файл.
|
||||
func WriteFile(path, content string) {
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
@@ -72,21 +87,6 @@ func IndexMD(docsDir string, specs []types.ServiceSpec) {
|
||||
}
|
||||
}
|
||||
|
||||
// CollectSubresources возвращает уникальные subresource-имена.
|
||||
func CollectSubresources(spec types.ServiceSpec) []string {
|
||||
seen := map[string]bool{}
|
||||
var out []string
|
||||
for _, op := range spec.Operations {
|
||||
if op.Kind == "subresource" && op.Subresource != "" {
|
||||
if !seen[op.Subresource] {
|
||||
seen[op.Subresource] = true
|
||||
out = append(out, op.Subresource)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// FindParams находит параметры операции по action.
|
||||
func FindParams(ops []types.OperationSpec, action string) []types.ParamSpec {
|
||||
for _, op := range ops {
|
||||
@@ -187,10 +187,15 @@ func htmlToMarkdown(s string) string {
|
||||
func buildExamplePage(spec types.ServiceSpec, nav, version string, apiEndpoint string, providerSource string) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(buildHeader(spec, nav))
|
||||
b.WriteString(fmt.Sprintf("## Copy-ready manifest (`%s_main.tf`)\n\n", spec.Name))
|
||||
b.WriteString(fmt.Sprintf("## Minimal example — only required parameters\n\n"))
|
||||
b.WriteString("```hcl\n")
|
||||
b.WriteString(minimalExampleBlock(spec, version, apiEndpoint, providerSource))
|
||||
b.WriteString("```\n\n")
|
||||
b.WriteString("<details><summary>Full example (all parameters, including defaults)</summary>\n\n")
|
||||
b.WriteString("```hcl\n")
|
||||
b.WriteString(exampleBlock(spec, version, apiEndpoint, providerSource))
|
||||
b.WriteString("```\n\n")
|
||||
b.WriteString("</details>\n\n")
|
||||
b.WriteString("## Outputs usage\n\n")
|
||||
b.WriteString("```hcl\n")
|
||||
b.WriteString("# Выходные параметры можно использовать как\n")
|
||||
@@ -242,6 +247,40 @@ func exampleBlock(spec types.ServiceSpec, version string, apiEndpoint string, pr
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// minimalExampleBlock — только required параметры без default (минимальный работающий пример).
|
||||
func minimalExampleBlock(spec types.ServiceSpec, version string, apiEndpoint string, providerSource string) string {
|
||||
createParams := FindParams(spec.Operations, "create")
|
||||
var minimalParams []types.ParamSpec
|
||||
for _, p := range createParams {
|
||||
if p.Required && !hasDefault(p.Default) {
|
||||
minimalParams = append(minimalParams, p)
|
||||
}
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("terraform {\n")
|
||||
b.WriteString(" required_providers {\n")
|
||||
b.WriteString(" nubes = {\n")
|
||||
b.WriteString(fmt.Sprintf(" source = \"%s\"\n", providerSource))
|
||||
b.WriteString(fmt.Sprintf(" version = \"%s\"\n", version))
|
||||
b.WriteString(" }\n")
|
||||
b.WriteString(" }\n")
|
||||
b.WriteString("}\n\n")
|
||||
b.WriteString("provider \"nubes\" {\n")
|
||||
b.WriteString(" api_token = \"***\"\n")
|
||||
b.WriteString(fmt.Sprintf(" api_endpoint = \"%s\"\n", apiEndpoint))
|
||||
b.WriteString("}\n")
|
||||
b.WriteString(fmt.Sprintf("resource \"nubes_%s\" \"baza\" {\n", spec.Name))
|
||||
b.WriteString(" resource_name = \"my-resource\"\n\n")
|
||||
|
||||
for _, p := range minimalParams {
|
||||
b.WriteString(formatParamOrBlock(p, " ", true))
|
||||
}
|
||||
|
||||
b.WriteString("}\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func buildCreateParamsPage(spec types.ServiceSpec, nav string) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(buildHeader(spec, nav))
|
||||
@@ -521,13 +560,12 @@ func renderParamTable(params []types.ParamSpec, requiredTable, noDefault bool) s
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("<table class=\"%s\">\n", tableClass))
|
||||
if noDefault {
|
||||
b.WriteString("<thead><tr><th>ID</th><th>Code</th><th>Type</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
b.WriteString("<thead><tr><th>Code</th><th>Type</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
} else {
|
||||
b.WriteString("<thead><tr><th>ID</th><th>Code</th><th>Type</th><th>Default</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
b.WriteString("<thead><tr><th>Code</th><th>Type</th><th>Default</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
}
|
||||
for _, p := range params {
|
||||
b.WriteString("<tr>")
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", escapeText(formatID(p.ID))))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", formatParamCode(p.Code)))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", formatTypeCell(p)))
|
||||
if !noDefault {
|
||||
@@ -547,10 +585,9 @@ func renderModifyTable(params []types.ParamSpec) string {
|
||||
}
|
||||
var b strings.Builder
|
||||
b.WriteString("<table class=\"resource-table resource-table-compact\">\n")
|
||||
b.WriteString("<thead><tr><th>ID</th><th>Code</th><th>Type</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
b.WriteString("<thead><tr><th>Code</th><th>Type</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
for _, p := range params {
|
||||
b.WriteString("<tr>")
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", escapeText(formatID(p.ID))))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", formatParamCode(p.Code)))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", formatTypeCell(p)))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", escapeText(pickTextTable(p))))
|
||||
@@ -806,7 +843,7 @@ func collectConstraints(p types.ParamSpec) string {
|
||||
parts = append(parts, fmt.Sprintf("regex=%s", p.Regex))
|
||||
}
|
||||
if len(p.ValueList) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("value_list=%s", strings.Join(p.ValueList, ", ")))
|
||||
parts = append(parts, fmt.Sprintf("Допустимые значения: %s", strings.Join(p.ValueList, ", ")))
|
||||
}
|
||||
if p.Func != "" {
|
||||
parts = append(parts, fmt.Sprintf("func=%s", p.Func))
|
||||
@@ -992,14 +1029,13 @@ func renderNestedParams(p types.ParamSpec) string {
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("\n### %s\n\n", label))
|
||||
b.WriteString("<table class=\"resource-table resource-table-compact resource-table-nested\">\n")
|
||||
b.WriteString("<thead><tr><th>ID</th><th>Code</th><th>Type</th><th>Required</th><th>Default</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
b.WriteString("<thead><tr><th>Code</th><th>Type</th><th>Required</th><th>Default</th><th>Description</th><th>Constraints</th></tr></thead>\n<tbody>\n")
|
||||
for _, sp := range p.SubParams {
|
||||
req := "no"
|
||||
if sp.Required {
|
||||
req = "**yes**"
|
||||
}
|
||||
b.WriteString("<tr>")
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", escapeText(formatID(sp.ID))))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", formatParamCode(sp.Code)))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", formatTypeCell(sp)))
|
||||
b.WriteString(fmt.Sprintf("<td>%s</td>", req))
|
||||
@@ -1011,3 +1047,68 @@ func renderNestedParams(p types.ParamSpec) string {
|
||||
b.WriteString("</tbody></table>\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// serviceCategory возвращает категорию для сервиса.
|
||||
func serviceCategory(name string) string {
|
||||
cats := map[string]string{
|
||||
"postgres": "Базы данных", "redis": "Базы данных", "mongodb": "Базы данных",
|
||||
"mariadb": "Базы данных", "clickhouse": "Базы данных",
|
||||
"rabbitmq": "Очереди", "kafka": "Очереди",
|
||||
"s3": "Хранилище", "s3bucket": "Хранилище", "nextcloud": "Хранилище",
|
||||
"k8s_velero": "K8s", "k8s_sthutrval_cluster": "K8s", "k8s_openbao": "K8s",
|
||||
"vc_mgmt_sthutrval_cluster": "K8s",
|
||||
"vc_org": "VMware", "vc_vdc": "VMware", "vc_nsxt": "VMware",
|
||||
"vcexternalip": "VMware", "vapp": "VMware", "vc_vm_v2": "VMware",
|
||||
"vc_vm_v3": "VMware", "vc_vdc_group": "VMware",
|
||||
"flask": "Приложения", "nodejs": "Приложения", "lucee": "Приложения",
|
||||
"http": "Приложения", "gitea": "Приложения", "superset": "Приложения",
|
||||
"pgadmin": "Приложения", "harbor": "Приложения", "akhq": "Приложения",
|
||||
"llm_ai": "Приложения", "template": "Приложения", "dummy": "Приложения",
|
||||
"valo_tenant": "Приложения",
|
||||
"zones_v2": "Сеть", "dnsrecord": "Сеть",
|
||||
}
|
||||
if cat, ok := cats[name]; ok {
|
||||
return cat
|
||||
}
|
||||
return "Другие"
|
||||
}
|
||||
|
||||
// WriteNavFragment генерирует _nav_fragment.yml для mkdocs sidebar.
|
||||
func WriteNavFragment(docsDir string, specs []types.ServiceSpec) {
|
||||
catMap := map[string][]types.ServiceSpec{}
|
||||
for _, s := range specs {
|
||||
cat := serviceCategory(s.Name)
|
||||
catMap[cat] = append(catMap[cat], s)
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
b.WriteString("# auto-generated by docs-generator — DO NOT EDIT\n")
|
||||
b.WriteString("resources_nav:\n")
|
||||
|
||||
order := []string{"Базы данных", "Очереди", "Хранилище", "K8s", "VMware", "Приложения", "Сеть", "Другие"}
|
||||
for _, cat := range order {
|
||||
svcs := catMap[cat]
|
||||
if len(svcs) == 0 {
|
||||
continue
|
||||
}
|
||||
b.WriteString(fmt.Sprintf(" - %s:\n", cat))
|
||||
for _, s := range svcs {
|
||||
disp := s.ServiceDisplayName
|
||||
if disp == "" {
|
||||
disp = s.Name
|
||||
}
|
||||
b.WriteString(fmt.Sprintf(" - %s:\n", disp))
|
||||
b.WriteString(fmt.Sprintf(" - Обзор: %s.md\n", s.Name))
|
||||
b.WriteString(fmt.Sprintf(" - Параметры создания: %s_params_create.md\n", s.Name))
|
||||
b.WriteString(fmt.Sprintf(" - Параметры изменения: %s_params_modify.md\n", s.Name))
|
||||
b.WriteString(fmt.Sprintf(" - Выходные данные: %s_outputs.md\n", s.Name))
|
||||
b.WriteString(fmt.Sprintf(" - Операции: %s_ops.md\n", s.Name))
|
||||
b.WriteString(fmt.Sprintf(" - Пример: %s_example.md\n", s.Name))
|
||||
}
|
||||
}
|
||||
|
||||
outPath := filepath.Join(docsDir, "_nav_fragment.yml")
|
||||
if err := os.WriteFile(outPath, b.Bytes(), 0o644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to write _nav_fragment.yml: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user