Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@
- **Feature:** Add fields `Labels` and `Maintenance` to model struct `Cluster` and `CreateOrUpdateClusterPayload`
- **Feature:** Add field `ApplicationLoadBalancer` to model struct `Extension`
- **Feature:** Add field `Cni` to model struct `Network`
- `sqlserverflex`:
- [v1.17.0](services/sqlserverflex/CHANGELOG.md#v1170)
- `v3api`:
- **Feature:** add missing `DeleteUserWaitHandler` and `CreateUserWaitHandler`
- `telemetrylink`:
- [v0.5.0](services/telemetrylink/CHANGELOG.md#v050)
- **Improvement:** Add validation for `Description` field
Expand Down
4 changes: 4 additions & 0 deletions services/sqlserverflex/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## v1.17.0
- `v3api`:
- **Feature:** add missing `DeleteUserWaitHandler` and `CreateUserWaitHandler`

## v1.16.1
- **Improvement**: Improve http error handling
- `v3api`: **Improvement**: Improve http error handling
Expand Down
2 changes: 1 addition & 1 deletion services/sqlserverflex/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.16.1
v1.17.0
47 changes: 47 additions & 0 deletions services/sqlserverflex/v3api/wait/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
"net/http"
"time"

"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/wait"
sqlserverflex "github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/v3api"
)

const userActiveState = "PROCESSED"

func createOrUpdateInstanceWaitHandler(ctx context.Context, client sqlserverflex.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
waitConfig := wait.WaiterHelper[sqlserverflex.GetInstanceResponse, sqlserverflex.State]{
FetchInstance: client.GetInstance(ctx, projectId, region, instanceId).Execute,
Expand Down Expand Up @@ -66,3 +69,47 @@ func DeleteInstanceWaitHandler(ctx context.Context, client sqlserverflex.Default
handler.SetTimeout(15 * time.Minute)
return handler
}

// CreateUserWaitHandler will wait for user creation
func CreateUserWaitHandler(ctx context.Context, client sqlserverflex.DefaultAPI, projectId, region, instanceId string, userId int64) *wait.AsyncActionHandler[sqlserverflex.GetUserResponse] {
waitConfig := wait.WaiterHelper[sqlserverflex.GetUserResponse, string]{
FetchInstance: client.GetUser(ctx, projectId, region, instanceId, userId).Execute,
GetState: func(resp *sqlserverflex.GetUserResponse) (string, error) {
if resp == nil {
return "", errors.New("empty response")
}
if resp.Status == "" {
return "", errors.New("state is missing in response")
}
return resp.Status, nil
},
ActiveState: []string{userActiveState},
ErrorState: []string{},
// The API does not have a dedicated failure state for this resource,
// so we rely on the timeout for cases where it never becomes active.
}
handler := wait.New(waitConfig.Wait())
handler.SetSleepBeforeWait(5 * time.Second)
handler.SetTimeout(15 * time.Minute)
return handler
}

// DeleteUserWaitHandler will wait for user deletion
func DeleteUserWaitHandler(ctx context.Context, a sqlserverflex.DefaultAPI, projectId, region, instanceId string, userId int64) *wait.AsyncActionHandler[struct{}] {
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
_, err = a.GetUser(ctx, projectId, region, instanceId, userId).Execute()
if err == nil {
return false, nil, nil
}
var oapiErr *oapierror.GenericOpenAPIError
if !errors.As(err, &oapiErr) {
return false, nil, err
}
if oapiErr.StatusCode != 404 {
return false, nil, err
}
return true, nil, nil
})
handler.SetTimeout(1 * time.Minute)
return handler
}
142 changes: 142 additions & 0 deletions services/sqlserverflex/v3api/wait/wait_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ type mockSettings struct {
instanceState sqlserverflex.State
instanceIsDeleted bool
instanceGetFails bool
userGetFails bool
userId int64
userIsDeleted bool
userStatus string
}

// Used for testing instance operations
Expand All @@ -43,6 +47,24 @@ func newAPIMock(settings mockSettings) sqlserverflex.DefaultAPI {
State: settings.instanceState,
}, nil
}),
GetUserExecuteMock: utils.Ptr(func(_ sqlserverflex.ApiGetUserRequest) (*sqlserverflex.GetUserResponse, error) {
if settings.userGetFails {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 423,
}
}

if settings.userIsDeleted {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 404,
}
}

return &sqlserverflex.GetUserResponse{
Id: settings.userId,
Status: settings.userStatus,
}, nil
}),
}
}

Expand Down Expand Up @@ -189,3 +211,123 @@ func TestDeleteInstanceWaitHandler(t *testing.T) {
})
}
}

func TestCreateUserWaitHandler(t *testing.T) {
tests := []struct {
desc string
userGetFails bool
userStatus string
usersGetErrorStatus int
wantErr bool
wantResp bool
}{
{
desc: "create_succeeded",
userGetFails: false,
userStatus: userActiveState,
wantErr: false,
wantResp: true,
},
{
desc: "user_get_fails",
userGetFails: true,
wantErr: true,
wantResp: false,
},
{
desc: "users_get_fails",
userGetFails: true,
usersGetErrorStatus: 423,
wantErr: true,
wantResp: false,
},
{
desc: "timeout",
userGetFails: false,
userStatus: "",
wantErr: true,
wantResp: false,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
userId := int64(34)

apiClient := newAPIMock(mockSettings{
userGetFails: tt.userGetFails,
userId: userId,
userStatus: tt.userStatus,
})

var wantRes *sqlserverflex.GetUserResponse
if tt.wantResp {
wantRes = &sqlserverflex.GetUserResponse{
Id: userId,
Status: tt.userStatus,
}
}

handler := CreateUserWaitHandler(context.Background(), apiClient, "", "", "", userId)

gotRes, err := handler.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)
}
})
})
}
}

func TestDeleteUserWaitHandler(t *testing.T) {
tests := []struct {
desc string
deleteFails bool
getFails bool
wantErr bool
}{
{
desc: "delete_succeeded",
deleteFails: false,
getFails: false,
wantErr: false,
},
{
desc: "delete_failed",
deleteFails: true,
getFails: false,
wantErr: true,
},
{
desc: "get_fails",
deleteFails: false,
getFails: true,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
userId := int64(34)

apiClient := newAPIMock(mockSettings{
userGetFails: tt.getFails,
userId: userId,
userIsDeleted: !tt.deleteFails,
})

handler := DeleteUserWaitHandler(context.Background(), apiClient, "", "", "", userId)

_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())

if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
}
})
})
}
}
Loading