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
13 changes: 13 additions & 0 deletions api/handler/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"slices"
"strconv"
"strings"

"opencsg.com/csghub-server/common/errorx"

Expand Down Expand Up @@ -792,6 +793,7 @@ func (h *UserHandler) GetFinetuneInstances(ctx *gin.Context) {
// @Param page query int false "page index" default(1)
// @Param current_user query string false "current user"
// @Param search query string false "search by path or deployname"
// @Param status query string false "status filter"
// @Success 200 {object} types.ResponseWithTotal{data=[]types.DeployRequest,total=int} "OK"
// @Failure 400 {object} types.APIBadRequest "Bad request"
// @Failure 500 {object} types.APIInternalServerError "Internal server error"
Expand All @@ -813,6 +815,17 @@ func (h *UserHandler) GetRunServerless(ctx *gin.Context) {
req.RepoType = types.ModelRepo
req.DeployType = types.ServerlessType
req.Query = ctx.Query("search")

statusQuery := ctx.Query("status")
if len(statusQuery) > 0 {
code, err := strconv.Atoi(strings.TrimSpace(statusQuery))
if err == nil {
req.Status = append(req.Status, code)
} else {
httpbase.BadRequestWithExt(ctx, err)
}
}

ds, total, err := h.user.ListServerless(ctx.Request.Context(), req)
if err != nil {
slog.ErrorContext(ctx.Request.Context(), "Failed to get serverless list", slog.Any("error", err), slog.Any("req", req))
Expand Down
4 changes: 4 additions & 0 deletions builder/store/database/deploy_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,10 @@ func (s *deployTaskStoreImpl) ListServerless(ctx context.Context, req types.Depl
query := s.db.Operator.Core.NewSelect().Model(&result).Where("type = ?", req.DeployType)
query = query.Where("status != ?", common.Deleted)

if len(req.Status) > 0 {
query = query.Where("status in (?)", bun.In(req.Status))
}

searchQuery := strings.TrimSpace(req.Query)
if searchQuery != "" {
searchPattern := "%" + strings.ToLower(searchQuery) + "%"
Expand Down
58 changes: 58 additions & 0 deletions builder/store/database/deploy_task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,64 @@ func TestDeployTaskStore_ListServerless_Search(t *testing.T) {
require.Nil(t, err)
require.Equal(t, 3, total) // Total should be 4
require.Equal(t, 2, len(dps)) // But only 2 per page

// Test 9: Filter by status (Running only)
dps, total, err = store.ListServerless(ctx, types.DeployReq{
DeployType: types.ServerlessType,
Status: []int{common.Running},
PageOpts: types.PageOpts{
Page: 1,
PageSize: 10,
},
})
require.Nil(t, err)
require.Equal(t, 4, total) // 4 Running serverless deploys
require.Equal(t, 4, len(dps))
for _, dp := range dps {
require.Equal(t, common.Running, dp.Status)
}

// Test 10: Filter by status (Stopped only, should return 0)
dps, total, err = store.ListServerless(ctx, types.DeployReq{
DeployType: types.ServerlessType,
Status: []int{common.Stopped},
PageOpts: types.PageOpts{
Page: 1,
PageSize: 10,
},
})
require.Nil(t, err)
require.Equal(t, 0, total)
require.Equal(t, 0, len(dps))

// Test 11: Filter by status combined with search
dps, total, err = store.ListServerless(ctx, types.DeployReq{
DeployType: types.ServerlessType,
Status: []int{common.Running},
Query: "qwen",
PageOpts: types.PageOpts{
Page: 1,
PageSize: 10,
},
})
require.Nil(t, err)
require.Equal(t, 3, total) // 3 Running serverless deploys matching "qwen"
require.Equal(t, 3, len(dps))

// Test 12: Filter by multiple statuses (Running + Deleted)
// Note: when Status filter is specified, both "status != Deleted" and "status IN (...)" apply as AND conditions,
// so Deleted records are still excluded by the first condition. This is a known limitation to be fixed.
dps, total, err = store.ListServerless(ctx, types.DeployReq{
DeployType: types.ServerlessType,
Status: []int{common.Running, common.Deleted},
PageOpts: types.PageOpts{
Page: 1,
PageSize: 10,
},
})
require.Nil(t, err)
require.Equal(t, 4, total) // 4 Running serverless deploys (Deleted excluded by "status != Deleted" AND condition)
require.Equal(t, 4, len(dps))
}

func TestDeployTaskStore_GetClusterDeploys(t *testing.T) {
Expand Down
9 changes: 0 additions & 9 deletions component/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,15 +709,6 @@ func (c *userComponentImpl) LikesDatasets(ctx context.Context, req *types.UserDa
}

func (c *userComponentImpl) ListServerless(ctx context.Context, req types.DeployReq) ([]types.DeployRequest, int, error) {
user, err := c.userStore.FindByUsername(ctx, req.CurrentUser)
if err != nil {
newError := fmt.Errorf("failed to check for the presence of the user:%s, error:%w", req.CurrentUser, err)
return nil, 0, newError
}
isAdmin := c.repoComponent.IsAdminRole(user)
if !isAdmin {
return nil, 0, fmt.Errorf("user %s does not have admin privileges", req.CurrentUser)
}
deploys, total, err := c.deployTaskStore.ListServerless(ctx, req)
if err != nil {
newError := fmt.Errorf("failed to get user serverless for %s with error:%w", req.RepoType, err)
Expand Down
34 changes: 32 additions & 2 deletions component/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"opencsg.com/csghub-server/builder/deploy/common"
"opencsg.com/csghub-server/builder/store/database"
"opencsg.com/csghub-server/common/types"
)
Expand Down Expand Up @@ -588,8 +589,6 @@ func TestUserComponent_ListServeless(t *testing.T) {
PageSize: 10,
},
}
uc.mocks.stores.UserMock().EXPECT().FindByUsername(ctx, "user").Return(database.User{ID: 1}, nil)
uc.mocks.components.repo.EXPECT().IsAdminRole(database.User{ID: 1}).Return(true)
uc.mocks.stores.DeployTaskMock().EXPECT().ListServerless(ctx, *req).Return([]database.Deploy{
{
SvcName: "svc", ClusterID: "cluster", SKU: "sku",
Expand All @@ -610,6 +609,37 @@ func TestUserComponent_ListServeless(t *testing.T) {

}

func TestUserComponent_ListServerless_StatusFilter(t *testing.T) {
ctx := context.TODO()
uc := initializeTestUserComponent(ctx, t)

req := &types.DeployReq{
CurrentUser: "user",
Status: []int{common.Running},
PageOpts: types.PageOpts{
Page: 1,
PageSize: 10,
},
}
uc.mocks.stores.DeployTaskMock().EXPECT().ListServerless(ctx, *req).Return([]database.Deploy{
{
SvcName: "svc", ClusterID: "cluster", SKU: "sku",
GitPath: "models_foo/bar", Hardware: `{"memory": "foo"}`,
RepoID: 123, Status: common.Running,
},
}, 1, nil)

data, total, err := uc.ListServerless(ctx, *req)
require.Nil(t, err)
require.Equal(t, 1, total)
require.Equal(t, []types.DeployRequest{
{
Path: "models_foo/bar", Status: "Running", GitPath: "models_foo/bar", Hardware: `{"memory": "foo"}`,
RepoID: 123, SvcName: "svc", ClusterID: "cluster", SKU: "sku",
},
}, data)
}

func TestUserComponent_GetUserByName(t *testing.T) {
ctx := context.TODO()
uc := initializeTestUserComponent(ctx, t)
Expand Down
Loading