Skip to content

Commit

Permalink
refactor and enable tests
Browse files Browse the repository at this point in the history
  • Loading branch information
guineveresaenger committed Apr 18, 2024
1 parent 9b3ed86 commit 54aaaf2
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 82 deletions.
108 changes: 62 additions & 46 deletions pkg/tfgen/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ func parseArgFromMarkdownLine(line string) (string, string, bool, bool) {
matches := argumentBulletRegexp.FindStringSubmatch(line)
indentedBullet := false
if len(matches) > 4 {
if strings.HasPrefix(matches[0], " ") {
if strings.HasPrefix(matches[0], " ") {
indentedBullet = true
}
return matches[1], matches[4], true, indentedBullet
Expand All @@ -838,6 +838,7 @@ var genericNestedRegexp = regexp.MustCompile("supports? the following:")
var nestedObjectRegexps = []*regexp.Regexp{
// For example:
// s3_bucket.html.markdown: "The `website` object supports the following:"
// appmesh_gateway_route.html.markdown: "The `grpc_route`, `http_route` and `http2_route` objects supports the following:"

Check failure on line 841 in pkg/tfgen/docs.go

View workflow job for this annotation

GitHub Actions / Test and Lint / lint

line is 123 characters (lll)
// ami.html.markdown: "When `virtualization_type` is "hvm" the following additional arguments apply:"
regexp.MustCompile("`([a-z_0-9]+)`.*following"),

Expand Down Expand Up @@ -869,14 +870,68 @@ var nestedObjectRegexps = []*regexp.Regexp{
regexp.MustCompile("`([a-zA-Z_.\\[\\]]+)`.*supports:"),
}

// getNestedBlockName takes line of a Terraform docs Markdown page and returns the name of the nested block it
// getMultipleNestedBlockNames is called when we detect that a resource matches the "`([a-z_0-9]+)`.*following" regex.
// We check if more than one nested block name is listed on this line, and we additionally check if there's an
// indication of an extra nested object, denoted by "'s", as in:
// "The `grpc_route`, `http_route` and `http2_route` 's `action` object supports the following:".
func getMultipleNestedBlockNames(match string) []string {
subNest := ""
var nestedBlockNames []string
// First we check for the presence of a possible nested property via 's
if strings.Contains(match, "'s ") {
// split the match along the 's
part1, part2, _ := strings.Cut(match, "'s")
match = part1
// Extract our subheading. It should be the second item in part2 of the match.
part2Slice := strings.Split(part2, "`")
if len(part2Slice) >= 2 {
subNest = part2Slice[1]
}
}
// As per previous regex match, the resource names will be surrounded by backticks, so we extract them
tokenInBackticks := regexp.MustCompile("`[^`]+`")
blockNames := tokenInBackticks.FindAllString(match, -1)
for _, blockName := range blockNames {
if blockName != "" {
blockName = strings.Trim(blockName, "`")
blockName = strings.ToLower(blockName)
blockName = strings.Replace(blockName, " ", "_", -1)
blockName = strings.TrimSuffix(blockName, "[]")
if subNest != "" {
// For the format ""The `grpc_route`, `http_route` and `http2_route` 's `action` object
//supports the following:" the result should be grpc_route.action
blockName = blockName + "." + subNest
}
nestedBlockNames = append(nestedBlockNames, blockName)
}
}
return nestedBlockNames
}

func splitMatchOnColon(match string) []string {
var nestedBlockNames []string
parts := strings.Split(match, ":")

blockName := strings.ToLower(parts[0])
blockName = strings.Replace(blockName, " ", "_", -1)

subNest := strings.ToLower(parts[1])
subNest = strings.TrimSpace(subNest)
subNest = strings.Replace(subNest, " ", "_", -1)

blockName = blockName + "." + subNest
nestedBlockNames = append(nestedBlockNames, blockName)
return nestedBlockNames
}

// getNestedBlockNames takes line of a Terraform docs Markdown page and returns the name(s) of the nested block it
// describes. If the line does not describe a nested block, an empty string is returned.
//
// Examples of nested blocks include (but are not limited to):
//
// - "The `private_cluster_config` block supports:" -> "private_cluster_config"
// - "The optional settings.backup_configuration subblock supports:" -> "settings.backup_configuration"
func getNestedBlockName(line string) []string {
func getNestedBlockNames(line string) []string {
nested := ""
var nestedBlockNames []string

Expand All @@ -885,55 +940,16 @@ func getNestedBlockName(line string) []string {

// If we match with the first regex, we have to see if we've got many to many matching for resources going on.
if len(matches) >= 2 && i == 0 {
firstMatch := matches[0]
subNest := ""
if strings.Contains(firstMatch, "'s ") {
// we have even more nesting!
// split the line into all of its components
part1, part2, _ := strings.Cut(matches[0], "'s")
firstMatch = part1
// find our subheading. it should be the second item in the second part.
part2Slice := strings.Split(part2, "`")
subNest = part2Slice[1]
}
tokenInBackticks := regexp.MustCompile("`[^`]+`")
newStrs := tokenInBackticks.FindAllString(firstMatch, -1)
for _, newStr := range newStrs {
if newStr != "" {
newStr = strings.Trim(newStr, "`")
nested = strings.ToLower(newStr)
nested = strings.Replace(nested, " ", "_", -1)
nested = strings.TrimSuffix(nested, "[]")
parts := strings.Split(nested, ".")
nested = parts[len(parts)-1]
if subNest != "" {
// For the format ""The `grpc_route`, `http_route` and `http2_route` 's `action` object
//supports the following:" the result should be grpc_route.action
nested = nested + "." + subNest
}
nestedBlockNames = append(nestedBlockNames, nested)
}
}
nestedBlockNames = getMultipleNestedBlockNames(matches[0])
break
} else if len(matches) >= 2 && i == 2 {
// there's a colon in the subheader; split the line
parts := strings.Split(matches[1], ":")
nested = strings.ToLower(parts[0])
nested = strings.Replace(nested, " ", "_", -1)
nested = strings.TrimSuffix(nested, ":")
subNest := strings.ToLower(parts[1])
subNest = strings.TrimSpace(subNest)
subNest = strings.Replace(subNest, " ", "_", -1)

nested = nested + "." + subNest
nestedBlockNames = append(nestedBlockNames, nested)
nestedBlockNames = splitMatchOnColon(matches[1])
break
} else if len(matches) >= 2 {
nested = strings.ToLower(matches[1])
nested = strings.Replace(nested, " ", "_", -1)
nested = strings.TrimSuffix(nested, "[]")
parts := strings.Split(nested, ".")
nested = parts[len(parts)-1]
nestedBlockNames = append(nestedBlockNames, nested)
break
}
Expand Down Expand Up @@ -1011,7 +1027,7 @@ func parseArgReferenceSection(subsection []string, ret *entityDocs) {
// section is over, but we take it to mean that the current
// heading is over.
lastMatch = ""
} else if nestedBlockCurrentLine := getNestedBlockName(line); hadSpace && len(nestedBlockCurrentLine) > 0 {
} else if nestedBlockCurrentLine := getNestedBlockNames(line); hadSpace && len(nestedBlockCurrentLine) > 0 {
// This tells us if there's a resource that is about to have subfields (nesteds)
// in subsequent lines.
//empty nesteds
Expand All @@ -1024,7 +1040,7 @@ func parseArgReferenceSection(subsection []string, ret *entityDocs) {
// This appends the current line to the previous match's description.
extendExistingHeading(line)

} else if nestedBlockCurrentLine := getNestedBlockName(line); len(nestedBlockCurrentLine) > 0 {
} else if nestedBlockCurrentLine := getNestedBlockNames(line); len(nestedBlockCurrentLine) > 0 {
// This tells us if there's a resource that is about to have subfields (nesteds)
// in subsequent lines.
//empty nesteds
Expand Down
76 changes: 40 additions & 36 deletions pkg/tfgen/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -716,34 +716,38 @@ subtitle 2 content
assert.Equal(t, expected, groupLines(strings.Split(input, "\n"), "## "))
}

//func TestParseArgFromMarkdownLine(t *testing.T) {
// //nolint:lll
// tests := []struct {
// input string
// expectedName string
// expectedDesc string
// expectedFound bool
// }{
// {"* `name` - (Required) A unique name to give the role.", "name", "A unique name to give the role.", true},
// {"* `key_vault_key_id` - (Optional) The Key Vault key URI for CMK encryption. Changing this forces a new resource to be created.", "key_vault_key_id", "The Key Vault key URI for CMK encryption. Changing this forces a new resource to be created.", true},
// {"* `urn` - The uniform resource name of the Droplet", "urn", "The uniform resource name of the Droplet", true},
// {"* `name`- The name of the Droplet", "name", "The name of the Droplet", true},
// {"* `jumbo_frame_capable` -Indicates whether jumbo frames (9001 MTU) are supported.", "jumbo_frame_capable", "Indicates whether jumbo frames (9001 MTU) are supported.", true},
// {"* `ssl_support_method`: Specifies how you want CloudFront to serve HTTPS", "ssl_support_method", "Specifies how you want CloudFront to serve HTTPS", true},
// {"* `principal_tags`: (Optional: []) - String to string map of variables.", "principal_tags", "String to string map of variables.", true},
// // In rare cases, we may have a match where description is empty like the following, taken from https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/r/spot_fleet_request.html.markdown
// {"* `instance_pools_to_use_count` - (Optional; Default: 1)", "instance_pools_to_use_count", "", true},
// {"", "", "", false},
// {"Most of these arguments directly correspond to the", "", "", false},
// }
//
// for _, test := range tests {
// name, desc, found := parseArgFromMarkdownLine(test.input)
// assert.Equal(t, test.expectedName, name)
// assert.Equal(t, test.expectedDesc, desc)
// assert.Equal(t, test.expectedFound, found)
// }
//}
func TestParseArgFromMarkdownLine(t *testing.T) {
//nolint:lll
tests := []struct {
input string
expectedName string
expectedDesc string
expectedFound bool
expectedIndent bool
}{
{"* `name` - (Required) A unique name to give the role.", "name", "A unique name to give the role.", true, false},
{"* `key_vault_key_id` - (Optional) The Key Vault key URI for CMK encryption. Changing this forces a new resource to be created.", "key_vault_key_id", "The Key Vault key URI for CMK encryption. Changing this forces a new resource to be created.", true, false},
{"* `urn` - The uniform resource name of the Droplet", "urn", "The uniform resource name of the Droplet", true, false},
{"* `name`- The name of the Droplet", "name", "The name of the Droplet", true, false},
{"* `jumbo_frame_capable` -Indicates whether jumbo frames (9001 MTU) are supported.", "jumbo_frame_capable", "Indicates whether jumbo frames (9001 MTU) are supported.", true, false},
{"* `ssl_support_method`: Specifies how you want CloudFront to serve HTTPS", "ssl_support_method", "Specifies how you want CloudFront to serve HTTPS", true, false},
{"* `principal_tags`: (Optional: []) - String to string map of variables.", "principal_tags", "String to string map of variables.", true, false},
{" * `id` - The id of the property", "id", "The id of the property", true, true},
{" * id - The id of the property", "", "", false, false},
//In rare cases, we may have a match where description is empty like the following, taken from https://github.com/hashicorp/terraform-provider-aws/blob/main/website/docs/r/spot_fleet_request.html.markdown
{"* `instance_pools_to_use_count` - (Optional; Default: 1)", "instance_pools_to_use_count", "", true, false},
{"", "", "", false, false},
{"Most of these arguments directly correspond to the", "", "", false, false},
}

for _, test := range tests {
name, desc, found, indented := parseArgFromMarkdownLine(test.input)
assert.Equal(t, test.expectedName, name)
assert.Equal(t, test.expectedDesc, desc)
assert.Equal(t, test.expectedFound, found)
assert.Equal(t, test.expectedIndent, indented)
}
}

func TestParseAttributesReferenceSection(t *testing.T) {
ret := entityDocs{
Expand Down Expand Up @@ -822,26 +826,26 @@ func TestGetNestedBlockName(t *testing.T) {
}{
//{"", []string(nil)},
//{"The `website` object supports the following:", []string{"website"}},
{"The `website` and `pages` objects support the following:", []string{"website", "pages"}},
//{"The optional `settings.location_preference` subblock supports:", []string{"location_preference"}},
//{"The optional `settings.ip_configuration.authorized_networks[]` sublist supports:", []string{"authorized_networks"}},
//{"The `website` and `pages` objects support the following:", []string{"website", "pages"}},
//{"The optional `settings.location_preference` subblock supports:", []string{"settings.location_preference"}},
//{"The optional `settings.ip_configuration.authorized_networks[]` sublist supports:", []string{"settings.ip_configuration.authorized_networks"}},
//{"#### result_configuration Argument Reference", []string{"result_configuration"}},
//{"### advanced_security_options", []string{"advanced_security_options"}},
//{"### `server_side_encryption`", []string{"server_side_encryption"}},
//{"### Failover Routing Policy", []string{"failover_routing_policy"}},
//{"##### `log_configuration`", []string{"log_configuration"}},
//{"### data_format_conversion_configuration", []string{"data_format_conversion_configuration"}},
////// This is a common starting line of base arguments, so should result in nil value:
{"#### build_batch_config: restrictions", []string{"build_batch_config.restrictions"}},
//{"#### logs_config: s3_logs", []string{"logs_config.s3_logs"}},
//{"###### S3 Input Format Config", []string{"s3_input_format_config"}},
//// This is a common starting line of base arguments, so should result in nil value:
//{"The following arguments are supported:", []string(nil)},
//{"* `kms_key_id` - ...", []string(nil)},
//{"## Import", []string(nil)},
//{"#### build_batch_config: restrictions", []string{"build_batch_config.restrictions"}},
//{"#### logs_config: s3_logs", []string{"logs_config.s3_logs"}},
//{"###### S3 Input Format Config", []string{"s3_input_format_config"}},
}

for _, tt := range tests {
assert.Equal(t, tt.expected, getNestedBlockName(tt.input))
assert.Equal(t, tt.expected, getNestedBlockNames(tt.input))
}
}

Expand Down

0 comments on commit 54aaaf2

Please sign in to comment.