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

Add subcategory support to the generate command #203

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Available commands are:
the generate command is run by default
generate generates a plugin website from code, templates, and examples for the current directory
validate validates a plugin website for the current directory

```

`generate` command:
Expand All @@ -39,14 +39,15 @@ $ tfplugindocs generate --help

Usage: tfplugindocs generate [<args>]

--examples-dir <ARG> examples directory (default: "examples")
--ignore-deprecated <ARG> don't generate documentation for deprecated resources and data-sources (default: "false")
--legacy-sidebar <ARG> generate the legacy .erb sidebar file (default: "false")
--examples-dir <ARG> examples directory (default: "examples")
--ignore-deprecated <ARG> don't generate documentation for deprecated resources and data-sources (default: "false")
--legacy-sidebar <ARG> generate the legacy .erb sidebar file (default: "false")
--provider-name <ARG> provider name, as used in Terraform configurations
--rendered-provider-name <ARG> provider name, as generated in documentation (ex. page titles, ...)
--rendered-website-dir <ARG> output directory (default: "docs")
--rendered-website-dir <ARG> output directory (default: "docs")
--subcategory <ARG> an optional subcategory mapping to group resources, can be specified multiple time e.g. --subcategory consul_acl=ACL --subcategory consul_admin="Admin Partition"
--tf-version <ARG> terraform binary version to download
--website-source-dir <ARG> templates directory (default: "templates")
--website-source-dir <ARG> templates directory (default: "templates")
--website-temp-dir <ARG> temporary directory (used during generation)
```

Expand Down Expand Up @@ -150,6 +151,7 @@ using the following data fields and functions:
|------------------------:|:------:|-------------------------------------------------------------------------------------------|
| `.Name` | string | Name of the resource/data-source (ex. `tls_certificate`) |
| `.Type` | string | Either `Resource` or `Data Source` |
| `.SubCategory` | string | The subcategory for this resource or an empty string if unset |
| `.Description` | string | Resource / Data Source description |
| `.HasExample` | bool | Is there an example file? |
| `.ExampleFile` | string | Path to the file with the terraform configuration example |
Expand Down
7 changes: 6 additions & 1 deletion internal/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type generateCmd struct {
flagWebsiteTmpDir string
flagWebsiteSourceDir string
tfVersion string
flagSubcategory provider.SubCategories
}

func (cmd *generateCmd) Synopsis() string {
Expand All @@ -42,7 +43,7 @@ func (cmd *generateCmd) Help() string {
}
})

strBuilder.WriteString(fmt.Sprintf("\nUsage: tfplugindocs generate [<args>]\n\n"))
strBuilder.WriteString("\nUsage: tfplugindocs generate [<args>]\n\n")
cmd.Flags().VisitAll(func(f *flag.Flag) {
if f.DefValue != "" {
strBuilder.WriteString(fmt.Sprintf(" --%s <ARG> %s%s%s (default: %q)\n",
Expand All @@ -67,6 +68,8 @@ func (cmd *generateCmd) Help() string {
}

func (cmd *generateCmd) Flags() *flag.FlagSet {
cmd.flagSubcategory = provider.SubCategories{}

fs := flag.NewFlagSet("generate", flag.ExitOnError)
fs.BoolVar(&cmd.flagLegacySidebar, "legacy-sidebar", false, "generate the legacy .erb sidebar file")
fs.StringVar(&cmd.flagProviderName, "provider-name", "", "provider name, as used in Terraform configurations")
Expand All @@ -77,6 +80,7 @@ func (cmd *generateCmd) Flags() *flag.FlagSet {
fs.StringVar(&cmd.flagWebsiteSourceDir, "website-source-dir", "templates", "templates directory")
fs.StringVar(&cmd.tfVersion, "tf-version", "", "terraform binary version to download")
fs.BoolVar(&cmd.flagIgnoreDeprecated, "ignore-deprecated", false, "don't generate documentation for deprecated resources and data-sources")
fs.Var(&cmd.flagSubcategory, "subcategory", "an optional subcategory mapping to group resources, can be specified multiple time e.g. --subcategory consul_acl=ACL --subcategory consul_admin=\"Admin Partition\"")
return fs
}

Expand All @@ -103,6 +107,7 @@ func (cmd *generateCmd) runInternal() error {
cmd.flagWebsiteSourceDir,
cmd.tfVersion,
cmd.flagIgnoreDeprecated,
cmd.flagSubcategory,
)
if err != nil {
return fmt.Errorf("unable to generate website: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (cmd *validateCmd) Help() string {
}
})

strBuilder.WriteString(fmt.Sprintf("\nUsage: tfplugindocs validate [<args>]\n\n"))
strBuilder.WriteString("\nUsage: tfplugindocs validate [<args>]\n\n")
cmd.Flags().VisitAll(func(f *flag.Flag) {
if f.DefValue != "" {
strBuilder.WriteString(fmt.Sprintf(" --%s <ARG> %s%s%s (default: %q)\n",
Expand Down
66 changes: 59 additions & 7 deletions internal/provider/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,51 @@ type generator struct {
websiteTmpDir string
websiteSourceDir string

subcategories SubCategories

ui cli.Ui
}

type SubCategories map[string]string

func (s *SubCategories) String() string {
if s == nil {
return ""
}

var subcategories []string
for k, v := range *s {
subcategories = append(subcategories, fmt.Sprintf("%s=%q", k, v))
}

return strings.Join(subcategories, ", ")
}

func (s *SubCategories) Set(v string) error {
parts := strings.SplitN(v, "=", 2)
if len(parts) != 2 {
return fmt.Errorf("wrong format for %q, expected prefix=\"SubCategory Name\"", v)
}

prefix := parts[0]
if sc, found := (*s)[prefix]; found {
return fmt.Errorf("%s is already registered with subcategory %q", prefix, sc)
}

(*s)[prefix] = parts[1]
return nil
}

func (s *SubCategories) Get(name string) string {
for k, v := range *s {
if strings.HasPrefix(name, k) {
return v
}
}

return ""
}

func (g *generator) infof(format string, a ...interface{}) {
g.ui.Info(fmt.Sprintf(format, a...))
}
Expand All @@ -86,7 +128,7 @@ func (g *generator) warnf(format string, a ...interface{}) {
g.ui.Warn(fmt.Sprintf(format, a...))
}

func Generate(ui cli.Ui, legacySidebar bool, providerName, renderedProviderName, renderedWebsiteDir, examplesDir, websiteTmpDir, websiteSourceDir, tfVersion string, ignoreDeprecated bool) error {
func Generate(ui cli.Ui, legacySidebar bool, providerName, renderedProviderName, renderedWebsiteDir, examplesDir, websiteTmpDir, websiteSourceDir, tfVersion string, ignoreDeprecated bool, subcategories SubCategories) error {
g := &generator{
ignoreDeprecated: ignoreDeprecated,
legacySidebar: legacySidebar,
Expand All @@ -99,6 +141,8 @@ func Generate(ui cli.Ui, legacySidebar bool, providerName, renderedProviderName,
websiteTmpDir: websiteTmpDir,
websiteSourceDir: websiteSourceDir,

subcategories: subcategories,

ui: ui,
}

Expand Down Expand Up @@ -192,7 +236,7 @@ func (g *generator) Generate(ctx context.Context) error {
return nil
}

func (g *generator) renderMissingResourceDoc(providerName, name, typeName string, schema *tfjson.Schema, websiteFileTemplate resourceFileTemplate, fallbackWebsiteFileTemplate resourceFileTemplate, websiteStaticCandidateTemplates []resourceFileTemplate, examplesFileTemplate resourceFileTemplate, examplesImportTemplate *resourceFileTemplate) error {
func (g *generator) renderMissingResourceDoc(providerName, name, typeName, subCategory string, schema *tfjson.Schema, websiteFileTemplate resourceFileTemplate, fallbackWebsiteFileTemplate resourceFileTemplate, websiteStaticCandidateTemplates []resourceFileTemplate, examplesFileTemplate resourceFileTemplate, examplesImportTemplate *resourceFileTemplate) error {
tmplPath, err := websiteFileTemplate.Render(name, providerName)
if err != nil {
return fmt.Errorf("unable to render path for resource %q: %w", name, err)
Expand Down Expand Up @@ -257,7 +301,7 @@ func (g *generator) renderMissingResourceDoc(providerName, name, typeName string
}

g.infof("generating template for %q", name)
md, err := targetResourceTemplate.Render(name, providerName, g.renderedProviderName, typeName, examplePath, importPath, schema)
md, err := targetResourceTemplate.Render(name, providerName, g.renderedProviderName, typeName, subCategory, examplePath, importPath, schema)
if err != nil {
return fmt.Errorf("unable to render template for %q: %w", name, err)
}
Expand Down Expand Up @@ -325,7 +369,10 @@ func (g *generator) renderMissingDocs(providerName string, providerSchema *tfjso
continue
}

err := g.renderMissingResourceDoc(providerName, name, "Resource", schema,
subCategory := g.subcategories.Get(name)

err := g.renderMissingResourceDoc(providerName, name, "Resource", subCategory,
schema,
websiteResourceFileTemplate,
websiteResourceFallbackFileTemplate,
websiteResourceFileStatic,
Expand All @@ -342,7 +389,10 @@ func (g *generator) renderMissingDocs(providerName string, providerSchema *tfjso
continue
}

err := g.renderMissingResourceDoc(providerName, name, "Data Source", schema,
subCategory := g.subcategories.Get(name)

err := g.renderMissingResourceDoc(providerName, name, "Data Source", subCategory,
schema,
websiteDataSourceFileTemplate,
websiteDataSourceFallbackFileTemplate,
websiteDataSourceFileStatic,
Expand Down Expand Up @@ -425,10 +475,11 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj
switch relDir {
case "data-sources/":
resSchema, resName := resourceSchema(providerSchema.DataSourceSchemas, shortName, relFile)
subCategory := g.subcategories.Get(resName)
exampleFilePath := filepath.Join(g.examplesDir, "data-sources", resName, "data-source.tf")
if resSchema != nil {
tmpl := resourceTemplate(tmplData)
render, err := tmpl.Render(resName, providerName, g.renderedProviderName, "Data Source", exampleFilePath, "", resSchema)
render, err := tmpl.Render(resName, providerName, g.renderedProviderName, "Data Source", subCategory, exampleFilePath, "", resSchema)
if err != nil {
return fmt.Errorf("unable to render data source template %q: %w", rel, err)
}
Expand All @@ -441,12 +492,13 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj
g.warnf("data source entitled %q, or %q does not exist", shortName, resName)
case "resources/":
resSchema, resName := resourceSchema(providerSchema.ResourceSchemas, shortName, relFile)
subCategory := g.subcategories.Get(resName)
exampleFilePath := filepath.Join(g.examplesDir, "resources", resName, "resource.tf")
importFilePath := filepath.Join(g.examplesDir, "resources", resName, "import.sh")

if resSchema != nil {
tmpl := resourceTemplate(tmplData)
render, err := tmpl.Render(resName, providerName, g.renderedProviderName, "Resource", exampleFilePath, importFilePath, resSchema)
render, err := tmpl.Render(resName, providerName, g.renderedProviderName, "Resource", subCategory, exampleFilePath, importFilePath, resSchema)
if err != nil {
return fmt.Errorf("unable to render resource template %q: %w", rel, err)
}
Expand Down
8 changes: 5 additions & 3 deletions internal/provider/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (t providerTemplate) Render(providerName, renderedProviderName, exampleFile
})
}

func (t resourceTemplate) Render(name, providerName, renderedProviderName, typeName, exampleFile, importFile string, schema *tfjson.Schema) (string, error) {
func (t resourceTemplate) Render(name, providerName, renderedProviderName, typeName, subCategory, exampleFile, importFile string, schema *tfjson.Schema) (string, error) {
schemaBuffer := bytes.NewBuffer(nil)
err := schemamd.Render(schema, schemaBuffer)
if err != nil {
Expand All @@ -181,6 +181,7 @@ func (t resourceTemplate) Render(name, providerName, renderedProviderName, typeN
Type string
Name string
Description string
SubCategory string

HasExample bool
ExampleFile string
Expand All @@ -198,6 +199,7 @@ func (t resourceTemplate) Render(name, providerName, renderedProviderName, typeN
Type: typeName,
Name: name,
Description: schema.Block.Description,
SubCategory: subCategory,

HasExample: exampleFile != "" && fileExists(exampleFile),
ExampleFile: exampleFile,
Expand All @@ -217,7 +219,7 @@ func (t resourceTemplate) Render(name, providerName, renderedProviderName, typeN
const defaultResourceTemplate resourceTemplate = `---
` + frontmatterComment + `
page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}"
subcategory: ""
subcategory: "{{.SubCategory}}"
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
Expand Down Expand Up @@ -246,7 +248,7 @@ Import is supported using the following syntax:
const defaultProviderTemplate providerTemplate = `---
` + frontmatterComment + `
page_title: "{{.ProviderShortName}} Provider"
subcategory: ""
subcategory: "{{.SubCategory}}"
description: |-
{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
Expand Down