feat(api): state filter + populated num_issues on project columns (#6)
This commit was merged in pull request #6.
This commit is contained in:
@@ -48,7 +48,9 @@ type Column struct {
|
||||
ProjectID int64 `xorm:"INDEX NOT NULL"`
|
||||
CreatorID int64 `xorm:"NOT NULL"`
|
||||
|
||||
NumIssues int64 `xorm:"-"`
|
||||
NumIssues int64 `xorm:"-"`
|
||||
NumOpenIssues int64 `xorm:"-"`
|
||||
NumClosedIssues int64 `xorm:"-"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
|
||||
@@ -5,8 +5,11 @@ package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// CountProjectColumns returns the total number of columns for a project
|
||||
@@ -27,6 +30,58 @@ func GetProjectColumns(ctx context.Context, projectID int64, opts db.ListOptions
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// LoadIssueCounts populates NumIssues, NumOpenIssues, and NumClosedIssues on
|
||||
// every column in the list using two grouped queries against project_issue
|
||||
// joined with issue. Columns with no attached issues stay at zero counts —
|
||||
// nothing else has to be wired up by the caller.
|
||||
func (cl ColumnList) LoadIssueCounts(ctx context.Context) error {
|
||||
if len(cl) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
columnIDs := make([]int64, 0, len(cl))
|
||||
for _, c := range cl {
|
||||
columnIDs = append(columnIDs, c.ID)
|
||||
}
|
||||
|
||||
openCounts, err := countColumnIssuesByState(ctx, columnIDs, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
closedCounts, err := countColumnIssuesByState(ctx, columnIDs, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, c := range cl {
|
||||
c.NumOpenIssues = openCounts[c.ID]
|
||||
c.NumClosedIssues = closedCounts[c.ID]
|
||||
c.NumIssues = c.NumOpenIssues + c.NumClosedIssues
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func countColumnIssuesByState(ctx context.Context, columnIDs []int64, isClosed bool) (map[int64]int64, error) {
|
||||
out := make(map[int64]int64, len(columnIDs))
|
||||
cond := builder.In("project_issue.project_board_id", columnIDs).
|
||||
And(builder.Eq{"issue.is_closed": isClosed})
|
||||
sub := builder.Select("project_issue.project_board_id AS project_board_id", "COUNT(*) AS cnt").
|
||||
From("project_issue").
|
||||
InnerJoin("issue", "issue.id = project_issue.issue_id").
|
||||
Where(cond).
|
||||
GroupBy("project_issue.project_board_id")
|
||||
rows, err := db.GetEngine(ctx).Query(sub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range rows {
|
||||
columnID, _ := strconv.ParseInt(string(r["project_board_id"]), 10, 64)
|
||||
cnt, _ := strconv.ParseInt(string(r["cnt"]), 10, 64)
|
||||
out[columnID] = cnt
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) (ColumnList, error) {
|
||||
columns := make([]*Column, 0, len(columnsIDs))
|
||||
if len(columnsIDs) == 0 {
|
||||
|
||||
@@ -17,6 +17,7 @@ func TestProjectColumns(t *testing.T) {
|
||||
t.Run("CountProjectColumns", testCountProjectColumns)
|
||||
t.Run("GetProjectColumns", testGetProjectColumns)
|
||||
t.Run("GetColumnsByIDs", testGetColumnsByIDs)
|
||||
t.Run("LoadIssueCountsEmpty", testLoadIssueCountsEmpty)
|
||||
}
|
||||
|
||||
func testCountProjectColumns(t *testing.T) {
|
||||
@@ -51,6 +52,14 @@ func testGetProjectColumns(t *testing.T) {
|
||||
assert.Len(t, allIDs, 3)
|
||||
}
|
||||
|
||||
func testLoadIssueCountsEmpty(t *testing.T) {
|
||||
// Empty input is a fast path — must not touch the database and must not error.
|
||||
// (The full open/closed-count behavior is exercised by the integration tests
|
||||
// in tests/integration/api_*_project_test.go, which can join against the issue
|
||||
// table; the unit-test fixture set here intentionally excludes it.)
|
||||
assert.NoError(t, ColumnList{}.LoadIssueCounts(t.Context()))
|
||||
}
|
||||
|
||||
func testGetColumnsByIDs(t *testing.T) {
|
||||
project, err := GetProjectByID(t.Context(), 1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user