-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: new scaffold command for creating resources/data sources (#1739)
* feat: new scaffold command for guiding new development * include template for acceptance test file
- Loading branch information
1 parent
c0ae26a
commit b9e795b
Showing
10 changed files
with
666 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"text/template" | ||
) | ||
|
||
const ( | ||
ResourceCmd = "resource" | ||
DataSourceCmd = "data-source" | ||
PluralDataSourceCmd = "plural-data-source" | ||
) | ||
|
||
// struct which is applied to go template files | ||
type ScaffoldParams struct { | ||
GenerationType string | ||
NamePascalCase string | ||
NameCamelCase string | ||
NameSnakeCase string | ||
NameLowerNoSpaces string | ||
} | ||
|
||
type FileGeneration struct { | ||
TemplatePath string | ||
OutputPath string | ||
} | ||
|
||
func main() { | ||
nameCamelCase := os.Args[1] | ||
generationType := os.Args[2] | ||
|
||
params := ScaffoldParams{ | ||
GenerationType: generationType, | ||
NamePascalCase: ToPascalCase(nameCamelCase), | ||
NameCamelCase: nameCamelCase, | ||
NameSnakeCase: ToSnakeCase(nameCamelCase), | ||
NameLowerNoSpaces: strings.ToLower(nameCamelCase), | ||
} | ||
|
||
files, err := filesToGenerate(¶ms) | ||
if err != nil { | ||
panic(err) | ||
} | ||
for _, file := range files { | ||
if err := generateFileFromTemplate(file, ¶ms); err != nil { | ||
panic(err) | ||
} | ||
} | ||
} | ||
|
||
func filesToGenerate(params *ScaffoldParams) ([]FileGeneration, error) { | ||
folderPath := fmt.Sprintf("internal/service/%s", params.NameLowerNoSpaces) | ||
|
||
switch params.GenerationType { | ||
case ResourceCmd: | ||
return []FileGeneration{ | ||
{ | ||
TemplatePath: "tools/scaffold/template/resource.tmpl", | ||
OutputPath: fmt.Sprintf("%s/resource_%s.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/acc_test.tmpl", | ||
OutputPath: fmt.Sprintf("%s/resource_%s_test.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/model.tmpl", | ||
OutputPath: fmt.Sprintf("%s/model_%s.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/model_test.tmpl", | ||
OutputPath: fmt.Sprintf("%s/model_%s_test.go", folderPath, params.NameSnakeCase), | ||
}, | ||
}, nil | ||
case DataSourceCmd: | ||
return []FileGeneration{ | ||
{ | ||
TemplatePath: "tools/scaffold/template/datasource.tmpl", | ||
OutputPath: fmt.Sprintf("%s/data_source_%s.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/acc_test.tmpl", | ||
OutputPath: fmt.Sprintf("%s/data_source_%s_test.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/model.tmpl", | ||
OutputPath: fmt.Sprintf("%s/model_%s.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/model_test.tmpl", | ||
OutputPath: fmt.Sprintf("%s/model_%s_test.go", folderPath, params.NameSnakeCase), | ||
}, | ||
}, nil | ||
case PluralDataSourceCmd: | ||
return []FileGeneration{ | ||
{ | ||
TemplatePath: "tools/scaffold/template/pluraldatasource.tmpl", | ||
OutputPath: fmt.Sprintf("%s/data_source_%ss.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/acc_test.tmpl", | ||
OutputPath: fmt.Sprintf("%s/data_source_%ss_test.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/model.tmpl", | ||
OutputPath: fmt.Sprintf("%s/model_%s.go", folderPath, params.NameSnakeCase), | ||
}, | ||
{ | ||
TemplatePath: "tools/scaffold/template/model_test.tmpl", | ||
OutputPath: fmt.Sprintf("%s/model_%s_test.go", folderPath, params.NameSnakeCase), | ||
}, | ||
}, nil | ||
default: | ||
return nil, errors.New("unknown generation type provided") | ||
} | ||
} | ||
|
||
func generateFileFromTemplate(generation FileGeneration, params *ScaffoldParams) error { | ||
tmpl, err := template.ParseFiles(generation.TemplatePath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// ensure content of existing files is not overwritten | ||
if _, err := os.Stat(generation.OutputPath); err == nil { | ||
log.Printf("File already exists: %s", generation.OutputPath) | ||
return nil | ||
} | ||
file := createDirsAndFile(generation.OutputPath) | ||
|
||
if err := tmpl.Execute(file, params); err != nil { | ||
return err | ||
} | ||
file.Close() | ||
return nil | ||
} | ||
|
||
func createDirsAndFile(path string) *os.File { | ||
dirPath := filepath.Dir(path) | ||
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil { | ||
log.Fatalf("Failed to create directories: %s", err) | ||
} | ||
|
||
file, err := os.Create(path) | ||
if err != nil { | ||
log.Fatalf("Failed to create file: %s", err) | ||
} | ||
return file | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package main | ||
|
||
import ( | ||
"strings" | ||
"unicode" | ||
) | ||
|
||
// toPascalCase converts camel case to pascal case. | ||
func ToPascalCase(input string) string { | ||
return strings.ToUpper(input[:1]) + input[1:] | ||
} | ||
|
||
// toSnakeCase converts camel case to snake case. | ||
func ToSnakeCase(input string) string { | ||
var result []rune | ||
for i, r := range input { | ||
if unicode.IsUpper(r) && i > 0 { | ||
result = append(result, '_') | ||
} | ||
result = append(result, unicode.ToLower(r)) | ||
} | ||
return string(result) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package {{.NameLowerNoSpaces}}_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
"github.com/mongodb/terraform-provider-mongodbatlas/internal/testutil/acc" | ||
) | ||
|
||
// TODO: if acceptance test will be run in an existing CI group of resources, the name should include the group in the prefix followed by the name of the resource e.i. TestAccStreamRSStreamInstance_basic | ||
// In addition, if acceptance test contains testing of both resource and data sources, the RS/DS can be omitted. | ||
func TestAcc{{.NamePascalCase}}{{if eq .GenerationType "resource"}}RS{{else}}DS{{end}}_basic(t *testing.T) { | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { acc.PreCheckBasic(t) }, | ||
ProtoV6ProviderFactories: acc.TestAccProviderV6Factories, | ||
// CheckDestroy: checkDestroy{{.NamePascalCase}}, | ||
Steps: []resource.TestStep{ // TODO: verify updates and import in case of resources | ||
// { | ||
// Config: {{.NameCamelCase}}Config(), | ||
// Check: {{.NameCamelCase}}AttributeChecks(), | ||
// }, | ||
// { | ||
// Config: {{.NameCamelCase}}Config(), | ||
// Check: {{.NameCamelCase}}AttributeChecks(), | ||
// }, | ||
// { | ||
// Config: {{.NameCamelCase}}Config(), | ||
// ResourceName: resourceName, | ||
// ImportStateIdFunc: check{{.NamePascalCase}}ImportStateIDFunc, | ||
// ImportState: true, | ||
// ImportStateVerify: true, | ||
}, | ||
}, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package {{.NameLowerNoSpaces}} | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/datasource" | ||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/mongodb/terraform-provider-mongodbatlas/internal/config" | ||
) | ||
|
||
const {{.NameCamelCase}}Name = "{{.NameSnakeCase}}" // TODO: if resource exists this can be deleted | ||
|
||
var _ datasource.DataSource = &{{.NameCamelCase}}DS{} | ||
var _ datasource.DataSourceWithConfigure = &{{.NameCamelCase}}DS{} | ||
|
||
func DataSource() datasource.DataSource { | ||
return &{{.NameCamelCase}}DS{ | ||
DSCommon: config.DSCommon{ | ||
DataSourceName: {{.NameCamelCase}}Name, | ||
}, | ||
} | ||
} | ||
|
||
type {{.NameCamelCase}}DS struct { | ||
config.DSCommon | ||
} | ||
|
||
// TODO: if resource exists and TF{{.NamePascalCase}}Model is identical to data source attributes the existing model should be reutilized | ||
// type TF{{.NamePascalCase}}DSModel struct { | ||
// ID types.String `tfsdk:"id"` | ||
// TODO: add attribute definitions | ||
//} | ||
|
||
func (d *{{.NameCamelCase}}DS) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { | ||
resp.Schema = schema.Schema{ | ||
Attributes: map[string]schema.Attribute{ | ||
"id": schema.StringAttribute{ | ||
Computed: true, | ||
}, | ||
// TODO: add attribute definitions | ||
}, | ||
} | ||
} | ||
|
||
func (d *{{.NameCamelCase}}DS) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { | ||
var {{.NameCamelCase}}Config TF{{.NamePascalCase}}Model | ||
resp.Diagnostics.Append(req.Config.Get(ctx, &{{.NameCamelCase}}Config)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
// TODO: make get request to resource | ||
|
||
// connV2 := r.Client.AtlasV2 | ||
//if err != nil { | ||
// resp.Diagnostics.AddError("error fetching resource", err.Error()) | ||
// return | ||
//} | ||
|
||
// TODO: process response into new terraform state | ||
new{{.NamePascalCase}}Model, diags := NewTF{{.NamePascalCase}}(ctx, apiResp) | ||
if diags.HasError() { | ||
resp.Diagnostics.Append(diags...) | ||
return | ||
} | ||
resp.Diagnostics.Append(resp.State.Set(ctx, new{{.NamePascalCase}}Model)...) | ||
} |
Oops, something went wrong.