From d425b436cda97892f31911a393e15bf8a08b0e8b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 2 Apr 2026 20:12:14 -0700 Subject: [PATCH] refactor --- models/project/column.go | 41 ------------------------ models/project/column_list.go | 42 +++++++++++++++++++++++++ models/project/column_list_test.go | 49 +++++++++++++++++++++++++++++ models/project/column_test.go | 42 ++----------------------- routers/api/v1/repo/project.go | 4 +-- routers/web/org/projects.go | 2 +- routers/web/repo/issue_page_meta.go | 2 +- routers/web/repo/projects.go | 2 +- tests/integration/project_test.go | 7 +++-- 9 files changed, 103 insertions(+), 88 deletions(-) create mode 100644 models/project/column_list.go create mode 100644 models/project/column_list_test.go diff --git a/models/project/column.go b/models/project/column.go index c2622f562b..a89e82a5ce 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -247,33 +247,6 @@ func UpdateColumn(ctx context.Context, column *Column) error { return err } -// GetColumns fetches all columns related to a project -func (p *Project) GetColumns(ctx context.Context) (ColumnList, error) { - columns := make([]*Column, 0, 5) - if err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting, id").Find(&columns); err != nil { - return nil, err - } - - return columns, nil -} - -// CountColumns returns the total number of columns for a project -func (p *Project) CountColumns(ctx context.Context) (int64, error) { - return db.GetEngine(ctx).Where("project_id=?", p.ID).Count(&Column{}) -} - -// GetColumnsPaginated fetches a page of columns for a project -func (p *Project) GetColumnsPaginated(ctx context.Context, opts db.ListOptions) (ColumnList, error) { - columns := make([]*Column, 0, opts.PageSize) - if err := db.SetSessionPagination(db.GetEngine(ctx), &opts). - Where("project_id=?", p.ID). - OrderBy("sorting, id"). - Find(&columns); err != nil { - return nil, err - } - return columns, nil -} - // getDefaultColumnWithFallback return default column if one exists // otherwise return the first column by sorting and set it as default column func (p *Project) getDefaultColumnWithFallback(ctx context.Context) (*Column, error) { @@ -368,20 +341,6 @@ func UpdateColumnSorting(ctx context.Context, cl ColumnList) error { }) } -func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) { - columns := make([]*Column, 0, 5) - if len(columnsIDs) == 0 { - return columns, nil - } - if err := db.GetEngine(ctx). - Where("project_id =?", projectID). - In("id", columnsIDs). - OrderBy("sorting").Find(&columns); err != nil { - return nil, err - } - return columns, nil -} - // MoveColumnsOnProject sorts columns in a project func MoveColumnsOnProject(ctx context.Context, project *Project, sortedColumnIDs map[int64]int64) error { return db.WithTx(ctx, func(ctx context.Context) error { diff --git a/models/project/column_list.go b/models/project/column_list.go new file mode 100644 index 0000000000..b3041a15e1 --- /dev/null +++ b/models/project/column_list.go @@ -0,0 +1,42 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package project + +import ( + "context" + + "code.gitea.io/gitea/models/db" +) + +// CountColumns returns the total number of columns for a project +func CountProjectColumns(ctx context.Context, projectID int64) (int64, error) { + return db.GetEngine(ctx).Where("project_id=?", projectID).Count(&Column{}) +} + +// GetProjectColumns returns a list of columns for a project with pagination +func GetProjectColumns(ctx context.Context, projectID int64, opts db.ListOptions) (ColumnList, error) { + columns := make([]*Column, 0, opts.PageSize) + s := db.GetEngine(ctx).Where("project_id=?", projectID).OrderBy("sorting, id") + if !opts.IsListAll() { + db.SetSessionPagination(s, &opts) + } + if err := s.Find(&columns); err != nil { + return nil, err + } + return columns, nil +} + +func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) { + columns := make([]*Column, 0, 5) + if len(columnsIDs) == 0 { + return columns, nil + } + if err := db.GetEngine(ctx). + Where("project_id =?", projectID). + In("id", columnsIDs). + OrderBy("sorting").Find(&columns); err != nil { + return nil, err + } + return columns, nil +} diff --git a/models/project/column_list_test.go b/models/project/column_list_test.go new file mode 100644 index 0000000000..1c296dd64d --- /dev/null +++ b/models/project/column_list_test.go @@ -0,0 +1,49 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package project + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestCountColumns(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + project, err := GetProjectByID(t.Context(), 1) + assert.NoError(t, err) + + count, err := CountProjectColumns(t.Context(), project.ID) + assert.NoError(t, err) + assert.EqualValues(t, 3, count) +} + +func TestGetColumnsPaginated(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + project, err := GetProjectByID(t.Context(), 1) + assert.NoError(t, err) + + // Page 1, limit 2 — returns first 2 columns + page1, err := GetProjectColumns(t.Context(), project.ID, db.ListOptions{Page: 1, PageSize: 2}) + assert.NoError(t, err) + assert.Len(t, page1, 2) + + // Page 2, limit 2 — returns remaining column + page2, err := GetProjectColumns(t.Context(), project.ID, db.ListOptions{Page: 2, PageSize: 2}) + assert.NoError(t, err) + assert.Len(t, page2, 1) + + // Page 1 and page 2 together cover all columns with no overlap + allIDs := make(map[int64]bool) + for _, c := range append(page1, page2...) { + assert.False(t, allIDs[c.ID], "duplicate column ID %d across pages", c.ID) + allIDs[c.ID] = true + } + assert.Len(t, allIDs, 3) +} diff --git a/models/project/column_test.go b/models/project/column_test.go index 0a07943fe1..ac95476eac 100644 --- a/models/project/column_test.go +++ b/models/project/column_test.go @@ -80,7 +80,7 @@ func Test_MoveColumnsOnProject(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) project1 := unittest.AssertExistsAndLoadBean(t, &Project{ID: 1}) - columns, err := project1.GetColumns(t.Context()) + columns, err := GetProjectColumns(t.Context(), project1.ID, db.ListOptionsAll) assert.NoError(t, err) assert.Len(t, columns, 3) assert.EqualValues(t, 0, columns[0].Sorting) // even if there is no default sorting, the code should also work @@ -94,7 +94,7 @@ func Test_MoveColumnsOnProject(t *testing.T) { }) assert.NoError(t, err) - columnsAfter, err := project1.GetColumns(t.Context()) + columnsAfter, err := GetProjectColumns(t.Context(), project1.ID, db.ListOptionsAll) assert.NoError(t, err) assert.Len(t, columnsAfter, 3) assert.Equal(t, columns[1].ID, columnsAfter[0].ID) @@ -106,7 +106,7 @@ func Test_NewColumn(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) project1 := unittest.AssertExistsAndLoadBean(t, &Project{ID: 1}) - columns, err := project1.GetColumns(t.Context()) + columns, err := GetProjectColumns(t.Context(), project1.ID, db.ListOptionsAll) assert.NoError(t, err) assert.Len(t, columns, 3) @@ -124,39 +124,3 @@ func Test_NewColumn(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "maximum number of columns reached") } - -func TestCountColumns(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - project, err := GetProjectByID(t.Context(), 1) - assert.NoError(t, err) - - count, err := project.CountColumns(t.Context()) - assert.NoError(t, err) - assert.EqualValues(t, 3, count) -} - -func TestGetColumnsPaginated(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - project, err := GetProjectByID(t.Context(), 1) - assert.NoError(t, err) - - // Page 1, limit 2 — returns first 2 columns - page1, err := project.GetColumnsPaginated(t.Context(), db.ListOptions{Page: 1, PageSize: 2}) - assert.NoError(t, err) - assert.Len(t, page1, 2) - - // Page 2, limit 2 — returns remaining column - page2, err := project.GetColumnsPaginated(t.Context(), db.ListOptions{Page: 2, PageSize: 2}) - assert.NoError(t, err) - assert.Len(t, page2, 1) - - // Page 1 and page 2 together cover all columns with no overlap - allIDs := make(map[int64]bool) - for _, c := range append(page1, page2...) { - assert.False(t, allIDs[c.ID], "duplicate column ID %d across pages", c.ID) - allIDs[c.ID] = true - } - assert.Len(t, allIDs, 3) -} diff --git a/routers/api/v1/repo/project.go b/routers/api/v1/repo/project.go index 6ae5554bee..619ecde57e 100644 --- a/routers/api/v1/repo/project.go +++ b/routers/api/v1/repo/project.go @@ -385,14 +385,14 @@ func ListProjectColumns(ctx *context.APIContext) { return } - total, err := project.CountColumns(ctx) + total, err := project_model.CountProjectColumns(ctx, project.ID) if err != nil { ctx.APIErrorInternal(err) return } listOptions := utils.GetListOptions(ctx) - columns, err := project.GetColumnsPaginated(ctx, listOptions) + columns, err := project_model.GetProjectColumns(ctx, project.ID, listOptions) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 4cdf81c155..a3eb773eba 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -318,7 +318,7 @@ func ViewProject(ctx *context.Context) { return } - columns, err := project.GetColumns(ctx) + columns, err := project_model.GetProjectColumns(ctx, project.ID, db.ListOptionsAll) if err != nil { ctx.ServerError("GetProjectColumns", err) return diff --git a/routers/web/repo/issue_page_meta.go b/routers/web/repo/issue_page_meta.go index 639333ab42..635ae8ef9c 100644 --- a/routers/web/repo/issue_page_meta.go +++ b/routers/web/repo/issue_page_meta.go @@ -173,7 +173,7 @@ func (d *IssuePageMetaData) retrieveProjectData(ctx *context.Context) { return } d.ProjectsData.SelectedProjectIDs = []int64{d.Issue.Project.ID} - columns, err := d.Issue.Project.GetColumns(ctx) + columns, err := project_model.GetProjectColumns(ctx, d.Issue.Project.ID, db.ListOptionsAll) if err != nil { ctx.ServerError("GetProjectColumns", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index c9bdc5be76..7f94f1e933 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -292,7 +292,7 @@ func ViewProject(ctx *context.Context) { return } - columns, err := project.GetColumns(ctx) + columns, err := project_model.GetProjectColumns(ctx, project.ID, db.ListOptionsAll) if err != nil { ctx.ServerError("GetProjectColumns", err) return diff --git a/tests/integration/project_test.go b/tests/integration/project_test.go index 1e38322dbf..46254ea44e 100644 --- a/tests/integration/project_test.go +++ b/tests/integration/project_test.go @@ -9,6 +9,7 @@ import ( "strconv" "testing" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" @@ -59,7 +60,7 @@ func TestMoveRepoProjectColumns(t *testing.T) { assert.NoError(t, err) } - columns, err := project1.GetColumns(t.Context()) + columns, err := project_model.GetProjectColumns(t.Context(), project1.ID, db.ListOptionsAll) assert.NoError(t, err) assert.Len(t, columns, 3) assert.EqualValues(t, 0, columns[0].Sorting) @@ -79,7 +80,7 @@ func TestMoveRepoProjectColumns(t *testing.T) { }) sess.MakeRequest(t, req, http.StatusOK) - columnsAfter, err := project1.GetColumns(t.Context()) + columnsAfter, err := project_model.GetProjectColumns(t.Context(), project1.ID, db.ListOptionsAll) assert.NoError(t, err) assert.Len(t, columnsAfter, 3) assert.Equal(t, columns[1].ID, columnsAfter[0].ID) @@ -189,7 +190,7 @@ func TestOrgProjectFilterByMilestone(t *testing.T) { require.NoError(t, project_model.NewProject(t.Context(), &project)) // Get the default column - columns, err := project.GetColumns(t.Context()) + columns, err := project_model.GetProjectColumns(t.Context(), project.ID, db.ListOptionsAll) require.NoError(t, err) require.NotEmpty(t, columns) defaultColumnID := columns[0].ID