fix: add databases generated file

feat: add useful command
This commit is contained in:
Marcel S. Henselin 2026-02-05 16:08:16 +01:00
parent 581e45eb9c
commit d8c63f848a
12 changed files with 418 additions and 232 deletions

View file

@ -4,6 +4,9 @@ import (
"bytes"
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io"
"log"
"log/slog"
@ -260,6 +263,7 @@ type templateData struct {
NameCamel string
NamePascal string
NameSnake string
Fields []string
}
func fileExists(path string) bool {
@ -317,11 +321,16 @@ func createBoilerplate(rootFolder, folder string) error {
return errors.New("resource name is invalid")
}
fields, tokenErr := getTokens(dsFile)
if tokenErr != nil {
return fmt.Errorf("error reading tokens: %w", tokenErr)
}
tplName := "data_source_scaffold.gotmpl"
err = writeTemplateToFile(
tplName,
path.Join(rootFolder, "cmd", "cmd", "build", "templates", tplName),
path.Join(folder, svc.Name(), res.Name(), "datasource.go"),
dsGoFile,
&templateData{
PackageName: svc.Name(),
PackageNameCamel: ToCamelCase(svc.Name()),
@ -329,6 +338,7 @@ func createBoilerplate(rootFolder, folder string) error {
NameCamel: ToCamelCase(resourceName),
NamePascal: ToPascalCase(resourceName),
NameSnake: resourceName,
Fields: fields,
},
)
if err != nil {
@ -342,11 +352,16 @@ func createBoilerplate(rootFolder, folder string) error {
return errors.New("resource name is invalid")
}
fields, tokenErr := getTokens(resFile)
if tokenErr != nil {
return fmt.Errorf("error reading tokens: %w", tokenErr)
}
tplName := "resource_scaffold.gotmpl"
err = writeTemplateToFile(
tplName,
path.Join(rootFolder, "cmd", "cmd", "build", "templates", tplName),
path.Join(folder, svc.Name(), res.Name(), "resource.go"),
resGoFile,
&templateData{
PackageName: svc.Name(),
PackageNameCamel: ToCamelCase(svc.Name()),
@ -354,6 +369,7 @@ func createBoilerplate(rootFolder, folder string) error {
NameCamel: ToCamelCase(resourceName),
NamePascal: ToPascalCase(resourceName),
NameSnake: resourceName,
Fields: fields,
},
)
if err != nil {
@ -813,3 +829,41 @@ func getRoot() (*string, error) {
lines := strings.Split(string(out), "\n")
return &lines[0], nil
}
func getTokens(fileName string) ([]string, error) {
fset := token.NewFileSet()
var result []string
node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err != nil {
return nil, err
}
ast.Inspect(node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
// fmt.Printf("found model: %s\n", ts.Name.Name)
ast.Inspect(ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
// fmt.Printf(" found: %+v\n", tts.Names[0])
// spew.Dump(tts.Type)
result = append(result, tts.Names[0].String())
//fld, fldOk := tts.Type.(*ast.Ident)
//if fldOk {
// fmt.Printf("type: %+v\n", fld)
//}
}
return true
})
}
}
return true
})
return result, nil
}

View file

@ -6,15 +6,16 @@ import (
"net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
{{.PackageName}}Pkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}"
{{.PackageName}}Utils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/utils"
{{.PackageName}}Gen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/datasources_gen"
)
@ -26,6 +27,11 @@ func New{{.NamePascal}}DataSource() datasource.DataSource {
return &{{.NameCamel}}DataSource{}
}
type dsModel struct {
{{.PackageName}}Gen.{{.NamePascal}}Model
TfId types.String `tfsdk:"id"`
}
type {{.NameCamel}}DataSource struct{
client *{{.PackageName}}Pkg.APIClient
providerData core.ProviderData
@ -37,6 +43,11 @@ func (d *{{.NameCamel}}DataSource) Metadata(_ context.Context, req datasource.Me
func (d *{{.NameCamel}}DataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = {{.PackageName}}Gen.{{.NamePascal}}DataSourceSchema(ctx)
resp.Schema.Attributes["id"] = schema.StringAttribute{
Computed: true,
Description: "The terraform internal identifier.",
MarkdownDescription: "The terraform internal identifier.",
}
}
// Configure adds the provider configured client to the data source.
@ -51,8 +62,30 @@ func (d *{{.NameCamel}}DataSource) Configure(
return
}
apiClient := {{.PackageName}}Utils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
apiClientConfigOptions := []config.ConfigurationOption{
config.WithCustomAuth(d.providerData.RoundTripper),
utils.UserAgentConfigOption(d.providerData.Version),
}
if d.providerData.{{.PackageNamePascal}}CustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(d.providerData.{{.PackageNamePascal}}CustomEndpoint),
)
} else {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithRegion(d.providerData.GetRegion()),
)
}
apiClient, err := {{.PackageName}}Pkg.NewAPIClient(apiClientConfigOptions...)
if err != nil {
resp.Diagnostics.AddError(
"Error configuring API client",
fmt.Sprintf(
"Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration",
err,
),
)
return
}
d.client = apiClient
@ -60,7 +93,7 @@ func (d *{{.NameCamel}}DataSource) Configure(
}
func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data {{.PackageName}}Gen.{{.NamePascal}}Model
var data dsModel
// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
@ -77,8 +110,11 @@ func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.Read
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// TODO: implement needed fields
ctx = tflog.SetField(ctx, "{{.NameCamel}}_id", {{.NameCamel}}Id)
// TODO: refactor to correct implementation
{{.NameCamel}}Resp, err := d.client.Get{{.NamePascal}}Request(ctx, projectId, region, {{.NameCamel}}Id).Execute()
if err != nil {
utils.LogError(
@ -98,22 +134,15 @@ func (d *{{.NameCamel}}DataSource) Read(ctx context.Context, req datasource.Read
ctx = core.LogResponse(ctx)
// Todo: Read API call logic
data.TfId = utils.BuildInternalTerraformId(projectId, region, ..)
// Example data value setting
// data.Id = types.StringValue("example-id")
err = mapResponseToModel(ctx, {{.NameCamel}}Resp, &data, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
fmt.Sprintf("%s Read", errorPrefix),
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// TODO: fill remaining fields
{{- range .Fields }}
// data.{{.}} = types.Sometype(apiResponse.Get{{.}}())
{{- end -}}
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, fmt.Sprintf("%s read successful", errorPrefix))
}

147
cmd/cmd/getFieldsCmd.go Normal file
View file

@ -0,0 +1,147 @@
package cmd
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"path"
"path/filepath"
"strings"
"github.com/spf13/cobra"
)
var (
inFile string
svcName string
resName string
resType string
filePath string
)
var getFieldsCmd = &cobra.Command{
Use: "get-fields",
Short: "get fields from file",
Long: `...`,
PreRunE: func(cmd *cobra.Command, args []string) error {
typeStr := "data_source"
if resType != "resource" && resType != "datasource" {
return fmt.Errorf("--type can only be resource or datasource")
}
if resType == "resource" {
typeStr = resType
}
if inFile == "" && svcName == "" && resName == "" {
return fmt.Errorf("--infile or --service and --resource must be provided")
}
if inFile != "" {
if svcName != "" || resName != "" {
return fmt.Errorf("--infile is provided and excludes --service and --resource")
}
p, err := filepath.Abs(inFile)
if err != nil {
return err
}
filePath = p
return nil
}
if svcName != "" && resName == "" {
return fmt.Errorf("if --service is provided, you MUST also provide --resource")
}
if svcName == "" && resName != "" {
return fmt.Errorf("if --resource is provided, you MUST also provide --service")
}
p, err := filepath.Abs(
path.Join(
"stackit",
"internal",
"services",
svcName,
resName,
fmt.Sprintf("%ss_gen", resType),
fmt.Sprintf("%s_%s_gen.go", resName, typeStr),
),
)
if err != nil {
return err
}
filePath = p
//// Enum check
//switch format {
//case "json", "yaml":
//default:
// return fmt.Errorf("invalid --format: %s (want json|yaml)", format)
//}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return getFields(filePath)
},
}
func getFields(f string) error {
tokens, err := getTokens(f)
if err != nil {
return err
}
for _, item := range tokens {
fmt.Printf("%s \n", item)
}
return nil
}
func getTokens(fileName string) ([]string, error) {
fset := token.NewFileSet()
var result []string
node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err != nil {
return nil, err
}
ast.Inspect(node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
// fmt.Printf("found model: %s\n", ts.Name.Name)
ast.Inspect(ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
// fmt.Printf(" found: %+v\n", tts.Names[0])
// spew.Dump(tts.Type)
result = append(result, tts.Names[0].String())
//fld, fldOk := tts.Type.(*ast.Ident)
//if fldOk {
// fmt.Printf("type: %+v\n", fld)
//}
}
return true
})
}
}
return true
})
return result, nil
}
func NewGetFieldsCmd() *cobra.Command {
return getFieldsCmd
}
func init() { // nolint: gochecknoinits
getFieldsCmd.Flags().StringVarP(&inFile, "infile", "i", "", "input filename incl path")
getFieldsCmd.Flags().StringVarP(&svcName, "service", "s", "", "service name")
getFieldsCmd.Flags().StringVarP(&resName, "resource", "r", "", "resource name")
getFieldsCmd.Flags().StringVarP(&resType, "type", "t", "resource", "resource type (data-source or resource [default])")
}

View file

@ -28,6 +28,7 @@ func main() {
rootCmd.AddCommand(
cmd.NewBuildCmd(),
cmd.NewPublishCmd(),
cmd.NewGetFieldsCmd(),
)
err := rootCmd.Execute()