Bump github.com/hashicorp/terraform-plugin-docs from 0.7.0 to 0.13.0

Bumps [github.com/hashicorp/terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs) from 0.7.0 to 0.13.0.
- [Release notes](https://github.com/hashicorp/terraform-plugin-docs/releases)
- [Changelog](https://github.com/hashicorp/terraform-plugin-docs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/terraform-plugin-docs/compare/v0.7.0...v0.13.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/terraform-plugin-docs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2022-12-18 17:14:23 +00:00
committed by GitHub
parent ad07770b6b
commit b4859cda6b
210 changed files with 32649 additions and 2138 deletions

View File

@ -44,6 +44,8 @@ func Wrap(outer, inner error) error {
//
// format is the format of the error message. The string '{{err}}' will
// be replaced with the original error message.
//
// Deprecated: Use fmt.Errorf()
func Wrapf(format string, err error) error {
outerMsg := "<nil>"
if err != nil {
@ -148,6 +150,9 @@ func Walk(err error, cb WalkFunc) {
for _, err := range e.WrappedErrors() {
Walk(err, cb)
}
case interface{ Unwrap() error }:
cb(err)
Walk(e.Unwrap(), cb)
default:
cb(err)
}
@ -167,3 +172,7 @@ func (w *wrappedError) Error() string {
func (w *wrappedError) WrappedErrors() []error {
return []error{w.Outer, w.Inner}
}
func (w *wrappedError) Unwrap() error {
return w.Inner
}

View File

@ -3,6 +3,7 @@ package cmd
import (
"flag"
"fmt"
"strings"
"github.com/hashicorp/terraform-plugin-docs/internal/provider"
)
@ -10,8 +11,17 @@ import (
type generateCmd struct {
commonCmd
flagLegacySidebar bool
tfVersion string
flagLegacySidebar bool
flagIgnoreDeprecated bool
flagProviderName string
flagRenderedProviderName string
flagRenderedWebsiteDir string
flagExamplesDir string
flagWebsiteTmpDir string
flagWebsiteSourceDir string
tfVersion string
}
func (cmd *generateCmd) Synopsis() string {
@ -19,13 +29,54 @@ func (cmd *generateCmd) Synopsis() string {
}
func (cmd *generateCmd) Help() string {
return `Usage: tfplugindocs generate`
strBuilder := &strings.Builder{}
longestName := 0
longestUsage := 0
cmd.Flags().VisitAll(func(f *flag.Flag) {
if len(f.Name) > longestName {
longestName = len(f.Name)
}
if len(f.Usage) > longestUsage {
longestUsage = len(f.Usage)
}
})
strBuilder.WriteString(fmt.Sprintf("\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",
f.Name,
strings.Repeat(" ", longestName-len(f.Name)+2),
f.Usage,
strings.Repeat(" ", longestUsage-len(f.Usage)+2),
f.DefValue,
))
} else {
strBuilder.WriteString(fmt.Sprintf(" --%s <ARG> %s%s%s\n",
f.Name,
strings.Repeat(" ", longestName-len(f.Name)+2),
f.Usage,
strings.Repeat(" ", longestUsage-len(f.Usage)+2),
))
}
})
strBuilder.WriteString("\n")
return strBuilder.String()
}
func (cmd *generateCmd) Flags() *flag.FlagSet {
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")
fs.StringVar(&cmd.flagRenderedProviderName, "rendered-provider-name", "", "provider name, as generated in documentation (ex. page titles, ...)")
fs.StringVar(&cmd.flagRenderedWebsiteDir, "rendered-website-dir", "docs", "output directory")
fs.StringVar(&cmd.flagExamplesDir, "examples-dir", "examples", "examples directory")
fs.StringVar(&cmd.flagWebsiteTmpDir, "website-temp-dir", "", "temporary directory (used during generation)")
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")
return fs
}
@ -41,7 +92,18 @@ func (cmd *generateCmd) Run(args []string) int {
}
func (cmd *generateCmd) runInternal() error {
err := provider.Generate(cmd.ui, cmd.flagLegacySidebar, cmd.tfVersion)
err := provider.Generate(
cmd.ui,
cmd.flagLegacySidebar,
cmd.flagProviderName,
cmd.flagRenderedProviderName,
cmd.flagRenderedWebsiteDir,
cmd.flagExamplesDir,
cmd.flagWebsiteTmpDir,
cmd.flagWebsiteSourceDir,
cmd.tfVersion,
cmd.flagIgnoreDeprecated,
)
if err != nil {
return fmt.Errorf("unable to generate website: %w", err)
}

View File

@ -3,6 +3,7 @@ package cmd
import (
"flag"
"fmt"
"strings"
"github.com/hashicorp/terraform-plugin-docs/internal/provider"
)
@ -16,7 +17,41 @@ func (cmd *validateCmd) Synopsis() string {
}
func (cmd *validateCmd) Help() string {
return `Usage: tfplugindocs validate`
strBuilder := &strings.Builder{}
longestName := 0
longestUsage := 0
cmd.Flags().VisitAll(func(f *flag.Flag) {
if len(f.Name) > longestName {
longestName = len(f.Name)
}
if len(f.Usage) > longestUsage {
longestUsage = len(f.Usage)
}
})
strBuilder.WriteString(fmt.Sprintf("\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",
f.Name,
strings.Repeat(" ", longestName-len(f.Name)+2),
f.Usage,
strings.Repeat(" ", longestUsage-len(f.Usage)+2),
f.DefValue,
))
} else {
strBuilder.WriteString(fmt.Sprintf(" --%s <ARG> %s%s%s\n",
f.Name,
strings.Repeat(" ", longestName-len(f.Name)+2),
f.Usage,
strings.Repeat(" ", longestUsage-len(f.Usage)+2),
))
}
})
strBuilder.WriteString("\n")
return strBuilder.String()
}
func (cmd *validateCmd) Flags() *flag.FlagSet {

View File

@ -22,25 +22,12 @@ import (
"github.com/mitchellh/cli"
)
// TODO: convert these to flags?
var (
providerName string
// rendered website dir
renderedWebsiteDir = "docs"
// examples directory defaults
examplesDir = "examples"
// relative to examples dir
examplesResourceFileTemplate = resourceFileTemplate("resources/{{.Name}}/resource.tf")
examplesResourceImportTemplate = resourceFileTemplate("resources/{{.Name}}/import.sh")
examplesDataSourceFileTemplate = resourceFileTemplate("data-sources/{{ .Name }}/data-source.tf")
examplesProviderFileTemplate = providerFileTemplate("provider/provider.tf")
// templated website directory defaults
websiteTmp = ""
websiteSourceDir = "templates" // used for override content
websiteResourceFileTemplate = resourceFileTemplate("resources/{{ .ShortName }}.md.tmpl")
websiteResourceFallbackFileTemplate = resourceFileTemplate("resources.md.tmpl")
websiteResourceFileStatic = []resourceFileTemplate{
@ -77,8 +64,16 @@ var (
)
type generator struct {
legacySidebar bool
tfVersion string
ignoreDeprecated bool
legacySidebar bool
tfVersion string
providerName string
renderedProviderName string
renderedWebsiteDir string
examplesDir string
websiteTmpDir string
websiteSourceDir string
ui cli.Ui
}
@ -91,10 +86,18 @@ func (g *generator) warnf(format string, a ...interface{}) {
g.ui.Warn(fmt.Sprintf(format, a...))
}
func Generate(ui cli.Ui, legacySidebar bool, tfVersion string) error {
func Generate(ui cli.Ui, legacySidebar bool, providerName, renderedProviderName, renderedWebsiteDir, examplesDir, websiteTmpDir, websiteSourceDir, tfVersion string, ignoreDeprecated bool) error {
g := &generator{
legacySidebar: legacySidebar,
tfVersion: tfVersion,
ignoreDeprecated: ignoreDeprecated,
legacySidebar: legacySidebar,
tfVersion: tfVersion,
providerName: providerName,
renderedProviderName: renderedProviderName,
renderedWebsiteDir: renderedWebsiteDir,
examplesDir: examplesDir,
websiteTmpDir: websiteTmpDir,
websiteSourceDir: websiteSourceDir,
ui: ui,
}
@ -112,34 +115,39 @@ func (g *generator) Generate(ctx context.Context) error {
return err
}
if providerName == "" {
providerName := g.providerName
if g.providerName == "" {
providerName = filepath.Base(wd)
}
g.infof("rendering website for provider %q", providerName)
if g.renderedProviderName == "" {
g.renderedProviderName = providerName
}
g.infof("rendering website for provider %q (as %q)", providerName, g.renderedProviderName)
switch {
case websiteTmp == "":
websiteTmp, err = ioutil.TempDir("", "tfws")
case g.websiteTmpDir == "":
g.websiteTmpDir, err = ioutil.TempDir("", "tfws")
if err != nil {
return err
}
defer os.RemoveAll(websiteTmp)
defer os.RemoveAll(g.websiteTmpDir)
default:
g.infof("cleaning tmp dir %q", websiteTmp)
err = os.RemoveAll(websiteTmp)
g.infof("cleaning tmp dir %q", g.websiteTmpDir)
err = os.RemoveAll(g.websiteTmpDir)
if err != nil {
return err
}
g.infof("creating tmp dir %q", websiteTmp)
err = os.MkdirAll(websiteTmp, 0755)
g.infof("creating tmp dir %q", g.websiteTmpDir)
err = os.MkdirAll(g.websiteTmpDir, 0755)
if err != nil {
return err
}
}
websiteSourceDirInfo, err := os.Stat(websiteSourceDir)
websiteSourceDirInfo, err := os.Stat(g.websiteSourceDir)
switch {
case os.IsNotExist(err):
// do nothing, no template dir
@ -147,11 +155,11 @@ func (g *generator) Generate(ctx context.Context) error {
return err
default:
if !websiteSourceDirInfo.IsDir() {
return fmt.Errorf("template path is not a directory: %s", websiteSourceDir)
return fmt.Errorf("template path is not a directory: %s", g.websiteSourceDir)
}
g.infof("copying any existing content to tmp dir")
err = cp(websiteSourceDir, filepath.Join(websiteTmp, "templates"))
err = cp(g.websiteSourceDir, filepath.Join(g.websiteTmpDir, "templates"))
if err != nil {
return err
}
@ -189,7 +197,7 @@ func (g *generator) renderMissingResourceDoc(providerName, name, typeName string
if err != nil {
return fmt.Errorf("unable to render path for resource %q: %w", name, err)
}
tmplPath = filepath.Join(websiteTmp, websiteSourceDir, tmplPath)
tmplPath = filepath.Join(g.websiteTmpDir, g.websiteSourceDir, tmplPath)
if fileExists(tmplPath) {
g.infof("resource %q template exists, skipping", name)
return nil
@ -200,7 +208,7 @@ func (g *generator) renderMissingResourceDoc(providerName, name, typeName string
if err != nil {
return fmt.Errorf("unable to render path for resource %q: %w", name, err)
}
candidatePath = filepath.Join(websiteTmp, websiteSourceDir, candidatePath)
candidatePath = filepath.Join(g.websiteTmpDir, g.websiteSourceDir, candidatePath)
if fileExists(candidatePath) {
g.infof("resource %q static file exists, skipping", name)
return nil
@ -212,7 +220,7 @@ func (g *generator) renderMissingResourceDoc(providerName, name, typeName string
return fmt.Errorf("unable to render example file path for %q: %w", name, err)
}
if examplePath != "" {
examplePath = filepath.Join(examplesDir, examplePath)
examplePath = filepath.Join(g.examplesDir, examplePath)
}
if !fileExists(examplePath) {
examplePath = ""
@ -225,7 +233,7 @@ func (g *generator) renderMissingResourceDoc(providerName, name, typeName string
return fmt.Errorf("unable to render example import file path for %q: %w", name, err)
}
if importPath != "" {
importPath = filepath.Join(examplesDir, importPath)
importPath = filepath.Join(g.examplesDir, importPath)
}
if !fileExists(importPath) {
importPath = ""
@ -238,7 +246,7 @@ func (g *generator) renderMissingResourceDoc(providerName, name, typeName string
if err != nil {
return fmt.Errorf("unable to render path for resource %q: %w", name, err)
}
fallbackTmplPath = filepath.Join(websiteTmp, websiteSourceDir, fallbackTmplPath)
fallbackTmplPath = filepath.Join(g.websiteTmpDir, g.websiteSourceDir, fallbackTmplPath)
if fileExists(fallbackTmplPath) {
g.infof("resource %q fallback template exists", name)
tmplData, err := ioutil.ReadFile(fallbackTmplPath)
@ -249,7 +257,7 @@ func (g *generator) renderMissingResourceDoc(providerName, name, typeName string
}
g.infof("generating template for %q", name)
md, err := targetResourceTemplate.Render(name, providerName, typeName, examplePath, importPath, schema)
md, err := targetResourceTemplate.Render(name, providerName, g.renderedProviderName, typeName, examplePath, importPath, schema)
if err != nil {
return fmt.Errorf("unable to render template for %q: %w", name, err)
}
@ -267,7 +275,7 @@ func (g *generator) renderMissingProviderDoc(providerName string, schema *tfjson
if err != nil {
return fmt.Errorf("unable to render path for provider %q: %w", providerName, err)
}
tmplPath = filepath.Join(websiteTmp, websiteSourceDir, tmplPath)
tmplPath = filepath.Join(g.websiteTmpDir, g.websiteSourceDir, tmplPath)
if fileExists(tmplPath) {
g.infof("provider %q template exists, skipping", providerName)
return nil
@ -278,7 +286,7 @@ func (g *generator) renderMissingProviderDoc(providerName string, schema *tfjson
if err != nil {
return fmt.Errorf("unable to render path for provider %q: %w", providerName, err)
}
candidatePath = filepath.Join(websiteTmp, websiteSourceDir, candidatePath)
candidatePath = filepath.Join(g.websiteTmpDir, g.websiteSourceDir, candidatePath)
if fileExists(candidatePath) {
g.infof("provider %q static file exists, skipping", providerName)
return nil
@ -290,14 +298,14 @@ func (g *generator) renderMissingProviderDoc(providerName string, schema *tfjson
return fmt.Errorf("unable to render example file path for %q: %w", providerName, err)
}
if examplePath != "" {
examplePath = filepath.Join(examplesDir, examplePath)
examplePath = filepath.Join(g.examplesDir, examplePath)
}
if !fileExists(examplePath) {
examplePath = ""
}
g.infof("generating template for %q", providerName)
md, err := defaultProviderTemplate.Render(providerName, examplePath, schema)
md, err := defaultProviderTemplate.Render(providerName, g.renderedProviderName, examplePath, schema)
if err != nil {
return fmt.Errorf("unable to render template for %q: %w", providerName, err)
}
@ -313,6 +321,10 @@ func (g *generator) renderMissingProviderDoc(providerName string, schema *tfjson
func (g *generator) renderMissingDocs(providerName string, providerSchema *tfjson.ProviderSchema) error {
g.infof("generating missing resource content")
for name, schema := range providerSchema.ResourceSchemas {
if g.ignoreDeprecated && schema.Block.Deprecated {
continue
}
err := g.renderMissingResourceDoc(providerName, name, "Resource", schema,
websiteResourceFileTemplate,
websiteResourceFallbackFileTemplate,
@ -326,6 +338,10 @@ func (g *generator) renderMissingDocs(providerName string, providerSchema *tfjso
g.infof("generating missing data source content")
for name, schema := range providerSchema.DataSourceSchemas {
if g.ignoreDeprecated && schema.Block.Deprecated {
continue
}
err := g.renderMissingResourceDoc(providerName, name, "Data Source", schema,
websiteDataSourceFileTemplate,
websiteDataSourceFallbackFileTemplate,
@ -352,7 +368,7 @@ func (g *generator) renderMissingDocs(providerName string, providerSchema *tfjso
func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfjson.ProviderSchema) error {
g.infof("cleaning rendered website dir")
err := os.RemoveAll(renderedWebsiteDir)
err := os.RemoveAll(g.renderedWebsiteDir)
if err != nil {
return err
}
@ -361,13 +377,13 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj
g.infof("rendering templated website to static markdown")
err = filepath.Walk(websiteTmp, func(path string, info os.FileInfo, err error) error {
err = filepath.Walk(g.websiteTmpDir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
// skip directories
return nil
}
rel, err := filepath.Rel(filepath.Join(websiteTmp, websiteSourceDir), path)
rel, err := filepath.Rel(filepath.Join(g.websiteTmpDir, g.websiteSourceDir), path)
if err != nil {
return err
}
@ -380,7 +396,7 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj
return nil
}
renderedPath := filepath.Join(renderedWebsiteDir, rel)
renderedPath := filepath.Join(g.renderedWebsiteDir, rel)
err = os.MkdirAll(filepath.Dir(renderedPath), 0755)
if err != nil {
return err
@ -408,11 +424,11 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj
g.infof("rendering %q", rel)
switch relDir {
case "data-sources/":
resName := shortName + "_" + removeAllExt(relFile)
resSchema, ok := providerSchema.DataSourceSchemas[resName]
if ok {
resSchema, resName := resourceSchema(providerSchema.DataSourceSchemas, shortName, relFile)
exampleFilePath := filepath.Join(g.examplesDir, "data-sources", resName, "data-source.tf")
if resSchema != nil {
tmpl := resourceTemplate(tmplData)
render, err := tmpl.Render(resName, providerName, "Data Source", "", "", resSchema)
render, err := tmpl.Render(resName, providerName, g.renderedProviderName, "Data Source", exampleFilePath, "", resSchema)
if err != nil {
return fmt.Errorf("unable to render data source template %q: %w", rel, err)
}
@ -422,12 +438,15 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj
}
return nil
}
g.warnf("data source entitled %q, or %q does not exist", shortName, resName)
case "resources/":
resName := shortName + "_" + removeAllExt(relFile)
resSchema, ok := providerSchema.ResourceSchemas[resName]
if ok {
resSchema, resName := resourceSchema(providerSchema.ResourceSchemas, shortName, relFile)
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, "Resource", "", "", resSchema)
render, err := tmpl.Render(resName, providerName, g.renderedProviderName, "Resource", exampleFilePath, importFilePath, resSchema)
if err != nil {
return fmt.Errorf("unable to render resource template %q: %w", rel, err)
}
@ -437,10 +456,12 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj
}
return nil
}
g.warnf("resource entitled %q, or %q does not exist", shortName, resName)
case "": // provider
if relFile == "index.md.tmpl" {
tmpl := providerTemplate(tmplData)
render, err := tmpl.Render(providerName, "", providerSchema.ConfigSchema)
exampleFilePath := filepath.Join(g.examplesDir, "provider", "provider.tf")
render, err := tmpl.Render(providerName, g.renderedProviderName, exampleFilePath, providerSchema.ConfigSchema)
if err != nil {
return fmt.Errorf("unable to render provider template %q: %w", rel, err)
}
@ -505,7 +526,7 @@ provider %[1]q {
}
i := install.NewInstaller()
sources := []src.Source{}
var sources []src.Source
if g.tfVersion != "" {
g.infof("downloading Terraform CLI binary version from releases.hashicorp.com: %s", g.tfVersion)
sources = []src.Source{

View File

@ -7,7 +7,11 @@ import (
"strings"
"text/template"
"golang.org/x/text/cases"
"golang.org/x/text/language"
tfjson "github.com/hashicorp/terraform-json"
"github.com/hashicorp/terraform-plugin-docs/internal/mdplain"
"github.com/hashicorp/terraform-plugin-docs/internal/tmplfuncs"
"github.com/hashicorp/terraform-plugin-docs/schemamd"
@ -30,17 +34,19 @@ type (
func newTemplate(name, text string) (*template.Template, error) {
tmpl := template.New(name)
titleCaser := cases.Title(language.Und)
tmpl.Funcs(template.FuncMap(map[string]interface{}{
tmpl.Funcs(map[string]interface{}{
"codefile": tmplfuncs.CodeFile,
"lower": strings.ToLower,
"plainmarkdown": mdplain.PlainMarkdown,
"prefixlines": tmplfuncs.PrefixLines,
"tffile": func(file string) (string, error) {
// TODO: omit comment handling
return tmplfuncs.CodeFile("terraform", file)
},
"trimspace": strings.TrimSpace,
}))
"split": strings.Split,
"tffile": terraformCodeFile,
"title": titleCaser.String,
"trimspace": strings.TrimSpace,
"upper": strings.ToUpper,
})
var err error
tmpl, err = tmpl.Parse(text)
@ -51,6 +57,11 @@ func newTemplate(name, text string) (*template.Template, error) {
return tmpl, nil
}
func terraformCodeFile(file string) (string, error) {
// TODO: omit comment handling
return tmplfuncs.CodeFile("terraform", file)
}
func renderTemplate(name string, text string, out io.Writer, data interface{}) error {
tmpl, err := newTemplate(name, text)
if err != nil {
@ -116,7 +127,7 @@ func (t providerFileTemplate) Render(name string) (string, error) {
}{name, providerShortName(name)})
}
func (t providerTemplate) Render(providerName, exampleFile string, schema *tfjson.Schema) (string, error) {
func (t providerTemplate) Render(providerName, renderedProviderName, exampleFile string, schema *tfjson.Schema) (string, error) {
schemaBuffer := bytes.NewBuffer(nil)
err := schemamd.Render(schema, schemaBuffer)
if err != nil {
@ -128,34 +139,33 @@ func (t providerTemplate) Render(providerName, exampleFile string, schema *tfjso
return "", nil
}
return renderStringTemplate("providerTemplate", s, struct {
Type string
Name string
Description string
HasExample bool
ExampleFile string
HasImport bool
ImportFile string
ProviderName string
ProviderShortName string
SchemaMarkdown string
RenderedProviderName string
}{
Description: schema.Block.Description,
HasExample: exampleFile != "",
HasExample: exampleFile != "" && fileExists(exampleFile),
ExampleFile: exampleFile,
ProviderName: providerName,
ProviderShortName: providerShortName(providerName),
SchemaMarkdown: schemaComment + "\n" + schemaBuffer.String(),
RenderedProviderName: renderedProviderName,
})
}
func (t resourceTemplate) Render(name, providerName, typeName, exampleFile, importFile string, schema *tfjson.Schema) (string, error) {
func (t resourceTemplate) Render(name, providerName, renderedProviderName, typeName, exampleFile, importFile string, schema *tfjson.Schema) (string, error) {
schemaBuffer := bytes.NewBuffer(nil)
err := schemamd.Render(schema, schemaBuffer)
if err != nil {
@ -182,21 +192,25 @@ func (t resourceTemplate) Render(name, providerName, typeName, exampleFile, impo
ProviderShortName string
SchemaMarkdown string
RenderedProviderName string
}{
Type: typeName,
Name: name,
Description: schema.Block.Description,
HasExample: exampleFile != "",
HasExample: exampleFile != "" && fileExists(exampleFile),
ExampleFile: exampleFile,
HasImport: importFile != "",
HasImport: importFile != "" && fileExists(importFile),
ImportFile: importFile,
ProviderName: providerName,
ProviderShortName: providerShortName(providerName),
SchemaMarkdown: schemaComment + "\n" + schemaBuffer.String(),
RenderedProviderName: renderedProviderName,
})
}

View File

@ -9,6 +9,8 @@ import (
"os/exec"
"path/filepath"
"strings"
tfjson "github.com/hashicorp/terraform-json"
)
func providerShortName(n string) string {
@ -52,6 +54,23 @@ func removeAllExt(file string) string {
}
}
// resourceSchema determines whether there is a schema in the supplied schemas map which
// has either the providerShortName or the providerShortName concatenated with the
// templateFileName (stripped of file extension.
func resourceSchema(schemas map[string]*tfjson.Schema, providerShortName, templateFileName string) (*tfjson.Schema, string) {
if schema, ok := schemas[providerShortName]; ok {
return schema, providerShortName
}
resName := providerShortName + "_" + removeAllExt(templateFileName)
if schema, ok := schemas[resName]; ok {
return schema, resName
}
return nil, resName
}
func writeFile(path string, data string) error {
dir, _ := filepath.Split(path)
err := os.MkdirAll(dir, 0755)

View File

@ -16,12 +16,17 @@ func childAttributeIsOptional(att *tfjson.SchemaAttribute) bool {
return att.Optional
}
// childBlockIsOptional returns true for blocks with with min items 0 and any required or optional children.
// childBlockIsOptional returns true for blocks with with min items 0
// which are either empty or have any required or optional children.
func childBlockIsOptional(block *tfjson.SchemaBlockType) bool {
if block.MinItems > 0 {
return false
}
if len(block.Block.NestedBlocks) == 0 && len(block.Block.Attributes) == 0 {
return true
}
for _, childBlock := range block.Block.NestedBlocks {
if childBlockIsRequired(childBlock) {
return true

View File

@ -70,10 +70,6 @@ func writeAttribute(w io.Writer, path []string, att *tfjson.SchemaAttribute, gro
return nil, err
}
if name == "id" && att.Description == "" {
att.Description = "The ID of this resource."
}
if att.AttributeNestedType == nil {
err = WriteAttributeDescription(w, att, false)
} else {
@ -225,7 +221,18 @@ nameLoop:
}
} else if childAtt, ok := block.Attributes[n]; ok {
for i, gf := range groupFilters {
if gf.filterAttribute(childAtt) {
// By default, the attribute `id` is place in the "Read-Only" group
// if the provider schema contained no `.Description` for it.
//
// If a `.Description` is provided instead, the behaviour will be the
// same as for every other attribute.
if strings.ToLower(n) == "id" && childAtt.Description == "" {
if strings.Contains(gf.topLevelTitle, "Read-Only") {
childAtt.Description = "The ID of this resource."
groups[i] = append(groups[i], n)
continue nameLoop
}
} else if gf.filterAttribute(childAtt) {
groups[i] = append(groups[i], n)
continue nameLoop
}
@ -286,7 +293,9 @@ nameLoop:
}
for _, name := range sortedNames {
path := append(parents, name)
path := make([]string, len(parents), len(parents)+1)
copy(path, parents)
path = append(path, name)
if childBlock, ok := block.NestedBlocks[name]; ok {
nt, err := writeBlockType(w, path, childBlock)
@ -466,36 +475,55 @@ func writeObjectChildren(w io.Writer, parents []string, ty cty.Type, group group
}
func writeNestedAttributeChildren(w io.Writer, parents []string, nestedAttributes *tfjson.SchemaNestedAttributeType, group groupFilter) error {
_, err := io.WriteString(w, group.nestedTitle+"\n\n")
if err != nil {
return err
}
sortedNames := []string{}
for n := range nestedAttributes.Attributes {
sortedNames = append(sortedNames, n)
}
sort.Strings(sortedNames)
nestedTypes := []nestedType{}
groups := map[int][]string{}
for _, name := range sortedNames {
att := nestedAttributes.Attributes[name]
path := append(parents, name)
nt, err := writeAttribute(w, path, att, group)
if err != nil {
return fmt.Errorf("unable to render attribute %q: %w", name, err)
for i, gf := range groupFilters {
if gf.filterAttribute(att) {
groups[i] = append(groups[i], name)
}
}
}
nestedTypes := []nestedType{}
for i, gf := range groupFilters {
names, ok := groups[i]
if !ok || len(names) == 0 {
continue
}
nestedTypes = append(nestedTypes, nt...)
_, err := io.WriteString(w, gf.nestedTitle+"\n\n")
if err != nil {
return err
}
for _, name := range names {
att := nestedAttributes.Attributes[name]
path := append(parents, name)
nt, err := writeAttribute(w, path, att, group)
if err != nil {
return fmt.Errorf("unable to render attribute %q: %w", name, err)
}
nestedTypes = append(nestedTypes, nt...)
}
_, err = io.WriteString(w, "\n")
if err != nil {
return err
}
}
_, err = io.WriteString(w, "\n")
if err != nil {
return err
}
err = writeNestedTypes(w, nestedTypes)
err := writeNestedTypes(w, nestedTypes)
if err != nil {
return err
}