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 --blocks-section flag to render blocks without specifying if they are required or optional #366

Open
wants to merge 3 commits 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

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions internal/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type generateCmd struct {
flagProviderName string
flagRenderedProviderName string

flagBlocksSection bool
flagProviderDir string
flagProvidersSchema string
flagRenderedWebsiteDir string
Expand Down Expand Up @@ -72,6 +73,7 @@ func (cmd *generateCmd) Help() string {

func (cmd *generateCmd) Flags() *flag.FlagSet {
fs := flag.NewFlagSet("generate", flag.ExitOnError)
fs.BoolVar(&cmd.flagBlocksSection, "blocks-section", false, "render blocks in a separate section instead of including them with attributes in the required and optional sections.")
fs.StringVar(&cmd.flagProviderName, "provider-name", "", "provider name, as used in Terraform configurations; defaults to the --provider-dir short name (after removing `terraform-provider-` prefix)")
fs.StringVar(&cmd.flagProviderDir, "provider-dir", "", "relative or absolute path to the root provider code directory when running the command outside the root provider code directory")
fs.StringVar(&cmd.flagProvidersSchema, "providers-schema", "", "path to the providers schema JSON file, which contains the output of the terraform providers schema -json command. Setting this flag will skip building the provider and calling Terraform CLI")
Expand Down Expand Up @@ -109,6 +111,7 @@ func (cmd *generateCmd) runInternal() error {
cmd.flagWebsiteSourceDir,
cmd.tfVersion,
cmd.flagIgnoreDeprecated,
cmd.flagBlocksSection,
)
if err != nil {
return fmt.Errorf("unable to generate website: %w", err)
Expand Down
14 changes: 8 additions & 6 deletions internal/provider/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ var (
)

type generator struct {
blocksSection bool
ignoreDeprecated bool
tfVersion string

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

func Generate(ui cli.Ui, providerDir, providerName, providersSchemaPath, renderedProviderName, renderedWebsiteDir, examplesDir, websiteTmpDir, templatesDir, tfVersion string, ignoreDeprecated bool) error {
func Generate(ui cli.Ui, providerDir, providerName, providersSchemaPath, renderedProviderName, renderedWebsiteDir, examplesDir, websiteTmpDir, templatesDir, tfVersion string, ignoreDeprecated, blocksSection bool) error {
// Ensure provider directory is resolved absolute path
if providerDir == "" {
wd, err := os.Getwd()
Expand Down Expand Up @@ -136,6 +137,7 @@ func Generate(ui cli.Ui, providerDir, providerName, providersSchemaPath, rendere
}

g := &generator{
blocksSection: blocksSection,
ignoreDeprecated: ignoreDeprecated,
tfVersion: tfVersion,

Expand Down Expand Up @@ -231,7 +233,7 @@ func (g *generator) Generate(ctx context.Context) error {
}

g.infof("rendering static website")
err = g.renderStaticWebsite(providerSchema)
err = g.renderStaticWebsite(providerSchema, g.blocksSection)
if err != nil {
return fmt.Errorf("error rendering static website: %w", err)
}
Expand Down Expand Up @@ -442,7 +444,7 @@ func (g *generator) generateMissingTemplates(providerSchema *tfjson.ProviderSche
return nil
}

func (g *generator) renderStaticWebsite(providerSchema *tfjson.ProviderSchema) error {
func (g *generator) renderStaticWebsite(providerSchema *tfjson.ProviderSchema, blocksSection bool) error {
g.infof("cleaning rendered website dir")
dirEntry, err := os.ReadDir(g.ProviderDocsDir())
if err != nil && !os.IsNotExist(err) {
Expand Down Expand Up @@ -532,7 +534,7 @@ func (g *generator) renderStaticWebsite(providerSchema *tfjson.ProviderSchema) e

if resSchema != nil {
tmpl := resourceTemplate(tmplData)
render, err := tmpl.Render(g.providerDir, resName, g.providerName, g.renderedProviderName, "Data Source", exampleFilePath, "", resSchema)
render, err := tmpl.Render(g.providerDir, resName, g.providerName, g.renderedProviderName, "Data Source", exampleFilePath, "", resSchema, blocksSection)
if err != nil {
return fmt.Errorf("unable to render data source template %q: %w", rel, err)
}
Expand All @@ -550,7 +552,7 @@ func (g *generator) renderStaticWebsite(providerSchema *tfjson.ProviderSchema) e

if resSchema != nil {
tmpl := resourceTemplate(tmplData)
render, err := tmpl.Render(g.providerDir, resName, g.providerName, g.renderedProviderName, "Resource", exampleFilePath, importFilePath, resSchema)
render, err := tmpl.Render(g.providerDir, resName, g.providerName, g.renderedProviderName, "Resource", exampleFilePath, importFilePath, resSchema, blocksSection)
if err != nil {
return fmt.Errorf("unable to render resource template %q: %w", rel, err)
}
Expand Down Expand Up @@ -583,7 +585,7 @@ func (g *generator) renderStaticWebsite(providerSchema *tfjson.ProviderSchema) e
if relFile == "index.md.tmpl" {
tmpl := providerTemplate(tmplData)
exampleFilePath := filepath.Join(g.ProviderExamplesDir(), "provider", "provider.tf")
render, err := tmpl.Render(g.providerDir, g.providerName, g.renderedProviderName, exampleFilePath, providerSchema.ConfigSchema)
render, err := tmpl.Render(g.providerDir, g.providerName, g.renderedProviderName, exampleFilePath, providerSchema.ConfigSchema, blocksSection)
if err != nil {
return fmt.Errorf("unable to render provider template %q: %w", rel, err)
}
Expand Down
8 changes: 4 additions & 4 deletions internal/provider/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@ func (t docTemplate) Render(providerDir string, out io.Writer) error {
return renderTemplate(providerDir, "docTemplate", s, out, nil)
}

func (t providerTemplate) Render(providerDir, providerName, renderedProviderName, exampleFile string, schema *tfjson.Schema) (string, error) {
func (t providerTemplate) Render(providerDir, providerName, renderedProviderName, exampleFile string, schema *tfjson.Schema, blocksSection bool) (string, error) {
schemaBuffer := bytes.NewBuffer(nil)
err := schemamd.Render(schema, schemaBuffer)
err := schemamd.Render(schema, schemaBuffer, blocksSection)
if err != nil {
return "", fmt.Errorf("unable to render schema: %w", err)
}
Expand Down Expand Up @@ -158,9 +158,9 @@ func (t providerTemplate) Render(providerDir, providerName, renderedProviderName
})
}

func (t resourceTemplate) Render(providerDir, name, providerName, renderedProviderName, typeName, exampleFile, importFile string, schema *tfjson.Schema) (string, error) {
func (t resourceTemplate) Render(providerDir, name, providerName, renderedProviderName, typeName, exampleFile, importFile string, schema *tfjson.Schema, blocksSection bool) (string, error) {
schemaBuffer := bytes.NewBuffer(nil)
err := schemamd.Render(schema, schemaBuffer)
err := schemamd.Render(schema, schemaBuffer, blocksSection)
if err != nil {
return "", fmt.Errorf("unable to render schema: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/provider/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ provider "scaffolding" {
},
}

result, err := tpl.Render("testdata/test-provider-dir", "testTemplate", "test-provider", "test-provider", "Resource", "provider.tf", "provider.tf", &schema)
result, err := tpl.Render("testdata/test-provider-dir", "testTemplate", "test-provider", "test-provider", "Resource", "provider.tf", "provider.tf", &schema, false)
if err != nil {
t.Error(err)
}
Expand Down Expand Up @@ -133,7 +133,7 @@ provider "scaffolding" {
},
}

result, err := tpl.Render("testdata/test-provider-dir", "testTemplate", "test-provider", "provider.tf", &schema)
result, err := tpl.Render("testdata/test-provider-dir", "testTemplate", "test-provider", "provider.tf", &schema, false)
if err != nil {
t.Error(err)
}
Expand Down
8 changes: 8 additions & 0 deletions internal/schemamd/behaviors.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,11 @@ func childBlockIsReadOnly(block *tfjson.SchemaBlockType) bool {

return true
}

func childBlockIsWritable(block *tfjson.SchemaBlockType) bool {
return !childBlockIsReadOnly(block)
}

func omitChild[T *tfjson.SchemaAttribute | *tfjson.SchemaBlockType](_ T) bool {
return false
}
48 changes: 33 additions & 15 deletions internal/schemamd/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import (
// },
// "version": 0
// },
func Render(schema *tfjson.Schema, w io.Writer) error {
func Render(schema *tfjson.Schema, w io.Writer, blocksSection bool) error {
_, err := io.WriteString(w, "## Schema\n\n")
if err != nil {
return err
}

err = writeRootBlock(w, schema.Block)
err = writeRootBlock(w, schema.Block, blocksSection)
if err != nil {
return fmt.Errorf("unable to render schema: %w", err)
}
Expand All @@ -49,13 +49,29 @@ var (
// * Required
// * Optional
// * Read-Only
groupFilters = []groupFilter{
defaultGroupFilters = []groupFilter{
{"### Required", "Required:", childAttributeIsRequired, childBlockIsRequired},
{"### Optional", "Optional:", childAttributeIsOptional, childBlockIsOptional},
{"### Read-Only", "Read-Only:", childAttributeIsReadOnly, childBlockIsReadOnly},
}

// When --blocks-section is enabled, blocks are rendered in a separate section regardless of their optional or required characteristics.
blocksSectionGroupFilters = []groupFilter{
{"### Required Attributes", "Required Attributes:", childAttributeIsRequired, omitChild[*tfjson.SchemaBlockType]},
{"### Optional Attributes", "Optional Attributes:", childAttributeIsOptional, omitChild[*tfjson.SchemaBlockType]},
{"### Blocks", "Blocks:", omitChild[*tfjson.SchemaAttribute], childBlockIsWritable},
{"### Read-Only", "Read-Only:", childAttributeIsReadOnly, childBlockIsReadOnly},
kangasta marked this conversation as resolved.
Show resolved Hide resolved
}
)

func getGroupFilters(blocksSection bool) []groupFilter {
if blocksSection {
return blocksSectionGroupFilters
}

return defaultGroupFilters
}

type nestedType struct {
anchorID string
pathTitle string
Expand Down Expand Up @@ -179,8 +195,8 @@ func writeBlockType(w io.Writer, path []string, block *tfjson.SchemaBlockType) (
return []nestedType{nt}, nil
}

func writeRootBlock(w io.Writer, block *tfjson.SchemaBlock) error {
return writeBlockChildren(w, nil, block, true)
func writeRootBlock(w io.Writer, block *tfjson.SchemaBlock, blocksSection bool) error {
return writeBlockChildren(w, nil, block, true, blocksSection)
}

// A Block contains:
Expand Down Expand Up @@ -210,7 +226,7 @@ func writeRootBlock(w io.Writer, block *tfjson.SchemaBlock) error {
// },
// "description_kind": "plain"
// },
func writeBlockChildren(w io.Writer, parents []string, block *tfjson.SchemaBlock, root bool) error {
func writeBlockChildren(w io.Writer, parents []string, block *tfjson.SchemaBlock, root bool, blocksSection bool) error {
names := []string{}
for n := range block.Attributes {
names = append(names, n)
Expand All @@ -221,6 +237,7 @@ func writeBlockChildren(w io.Writer, parents []string, block *tfjson.SchemaBlock

groups := map[int][]string{}

groupFilters := getGroupFilters(blocksSection)
// Group Attributes/Blocks by characteristics.
nameLoop:
for _, n := range names {
Expand Down Expand Up @@ -336,15 +353,15 @@ nameLoop:
}
}

err := writeNestedTypes(w, nestedTypes)
err := writeNestedTypes(w, nestedTypes, blocksSection)
if err != nil {
return err
}

return nil
}

func writeNestedTypes(w io.Writer, nestedTypes []nestedType) error {
func writeNestedTypes(w io.Writer, nestedTypes []nestedType, blocksSection bool) error {
for _, nt := range nestedTypes {
_, err := io.WriteString(w, "<a id=\""+nt.anchorID+"\"></a>\n")
if err != nil {
Expand All @@ -358,17 +375,17 @@ func writeNestedTypes(w io.Writer, nestedTypes []nestedType) error {

switch {
case nt.block != nil:
err = writeBlockChildren(w, nt.path, nt.block, false)
err = writeBlockChildren(w, nt.path, nt.block, false, blocksSection)
if err != nil {
return err
}
case nt.object != nil:
err = writeObjectChildren(w, nt.path, *nt.object, nt.group)
err = writeObjectChildren(w, nt.path, *nt.object, nt.group, blocksSection)
if err != nil {
return err
}
case nt.attrs != nil:
err = writeNestedAttributeChildren(w, nt.path, nt.attrs, nt.group)
err = writeNestedAttributeChildren(w, nt.path, nt.attrs, nt.group, blocksSection)
if err != nil {
return err
}
Expand Down Expand Up @@ -450,7 +467,7 @@ func writeObjectAttribute(w io.Writer, path []string, att cty.Type, group groupF
return nestedTypes, nil
}

func writeObjectChildren(w io.Writer, parents []string, ty cty.Type, group groupFilter) error {
func writeObjectChildren(w io.Writer, parents []string, ty cty.Type, group groupFilter, blocksSection bool) error {
_, err := io.WriteString(w, group.nestedTitle+"\n\n")
if err != nil {
return err
Expand Down Expand Up @@ -483,21 +500,22 @@ func writeObjectChildren(w io.Writer, parents []string, ty cty.Type, group group
return err
}

err = writeNestedTypes(w, nestedTypes)
err = writeNestedTypes(w, nestedTypes, blocksSection)
if err != nil {
return err
}

return nil
}

func writeNestedAttributeChildren(w io.Writer, parents []string, nestedAttributes *tfjson.SchemaNestedAttributeType, group groupFilter) error {
func writeNestedAttributeChildren(w io.Writer, parents []string, nestedAttributes *tfjson.SchemaNestedAttributeType, group groupFilter, blocksSection bool) error {
sortedNames := []string{}
for n := range nestedAttributes.Attributes {
sortedNames = append(sortedNames, n)
}
sort.Strings(sortedNames)

groupFilters := getGroupFilters(blocksSection)
groups := map[int][]string{}
for _, name := range sortedNames {
att := nestedAttributes.Attributes[name]
Expand Down Expand Up @@ -542,7 +560,7 @@ func writeNestedAttributeChildren(w io.Writer, parents []string, nestedAttribute
}
}

err := writeNestedTypes(w, nestedTypes)
err := writeNestedTypes(w, nestedTypes, blocksSection)
if err != nil {
return err
}
Expand Down
28 changes: 24 additions & 4 deletions internal/schemamd/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,60 @@ func TestRender(t *testing.T) {
t.Parallel()

for _, c := range []struct {
name string
inputFile string
expectedFile string
name string
inputFile string
expectedFile string
blocksSection bool
}{
{
"aws_route_table_association",
"testdata/aws_route_table_association.schema.json",
"testdata/aws_route_table_association.md",
false,
},
{
"aws_acm_certificate",
"testdata/aws_acm_certificate.schema.json",
"testdata/aws_acm_certificate.md",
false,
},
{
"awscc_logs_log_group",
"testdata/awscc_logs_log_group.schema.json",
"testdata/awscc_logs_log_group.md",
false,
},
{
"awscc_acmpca_certificate",
"testdata/awscc_acmpca_certificate.schema.json",
"testdata/awscc_acmpca_certificate.md",
false,
},
{
"framework_types",
"testdata/framework_types.schema.json",
"testdata/framework_types.md",
false,
},
{
"framework_types_blocks_section",
"testdata/framework_types.schema.json",
"testdata/framework_types_blocks_section.md",
true,
},
{
// Reference: https://github.com/hashicorp/terraform-plugin-docs/issues/380
"deep_nested_attributes",
"testdata/deep_nested_attributes.schema.json",
"testdata/deep_nested_attributes.md",
false,
},
{
// Reference: https://github.com/hashicorp/terraform-plugin-docs/issues/380
"deep_nested_attributes_blocks_section",
"testdata/deep_nested_attributes.schema.json",
"testdata/deep_nested_attributes_blocks_section.md",
true,
},
} {
c := c
Expand All @@ -77,7 +97,7 @@ func TestRender(t *testing.T) {
}

b := &strings.Builder{}
err = schemamd.Render(&schema, b)
err = schemamd.Render(&schema, b, c.blocksSection)
if err != nil {
t.Fatal(err)
}
Expand Down
Loading