diff --git a/pkg/postgresflexalpha/wait/wait.go b/pkg/postgresflexalpha/wait/wait.go index 283cb315..ac09c308 100644 --- a/pkg/postgresflexalpha/wait/wait.go +++ b/pkg/postgresflexalpha/wait/wait.go @@ -21,22 +21,89 @@ const ( // Interface needed for tests type APIClientInstanceInterface interface { - GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*postgresflex.GetInstanceResponse, error) - ListUsersRequestExecute(ctx context.Context, projectId, region, instanceId string) (*postgresflex.ListUserResponse, error) + GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) ( + *postgresflex.GetInstanceResponse, + error, + ) + + ListUsersRequestExecute( + ctx context.Context, projectId string, region string, + instanceId string, + ) (*postgresflex.ListUserResponse, error) } // Interface needed for tests type APIClientUserInterface interface { - GetUserRequestExecute(ctx context.Context, projectId, region, instanceId string, userId int64) (*postgresflex.GetUserResponse, error) + GetUserRequestExecute(ctx context.Context, projectId, region, instanceId string, userId int64) ( + *postgresflex.GetUserResponse, + error, + ) } // CreateInstanceWaitHandler will wait for instance creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] { +func CreateInstanceWaitHandler( + ctx context.Context, a APIClientInstanceInterface, projectId, region, + instanceId string, +) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] { instanceCreated := false var instanceGetResponse *postgresflex.GetInstanceResponse - handler := wait.New(func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) { - if !instanceCreated { + handler := wait.New( + func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) { + if !instanceCreated { + s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) + if err != nil { + return false, nil, err + } + if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil { + return false, nil, nil + } + switch *s.Status { + default: + return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status) + case InstanceStateEmpty: + return false, nil, nil + case InstanceStateProgressing: + return false, nil, nil + case InstanceStateSuccess: + instanceCreated = true + instanceGetResponse = s + case InstanceStateFailed: + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) + } + } + + // // User operations aren't available right after an instance is deemed successful + // // To check if they are, perform a users request + _, err = a.ListUsersRequestExecute(ctx, projectId, region, instanceId) + if err == nil { + return true, instanceGetResponse, nil + } + oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped + if !ok { + return false, nil, err + } + if oapiErr.StatusCode < 500 { + return true, instanceGetResponse, fmt.Errorf( + "users request after instance creation returned %d status code", + oapiErr.StatusCode, + ) + } + return false, nil, nil + }, + ) + // Sleep before wait is set because sometimes API returns 404 right after creation request + handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(15 * time.Second) + return handler +} + +// PartialUpdateInstanceWaitHandler will wait for instance update +func PartialUpdateInstanceWaitHandler( + ctx context.Context, a APIClientInstanceInterface, projectId, region, + instanceId string, +) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] { + handler := wait.New( + func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) { s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) if err != nil { return false, nil, err @@ -52,119 +119,93 @@ func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface case InstanceStateProgressing: return false, nil, nil case InstanceStateSuccess: - instanceCreated = true - instanceGetResponse = s + return true, s, nil case InstanceStateFailed: - return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) } - } - - // User operations aren't available right after an instance is deemed successful - // To check if they are, perform a users request - _, err = a.ListUsersRequestExecute(ctx, projectId, region, instanceId) - if err == nil { - return true, instanceGetResponse, nil - } - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if !ok { - return false, nil, err - } - if oapiErr.StatusCode < 500 { - return true, instanceGetResponse, fmt.Errorf("users request after instance creation returned %d status code", oapiErr.StatusCode) - } - return false, nil, nil - }) - // Sleep before wait is set because sometimes API returns 404 right after creation request - handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(15 * time.Second) - return handler -} - -// PartialUpdateInstanceWaitHandler will wait for instance update -func PartialUpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] { - handler := wait.New(func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) { - s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) - if err != nil { - return false, nil, err - } - if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil { - return false, nil, nil - } - switch *s.Status { - default: - return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status) - case InstanceStateEmpty: - return false, nil, nil - case InstanceStateProgressing: - return false, nil, nil - case InstanceStateSuccess: - return true, s, nil - case InstanceStateFailed: - return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) - } - }) + }, + ) handler.SetTimeout(45 * time.Minute) return handler } // DeleteInstanceWaitHandler will wait for instance deletion -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] { - handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { - s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) - if err != nil { - return false, nil, err - } - if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil { - return false, nil, nil - } - switch *s.Status { - default: - return true, nil, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status) - case InstanceStateSuccess: - return false, nil, nil - case InstanceStateDeleted: - return true, nil, nil - } - }) +func DeleteInstanceWaitHandler( + ctx context.Context, + a APIClientInstanceInterface, + projectId, region, instanceId string, +) *wait.AsyncActionHandler[struct{}] { + handler := wait.New( + func() (waitFinished bool, response *struct{}, err error) { + s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) + if err != nil { + return false, nil, err + } + if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil { + return false, nil, nil + } + switch *s.Status { + default: + return true, nil, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status) + case InstanceStateSuccess: + return false, nil, nil + case InstanceStateDeleted: + return true, nil, nil + } + }, + ) handler.SetTimeout(5 * time.Minute) return handler } // ForceDeleteInstanceWaitHandler will wait for instance deletion -func ForceDeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] { - handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { - _, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) - if err == nil { - return false, nil, nil - } - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if !ok { - return false, nil, err - } - if oapiErr.StatusCode != 404 { - return false, nil, err - } - return true, nil, nil - }) +func ForceDeleteInstanceWaitHandler( + ctx context.Context, + a APIClientInstanceInterface, + projectId, region, instanceId string, +) *wait.AsyncActionHandler[struct{}] { + handler := wait.New( + func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) + if err == nil { + return false, nil, nil + } + oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped + if !ok { + return false, nil, err + } + if oapiErr.StatusCode != 404 { + return false, nil, err + } + return true, nil, nil + }, + ) handler.SetTimeout(15 * time.Minute) return handler } // DeleteUserWaitHandler will wait for delete -func DeleteUserWaitHandler(ctx context.Context, a APIClientUserInterface, projectId, region, instanceId string, userId int64) *wait.AsyncActionHandler[struct{}] { - handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { - _, err = a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId) - if err == nil { - return false, nil, nil - } - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if !ok { - return false, nil, err - } - if oapiErr.StatusCode != 404 { - return false, nil, err - } - return true, nil, nil - }) +func DeleteUserWaitHandler( + ctx context.Context, + a APIClientUserInterface, + projectId, region, instanceId string, userId int64, +) *wait.AsyncActionHandler[struct{}] { + handler := wait.New( + func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId) + if err == nil { + return false, nil, nil + } + oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped + if !ok { + return false, nil, err + } + if oapiErr.StatusCode != 404 { + return false, nil, err + } + return true, nil, nil + }, + ) handler.SetTimeout(1 * time.Minute) return handler } diff --git a/pkg/postgresflexalpha/wait/wait_test.go b/pkg/postgresflexalpha/wait/wait_test.go index a0270001..ce342f4a 100644 --- a/pkg/postgresflexalpha/wait/wait_test.go +++ b/pkg/postgresflexalpha/wait/wait_test.go @@ -2,6 +2,7 @@ package wait import ( "context" + "math" "testing" "time" @@ -157,19 +158,19 @@ func TestCreateInstanceWaitHandler(t *testing.T) { Id: &instanceId, Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(tt.instanceState)), } - } - handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) + handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background()) + gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background()) - if (err != nil) != tt.wantErr { - t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) - } - if !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - }) + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }, + ) } } @@ -232,19 +233,19 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { Id: &instanceId, Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(tt.instanceState)), } - } - handler := PartialUpdateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) + handler := PartialUpdateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) - if (err != nil) != tt.wantErr { - t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) - } - if !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - }) + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }, + ) } } @@ -274,23 +275,25 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - instanceId := "foo-bar" + t.Run( + tt.desc, func(t *testing.T) { + instanceId := "foo-bar" - apiClient := &apiClientInstanceMocked{ - instanceGetFails: tt.instanceGetFails, - instanceId: instanceId, - instanceState: tt.instanceState, - } + apiClient := &apiClientInstanceMocked{ + instanceGetFails: tt.instanceGetFails, + instanceId: instanceId, + instanceState: tt.instanceState, + } - handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) + handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) - _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) - if (err != nil) != tt.wantErr { - t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) - } - }) + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + }, + ) } } @@ -320,24 +323,26 @@ func TestForceDeleteInstanceWaitHandler(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - instanceId := "foo-bar" + t.Run( + tt.desc, func(t *testing.T) { + instanceId := "foo-bar" - apiClient := &apiClientInstanceMocked{ - instanceGetFails: tt.instanceGetFails, - instanceIsForceDeleted: tt.instanceState == InstanceStateDeleted, - instanceId: instanceId, - instanceState: tt.instanceState, - } + apiClient := &apiClientInstanceMocked{ + instanceGetFails: tt.instanceGetFails, + instanceIsForceDeleted: tt.instanceState == InstanceStateDeleted, + instanceId: instanceId, + instanceState: tt.instanceState, + } - handler := ForceDeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) + handler := ForceDeleteInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId) - _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) - if (err != nil) != tt.wantErr { - t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) - } - }) + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + }, + ) } } @@ -377,13 +382,14 @@ func TestDeleteUserWaitHandler(t *testing.T) { isUserDeleted: !tt.deleteFails, } - handler := DeleteUserWaitHandler(context.Background(), apiClient, "", "", "", userId) + handler := DeleteUserWaitHandler(context.Background(), apiClient, "", "", "", userId) - _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) - if (err != nil) != tt.wantErr { - t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) - } - }) + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + }, + ) } } diff --git a/stackit/internal/services/postgresflexalpha/user/datasource.go b/stackit/internal/services/postgresflexalpha/user/datasource.go index 79861e19..5a22fe68 100644 --- a/stackit/internal/services/postgresflexalpha/user/datasource.go +++ b/stackit/internal/services/postgresflexalpha/user/datasource.go @@ -1,14 +1,14 @@ -// Copyright (c) STACKIT - -package postgresflexa +package postgresflexalpha import ( "context" "fmt" "net/http" + "strconv" + "github.com/stackitcloud/terraform-provider-stackit/pkg/postgresflexalpha" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" - postgresflexUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/postgresflex/utils" + postgresflexalphaUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/postgresflexalpha/utils" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource" @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" ) // Ensure the implementation satisfies the expected interfaces. @@ -29,15 +28,17 @@ var ( ) type DataSourceModel struct { - Id types.String `tfsdk:"id"` // needed by TF - UserId types.String `tfsdk:"user_id"` - InstanceId types.String `tfsdk:"instance_id"` - ProjectId types.String `tfsdk:"project_id"` - Username types.String `tfsdk:"username"` - Roles types.Set `tfsdk:"roles"` - Host types.String `tfsdk:"host"` - Port types.Int64 `tfsdk:"port"` - Region types.String `tfsdk:"region"` + Id types.String `tfsdk:"id"` // needed by TF + UserId types.Int64 `tfsdk:"user_id"` + InstanceId types.String `tfsdk:"instance_id"` + ProjectId types.String `tfsdk:"project_id"` + Username types.String `tfsdk:"username"` + Roles types.Set `tfsdk:"roles"` + Host types.String `tfsdk:"host"` + Port types.Int64 `tfsdk:"port"` + Region types.String `tfsdk:"region"` + Status types.String `tfsdk:"status"` + ConnectionString types.String `tfsdk:"connection_string"` } // NewUserDataSource is a helper function to simplify the provider implementation. @@ -47,24 +48,32 @@ func NewUserDataSource() datasource.DataSource { // userDataSource is the data source implementation. type userDataSource struct { - client *postgresflex.APIClient + client *postgresflexalpha.APIClient providerData core.ProviderData } // Metadata returns the data source type name. -func (r *userDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgresflex_user" +func (r *userDataSource) Metadata( + _ context.Context, + req datasource.MetadataRequest, + resp *datasource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_user" } // Configure adds the provider configured client to the data source. -func (r *userDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { +func (r *userDataSource) Configure( + ctx context.Context, + req datasource.ConfigureRequest, + resp *datasource.ConfigureResponse, +) { var ok bool r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) if !ok { return } - apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) + apiClient := postgresflexalphaUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } @@ -75,12 +84,14 @@ func (r *userDataSource) Configure(ctx context.Context, req datasource.Configure // Schema defines the schema for the data source. func (r *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { descriptions := map[string]string{ - "main": "Postgres Flex user data source schema. Must have a `region` specified in the provider configuration.", - "id": "Terraform's internal data source. ID. It is structured as \"`project_id`,`region`,`instance_id`,`user_id`\".", - "user_id": "User ID.", - "instance_id": "ID of the PostgresFlex instance.", - "project_id": "STACKIT project ID to which the instance is associated.", - "region": "The resource region. If not defined, the provider region is used.", + "main": "Postgres Flex user data source schema. Must have a `region` specified in the provider configuration.", + "id": "Terraform's internal data source. ID. It is structured as \"`project_id`,`region`,`instance_id`,`user_id`\".", + "user_id": "User ID.", + "instance_id": "ID of the PostgresFlex instance.", + "project_id": "STACKIT project ID to which the instance is associated.", + "region": "The resource region. If not defined, the provider region is used.", + "status": "The current status of the user.", + "connection_string": "The connection string for the user to the instance.", } resp.Schema = schema.Schema{ @@ -131,12 +142,22 @@ func (r *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r Optional: true, Description: descriptions["region"], }, + "status": schema.StringAttribute{ + Computed: true, + }, + "connection_string": schema.StringAttribute{ + Computed: true, + }, }, } } // Read refreshes the Terraform state with the latest data. -func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform +func (r *userDataSource) Read( + ctx context.Context, + req datasource.ReadRequest, + resp *datasource.ReadResponse, +) { // nolint:gocritic // function signature required by Terraform var model DataSourceModel diags := req.Config.Get(ctx, &model) resp.Diagnostics.Append(diags...) @@ -148,21 +169,26 @@ func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() - userId := model.UserId.ValueString() + userId := model.UserId.ValueInt64() region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "user_id", userId) ctx = tflog.SetField(ctx, "region", region) - recordSetResp, err := r.client.GetUser(ctx, projectId, region, instanceId, userId).Execute() + recordSetResp, err := r.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute() if err != nil { utils.LogError( ctx, &resp.Diagnostics, err, "Reading user", - fmt.Sprintf("User with ID %q or instance with ID %q does not exist in project %q.", userId, instanceId, projectId), + fmt.Sprintf( + "User with ID %q or instance with ID %q does not exist in project %q.", + userId, + instanceId, + projectId, + ), map[int]string{ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId), }, @@ -176,7 +202,12 @@ func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r // Map response body to schema and populate Computed attribute values err = mapDataSourceFields(recordSetResp, &model, region) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Processing API payload: %v", err)) + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "Error reading user", + fmt.Sprintf("Processing API payload: %v", err), + ) return } @@ -189,35 +220,36 @@ func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r tflog.Info(ctx, "Postgres Flex user read") } -func mapDataSourceFields(userResp *postgresflex.GetUserResponse, model *DataSourceModel, region string) error { - if userResp == nil || userResp.Item == nil { +func mapDataSourceFields(userResp *postgresflexalpha.GetUserResponse, model *DataSourceModel, region string) error { + if userResp == nil { return fmt.Errorf("response is nil") } if model == nil { return fmt.Errorf("model input is nil") } - user := userResp.Item + user := userResp - var userId string - if model.UserId.ValueString() != "" { - userId = model.UserId.ValueString() + var userId int64 + if model.UserId.ValueInt64() != 0 { + userId = model.UserId.ValueInt64() } else if user.Id != nil { userId = *user.Id } else { return fmt.Errorf("user id not present") } + model.Id = utils.BuildInternalTerraformId( - model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), userId, + model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(userId, 10), ) - model.UserId = types.StringValue(userId) - model.Username = types.StringPointerValue(user.Username) + model.UserId = types.Int64Value(userId) + model.Username = types.StringPointerValue(user.Name) if user.Roles == nil { model.Roles = types.SetNull(types.StringType) } else { - roles := []attr.Value{} + var roles []attr.Value for _, role := range *user.Roles { - roles = append(roles, types.StringValue(role)) + roles = append(roles, types.StringValue(string(role))) } rolesSet, diags := types.SetValue(types.StringType, roles) if diags.HasError() { @@ -228,5 +260,7 @@ func mapDataSourceFields(userResp *postgresflex.GetUserResponse, model *DataSour model.Host = types.StringPointerValue(user.Host) model.Port = types.Int64PointerValue(user.Port) model.Region = types.StringValue(region) + model.Status = types.StringPointerValue(user.Status) + model.ConnectionString = types.StringPointerValue(user.ConnectionString) return nil } diff --git a/stackit/internal/services/postgresflexalpha/user/datasource_test.go b/stackit/internal/services/postgresflexalpha/user/datasource_test.go index d49ef243..ecc2136b 100644 --- a/stackit/internal/services/postgresflexalpha/user/datasource_test.go +++ b/stackit/internal/services/postgresflexalpha/user/datasource_test.go @@ -1,6 +1,4 @@ -// Copyright (c) STACKIT - -package postgresflexa +package postgresflexalpha import ( "testing" @@ -9,27 +7,25 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" + "github.com/stackitcloud/terraform-provider-stackit/pkg/postgresflexalpha" ) func TestMapDataSourceFields(t *testing.T) { const testRegion = "region" tests := []struct { description string - input *postgresflex.GetUserResponse + input *postgresflexalpha.GetUserResponse region string expected DataSourceModel isValid bool }{ { "default_values", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{}, - }, + &postgresflexalpha.GetUserResponse{}, testRegion, DataSourceModel{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), InstanceId: types.StringValue("iid"), ProjectId: types.StringValue("pid"), Username: types.StringNull(), @@ -42,58 +38,62 @@ func TestMapDataSourceFields(t *testing.T) { }, { "simple_values", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{ - Roles: &[]string{ - "role_1", - "role_2", - "", - }, - Username: utils.Ptr("username"), - Host: utils.Ptr("host"), - Port: utils.Ptr(int64(1234)), + &postgresflexalpha.GetUserResponse{ + Roles: &[]postgresflexalpha.UserRole{ + "role_1", + "role_2", + "", }, + Name: utils.Ptr("username"), + Host: utils.Ptr("host"), + Port: utils.Ptr(int64(1234)), }, testRegion, DataSourceModel{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), InstanceId: types.StringValue("iid"), ProjectId: types.StringValue("pid"), Username: types.StringValue("username"), - Roles: types.SetValueMust(types.StringType, []attr.Value{ - types.StringValue("role_1"), - types.StringValue("role_2"), - types.StringValue(""), - }), - Host: types.StringValue("host"), - Port: types.Int64Value(1234), - Region: types.StringValue(testRegion), + Roles: types.SetValueMust( + types.StringType, []attr.Value{ + types.StringValue("role_1"), + types.StringValue("role_2"), + types.StringValue(""), + }, + ), + Host: types.StringValue("host"), + Port: types.Int64Value(1234), + Region: types.StringValue(testRegion), + Status: types.StringNull(), + ConnectionString: types.StringNull(), }, true, }, { "null_fields_and_int_conversions", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{ - Id: utils.Ptr("uid"), - Roles: &[]string{}, - Username: nil, - Host: nil, - Port: utils.Ptr(int64(2123456789)), - }, + &postgresflexalpha.GetUserResponse{ + Id: utils.Ptr(int64(1)), + Roles: &[]postgresflexalpha.UserRole{}, + Name: nil, + Host: nil, + Port: utils.Ptr(int64(2123456789)), + Status: utils.Ptr("status"), + ConnectionString: utils.Ptr("connection_string"), }, testRegion, DataSourceModel{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), - InstanceId: types.StringValue("iid"), - ProjectId: types.StringValue("pid"), - Username: types.StringNull(), - Roles: types.SetValueMust(types.StringType, []attr.Value{}), - Host: types.StringNull(), - Port: types.Int64Value(2123456789), - Region: types.StringValue(testRegion), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), + InstanceId: types.StringValue("iid"), + ProjectId: types.StringValue("pid"), + Username: types.StringNull(), + Roles: types.SetValueMust(types.StringType, []attr.Value{}), + Host: types.StringNull(), + Port: types.Int64Value(2123456789), + Region: types.StringValue(testRegion), + Status: types.StringValue("status"), + ConnectionString: types.StringValue("connection_string"), }, true, }, @@ -106,41 +106,41 @@ func TestMapDataSourceFields(t *testing.T) { }, { "nil_response_2", - &postgresflex.GetUserResponse{}, + &postgresflexalpha.GetUserResponse{}, testRegion, DataSourceModel{}, false, }, { "no_resource_id", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{}, - }, + &postgresflexalpha.GetUserResponse{}, testRegion, DataSourceModel{}, false, }, } for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - state := &DataSourceModel{ - ProjectId: tt.expected.ProjectId, - InstanceId: tt.expected.InstanceId, - UserId: tt.expected.UserId, - } - err := mapDataSourceFields(tt.input, state, tt.region) - if !tt.isValid && err == nil { - t.Fatalf("Should have failed") - } - if tt.isValid && err != nil { - t.Fatalf("Should not have failed: %v", err) - } - if tt.isValid { - diff := cmp.Diff(state, &tt.expected) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) + t.Run( + tt.description, func(t *testing.T) { + state := &DataSourceModel{ + ProjectId: tt.expected.ProjectId, + InstanceId: tt.expected.InstanceId, + UserId: tt.expected.UserId, } - } - }) + err := mapDataSourceFields(tt.input, state, tt.region) + if !tt.isValid && err == nil { + t.Fatalf("Should have failed") + } + if tt.isValid && err != nil { + t.Fatalf("Should not have failed: %v", err) + } + if tt.isValid { + diff := cmp.Diff(state, &tt.expected) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + } + }, + ) } } diff --git a/stackit/internal/services/postgresflexalpha/user/resource.go b/stackit/internal/services/postgresflexalpha/user/resource.go index fbc3035c..e7eb944d 100644 --- a/stackit/internal/services/postgresflexalpha/user/resource.go +++ b/stackit/internal/services/postgresflexalpha/user/resource.go @@ -1,14 +1,15 @@ -// Copyright (c) STACKIT - -package postgresflexa +package postgresflexalpha import ( "context" "fmt" "net/http" + "strconv" "strings" - postgresflexUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/postgresflex/utils" + "github.com/stackitcloud/terraform-provider-stackit/pkg/postgresflexalpha" + "github.com/stackitcloud/terraform-provider-stackit/pkg/postgresflexalpha/wait" + postgresflexalphaUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/postgresflexalpha/utils" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -27,8 +28,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" - "github.com/stackitcloud/stackit-sdk-go/services/postgresflex/wait" ) // Ensure the implementation satisfies the expected interfaces. @@ -40,17 +39,19 @@ var ( ) type Model struct { - Id types.String `tfsdk:"id"` // needed by TF - UserId types.String `tfsdk:"user_id"` - InstanceId types.String `tfsdk:"instance_id"` - ProjectId types.String `tfsdk:"project_id"` - Username types.String `tfsdk:"username"` - Roles types.Set `tfsdk:"roles"` - Password types.String `tfsdk:"password"` - Host types.String `tfsdk:"host"` - Port types.Int64 `tfsdk:"port"` - Uri types.String `tfsdk:"uri"` - Region types.String `tfsdk:"region"` + Id types.String `tfsdk:"id"` // needed by TF + UserId types.Int64 `tfsdk:"user_id"` + InstanceId types.String `tfsdk:"instance_id"` + ProjectId types.String `tfsdk:"project_id"` + Username types.String `tfsdk:"username"` + Roles types.Set `tfsdk:"roles"` + Password types.String `tfsdk:"password"` + Host types.String `tfsdk:"host"` + Port types.Int64 `tfsdk:"port"` + Uri types.String `tfsdk:"uri"` + Region types.String `tfsdk:"region"` + Status types.String `tfsdk:"status"` + ConnectionString types.String `tfsdk:"connection_string"` } // NewUserResource is a helper function to simplify the provider implementation. @@ -60,13 +61,17 @@ func NewUserResource() resource.Resource { // userResource is the resource implementation. type userResource struct { - client *postgresflex.APIClient + client *postgresflexalpha.APIClient providerData core.ProviderData } // ModifyPlan implements resource.ResourceWithModifyPlan. // Use the modifier to set the effective region in the current plan. -func (r *userResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform +func (r *userResource) ModifyPlan( + ctx context.Context, + req resource.ModifyPlanRequest, + resp *resource.ModifyPlanResponse, +) { // nolint:gocritic // function signature required by Terraform var configModel Model // skip initial empty configuration to avoid follow-up errors if req.Config.Raw.IsNull() { @@ -96,7 +101,7 @@ func (r *userResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRe // Metadata returns the resource type name. func (r *userResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgresflex_user" + resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_user" } // Configure adds the provider configured client to the resource. @@ -107,7 +112,7 @@ func (r *userResource) Configure(ctx context.Context, req resource.ConfigureRequ return } - apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) + apiClient := postgresflexalphaUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) if resp.Diagnostics.HasError() { return } @@ -120,13 +125,15 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp rolesOptions := []string{"login", "createdb"} descriptions := map[string]string{ - "main": "Postgres Flex user resource schema. Must have a `region` specified in the provider configuration.", - "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`,`user_id`\".", - "user_id": "User ID.", - "instance_id": "ID of the PostgresFlex instance.", - "project_id": "STACKIT project ID to which the instance is associated.", - "roles": "Database access levels for the user. " + utils.FormatPossibleValues(rolesOptions...), - "region": "The resource region. If not defined, the provider region is used.", + "main": "Postgres Flex user resource schema. Must have a `region` specified in the provider configuration.", + "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`,`user_id`\".", + "user_id": "User ID.", + "instance_id": "ID of the PostgresFlex instance.", + "project_id": "STACKIT project ID to which the instance is associated.", + "roles": "Database access levels for the user. " + utils.FormatPossibleValues(rolesOptions...), + "region": "The resource region. If not defined, the provider region is used.", + "status": "The current status of the user.", + "connection_string": "The connection string for the user to the instance.", } resp.Schema = schema.Schema{ @@ -212,12 +219,22 @@ func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp stringplanmodifier.RequiresReplace(), }, }, + "status": schema.StringAttribute{ + Computed: true, + }, + "connection_string": schema.StringAttribute{ + Computed: true, + }, }, } } // Create creates the resource and sets the initial Terraform state. -func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform +func (r *userResource) Create( + ctx context.Context, + req resource.CreateRequest, + resp *resource.CreateResponse, +) { // nolint:gocritic // function signature required by Terraform var model Model diags := req.Plan.Get(ctx, &model) resp.Diagnostics.Append(diags...) @@ -244,13 +261,18 @@ func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, r } // Generate API request body from model - payload, err := toCreatePayload(&model, roles) + payload, err := toCreatePayload(&model, &roles) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Creating API payload: %v", err)) return } // Create new user - userResp, err := r.client.CreateUser(ctx, projectId, region, instanceId).CreateUserPayload(*payload).Execute() + userResp, err := r.client.CreateUserRequest( + ctx, + projectId, + region, + instanceId, + ).CreateUserRequestPayload(*payload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err)) return @@ -258,17 +280,26 @@ func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, r ctx = core.LogResponse(ctx) - if userResp == nil || userResp.Item == nil || userResp.Item.Id == nil || *userResp.Item.Id == "" { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", "API didn't return user Id. A user might have been created") + if userResp.Id == nil || *userResp.Id == 0 { + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "Error creating user", + "API didn't return user Id. A user might have been created", + ) return } - userId := *userResp.Item.Id + userId := *userResp.Id ctx = tflog.SetField(ctx, "user_id", userId) - // Map response body to schema - err = mapFieldsCreate(userResp, &model, region) + err = mapFieldsCreate(userResp, toPayloadRoles(&roles), &model, region) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Processing API payload: %v", err)) + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "Error creating user", + fmt.Sprintf("Processing API payload: %v", err), + ) return } // Set state to fully populated data @@ -281,7 +312,11 @@ func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, r } // Read refreshes the Terraform state with the latest data. -func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform +func (r *userResource) Read( + ctx context.Context, + req resource.ReadRequest, + resp *resource.ReadResponse, +) { // nolint:gocritic // function signature required by Terraform var model Model diags := req.State.Get(ctx, &model) resp.Diagnostics.Append(diags...) @@ -293,14 +328,14 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() - userId := model.UserId.ValueString() + userId := model.UserId.ValueInt64() region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "user_id", userId) ctx = tflog.SetField(ctx, "region", region) - recordSetResp, err := r.client.GetUser(ctx, projectId, region, instanceId, userId).Execute() + recordSetResp, err := r.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute() if err != nil { oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped if ok && oapiErr.StatusCode == http.StatusNotFound { @@ -316,7 +351,12 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp // Map response body to schema err = mapFields(recordSetResp, &model, region) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Processing API payload: %v", err)) + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "Error reading user", + fmt.Sprintf("Processing API payload: %v", err), + ) return } @@ -330,7 +370,11 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp } // Update updates the resource and sets the updated Terraform state on success. -func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform +func (r *userResource) Update( + ctx context.Context, + req resource.UpdateRequest, + resp *resource.UpdateResponse, +) { // nolint:gocritic // function signature required by Terraform // Retrieve values from plan var model Model diags := req.Plan.Get(ctx, &model) @@ -343,7 +387,7 @@ func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() - userId := model.UserId.ValueString() + userId := model.UserId.ValueInt64() region := model.Region.ValueString() ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -368,14 +412,20 @@ func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, r } // Generate API request body from model - payload, err := toUpdatePayload(&model, roles) + payload, err := toUpdatePayload(&model, &roles) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Updating API payload: %v", err)) return } // Update existing instance - err = r.client.UpdateUser(ctx, projectId, region, instanceId, userId).UpdateUserPayload(*payload).Execute() + err = r.client.UpdateUserRequest( + ctx, + projectId, + region, + instanceId, + userId, + ).UpdateUserRequestPayload(*payload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", err.Error()) return @@ -383,7 +433,7 @@ func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, r ctx = core.LogResponse(ctx) - userResp, err := r.client.GetUser(ctx, projectId, region, instanceId, userId).Execute() + userResp, err := r.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Calling API: %v", err)) return @@ -392,7 +442,12 @@ func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, r // Map response body to schema err = mapFields(userResp, &stateModel, region) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Processing API payload: %v", err)) + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "Error updating user", + fmt.Sprintf("Processing API payload: %v", err), + ) return } @@ -406,7 +461,11 @@ func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, r } // Delete deletes the resource and removes the Terraform state on success. -func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform +func (r *userResource) Delete( + ctx context.Context, + req resource.DeleteRequest, + resp *resource.DeleteResponse, +) { // nolint:gocritic // function signature required by Terraform // Retrieve values from plan var model Model diags := req.State.Get(ctx, &model) @@ -419,7 +478,7 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() - userId := model.UserId.ValueString() + userId := model.UserId.ValueInt64() region := model.Region.ValueString() ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -427,7 +486,7 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r ctx = tflog.SetField(ctx, "region", region) // Delete existing record set - err := r.client.DeleteUser(ctx, projectId, region, instanceId, userId).Execute() + err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err)) } @@ -436,7 +495,12 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r _, err = wait.DeleteUserWaitHandler(ctx, r.client, projectId, region, instanceId, userId).WaitWithContext(ctx) if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Instance deletion waiting: %v", err)) + core.LogAndAddError( + ctx, + &resp.Diagnostics, + "Error deleting user", + fmt.Sprintf("Instance deletion waiting: %v", err), + ) return } tflog.Info(ctx, "Postgres Flex user deleted") @@ -444,12 +508,20 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r // ImportState imports a resource into the Terraform state on success. // The expected format of the resource import identifier is: project_id,zone_id,record_set_id -func (r *userResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { +func (r *userResource) ImportState( + ctx context.Context, + req resource.ImportStateRequest, + resp *resource.ImportStateResponse, +) { idParts := strings.Split(req.ID, core.Separator) if len(idParts) != 4 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" || idParts[3] == "" { - core.LogAndAddError(ctx, &resp.Diagnostics, + core.LogAndAddError( + ctx, &resp.Diagnostics, "Error importing user", - fmt.Sprintf("Expected import identifier with format [project_id],[region],[instance_id],[user_id], got %q", req.ID), + fmt.Sprintf( + "Expected import identifier with format [project_id],[region],[instance_id],[user_id], got %q", + req.ID, + ), ) return } @@ -458,43 +530,49 @@ func (r *userResource) ImportState(ctx context.Context, req resource.ImportState 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("user_id"), idParts[3])...) - core.LogAndAddWarning(ctx, &resp.Diagnostics, - "Postgresflex user imported with empty password and empty uri", + core.LogAndAddWarning( + ctx, + &resp.Diagnostics, + "postgresflexalpha user imported with empty password and empty uri", "The user password and uri are not imported as they are only available upon creation of a new user. The password and uri fields will be empty.", ) - tflog.Info(ctx, "Postgresflex user state imported") + tflog.Info(ctx, "postgresflexalpha user state imported") } -func mapFieldsCreate(userResp *postgresflex.CreateUserResponse, model *Model, region string) error { - if userResp == nil || userResp.Item == nil { +func mapFieldsCreate( + userResp *postgresflexalpha.CreateUserResponse, + rolesArg *[]postgresflexalpha.UserRole, + model *Model, + region string, +) error { + if userResp == nil { return fmt.Errorf("response is nil") } if model == nil { return fmt.Errorf("model input is nil") } - user := userResp.Item + user := userResp if user.Id == nil { return fmt.Errorf("user id not present") } userId := *user.Id model.Id = utils.BuildInternalTerraformId( - model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), userId, + model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(userId, 10), ) - model.UserId = types.StringValue(userId) - model.Username = types.StringPointerValue(user.Username) + model.UserId = types.Int64Value(userId) + model.Username = types.StringPointerValue(user.Name) if user.Password == nil { return fmt.Errorf("user password not present") } - model.Password = types.StringValue(*user.Password) - if user.Roles == nil { + if rolesArg == nil { model.Roles = types.SetNull(types.StringType) } else { - roles := []attr.Value{} - for _, role := range *user.Roles { - roles = append(roles, types.StringValue(role)) + var roles []attr.Value + for _, role := range *rolesArg { + roles = append(roles, types.StringValue(string(role))) } rolesSet, diags := types.SetValue(types.StringType, roles) if diags.HasError() { @@ -502,42 +580,44 @@ func mapFieldsCreate(userResp *postgresflex.CreateUserResponse, model *Model, re } model.Roles = rolesSet } - model.Host = types.StringPointerValue(user.Host) - model.Port = types.Int64PointerValue(user.Port) - model.Uri = types.StringPointerValue(user.Uri) + + model.Password = types.StringValue(*user.Password) model.Region = types.StringValue(region) + model.Status = types.StringPointerValue(user.Status) + model.ConnectionString = types.StringPointerValue(user.ConnectionString) + return nil } -func mapFields(userResp *postgresflex.GetUserResponse, model *Model, region string) error { - if userResp == nil || userResp.Item == nil { +func mapFields(userResp *postgresflexalpha.GetUserResponse, model *Model, region string) error { + if userResp == nil { return fmt.Errorf("response is nil") } if model == nil { return fmt.Errorf("model input is nil") } - user := userResp.Item + user := userResp - var userId string - if model.UserId.ValueString() != "" { - userId = model.UserId.ValueString() + var userId int64 + if model.UserId.ValueInt64() != 0 { + userId = model.UserId.ValueInt64() } else if user.Id != nil { userId = *user.Id } else { return fmt.Errorf("user id not present") } model.Id = utils.BuildInternalTerraformId( - model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), userId, + model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(userId, 10), ) - model.UserId = types.StringValue(userId) - model.Username = types.StringPointerValue(user.Username) + model.UserId = types.Int64Value(userId) + model.Username = types.StringPointerValue(user.Name) if user.Roles == nil { model.Roles = types.SetNull(types.StringType) } else { - roles := []attr.Value{} + var roles []attr.Value for _, role := range *user.Roles { - roles = append(roles, types.StringValue(role)) + roles = append(roles, types.StringValue(string(role))) } rolesSet, diags := types.SetValue(types.StringType, roles) if diags.HasError() { @@ -548,10 +628,12 @@ func mapFields(userResp *postgresflex.GetUserResponse, model *Model, region stri model.Host = types.StringPointerValue(user.Host) model.Port = types.Int64PointerValue(user.Port) model.Region = types.StringValue(region) + model.Status = types.StringPointerValue(user.Status) + model.ConnectionString = types.StringPointerValue(user.ConnectionString) return nil } -func toCreatePayload(model *Model, roles []string) (*postgresflex.CreateUserPayload, error) { +func toCreatePayload(model *Model, roles *[]string) (*postgresflexalpha.CreateUserRequestPayload, error) { if model == nil { return nil, fmt.Errorf("nil model") } @@ -559,13 +641,24 @@ func toCreatePayload(model *Model, roles []string) (*postgresflex.CreateUserPayl return nil, fmt.Errorf("nil roles") } - return &postgresflex.CreateUserPayload{ - Roles: &roles, - Username: conversion.StringValueToPointer(model.Username), + return &postgresflexalpha.CreateUserRequestPayload{ + Roles: toPayloadRoles(roles), + Name: conversion.StringValueToPointer(model.Username), }, nil } -func toUpdatePayload(model *Model, roles []string) (*postgresflex.UpdateUserPayload, error) { +func toPayloadRoles(roles *[]string) *[]postgresflexalpha.UserRole { + var userRoles = make([]postgresflexalpha.UserRole, 0, len(*roles)) + for _, role := range *roles { + userRoles = append(userRoles, postgresflexalpha.UserRole(role)) + } + return &userRoles +} + +func toUpdatePayload(model *Model, roles *[]string) ( + *postgresflexalpha.UpdateUserRequestPayload, + error, +) { if model == nil { return nil, fmt.Errorf("nil model") } @@ -573,7 +666,7 @@ func toUpdatePayload(model *Model, roles []string) (*postgresflex.UpdateUserPayl return nil, fmt.Errorf("nil roles") } - return &postgresflex.UpdateUserPayload{ - Roles: &roles, + return &postgresflexalpha.UpdateUserRequestPayload{ + Roles: toPayloadRoles(roles), }, nil } diff --git a/stackit/internal/services/postgresflexalpha/user/resource_test.go b/stackit/internal/services/postgresflexalpha/user/resource_test.go index 6dbe2e18..5f6732ef 100644 --- a/stackit/internal/services/postgresflexalpha/user/resource_test.go +++ b/stackit/internal/services/postgresflexalpha/user/resource_test.go @@ -1,6 +1,4 @@ -// Copyright (c) STACKIT - -package postgresflexa +package postgresflexalpha import ( "testing" @@ -9,137 +7,138 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/postgresflex" + "github.com/stackitcloud/terraform-provider-stackit/pkg/postgresflexalpha" ) func TestMapFieldsCreate(t *testing.T) { const testRegion = "region" tests := []struct { description string - input *postgresflex.CreateUserResponse + input *postgresflexalpha.CreateUserResponse + updateRoles *postgresflexalpha.UpdateUserRequestPayload region string expected Model isValid bool }{ { "default_values", - &postgresflex.CreateUserResponse{ - Item: &postgresflex.User{ - Id: utils.Ptr("uid"), - Password: utils.Ptr(""), - }, + &postgresflexalpha.CreateUserResponse{ + Id: utils.Ptr(int64(1)), + Password: utils.Ptr(""), }, + &postgresflexalpha.UpdateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{}, + }, + testRegion, Model{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), - InstanceId: types.StringValue("iid"), - ProjectId: types.StringValue("pid"), - Username: types.StringNull(), - Roles: types.SetNull(types.StringType), - Password: types.StringValue(""), - Host: types.StringNull(), - Port: types.Int64Null(), - Uri: types.StringNull(), - Region: types.StringValue(testRegion), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), + InstanceId: types.StringValue("iid"), + ProjectId: types.StringValue("pid"), + Username: types.StringNull(), + Roles: types.SetValueMust(types.StringType, []attr.Value{}), + Password: types.StringValue(""), + Host: types.StringNull(), + Port: types.Int64Null(), + Uri: types.StringNull(), + Region: types.StringValue(testRegion), + Status: types.StringNull(), + ConnectionString: types.StringNull(), }, true, }, { "simple_values", - &postgresflex.CreateUserResponse{ - Item: &postgresflex.User{ - Id: utils.Ptr("uid"), - Roles: &[]string{ - "role_1", - "role_2", - "", - }, - Username: utils.Ptr("username"), - Password: utils.Ptr("password"), - Host: utils.Ptr("host"), - Port: utils.Ptr(int64(1234)), - Uri: utils.Ptr("uri"), - }, + &postgresflexalpha.CreateUserResponse{ + Id: utils.Ptr(int64(1)), + Name: utils.Ptr("username"), + Password: utils.Ptr("password"), + ConnectionString: utils.Ptr("connection_string"), + Status: utils.Ptr("status"), + }, + &postgresflexalpha.UpdateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{}, }, testRegion, Model{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), - InstanceId: types.StringValue("iid"), - ProjectId: types.StringValue("pid"), - Username: types.StringValue("username"), - Roles: types.SetValueMust(types.StringType, []attr.Value{ - types.StringValue("role_1"), - types.StringValue("role_2"), - types.StringValue(""), - }), - Password: types.StringValue("password"), - Host: types.StringValue("host"), - Port: types.Int64Value(1234), - Uri: types.StringValue("uri"), - Region: types.StringValue(testRegion), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), + InstanceId: types.StringValue("iid"), + ProjectId: types.StringValue("pid"), + Username: types.StringValue("username"), + Roles: types.SetValueMust(types.StringType, []attr.Value{}), + Password: types.StringValue("password"), + Host: types.StringNull(), + Port: types.Int64Null(), + Uri: types.StringNull(), + Region: types.StringValue(testRegion), + Status: types.StringValue("status"), + ConnectionString: types.StringValue("connection_string"), }, true, }, { "null_fields_and_int_conversions", - &postgresflex.CreateUserResponse{ - Item: &postgresflex.User{ - Id: utils.Ptr("uid"), - Roles: &[]string{}, - Username: nil, - Password: utils.Ptr(""), - Host: nil, - Port: utils.Ptr(int64(2123456789)), - Uri: nil, - }, + &postgresflexalpha.CreateUserResponse{ + Id: utils.Ptr(int64(1)), + Name: nil, + Password: utils.Ptr(""), + ConnectionString: nil, + Status: nil, + }, + &postgresflexalpha.UpdateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{}, }, testRegion, Model{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), - InstanceId: types.StringValue("iid"), - ProjectId: types.StringValue("pid"), - Username: types.StringNull(), - Roles: types.SetValueMust(types.StringType, []attr.Value{}), - Password: types.StringValue(""), - Host: types.StringNull(), - Port: types.Int64Value(2123456789), - Uri: types.StringNull(), - Region: types.StringValue(testRegion), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), + InstanceId: types.StringValue("iid"), + ProjectId: types.StringValue("pid"), + Username: types.StringNull(), + Roles: types.SetValueMust(types.StringType, []attr.Value{}), + Password: types.StringValue(""), + Host: types.StringNull(), + Port: types.Int64Null(), + Uri: types.StringNull(), + Region: types.StringValue(testRegion), + Status: types.StringNull(), + ConnectionString: types.StringNull(), }, true, }, { "nil_response", nil, + nil, testRegion, Model{}, false, }, { "nil_response_2", - &postgresflex.CreateUserResponse{}, + &postgresflexalpha.CreateUserResponse{}, + &postgresflexalpha.UpdateUserRequestPayload{}, testRegion, Model{}, false, }, { "no_resource_id", - &postgresflex.CreateUserResponse{ - Item: &postgresflex.User{}, - }, + &postgresflexalpha.CreateUserResponse{}, + &postgresflexalpha.UpdateUserRequestPayload{}, testRegion, Model{}, false, }, { "no_password", - &postgresflex.CreateUserResponse{ - Item: &postgresflex.User{ - Id: utils.Ptr("uid"), - }, + &postgresflexalpha.CreateUserResponse{ + Id: utils.Ptr(int64(1)), + }, + &postgresflexalpha.UpdateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{}, }, testRegion, Model{}, @@ -147,25 +146,31 @@ func TestMapFieldsCreate(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - state := &Model{ - ProjectId: tt.expected.ProjectId, - InstanceId: tt.expected.InstanceId, - } - err := mapFieldsCreate(tt.input, state, tt.region) - if !tt.isValid && err == nil { - t.Fatalf("Should have failed") - } - if tt.isValid && err != nil { - t.Fatalf("Should not have failed: %v", err) - } - if tt.isValid { - diff := cmp.Diff(state, &tt.expected) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) + t.Run( + tt.description, func(t *testing.T) { + state := &Model{ + ProjectId: tt.expected.ProjectId, + InstanceId: tt.expected.InstanceId, } - } - }) + var roles *[]postgresflexalpha.UserRole + if tt.updateRoles != nil { + roles = tt.updateRoles.Roles + } + err := mapFieldsCreate(tt.input, roles, state, tt.region) + if !tt.isValid && err == nil { + t.Fatalf("Should have failed") + } + if tt.isValid && err != nil { + t.Fatalf("Should not have failed: %v", err) + } + if tt.isValid { + diff := cmp.Diff(state, &tt.expected) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + } + }, + ) } } @@ -173,84 +178,85 @@ func TestMapFields(t *testing.T) { const testRegion = "region" tests := []struct { description string - input *postgresflex.GetUserResponse + input *postgresflexalpha.GetUserResponse region string expected Model isValid bool }{ { "default_values", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{}, - }, + &postgresflexalpha.GetUserResponse{}, testRegion, Model{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), - InstanceId: types.StringValue("iid"), - ProjectId: types.StringValue("pid"), - Username: types.StringNull(), - Roles: types.SetNull(types.StringType), - Host: types.StringNull(), - Port: types.Int64Null(), - Region: types.StringValue(testRegion), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(int64(1)), + InstanceId: types.StringValue("iid"), + ProjectId: types.StringValue("pid"), + Username: types.StringNull(), + Roles: types.SetNull(types.StringType), + Host: types.StringNull(), + Port: types.Int64Null(), + Region: types.StringValue(testRegion), + Status: types.StringNull(), + ConnectionString: types.StringNull(), }, true, }, { "simple_values", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{ - Roles: &[]string{ - "role_1", - "role_2", - "", - }, - Username: utils.Ptr("username"), - Host: utils.Ptr("host"), - Port: utils.Ptr(int64(1234)), + &postgresflexalpha.GetUserResponse{ + Roles: &[]postgresflexalpha.UserRole{ + "role_1", + "role_2", + "", }, + Name: utils.Ptr("username"), + Host: utils.Ptr("host"), + Port: utils.Ptr(int64(1234)), }, testRegion, Model{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), InstanceId: types.StringValue("iid"), ProjectId: types.StringValue("pid"), Username: types.StringValue("username"), - Roles: types.SetValueMust(types.StringType, []attr.Value{ - types.StringValue("role_1"), - types.StringValue("role_2"), - types.StringValue(""), - }), - Host: types.StringValue("host"), - Port: types.Int64Value(1234), - Region: types.StringValue(testRegion), + Roles: types.SetValueMust( + types.StringType, []attr.Value{ + types.StringValue("role_1"), + types.StringValue("role_2"), + types.StringValue(""), + }, + ), + Host: types.StringValue("host"), + Port: types.Int64Value(1234), + Region: types.StringValue(testRegion), + Status: types.StringNull(), + ConnectionString: types.StringNull(), }, true, }, { "null_fields_and_int_conversions", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{ - Id: utils.Ptr("uid"), - Roles: &[]string{}, - Username: nil, - Host: nil, - Port: utils.Ptr(int64(2123456789)), - }, + &postgresflexalpha.GetUserResponse{ + Id: utils.Ptr(int64(1)), + Name: nil, + Host: nil, + Port: utils.Ptr(int64(2123456789)), }, testRegion, Model{ - Id: types.StringValue("pid,region,iid,uid"), - UserId: types.StringValue("uid"), - InstanceId: types.StringValue("iid"), - ProjectId: types.StringValue("pid"), - Username: types.StringNull(), - Roles: types.SetValueMust(types.StringType, []attr.Value{}), - Host: types.StringNull(), - Port: types.Int64Value(2123456789), - Region: types.StringValue(testRegion), + Id: types.StringValue("pid,region,iid,1"), + UserId: types.Int64Value(1), + InstanceId: types.StringValue("iid"), + ProjectId: types.StringValue("pid"), + Username: types.StringNull(), + Roles: types.SetValueMust(types.StringType, []attr.Value{}), + Host: types.StringNull(), + Port: types.Int64Value(2123456789), + Region: types.StringValue(testRegion), + Status: types.StringNull(), + ConnectionString: types.StringNull(), }, true, }, @@ -263,42 +269,42 @@ func TestMapFields(t *testing.T) { }, { "nil_response_2", - &postgresflex.GetUserResponse{}, + &postgresflexalpha.GetUserResponse{}, testRegion, Model{}, false, }, { "no_resource_id", - &postgresflex.GetUserResponse{ - Item: &postgresflex.UserResponse{}, - }, + &postgresflexalpha.GetUserResponse{}, testRegion, Model{}, false, }, } for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - state := &Model{ - ProjectId: tt.expected.ProjectId, - InstanceId: tt.expected.InstanceId, - UserId: tt.expected.UserId, - } - err := mapFields(tt.input, state, tt.region) - if !tt.isValid && err == nil { - t.Fatalf("Should have failed") - } - if tt.isValid && err != nil { - t.Fatalf("Should not have failed: %v", err) - } - if tt.isValid { - diff := cmp.Diff(state, &tt.expected) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) + t.Run( + tt.description, func(t *testing.T) { + state := &Model{ + ProjectId: tt.expected.ProjectId, + InstanceId: tt.expected.InstanceId, + UserId: tt.expected.UserId, } - } - }) + err := mapFields(tt.input, state, tt.region) + if !tt.isValid && err == nil { + t.Fatalf("Should have failed") + } + if tt.isValid && err != nil { + t.Fatalf("Should not have failed: %v", err) + } + if tt.isValid { + diff := cmp.Diff(state, &tt.expected) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + } + }, + ) } } @@ -306,35 +312,35 @@ func TestToCreatePayload(t *testing.T) { tests := []struct { description string input *Model - inputRoles []string - expected *postgresflex.CreateUserPayload + inputRoles *[]string + expected *postgresflexalpha.CreateUserRequestPayload isValid bool }{ { "default_values", &Model{}, - []string{}, - &postgresflex.CreateUserPayload{ - Roles: &[]string{}, - Username: nil, + &[]string{}, + &postgresflexalpha.CreateUserRequestPayload{ + Name: nil, + Roles: &[]postgresflexalpha.UserRole{}, }, true, }, { - "default_values", + "simple_values", &Model{ Username: types.StringValue("username"), }, - []string{ + &[]string{ "role_1", "role_2", }, - &postgresflex.CreateUserPayload{ - Roles: &[]string{ + &postgresflexalpha.CreateUserRequestPayload{ + Name: utils.Ptr("username"), + Roles: &[]postgresflexalpha.UserRole{ "role_1", "role_2", }, - Username: utils.Ptr("username"), }, true, }, @@ -343,21 +349,21 @@ func TestToCreatePayload(t *testing.T) { &Model{ Username: types.StringNull(), }, - []string{ + &[]string{ "", }, - &postgresflex.CreateUserPayload{ - Roles: &[]string{ + &postgresflexalpha.CreateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{ "", }, - Username: nil, + Name: nil, }, true, }, { "nil_model", nil, - []string{}, + &[]string{}, nil, false, }, @@ -370,21 +376,23 @@ func TestToCreatePayload(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - output, err := toCreatePayload(tt.input, tt.inputRoles) - if !tt.isValid && err == nil { - t.Fatalf("Should have failed") - } - if tt.isValid && err != nil { - t.Fatalf("Should not have failed: %v", err) - } - if tt.isValid { - diff := cmp.Diff(output, tt.expected) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) + t.Run( + tt.description, func(t *testing.T) { + output, err := toCreatePayload(tt.input, tt.inputRoles) + if !tt.isValid && err == nil { + t.Fatalf("Should have failed") } - } - }) + if tt.isValid && err != nil { + t.Fatalf("Should not have failed: %v", err) + } + if tt.isValid { + diff := cmp.Diff(output, tt.expected) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + } + }, + ) } } @@ -392,16 +400,16 @@ func TestToUpdatePayload(t *testing.T) { tests := []struct { description string input *Model - inputRoles []string - expected *postgresflex.UpdateUserPayload + inputRoles *[]string + expected *postgresflexalpha.UpdateUserRequestPayload isValid bool }{ { "default_values", &Model{}, - []string{}, - &postgresflex.UpdateUserPayload{ - Roles: &[]string{}, + &[]string{}, + &postgresflexalpha.UpdateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{}, }, true, }, @@ -410,12 +418,12 @@ func TestToUpdatePayload(t *testing.T) { &Model{ Username: types.StringValue("username"), }, - []string{ + &[]string{ "role_1", "role_2", }, - &postgresflex.UpdateUserPayload{ - Roles: &[]string{ + &postgresflexalpha.UpdateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{ "role_1", "role_2", }, @@ -427,11 +435,11 @@ func TestToUpdatePayload(t *testing.T) { &Model{ Username: types.StringNull(), }, - []string{ + &[]string{ "", }, - &postgresflex.UpdateUserPayload{ - Roles: &[]string{ + &postgresflexalpha.UpdateUserRequestPayload{ + Roles: &[]postgresflexalpha.UserRole{ "", }, }, @@ -440,7 +448,7 @@ func TestToUpdatePayload(t *testing.T) { { "nil_model", nil, - []string{}, + &[]string{}, nil, false, }, @@ -453,20 +461,22 @@ func TestToUpdatePayload(t *testing.T) { }, } for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - output, err := toUpdatePayload(tt.input, tt.inputRoles) - if !tt.isValid && err == nil { - t.Fatalf("Should have failed") - } - if tt.isValid && err != nil { - t.Fatalf("Should not have failed: %v", err) - } - if tt.isValid { - diff := cmp.Diff(output, tt.expected) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) + t.Run( + tt.description, func(t *testing.T) { + output, err := toUpdatePayload(tt.input, tt.inputRoles) + if !tt.isValid && err == nil { + t.Fatalf("Should have failed") } - } - }) + if tt.isValid && err != nil { + t.Fatalf("Should not have failed: %v", err) + } + if tt.isValid { + diff := cmp.Diff(output, tt.expected) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + } + }, + ) } }