Skip to content
Open
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
@@ -19,6 +19,7 @@ type generateCmd struct {
flagProviderName string
flagRenderedProviderName string

flagBlocksSection bool
flagProviderDir string
flagProvidersSchema string
flagRenderedWebsiteDir string
@@ -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")
@@ -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)
14 changes: 8 additions & 6 deletions internal/provider/generate.go
Original file line number Diff line number Diff line change
@@ -79,6 +79,7 @@ var (
)

type generator struct {
blocksSection bool
ignoreDeprecated bool
tfVersion string

@@ -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()
@@ -136,6 +137,7 @@ func Generate(ui cli.Ui, providerDir, providerName, providersSchemaPath, rendere
}

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

@@ -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)
}
@@ -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) {
@@ -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)
}
@@ -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)
}
@@ -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)
}
8 changes: 4 additions & 4 deletions internal/provider/template.go
Original file line number Diff line number Diff line change
@@ -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)
}
@@ -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)
}
4 changes: 2 additions & 2 deletions internal/provider/template_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
@@ -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)
}
8 changes: 8 additions & 0 deletions internal/schemamd/behaviors.go
Original file line number Diff line number Diff line change
@@ -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
@@ -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)
}
@@ -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},
}
)

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

return defaultGroupFilters
}

type nestedType struct {
anchorID string
pathTitle string
@@ -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:
@@ -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)
@@ -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 {
@@ -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 {
@@ -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
}
@@ -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
@@ -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]
@@ -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
}
28 changes: 24 additions & 4 deletions internal/schemamd/render_test.go
Original file line number Diff line number Diff line change
@@ -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
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## Schema

### Required Attributes

- `level_one` (Attributes) (see [below for nested schema](#nestedatt--level_one))

### Read-Only

- `id` (String) Example identifier

<a id="nestedatt--level_one"></a>
### Nested Schema for `level_one`

Optional Attributes:

- `level_two` (Attributes) (see [below for nested schema](#nestedatt--level_one--level_two))

<a id="nestedatt--level_one--level_two"></a>
### Nested Schema for `level_one.level_two`

Optional Attributes:

- `level_three` (Attributes) (see [below for nested schema](#nestedatt--level_one--level_two--level_three))

<a id="nestedatt--level_one--level_two--level_three"></a>
### Nested Schema for `level_one.level_two.level_three`

Optional Attributes:

- `level_four_primary` (Attributes) (see [below for nested schema](#nestedatt--level_one--level_two--level_three--level_four_primary))
- `level_four_secondary` (String)

<a id="nestedatt--level_one--level_two--level_three--level_four_primary"></a>
### Nested Schema for `level_one.level_two.level_three.level_four_primary`

Optional Attributes:

- `level_five` (Attributes) Parent should be level_one.level_two.level_three.level_four_primary. (see [below for nested schema](#nestedatt--level_one--level_two--level_three--level_four_primary--level_five))
- `level_four_primary_string` (String) Parent should be level_one.level_two.level_three.level_four_primary.

<a id="nestedatt--level_one--level_two--level_three--level_four_primary--level_five"></a>
### Nested Schema for `level_one.level_two.level_three.level_four_primary.level_five`

Optional Attributes:

- `level_five_string` (String) Parent should be level_one.level_two.level_three.level_four_primary.level_five.
132 changes: 132 additions & 0 deletions internal/schemamd/testdata/framework_types_blocks_section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
## Schema

### Optional Attributes

- `bool_attribute` (Boolean) example bool attribute
- `float64_attribute` (Number) example float64 attribute
- `int64_attribute` (Number) example int64 attribute
- `list_attribute` (List of String) example list attribute
- `map_attribute` (Map of String) example map attribute
- `number_attribute` (Number) example number attribute
- `object_attribute` (Object) example object attribute (see [below for nested schema](#nestedatt--object_attribute))
- `object_attribute_with_nested_object_attribute` (Object) example object attribute with nested object attribute (see [below for nested schema](#nestedatt--object_attribute_with_nested_object_attribute))
- `sensitive_bool_attribute` (Boolean, Sensitive) example sensitive bool attribute
- `sensitive_float64_attribute` (Number, Sensitive) example sensitive float64 attribute
- `sensitive_int64_attribute` (Number, Sensitive) example sensitive int64 attribute
- `sensitive_list_attribute` (List of String, Sensitive) example sensitive list attribute
- `sensitive_map_attribute` (Map of String, Sensitive) example sensitive map attribute
- `sensitive_number_attribute` (Number, Sensitive) example sensitive number attribute
- `sensitive_object_attribute` (Object, Sensitive) example sensitive object attribute (see [below for nested schema](#nestedatt--sensitive_object_attribute))
- `sensitive_set_attribute` (Set of String, Sensitive) example sensitive set attribute
- `sensitive_string_attribute` (String, Sensitive) example sensitive string attribute
- `set_attribute` (Set of String) example set attribute
- `string_attribute` (String) example string attribute

### Blocks

- `list_nested_block` (Block List) example list nested block (see [below for nested schema](#nestedblock--list_nested_block))
- `list_nested_block_sensitive_nested_attribute` (Block List) (see [below for nested schema](#nestedblock--list_nested_block_sensitive_nested_attribute))
- `set_nested_block` (Block Set) example set nested block (see [below for nested schema](#nestedblock--set_nested_block))
- `single_nested_block` (Block, Optional) example single nested block (see [below for nested schema](#nestedblock--single_nested_block))
- `single_nested_block_sensitive_nested_attribute` (Block, Optional) example sensitive single nested block (see [below for nested schema](#nestedblock--single_nested_block_sensitive_nested_attribute))

### Read-Only

- `id` (String) The ID of this resource.
- `set_nested_block_sensitive_nested_attribute` (Block Set) example sensitive set nested block (see [below for nested schema](#nestedblock--set_nested_block_sensitive_nested_attribute))

<a id="nestedatt--object_attribute"></a>
### Nested Schema for `object_attribute`

Optional Attributes:

- `object_attribute_attribute` (String)


<a id="nestedatt--object_attribute_with_nested_object_attribute"></a>
### Nested Schema for `object_attribute_with_nested_object_attribute`

Optional Attributes:

- `nested_object` (Object) (see [below for nested schema](#nestedobjatt--object_attribute_with_nested_object_attribute--nested_object))
- `object_attribute_attribute` (String)

<a id="nestedobjatt--object_attribute_with_nested_object_attribute--nested_object"></a>
### Nested Schema for `object_attribute_with_nested_object_attribute.nested_object`

Optional Attributes:

- `nested_object_attribute` (String)



<a id="nestedatt--sensitive_object_attribute"></a>
### Nested Schema for `sensitive_object_attribute`

Optional Attributes:

- `object_attribute_attribute` (String)


<a id="nestedblock--list_nested_block"></a>
### Nested Schema for `list_nested_block`

Optional Attributes:

- `list_nested_block_attribute` (String) example list nested block attribute
- `list_nested_block_attribute_with_default` (String) example list nested block attribute with default

Blocks:

- `nested_list_block` (Block List) (see [below for nested schema](#nestedblock--list_nested_block--nested_list_block))

<a id="nestedblock--list_nested_block--nested_list_block"></a>
### Nested Schema for `list_nested_block.nested_list_block`

Optional Attributes:

- `nested_block_string_attribute` (String) example nested block string attribute



<a id="nestedblock--list_nested_block_sensitive_nested_attribute"></a>
### Nested Schema for `list_nested_block_sensitive_nested_attribute`

Optional Attributes:

- `list_nested_block_attribute` (String) example list nested block attribute
- `list_nested_block_sensitive_attribute` (String, Sensitive) example sensitive list nested block attribute


<a id="nestedblock--set_nested_block"></a>
### Nested Schema for `set_nested_block`

Optional Attributes:

- `set_nested_block_attribute` (String) example set nested block attribute


<a id="nestedblock--single_nested_block"></a>
### Nested Schema for `single_nested_block`

Optional Attributes:

- `single_nested_block_attribute` (String) example single nested block attribute


<a id="nestedblock--single_nested_block_sensitive_nested_attribute"></a>
### Nested Schema for `single_nested_block_sensitive_nested_attribute`

Optional Attributes:

- `single_nested_block_attribute` (String) example single nested block attribute
- `single_nested_block_sensitive_attribute` (String, Sensitive) example sensitive single nested block attribute


<a id="nestedblock--set_nested_block_sensitive_nested_attribute"></a>
### Nested Schema for `set_nested_block_sensitive_nested_attribute`

Read-Only:

- `set_nested_block_attribute` (String) example set nested block attribute
- `set_nested_block_sensitive_attribute` (String, Sensitive) example sensitive set nested block attribute