From 14eebddc08766ed094015187f3da2064005a7db2 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Fri, 26 Apr 2024 13:45:09 -0400 Subject: [PATCH] generate: Prevent automatic id attribute behaviors under blocks (#365) Reference: https://github.com/hashicorp/terraform-plugin-docs/issues/335 Previously, the automatic `id` attribute handling intended for root level schema blocks was applied to all blocks, causing the generator to errantly set the grouping to `Read-Only` and the description to `The ID of this resource`. This was captured via a new integration test: ``` --- FAIL: Test_SchemaJson_GenerateAcceptanceTests (0.00s) --- FAIL: Test_SchemaJson_GenerateAcceptanceTests/nested_id_attribute (0.25s) testscript.go:558: # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 # Ensure only root level id attribute receives automatic description (0.236s) > [!unix] skip > exec tfplugindocs --provider-name=terraform-provider-scaffolding --providers-schema=schema.json [stdout] rendering website for provider "terraform-provider-scaffolding" (as "terraform-provider-scaffolding") exporting schema from JSON file getting provider schema generating missing templates generating missing resource content generating new template for "scaffolding_example" generating missing data source content generating missing function content generating missing provider content generating new template for "terraform-provider-scaffolding" rendering static website cleaning rendered website dir rendering templated website to static markdown rendering "index.md.tmpl" rendering "resources/example.md.tmpl" > cmp stdout expected-output.txt > cmp docs/index.md expected-index.md > cmp docs/resources/example.md expected-resource.md diff docs/resources/example.md expected-resource.md --- docs/resources/example.md +++ expected-resource.md @@ -48,17 +48,17 @@ ### Nested Schema for `list_nested_block_optional_id` -Read-Only: - -- `id` (String) The ID of this resource. +Optional: + +- `id` (String) ### Nested Schema for `list_nested_block_required_id` -Read-Only: - -- `id` (String) The ID of this resource. +Required: + +- `id` (String) @@ -72,33 +72,33 @@ ### Nested Schema for `set_nested_block_optional_id` -Read-Only: - -- `id` (String) The ID of this resource. +Optional: + +- `id` (String) ### Nested Schema for `set_nested_block_required_id` -Read-Only: - -- `id` (String) The ID of this resource. +Required: + +- `id` (String) ### Nested Schema for `single_nested_block_optional_id` -Read-Only: - -- `id` (String) The ID of this resource. +Optional: + +- `id` (String) ### Nested Schema for `single_nested_block_required_id` -Read-Only: - -- `id` (String) The ID of this resource. +Required: + +- `id` (String) @@ -114,7 +114,7 @@ Read-Only: -- `id` (String) The ID of this resource. +- `id` (String) @@ -122,7 +122,7 @@ Read-Only: -- `id` (String) The ID of this resource. +- `id` (String) @@ -130,4 +130,4 @@ Read-Only: -- `id` (String) The ID of this resource. +- `id` (String) FAIL: testdata/scripts/schema-json/generate/nested_id_attribute.txtar:9: docs/resources/example.md and expected-resource.md differ ``` The schema rendering logic could likely use a full refactoring as it has very high code complexity, but this change is only a very targeted fix for the acute and errant `id` attribute handling behavior. --- .../unreleased/BUG FIXES-20240426-111333.yaml | 5 + .../generate/nested_id_attribute.txtar | 381 ++++++++++++++++++ internal/schemamd/render.go | 2 +- 3 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 .changes/unreleased/BUG FIXES-20240426-111333.yaml create mode 100644 cmd/tfplugindocs/testdata/scripts/schema-json/generate/nested_id_attribute.txtar diff --git a/.changes/unreleased/BUG FIXES-20240426-111333.yaml b/.changes/unreleased/BUG FIXES-20240426-111333.yaml new file mode 100644 index 0000000..4915d21 --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20240426-111333.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: 'generate: Prevented automatic `id` attribute behaviors under blocks' +time: 2024-04-26T11:13:33.275469-04:00 +custom: + Issue: "365" diff --git a/cmd/tfplugindocs/testdata/scripts/schema-json/generate/nested_id_attribute.txtar b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/nested_id_attribute.txtar new file mode 100644 index 0000000..943c54c --- /dev/null +++ b/cmd/tfplugindocs/testdata/scripts/schema-json/generate/nested_id_attribute.txtar @@ -0,0 +1,381 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# Ensure only root level id attribute receives automatic description +[!unix] skip +exec tfplugindocs --provider-name=terraform-provider-scaffolding --providers-schema=schema.json +cmp stdout expected-output.txt +cmp docs/index.md expected-index.md +cmp docs/resources/example.md expected-resource.md + +-- expected-output.txt -- +rendering website for provider "terraform-provider-scaffolding" (as "terraform-provider-scaffolding") +exporting schema from JSON file +getting provider schema +generating missing templates +generating missing resource content +generating new template for "scaffolding_example" +generating missing data source content +generating missing function content +generating missing provider content +generating new template for "terraform-provider-scaffolding" +rendering static website +cleaning rendered website dir +rendering templated website to static markdown +rendering "index.md.tmpl" +rendering "resources/example.md.tmpl" +-- expected-index.md -- +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "scaffolding Provider" +subcategory: "" +description: |- + Example provider +--- + +# scaffolding Provider + +Example provider + + + + +## Schema + +### Optional + +- `endpoint` (String) Example provider attribute +-- expected-resource.md -- +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "scaffolding_example Resource - terraform-provider-scaffolding" +subcategory: "" +description: |- + example resource +--- + +# scaffolding_example (Resource) + +example resource + + + + +## Schema + +### Required + +- `required_object_attribute` (Object) example required object attribute (see [below for nested schema](#nestedatt--required_object_attribute)) + +### Optional + +- `list_nested_block_optional_id` (Block List) example list nested block with optional id attribute (see [below for nested schema](#nestedblock--list_nested_block_optional_id)) +- `list_nested_block_required_id` (Block List) example list nested block with required id attribute (see [below for nested schema](#nestedblock--list_nested_block_required_id)) +- `optional_object_attribute` (Object) example optional object attribute (see [below for nested schema](#nestedatt--optional_object_attribute)) +- `set_nested_block_optional_id` (Block Set) example set nested block with optional id attribute (see [below for nested schema](#nestedblock--set_nested_block_optional_id)) +- `set_nested_block_required_id` (Block Set) example set nested block with required id attribute (see [below for nested schema](#nestedblock--set_nested_block_required_id)) +- `single_nested_block_optional_id` (Block, Optional) example single nested block with optional id attribute (see [below for nested schema](#nestedblock--single_nested_block_optional_id)) +- `single_nested_block_required_id` (Block, Optional) example single nested block with required id attribute (see [below for nested schema](#nestedblock--single_nested_block_required_id)) + +### Read-Only + +- `computed_object_attribute` (Object) example computed object attribute (see [below for nested schema](#nestedatt--computed_object_attribute)) +- `id` (String) The ID of this resource. +- `list_nested_block_computed_id` (Block List) example list nested block with computed id attribute (see [below for nested schema](#nestedblock--list_nested_block_computed_id)) +- `set_nested_block_computed_id` (Block Set) example set nested block with computed id attribute (see [below for nested schema](#nestedblock--set_nested_block_computed_id)) +- `single_nested_block_computed_id` (Block, Read-only) example single nested block with computed id attribute (see [below for nested schema](#nestedblock--single_nested_block_computed_id)) + + +### Nested Schema for `required_object_attribute` + +Required: + +- `id` (String) + + + +### Nested Schema for `list_nested_block_optional_id` + +Optional: + +- `id` (String) + + + +### Nested Schema for `list_nested_block_required_id` + +Required: + +- `id` (String) + + + +### Nested Schema for `optional_object_attribute` + +Optional: + +- `id` (String) + + + +### Nested Schema for `set_nested_block_optional_id` + +Optional: + +- `id` (String) + + + +### Nested Schema for `set_nested_block_required_id` + +Required: + +- `id` (String) + + + +### Nested Schema for `single_nested_block_optional_id` + +Optional: + +- `id` (String) + + + +### Nested Schema for `single_nested_block_required_id` + +Required: + +- `id` (String) + + + +### Nested Schema for `computed_object_attribute` + +Read-Only: + +- `id` (String) + + + +### Nested Schema for `list_nested_block_computed_id` + +Read-Only: + +- `id` (String) + + + +### Nested Schema for `set_nested_block_computed_id` + +Read-Only: + +- `id` (String) + + + +### Nested Schema for `single_nested_block_computed_id` + +Read-Only: + +- `id` (String) +-- schema.json -- +{ + "format_version": "1.0", + "provider_schemas": { + "registry.terraform.io/hashicorp/scaffolding": { + "provider": { + "version": 0, + "block": { + "attributes": { + "endpoint": { + "type": "string", + "description": "Example provider attribute", + "description_kind": "plain", + "optional": true + } + }, + "description": "Example provider", + "description_kind": "plain" + } + }, + "resource_schemas": { + "scaffolding_example": { + "version": 0, + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "computed": true + }, + "computed_object_attribute": { + "type": [ + "object", + { + "id": "string" + } + ], + "description": "example computed object attribute", + "description_kind": "plain", + "computed": true + }, + "optional_object_attribute": { + "type": [ + "object", + { + "id": "string" + } + ], + "description": "example optional object attribute", + "description_kind": "plain", + "optional": true + }, + "required_object_attribute": { + "type": [ + "object", + { + "id": "string" + } + ], + "description": "example required object attribute", + "description_kind": "plain", + "required": true + } + }, + "block_types": { + "list_nested_block_computed_id": { + "nesting_mode": "list", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "computed": true + } + }, + "description": "example list nested block with computed id attribute", + "description_kind": "plain" + } + }, + "list_nested_block_optional_id": { + "nesting_mode": "list", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "optional": true + } + }, + "description": "example list nested block with optional id attribute", + "description_kind": "plain" + } + }, + "list_nested_block_required_id": { + "nesting_mode": "list", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "required": true + } + }, + "description": "example list nested block with required id attribute", + "description_kind": "plain" + } + }, + "set_nested_block_computed_id": { + "nesting_mode": "set", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "computed": true + } + }, + "description": "example set nested block with computed id attribute", + "description_kind": "plain" + } + }, + "set_nested_block_optional_id": { + "nesting_mode": "set", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "optional": true + } + }, + "description": "example set nested block with optional id attribute", + "description_kind": "plain" + } + }, + "set_nested_block_required_id": { + "nesting_mode": "set", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "required": true + } + }, + "description": "example set nested block with required id attribute", + "description_kind": "plain" + } + }, + "single_nested_block_computed_id": { + "nesting_mode": "single", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "computed": true + } + }, + "description": "example single nested block with computed id attribute", + "description_kind": "plain" + } + }, + "single_nested_block_optional_id": { + "nesting_mode": "single", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "optional": true + } + }, + "description": "example single nested block with optional id attribute", + "description_kind": "plain" + } + }, + "single_nested_block_required_id": { + "nesting_mode": "single", + "block": { + "attributes": { + "id": { + "type": "string", + "description_kind": "plain", + "required": true + } + }, + "description": "example single nested block with required id attribute", + "description_kind": "plain" + } + } + }, + "description": "example resource", + "description_kind": "plain" + } + } + } + } + } +} diff --git a/internal/schemamd/render.go b/internal/schemamd/render.go index 0b628f3..210ede3 100644 --- a/internal/schemamd/render.go +++ b/internal/schemamd/render.go @@ -238,7 +238,7 @@ nameLoop: // // 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.ToLower(n) == "id" && len(parents) == 0 && childAtt.Description == "" { if strings.Contains(gf.topLevelTitle, "Read-Only") { childAtt.Description = "The ID of this resource." groups[i] = append(groups[i], n)