feat: add database_id attribute to resource model and update related functions

This commit is contained in:
Andre_Harms 2026-02-05 09:14:03 +01:00
parent 502b2f5e0a
commit 91913c3446
6 changed files with 90 additions and 65 deletions

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}
},
)
}
}

View file

@ -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
}

View file

@ -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)
}
},
)
}
}

View file

@ -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(),
)...,
)