From b4b63ec961373e5c2cf15d3134a325d623f5dff2 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 6 Jun 2022 16:27:18 +0100 Subject: [PATCH 1/5] Allowing single word resources to use templates --- internal/provider/generate.go | 4 ++-- internal/provider/template.go | 1 + internal/provider/util.go | 12 ++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/internal/provider/generate.go b/internal/provider/generate.go index c0841bfc..0eb5b4b6 100644 --- a/internal/provider/generate.go +++ b/internal/provider/generate.go @@ -414,7 +414,7 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj g.infof("rendering %q", rel) switch relDir { case "data-sources/": - resName := shortName + "_" + removeAllExt(relFile) + resName := resourceName(shortName, relFile) resSchema, ok := providerSchema.DataSourceSchemas[resName] if ok { tmpl := resourceTemplate(tmplData) @@ -429,7 +429,7 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj return nil } case "resources/": - resName := shortName + "_" + removeAllExt(relFile) + resName := resourceName(shortName, relFile) resSchema, ok := providerSchema.ResourceSchemas[resName] if ok { tmpl := resourceTemplate(tmplData) diff --git a/internal/provider/template.go b/internal/provider/template.go index 2d02e69c..4612627b 100644 --- a/internal/provider/template.go +++ b/internal/provider/template.go @@ -8,6 +8,7 @@ import ( "text/template" tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-docs/internal/mdplain" "github.com/hashicorp/terraform-plugin-docs/internal/tmplfuncs" "github.com/hashicorp/terraform-plugin-docs/schemamd" diff --git a/internal/provider/util.go b/internal/provider/util.go index 052ddd84..277d4e8e 100644 --- a/internal/provider/util.go +++ b/internal/provider/util.go @@ -52,6 +52,18 @@ func removeAllExt(file string) string { } } +// resourceName determines whether the shortname and the relFile +// are identical after file extensions have been stripped from the +// latter. This allows single word resources (e.g., http) to use +// templates (e.g., http.md.tmpl). +func resourceName(shortName, relFile string) string { + if shortName == removeAllExt(relFile) { + return shortName + } + + return shortName + "_" + removeAllExt(relFile) +} + func writeFile(path string, data string) error { dir, _ := filepath.Split(path) err := os.MkdirAll(dir, 0755) From 937765256944346db1229dcc52cab84085d5fb5c Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 6 Jun 2022 16:31:02 +0100 Subject: [PATCH 2/5] Updating CHANGELOG.md --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f3acae8..98c9bb6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ +# 0.9.1 (Unreleased) + +BUG FIXES: + +* cmd/tfplugindocs: Allow single word resources to use templates ([147](https://github.com/hashicorp/terraform-plugin-docs/pull/147)). + # 0.9.0 (June 1, 2022) NEW FEATURES: -* cmd/tflugindocs: Additional CLI arguments `provider-name`, `rendered-provider-name`, `rendered-website-dir`, `examples-dir`, `website-temp-dir`, and `website-source-dir`. These allow to further customise generated doc ([#95](https://github.com/hashicorp/terraform-plugin-docs/pull/95)). +* cmd/tfplugindocs: Additional CLI arguments `provider-name`, `rendered-provider-name`, `rendered-website-dir`, `examples-dir`, `website-temp-dir`, and `website-source-dir`. These allow to further customise generated doc ([#95](https://github.com/hashicorp/terraform-plugin-docs/pull/95)). ENHANCEMENTS: -* cmd/tflugindocs: Implemented usage output (i.e. `--help`) for `generate` and `validate` commands ([#95](https://github.com/hashicorp/terraform-plugin-docs/pull/95)). +* cmd/tfplugindocs: Implemented usage output (i.e. `--help`) for `generate` and `validate` commands ([#95](https://github.com/hashicorp/terraform-plugin-docs/pull/95)). # 0.8.1 (May 10, 2022) From 27bf5535f4c63e8e12e999122b0016bf4bab38e3 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 6 Jun 2022 18:00:27 +0100 Subject: [PATCH 3/5] Add unit test for resourceName --- internal/provider/util_test.go | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 internal/provider/util_test.go diff --git a/internal/provider/util_test.go b/internal/provider/util_test.go new file mode 100644 index 00000000..36514aac --- /dev/null +++ b/internal/provider/util_test.go @@ -0,0 +1,38 @@ +package provider + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func Test_resourceName(t *testing.T) { + cases := []struct { + name string + providerShortName string + templateFileName string + expectedResourceName string + }{ + { + "provider short name same as template file name", + "http", + "http.md.tmpl", + "http", + }, + { + "provider short name different to template file name", + "tls", + "cert_request.md.tmpl", + "tls_cert_request", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actualResourceName := resourceName(c.providerShortName, c.templateFileName) + if !cmp.Equal(c.expectedResourceName, actualResourceName) { + t.Errorf("expected: %s, got: %s", c.expectedResourceName, actualResourceName) + } + }) + } +} From b0001a4804d40d1a499099747d4f2806737140b8 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 9 Jun 2022 16:51:50 +0100 Subject: [PATCH 4/5] Returning error if resource not found --- internal/provider/generate.go | 54 ++++++++++++++++++---------------- internal/provider/util.go | 23 ++++++++++----- internal/provider/util_test.go | 54 +++++++++++++++++++++++++++++++--- 3 files changed, 93 insertions(+), 38 deletions(-) diff --git a/internal/provider/generate.go b/internal/provider/generate.go index 0eb5b4b6..9b83dd24 100644 --- a/internal/provider/generate.go +++ b/internal/provider/generate.go @@ -414,35 +414,37 @@ func (g *generator) renderStaticWebsite(providerName string, providerSchema *tfj g.infof("rendering %q", rel) switch relDir { case "data-sources/": - resName := resourceName(shortName, relFile) - resSchema, ok := providerSchema.DataSourceSchemas[resName] - if ok { - tmpl := resourceTemplate(tmplData) - render, err := tmpl.Render(resName, providerName, "Data Source", "", "", resSchema) - if err != nil { - return fmt.Errorf("unable to render data source template %q: %w", rel, err) - } - _, err = out.WriteString(render) - if err != nil { - return fmt.Errorf("unable to write rendered string: %w", err) - } - return nil + resSchema, resName := resourceSchema(providerSchema.DataSourceSchemas, shortName, relFile) + if resSchema == nil { + return fmt.Errorf("unable to find resource for provider (%s) and template (%s)", shortName, relFile) + } + + tmpl := resourceTemplate(tmplData) + render, err := tmpl.Render(resName, providerName, "Data Source", "", "", resSchema) + if err != nil { + return fmt.Errorf("unable to render data source template %q: %w", rel, err) } + _, err = out.WriteString(render) + if err != nil { + return fmt.Errorf("unable to write rendered string: %w", err) + } + return nil case "resources/": - resName := resourceName(shortName, relFile) - resSchema, ok := providerSchema.ResourceSchemas[resName] - if ok { - tmpl := resourceTemplate(tmplData) - render, err := tmpl.Render(resName, providerName, "Resource", "", "", resSchema) - if err != nil { - return fmt.Errorf("unable to render resource template %q: %w", rel, err) - } - _, err = out.WriteString(render) - if err != nil { - return fmt.Errorf("unable to write regindered string: %w", err) - } - return nil + resSchema, resName := resourceSchema(providerSchema.ResourceSchemas, shortName, relFile) + if resSchema == nil { + return fmt.Errorf("unable to find resource for provider (%s) and template (%s)", shortName, relFile) + } + + tmpl := resourceTemplate(tmplData) + render, err := tmpl.Render(resName, providerName, "Resource", "", "", resSchema) + if err != nil { + return fmt.Errorf("unable to render resource template %q: %w", rel, err) } + _, err = out.WriteString(render) + if err != nil { + return fmt.Errorf("unable to write regindered string: %w", err) + } + return nil case "": // provider if relFile == "index.md.tmpl" { tmpl := providerTemplate(tmplData) diff --git a/internal/provider/util.go b/internal/provider/util.go index 277d4e8e..6410bbdd 100644 --- a/internal/provider/util.go +++ b/internal/provider/util.go @@ -9,6 +9,8 @@ import ( "os/exec" "path/filepath" "strings" + + tfjson "github.com/hashicorp/terraform-json" ) func providerShortName(n string) string { @@ -52,16 +54,21 @@ func removeAllExt(file string) string { } } -// resourceName determines whether the shortname and the relFile -// are identical after file extensions have been stripped from the -// latter. This allows single word resources (e.g., http) to use -// templates (e.g., http.md.tmpl). -func resourceName(shortName, relFile string) string { - if shortName == removeAllExt(relFile) { - return shortName +// resourceSchema determines whether there is a schema in the supplied schemas map which +// has either the providerShortName or the providerShortName concatenated with the +// templateFileName (stripped of file extension. +func resourceSchema(schemas map[string]*tfjson.Schema, providerShortName, templateFileName string) (*tfjson.Schema, string) { + if schema, ok := schemas[providerShortName]; ok { + return schema, providerShortName + } + + resName := providerShortName + "_" + removeAllExt(templateFileName) + + if schema, ok := schemas[resName]; ok { + return schema, resName } - return shortName + "_" + removeAllExt(relFile) + return nil, "" } func writeFile(path string, data string) error { diff --git a/internal/provider/util_test.go b/internal/provider/util_test.go index 36514aac..fae9bc68 100644 --- a/internal/provider/util_test.go +++ b/internal/provider/util_test.go @@ -4,32 +4,78 @@ import ( "testing" "github.com/google/go-cmp/cmp" + tfjson "github.com/hashicorp/terraform-json" ) -func Test_resourceName(t *testing.T) { +func Test_resourceSchema(t *testing.T) { cases := []struct { name string + schemas map[string]*tfjson.Schema providerShortName string templateFileName string + expectedSchema *tfjson.Schema expectedResourceName string }{ { - "provider short name same as template file name", + "provider short name matches schema name", + map[string]*tfjson.Schema{ + "http": {}, + }, "http", "http.md.tmpl", + &tfjson.Schema{}, "http", }, { - "provider short name different to template file name", + "provider short name does not match schema name", + map[string]*tfjson.Schema{ + "http": {}, + }, + "tls", + "http.md.tmpl", + nil, + "", + }, + { + "provider short name concatenated with template file name matches schema name", + map[string]*tfjson.Schema{ + "tls_cert_request": {}, + }, "tls", "cert_request.md.tmpl", + &tfjson.Schema{}, "tls_cert_request", }, + { + "provider short name concatenated with template file name does not match schema name", + map[string]*tfjson.Schema{ + "tls_cert_request": {}, + }, + "tls", + "not_found.md.tmpl", + nil, + "", + }, + { + "provider short name concatenated with same template file name matches schema name", + map[string]*tfjson.Schema{ + "tls_tls": {}, + }, + "tls", + "tls.md.tmpl", + &tfjson.Schema{}, + "tls_tls", + }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { - actualResourceName := resourceName(c.providerShortName, c.templateFileName) + actualSchema, actualResourceName := resourceSchema(c.schemas, c.providerShortName, c.templateFileName) + + if !cmp.Equal(c.expectedSchema, actualSchema) { + t.Errorf("expected: %+v, got: %+v", c.expectedSchema, actualSchema) + } + if !cmp.Equal(c.expectedResourceName, actualResourceName) { t.Errorf("expected: %s, got: %s", c.expectedResourceName, actualResourceName) } From d6cc3338eb49f911e63b6212acf64a1eb5dcf666 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 10 Jun 2022 09:17:54 +0100 Subject: [PATCH 5/5] Adding field names to test structs --- internal/provider/util_test.go | 72 ++++++++++++++++------------------ 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/internal/provider/util_test.go b/internal/provider/util_test.go index fae9bc68..3e94549c 100644 --- a/internal/provider/util_test.go +++ b/internal/provider/util_test.go @@ -8,68 +8,62 @@ import ( ) func Test_resourceSchema(t *testing.T) { - cases := []struct { - name string + cases := map[string]struct { schemas map[string]*tfjson.Schema providerShortName string templateFileName string expectedSchema *tfjson.Schema expectedResourceName string }{ - { - "provider short name matches schema name", - map[string]*tfjson.Schema{ + "provider short name matches schema name": { + schemas: map[string]*tfjson.Schema{ "http": {}, }, - "http", - "http.md.tmpl", - &tfjson.Schema{}, - "http", + providerShortName: "http", + templateFileName: "http.md.tmpl", + expectedSchema: &tfjson.Schema{}, + expectedResourceName: "http", }, - { - "provider short name does not match schema name", - map[string]*tfjson.Schema{ + "provider short name does not match schema name": { + schemas: map[string]*tfjson.Schema{ "http": {}, }, - "tls", - "http.md.tmpl", - nil, - "", + providerShortName: "tls", + templateFileName: "http.md.tmpl", + expectedSchema: nil, + expectedResourceName: "", }, - { - "provider short name concatenated with template file name matches schema name", - map[string]*tfjson.Schema{ + "provider short name concatenated with template file name matches schema name": { + schemas: map[string]*tfjson.Schema{ "tls_cert_request": {}, }, - "tls", - "cert_request.md.tmpl", - &tfjson.Schema{}, - "tls_cert_request", + providerShortName: "tls", + templateFileName: "cert_request.md.tmpl", + expectedSchema: &tfjson.Schema{}, + expectedResourceName: "tls_cert_request", }, - { - "provider short name concatenated with template file name does not match schema name", - map[string]*tfjson.Schema{ + "provider short name concatenated with template file name does not match schema name": { + schemas: map[string]*tfjson.Schema{ "tls_cert_request": {}, }, - "tls", - "not_found.md.tmpl", - nil, - "", + providerShortName: "tls", + templateFileName: "not_found.md.tmpl", + expectedSchema: nil, + expectedResourceName: "", }, - { - "provider short name concatenated with same template file name matches schema name", - map[string]*tfjson.Schema{ + "provider short name concatenated with same template file name matches schema name": { + schemas: map[string]*tfjson.Schema{ "tls_tls": {}, }, - "tls", - "tls.md.tmpl", - &tfjson.Schema{}, - "tls_tls", + providerShortName: "tls", + templateFileName: "tls.md.tmpl", + expectedSchema: &tfjson.Schema{}, + expectedResourceName: "tls_tls", }, } - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { + for name, c := range cases { + t.Run(name, func(t *testing.T) { actualSchema, actualResourceName := resourceSchema(c.schemas, c.providerShortName, c.templateFileName) if !cmp.Equal(c.expectedSchema, actualSchema) {