From 27a9d7f20e0172d32db6e83d2c4cd7dbf21002f0 Mon Sep 17 00:00:00 2001 From: "Marcel S. Henselin" Date: Wed, 4 Feb 2026 09:25:47 +0100 Subject: [PATCH] feat: refactor and extend builder --- cmd/cmd/build/build.go | 33 ++++++- .../build/templates/functions_scaffold.gotmpl | 96 +++++++++++++++++++ .../build/templates/resource_scaffold.gotmpl | 60 +++++++++--- 3 files changed, 171 insertions(+), 18 deletions(-) create mode 100644 cmd/cmd/build/templates/functions_scaffold.gotmpl diff --git a/cmd/cmd/build/build.go b/cmd/cmd/build/build.go index cb8a7801..3351d5da 100644 --- a/cmd/cmd/build/build.go +++ b/cmd/cmd/build/build.go @@ -312,7 +312,7 @@ func createBoilerplate(rootFolder, folder string) error { foundRes = fileExists(resGoFile) if handleDS && !foundDS { - slog.Info("Creating missing datasource.go", "service", svc.Name(), "resource", resourceName) + slog.Info(" creating missing datasource.go", "service", svc.Name(), "resource", resourceName) if !ValidateSnakeCase(resourceName) { return errors.New("resource name is invalid") } @@ -320,7 +320,7 @@ func createBoilerplate(rootFolder, folder string) error { tplName := "data_source_scaffold.gotmpl" err = writeTemplateToFile( tplName, - path.Join(rootFolder, "cmd", "build", "templates", tplName), + path.Join(rootFolder, "cmd", "cmd", "build", "templates", tplName), path.Join(folder, svc.Name(), res.Name(), "datasource.go"), &templateData{ PackageName: svc.Name(), @@ -337,7 +337,7 @@ func createBoilerplate(rootFolder, folder string) error { } if handleRes && !foundRes { - slog.Info("Creating missing resource.go", "service", svc.Name(), "resource", resourceName) + slog.Info(" creating missing resource.go", "service", svc.Name(), "resource", resourceName) if !ValidateSnakeCase(resourceName) { return errors.New("resource name is invalid") } @@ -345,7 +345,7 @@ func createBoilerplate(rootFolder, folder string) error { tplName := "resource_scaffold.gotmpl" err = writeTemplateToFile( tplName, - path.Join(rootFolder, "cmd", "build", "templates", tplName), + path.Join(rootFolder, "cmd", "cmd", "build", "templates", tplName), path.Join(folder, svc.Name(), res.Name(), "resource.go"), &templateData{ PackageName: svc.Name(), @@ -359,6 +359,31 @@ func createBoilerplate(rootFolder, folder string) error { if err != nil { return err } + + if !fileExists(path.Join(folder, svc.Name(), res.Name(), "functions.go")) { + slog.Info(" creating missing functions.go", "service", svc.Name(), "resource", resourceName) + if !ValidateSnakeCase(resourceName) { + return errors.New("resource name is invalid") + } + fncTplName := "functions_scaffold.gotmpl" + err = writeTemplateToFile( + fncTplName, + path.Join(rootFolder, "cmd", "cmd", "build", "templates", fncTplName), + path.Join(folder, svc.Name(), res.Name(), "functions.go"), + &templateData{ + PackageName: svc.Name(), + PackageNameCamel: ToCamelCase(svc.Name()), + PackageNamePascal: ToPascalCase(svc.Name()), + NameCamel: ToCamelCase(resourceName), + NamePascal: ToPascalCase(resourceName), + NameSnake: resourceName, + }, + ) + if err != nil { + return err + } + + } } } } diff --git a/cmd/cmd/build/templates/functions_scaffold.gotmpl b/cmd/cmd/build/templates/functions_scaffold.gotmpl new file mode 100644 index 00000000..d7ca6519 --- /dev/null +++ b/cmd/cmd/build/templates/functions_scaffold.gotmpl @@ -0,0 +1,96 @@ +package {{.PackageName}} + +import ( + "context" + "fmt" + "math" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" + + {{.PackageName}} "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}" + {{.PackageName}}ResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}alpha/instance/resources_gen" +) + +func mapResponseToModel( + ctx context.Context, + resp *{{.PackageName}}.Get{{.NamePascal}}Response, + m *{{.PackageName}}ResGen.{{.NamePascal}}Model, + tfDiags diag.Diagnostics, +) error { + // TODO: complete and refactor + m.Id = types.StringValue(resp.GetId()) + + sampleList, diags := types.ListValueFrom(ctx, types.StringType, resp.GetList()) + tfDiags.Append(diags...) + if diags.HasError() { + return fmt.Errorf( + "error converting list response value", + ) + } + sample, diags := {{.PackageName}}ResGen.NewSampleValue( + {{.PackageName}}ResGen.SampleValue{}.AttributeTypes(ctx), + map[string]attr.Value{ + "field": types.StringValue(string(resp.GetField())), + }, + ) + tfDiags.Append(diags...) + if diags.HasError() { + return fmt.Errorf( + "error converting sample response value", + "sample", + types.StringValue(string(resp.GetField())), + ) + } + m.Sample = sample + return nil +} + +func handleEncryption( + m *{{.PackageName}}ResGen.{{.NamePascal}}Model, + resp *{{.PackageName}}.Get{{.NamePascal}}Response, +) {{.PackageName}}ResGen.EncryptionValue { + if !resp.HasEncryption() || + resp.Encryption == nil || + resp.Encryption.KekKeyId == nil || + resp.Encryption.KekKeyRingId == nil || + resp.Encryption.KekKeyVersion == nil || + resp.Encryption.ServiceAccount == nil { + + if m.Encryption.IsNull() || m.Encryption.IsUnknown() { + return {{.PackageName}}ResGen.NewEncryptionValueNull() + } + return m.Encryption + } + + enc := {{.PackageName}}ResGen.NewEncryptionValueNull() + if kVal, ok := resp.Encryption.GetKekKeyIdOk(); ok { + enc.KekKeyId = types.StringValue(kVal) + } + if kkVal, ok := resp.Encryption.GetKekKeyRingIdOk(); ok { + enc.KekKeyRingId = types.StringValue(kkVal) + } + if kkvVal, ok := resp.Encryption.GetKekKeyVersionOk(); ok { + enc.KekKeyVersion = types.StringValue(kkvVal) + } + if sa, ok := resp.Encryption.GetServiceAccountOk(); ok { + enc.ServiceAccount = types.StringValue(sa) + } + return enc +} + +func toCreatePayload( + ctx context.Context, + model *{{.PackageName}}ResGen.{{.NamePascal}}Model, +) (*{{.PackageName}}.Create{{.NamePascal}}RequestPayload, error) { + if model == nil { + return nil, fmt.Errorf("nil model") + } + + return &{{.PackageName}}.Create{{.NamePascal}}RequestPayload{ + // TODO: fill fields + }, nil +} diff --git a/cmd/cmd/build/templates/resource_scaffold.gotmpl b/cmd/cmd/build/templates/resource_scaffold.gotmpl index ff967ff7..f3f2d729 100644 --- a/cmd/cmd/build/templates/resource_scaffold.gotmpl +++ b/cmd/cmd/build/templates/resource_scaffold.gotmpl @@ -2,15 +2,23 @@ package {{.PackageName}} import ( "context" + "fmt" + "strings" - "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "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" + "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/{{.PackageName}}" - {{.PackageName}}Gen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/resources_gen" + "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}}ResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/{{.PackageName}}/{{.NameSnake}}/resources_gen" ) var ( @@ -151,6 +159,9 @@ func (r *{{.NameCamel}}Resource) Read(ctx context.Context, req resource.ReadRequ // Read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } // Read identity data var identityData {{.NamePascal}}ResourceIdentityModel @@ -159,9 +170,13 @@ func (r *{{.NameCamel}}Resource) Read(ctx context.Context, req resource.ReadRequ return } - if resp.Diagnostics.HasError() { - return - } + + ctx = core.InitProviderContext(ctx) + + projectId := identityData.ProjectID.ValueString() + region := identityData.Region.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) // Todo: Read API call logic @@ -185,9 +200,8 @@ func (r *{{.NameCamel}}Resource) Read(ctx context.Context, req resource.ReadRequ func (r *{{.NameCamel}}Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var data {{.PackageName}}Gen.{{.NamePascal}}Model - // Read Terraform plan data into the model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } @@ -199,9 +213,13 @@ func (r *{{.NameCamel}}Resource) Update(ctx context.Context, req resource.Update return } - if resp.Diagnostics.HasError() { - return - } + + ctx = core.InitProviderContext(ctx) + + projectId := identityData.ProjectID.ValueString() + region := identityData.Region.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) // Todo: Update API call logic @@ -216,11 +234,25 @@ func (r *{{.NameCamel}}Resource) Delete(ctx context.Context, req resource.Delete // Read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } + // Read identity data + var identityData {{.NamePascal}}ResourceIdentityModel + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + if resp.Diagnostics.HasError() { + return + } + + + ctx = core.InitProviderContext(ctx) + + projectId := identityData.ProjectID.ValueString() + region := identityData.Region.ValueString() + ctx = tflog.SetField(ctx, "project_id", projectId) + ctx = tflog.SetField(ctx, "region", region) + // Todo: Delete API call logic tflog.Info(ctx, "{{.PackageName}}.{{.NamePascal}} deleted")