fix(genconfig): properly quote attribute names when necessary (#2533)

Signed-off-by: James Humphries <james@james-humphries.co.uk>
This commit is contained in:
James Humphries 2025-02-21 15:00:47 +00:00 committed by GitHub
parent c66319a56b
commit 23a26b0e58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 107 additions and 0 deletions

View File

@ -12,6 +12,7 @@ import (
"strings"
"github.com/hashicorp/hcl/v2"
hclsyntax "github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/json"
@ -90,6 +91,10 @@ func writeConfigAttributes(addr addrs.AbsResourceInstance, buf *strings.Builder,
}
if attrS.Required {
buf.WriteString(strings.Repeat(" ", indent))
// Handle cases where the name should be contained in quotes
if !hclsyntax.ValidIdentifier(name) {
name = string(hclwrite.TokensForValue(cty.StringVal(name)).Bytes())
}
buf.WriteString(fmt.Sprintf("%s = ", name))
tok := hclwrite.TokensForValue(attrS.EmptyValue())
if _, err := tok.WriteTo(buf); err != nil {
@ -104,6 +109,10 @@ func writeConfigAttributes(addr addrs.AbsResourceInstance, buf *strings.Builder,
writeAttrTypeConstraint(buf, attrS)
} else if attrS.Optional {
buf.WriteString(strings.Repeat(" ", indent))
// Handle cases where the name should be contained in quotes
if !hclsyntax.ValidIdentifier(name) {
name = string(hclwrite.TokensForValue(cty.StringVal(name)).Bytes())
}
buf.WriteString(fmt.Sprintf("%s = ", name))
tok := hclwrite.TokensForValue(attrS.EmptyValue())
if _, err := tok.WriteTo(buf); err != nil {

View File

@ -82,6 +82,104 @@ resource "tfcoremock_simple_resource" "empty" {
list_block { # OPTIONAL block
nested_value = null # OPTIONAL string
}
}`,
},
"simple_resource_with_propertyname_containing_a_dot": {
schema: &configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"list_block": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"nested_value.json": {
Type: cty.String,
Optional: true,
},
},
},
Nesting: configschema.NestingSingle,
},
},
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
"value": {
Type: cty.String,
Optional: true,
},
},
},
addr: addrs.AbsResourceInstance{
Module: nil,
Resource: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "tfcoremock_simple_resource",
Name: "empty",
},
Key: nil,
},
},
provider: addrs.LocalProviderConfig{
LocalName: "tfcoremock",
},
value: cty.NilVal,
expected: `
resource "tfcoremock_simple_resource" "empty" {
value = null # OPTIONAL string
list_block { # OPTIONAL block
"nested_value.json" = null # OPTIONAL string
}
}`,
},
"simple_resource_with_propertyname_containing_double_quotes": {
schema: &configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"list_block": {
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"nested_value\"example": {
Type: cty.String,
Optional: true,
},
},
},
Nesting: configschema.NestingSingle,
},
},
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
"value": {
Type: cty.String,
Optional: true,
},
},
},
addr: addrs.AbsResourceInstance{
Module: nil,
Resource: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "tfcoremock_simple_resource",
Name: "empty",
},
Key: nil,
},
},
provider: addrs.LocalProviderConfig{
LocalName: "tfcoremock",
},
value: cty.NilVal,
expected: `
resource "tfcoremock_simple_resource" "empty" {
value = null # OPTIONAL string
list_block { # OPTIONAL block
"nested_value\"example" = null # OPTIONAL string
}
}`,
},
"simple_resource_with_state": {