diff --git a/stackit/internal/services/postgresflexalpha/database/datasource.go b/stackit/internal/services/postgresflexalpha/database/datasource.go index 28d8e51d..9212bb0a 100644 --- a/stackit/internal/services/postgresflexalpha/database/datasource.go +++ b/stackit/internal/services/postgresflexalpha/database/datasource.go @@ -26,6 +26,7 @@ type DataSourceModel struct { ProjectId types.String `tfsdk:"project_id"` InstanceId types.String `tfsdk:"instance_id"` Region types.String `tfsdk:"region"` + DatabaseID types.Int64 `tfsdk:"database_id"` TerraformID types.String `tfsdk:"tf_id"` } @@ -93,11 +94,17 @@ func (r *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequ MarkdownDescription: "Region of the PostgresFlex instance.", Optional: true, } - s.Attributes["tf_id"] = schema.StringAttribute{ - Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`,`id`\\\".\",", + s.Attributes["database_id"] = schema.Int64Attribute{ + Description: "The ID of the database.", Optional: true, Computed: true, } + s.Attributes["tf_id"] = schema.StringAttribute{ + Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`," + + "`database_id`\\\".\",", + Optional: true, + Computed: true, + } resp.Schema = s } @@ -164,7 +171,7 @@ func (r *databaseDataSource) getDatabaseByNameOrID( projectId, region, instanceId string, diags *diag.Diagnostics, ) (*postgresflexalpha.ListDatabase, error) { - isIdSet := !model.Id.IsNull() && !model.Id.IsUnknown() + isIdSet := !model.DatabaseID.IsNull() && !model.DatabaseID.IsUnknown() isNameSet := !model.Name.IsNull() && !model.Name.IsUnknown() if (isIdSet && isNameSet) || (!isIdSet && !isNameSet) { @@ -176,8 +183,8 @@ func (r *databaseDataSource) getDatabaseByNameOrID( } if isIdSet { - databaseId := model.Id.ValueInt64() - ctx = tflog.SetField(ctx, "id", databaseId) + databaseId := model.DatabaseID.ValueInt64() + ctx = tflog.SetField(ctx, "database_id", databaseId) return getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId) } diff --git a/stackit/internal/services/postgresflexalpha/database/functions.go b/stackit/internal/services/postgresflexalpha/database/functions.go index b1c30bb9..4496faa1 100644 --- a/stackit/internal/services/postgresflexalpha/database/functions.go +++ b/stackit/internal/services/postgresflexalpha/database/functions.go @@ -3,6 +3,7 @@ package postgresflexalpha import ( "context" "fmt" + "strings" postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha" ) @@ -79,3 +80,12 @@ func getDatabase( return nil, fmt.Errorf("database not found for instance %s", instanceId) } + +// cleanString removes leading and trailing quotes which are sometimes returned by the API. +func cleanString(s *string) *string { + if s == nil { + return nil + } + res := strings.Trim(*s, "\"") + return &res +} diff --git a/stackit/internal/services/postgresflexalpha/database/functions_test.go b/stackit/internal/services/postgresflexalpha/database/functions_test.go index 5233478e..9f0b47fd 100644 --- a/stackit/internal/services/postgresflexalpha/database/functions_test.go +++ b/stackit/internal/services/postgresflexalpha/database/functions_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/google/go-cmp/cmp" "github.com/stackitcloud/stackit-sdk-go/core/utils" postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha" ) @@ -194,3 +195,38 @@ func TestGetDatabase(t *testing.T) { ) } } + +func TestCleanString(t *testing.T) { + testcases := []struct { + name string + given *string + expected *string + }{ + { + name: "should remove quotes", + given: utils.Ptr("\"quoted\""), + expected: utils.Ptr("quoted"), + }, + { + name: "should handle nil", + given: nil, + expected: nil, + }, + { + name: "should not change unquoted string", + given: utils.Ptr("unquoted"), + expected: utils.Ptr("unquoted"), + }, + } + + for _, tc := range testcases { + t.Run( + tc.name, func(t *testing.T) { + actual := cleanString(tc.given) + if diff := cmp.Diff(tc.expected, actual); diff != "" { + t.Errorf("string mismatch (-want +got):\n%s", diff) + } + }, + ) + } +} diff --git a/stackit/internal/services/postgresflexalpha/database/mapper.go b/stackit/internal/services/postgresflexalpha/database/mapper.go index 560dc38d..69830e45 100644 --- a/stackit/internal/services/postgresflexalpha/database/mapper.go +++ b/stackit/internal/services/postgresflexalpha/database/mapper.go @@ -2,7 +2,6 @@ package postgresflexalpha import ( "fmt" - "strings" "github.com/hashicorp/terraform-plugin-framework/types" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha" @@ -34,6 +33,7 @@ func mapFields( } model.Id = types.Int64Value(databaseId) + model.DatabaseID = types.Int64Value(databaseId) model.Name = types.StringPointerValue(source.Name) model.Owner = types.StringPointerValue(cleanString(source.Owner)) model.Region = types.StringValue(region) @@ -63,6 +63,7 @@ func mapResourceFields(source *postgresflexalpha.ListDatabase, model *ResourceMo } model.Id = types.Int64Value(databaseId) + model.DatabaseID = types.Int64Value(databaseId) model.Name = types.StringPointerValue(source.Name) model.Owner = types.StringPointerValue(cleanString(source.Owner)) return nil @@ -79,12 +80,3 @@ func toCreatePayload(model *ResourceModel) (*postgresflexalpha.CreateDatabaseReq Owner: model.Owner.ValueStringPointer(), }, nil } - -// cleanString removes leading and trailing quotes which are sometimes returned by the API. -func cleanString(s *string) *string { - if s == nil { - return nil - } - res := strings.Trim(*s, "\"") - return &res -} diff --git a/stackit/internal/services/postgresflexalpha/database/mapper_test.go b/stackit/internal/services/postgresflexalpha/database/mapper_test.go index 3fc661a1..ead6b1e3 100644 --- a/stackit/internal/services/postgresflexalpha/database/mapper_test.go +++ b/stackit/internal/services/postgresflexalpha/database/mapper_test.go @@ -45,7 +45,8 @@ func TestMapFields(t *testing.T) { Name: types.StringValue("my-db"), Owner: types.StringValue("my-owner"), }, - Region: types.StringValue("eu01"), + Region: types.StringValue("eu01"), + DatabaseID: types.Int64Value(1), }, }, }, @@ -70,7 +71,8 @@ func TestMapFields(t *testing.T) { Name: types.StringValue("my-db"), Owner: types.StringNull(), }, - Region: types.StringValue("eu01"), + DatabaseID: types.Int64Value(1), + Region: types.StringValue("eu01"), }, }, }, @@ -149,6 +151,7 @@ func TestMapResourceFields(t *testing.T) { Name: types.StringValue("my-db"), Owner: types.StringValue("my-owner"), }, + DatabaseID: types.Int64Value(1), }, }, }, @@ -233,38 +236,3 @@ func TestToCreatePayload(t *testing.T) { ) } } - -func TestCleanString(t *testing.T) { - testcases := []struct { - name string - given *string - expected *string - }{ - { - name: "should remove quotes", - given: utils.Ptr("\"quoted\""), - expected: utils.Ptr("quoted"), - }, - { - name: "should handle nil", - given: nil, - expected: nil, - }, - { - name: "should not change unquoted string", - given: utils.Ptr("unquoted"), - expected: utils.Ptr("unquoted"), - }, - } - - for _, tc := range testcases { - t.Run( - tc.name, func(t *testing.T) { - actual := cleanString(tc.given) - if diff := cmp.Diff(tc.expected, actual); diff != "" { - t.Errorf("string mismatch (-want +got):\n%s", diff) - } - }, - ) - } -} diff --git a/stackit/internal/services/postgresflexalpha/database/resource.go b/stackit/internal/services/postgresflexalpha/database/resource.go index b8c79dca..ca8230a3 100644 --- a/stackit/internal/services/postgresflexalpha/database/resource.go +++ b/stackit/internal/services/postgresflexalpha/database/resource.go @@ -13,6 +13,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" @@ -37,6 +40,7 @@ var ( type ResourceModel struct { postgresflexalpha2.DatabaseModel TerraformID types.String `tfsdk:"tf_id"` + DatabaseID types.Int64 `tfsdk:"database_id"` } // DatabaseResourceIdentityModel describes the resource's identity attributes. @@ -44,7 +48,7 @@ type DatabaseResourceIdentityModel struct { ProjectID types.String `tfsdk:"project_id"` Region types.String `tfsdk:"region"` InstanceID types.String `tfsdk:"instance_id"` - DatabaseID types.Int64 `tfsdk:"id"` + DatabaseID types.Int64 `tfsdk:"database_id"` } // NewDatabaseResource is a helper function to simplify the provider implementation. @@ -123,10 +127,18 @@ var modifiersFileByte []byte func (r *databaseResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { s := postgresflexalpha2.DatabaseResourceSchema(ctx) s.Attributes["tf_id"] = schema.StringAttribute{ - Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`,`id`\\\".\",", + Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`,`database_id`\\\".\",", Optional: true, Computed: true, } + s.Attributes["database_id"] = schema.Int64Attribute{ + Description: "ID of the database.", + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + Validators: []validator.Int64{}, + } fields, err := postgresflexUtils.ReadModifiersConfig(modifiersFileByte) if err != nil { @@ -159,7 +171,7 @@ func (r *databaseResource) IdentitySchema( "instance_id": identityschema.StringAttribute{ RequiredForImport: true, }, - "id": identityschema.StringAttribute{ + "database_id": identityschema.Int64Attribute{ // database id RequiredForImport: true, }, @@ -289,12 +301,12 @@ func (r *databaseResource) Read( projectId := identityData.ProjectID.ValueString() instanceId := identityData.InstanceID.ValueString() - databaseId := model.Id.ValueInt64() + databaseId := model.DatabaseID.ValueInt64() region := r.providerData.GetRegionWithOverride(identityData.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "region", region) - ctx = tflog.SetField(ctx, "id", databaseId) //database id + ctx = tflog.SetField(ctx, "database_id", databaseId) databaseResp, err := getDatabaseById(ctx, r.client, projectId, region, instanceId, databaseId) if err != nil { @@ -356,7 +368,7 @@ func (r *databaseResource) Update( instanceId := identityData.InstanceID.ValueString() region := r.providerData.GetRegionWithOverride(identityData.Region) - databaseId64 := model.Id.ValueInt64() + databaseId64 := model.DatabaseID.ValueInt64() // database id if databaseId64 > math.MaxInt32 { core.LogAndAddError(ctx, &resp.Diagnostics, "Error in type conversion", "int value too large (databaseId)") return @@ -366,7 +378,7 @@ func (r *databaseResource) Update( ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "region", region) - ctx = tflog.SetField(ctx, "id", databaseId) + ctx = tflog.SetField(ctx, "database_id", databaseId) // Retrieve values from state var stateModel ResourceModel @@ -453,7 +465,7 @@ func (r *databaseResource) Delete( projectId := identityData.ProjectID.ValueString() instanceId := identityData.InstanceID.ValueString() region := r.providerData.GetRegionWithOverride(identityData.Region) - databaseId64 := model.Id.ValueInt64() + databaseId64 := model.DatabaseID.ValueInt64() //database id if databaseId64 > math.MaxInt32 { core.LogAndAddError(ctx, &resp.Diagnostics, "Error in type conversion", "int value too large (databaseId)") @@ -463,7 +475,7 @@ func (r *databaseResource) Delete( ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "region", region) - ctx = tflog.SetField(ctx, "id", databaseId) //database id + ctx = tflog.SetField(ctx, "database_id", databaseId) // Delete existing record set err := r.client.DeleteDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseId) @@ -477,7 +489,7 @@ func (r *databaseResource) Delete( } // ImportState imports a resource into the Terraform state on success. -// The expected import identifier format is: [project_id],[region],[instance_id],[id] +// The expected import identifier format is: [project_id],[region],[instance_id],[database_id] func (r *databaseResource) ImportState( ctx context.Context, req resource.ImportStateRequest, @@ -489,7 +501,7 @@ func (r *databaseResource) ImportState( ctx, &resp.Diagnostics, "Error importing database", fmt.Sprintf( - "Expected import identifier with format [project_id],[region],[instance_id],[id], got %q", + "Expected import identifier with format [project_id],[region],[instance_id],[database_id], got %q", req.ID, ), ) @@ -499,7 +511,7 @@ func (r *databaseResource) ImportState( resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...) - resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), idParts[3])...) //database id + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_id"), idParts[3])...) core.LogAndAddWarning( ctx, &resp.Diagnostics, @@ -547,7 +559,7 @@ func (r *databaseResource) ImportState( resp.Diagnostics.Append( resp.State.SetAttribute( ctx, - path.Root("id"), // database id + path.Root("database_id"), identityData.DatabaseID.ValueInt64(), )..., )