-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
feat(api): add REST API for repository project boards #36831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
hanism01
wants to merge
31
commits into
go-gitea:main
Choose a base branch
from
hanism01:fix/project-board-api-review-feedback
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
10977db
feat(api): add comprehensive REST API for Project Boards
SupenBysz 022a77f
fix(api): address review feedback on project board API
b8a654a
Merge branch 'main' into fix/project-board-api-review-feedback
silverwind c508fb6
Merge branch 'fix/project-board-api-review-feedback' of github.com:ha…
lunny 8565da9
Fix lint
lunny 84cb665
Merge branch 'fix/project-board-api-review-feedback' of github.com:ha…
lunny 11b9593
improvements
lunny 506236f
Merge branch 'main' into fix/project-board-api-review-feedback
lunny 67c5029
Merge branch 'main' into fix/project-board-api-review-feedback
lunny 2f27c75
Merge branch 'main' into fix/project-board-api-review-feedback
lunny bca683f
Apply suggestion from @silverwind
silverwind 17cb8e9
Apply suggestion from @silverwind
silverwind 35a4116
Apply suggestion from @silverwind
silverwind d39f321
Apply suggestion from @silverwind
silverwind c0cdc8b
Simplify project board API: use state field, extract helpers, remove …
silverwind e583e56
Merge branch 'fix/project-board-api-review-feedback' of github.com:ha…
lunny d425b43
refactor
lunny f08a738
Merge branch 'main' into hanism01-fix/project-board-api-review-feedback
lunny 7512157
fix
lunny 5c141d3
some improvements
lunny d2dbc96
update swagger
lunny 631e733
Merge branch 'fix/project-board-api-review-feedback' of github.com:ha…
lunny f7b3810
Fix test
lunny 0fbf248
Merge branch 'main' into hanism01-fix/project-board-api-review-feedback
lunny 3554572
improvement
lunny d1cfe87
Merge branch 'main' into hanism01-fix/project-board-api-review-feedback
lunny 3fc8d79
Merge branch 'fix/project-board-api-review-feedback' of github.com:ha…
lunny 199162c
improvement
lunny c4038f1
Fix test
lunny 4fea94c
merge tests
wxiaoguang 7e680f1
fix AI slop
wxiaoguang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // 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 TestProjectColumns(t *testing.T) { | ||
| assert.NoError(t, unittest.PrepareTestDatabase()) | ||
| t.Run("CountProjectColumns", testCountProjectColumns) | ||
| t.Run("GetProjectColumns", testGetProjectColumns) | ||
| t.Run("GetColumnsByIDs", testGetColumnsByIDs) | ||
| } | ||
|
|
||
| func testCountProjectColumns(t *testing.T) { | ||
| 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 testGetProjectColumns(t *testing.T) { | ||
| 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) | ||
| } | ||
|
|
||
| func testGetColumnsByIDs(t *testing.T) { | ||
| project, err := GetProjectByID(t.Context(), 1) | ||
| assert.NoError(t, err) | ||
|
|
||
| columns, err := GetColumnsByIDs(t.Context(), project.ID, []int64{1, 3, 4}) | ||
| assert.NoError(t, err) | ||
| assert.Len(t, columns, 2) | ||
| assert.ElementsMatch(t, []int64{1, 3}, []int64{columns[0].ID, columns[1].ID}) | ||
|
|
||
| empty, err := GetColumnsByIDs(t.Context(), project.ID, nil) | ||
| assert.NoError(t, err) | ||
| assert.Empty(t, empty) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| // Copyright 2026 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package structs | ||
|
|
||
| import ( | ||
| "time" | ||
| ) | ||
|
|
||
| // Project represents a project | ||
| // swagger:model | ||
| type Project struct { | ||
| // Unique identifier of the project | ||
| ID int64 `json:"id"` | ||
| // Project title | ||
| Title string `json:"title"` | ||
| // Project description | ||
| Description string `json:"description"` | ||
| // Owner ID (for organization or user projects) | ||
| OwnerID int64 `json:"owner_id,omitempty"` | ||
| // Repository ID (for repository projects) | ||
| RepoID int64 `json:"repo_id,omitempty"` | ||
| // Creator ID | ||
| CreatorID int64 `json:"creator_id"` | ||
| // Whether the project is closed | ||
| IsClosed bool `json:"is_closed"` | ||
| // Template type: 0=none, 1=basic_kanban, 2=bug_triage | ||
| TemplateType int `json:"template_type"` | ||
| // Card type: 0=text_only, 1=images_and_text | ||
| CardType int `json:"card_type"` | ||
| // Project type: 1=individual, 2=repository, 3=organization | ||
| Type int `json:"type"` | ||
| // Number of open issues | ||
| NumOpenIssues int64 `json:"num_open_issues,omitempty"` | ||
| // Number of closed issues | ||
| NumClosedIssues int64 `json:"num_closed_issues,omitempty"` | ||
| // Total number of issues | ||
| NumIssues int64 `json:"num_issues,omitempty"` | ||
| // Created time | ||
| // swagger:strfmt date-time | ||
| Created time.Time `json:"created"` | ||
| // Updated time | ||
| // swagger:strfmt date-time | ||
| Updated time.Time `json:"updated"` | ||
| // Closed time | ||
| // swagger:strfmt date-time | ||
| ClosedDate *time.Time `json:"closed_date,omitempty"` | ||
| // Project URL | ||
| URL string `json:"url,omitempty"` | ||
| } | ||
|
|
||
| // CreateProjectOption represents options for creating a project | ||
| // swagger:model | ||
| type CreateProjectOption struct { | ||
| // required: true | ||
| Title string `json:"title" binding:"Required"` | ||
| // Project description | ||
| Description string `json:"description"` | ||
| // Template type: 0=none, 1=basic_kanban, 2=bug_triage | ||
| TemplateType int `json:"template_type"` | ||
| // Card type: 0=text_only, 1=images_and_text | ||
| CardType int `json:"card_type"` | ||
| } | ||
|
|
||
| // EditProjectOption represents options for editing a project | ||
| // swagger:model | ||
| type EditProjectOption struct { | ||
| // Project title | ||
| Title *string `json:"title,omitempty"` | ||
| // Project description | ||
| Description *string `json:"description,omitempty"` | ||
| // Card type: 0=text_only, 1=images_and_text | ||
| CardType *int `json:"card_type,omitempty"` | ||
| // State of the project (open or closed) | ||
| State *string `json:"state,omitempty"` | ||
| } | ||
|
|
||
| // ProjectColumn represents a project column (board) | ||
| // swagger:model | ||
| type ProjectColumn struct { | ||
| // Unique identifier of the column | ||
| ID int64 `json:"id"` | ||
| // Column title | ||
| Title string `json:"title"` | ||
| // Whether this is the default column | ||
| Default bool `json:"default"` | ||
| // Sorting order | ||
| Sorting int `json:"sorting"` | ||
| // Column color (hex format) | ||
| Color string `json:"color,omitempty"` | ||
| // Project ID | ||
| ProjectID int64 `json:"project_id"` | ||
| // Creator ID | ||
| CreatorID int64 `json:"creator_id"` | ||
| // Number of issues in this column | ||
| NumIssues int64 `json:"num_issues,omitempty"` | ||
| // Created time | ||
| // swagger:strfmt date-time | ||
| Created time.Time `json:"created"` | ||
| // Updated time | ||
| // swagger:strfmt date-time | ||
| Updated time.Time `json:"updated"` | ||
| } | ||
|
|
||
| // CreateProjectColumnOption represents options for creating a project column | ||
| // swagger:model | ||
| type CreateProjectColumnOption struct { | ||
| // required: true | ||
| Title string `json:"title" binding:"Required"` | ||
| // Column color (hex format, e.g., #FF0000) | ||
| Color string `json:"color,omitempty"` | ||
| } | ||
|
|
||
| // EditProjectColumnOption represents options for editing a project column | ||
| // swagger:model | ||
| type EditProjectColumnOption struct { | ||
| // Column title | ||
| Title *string `json:"title,omitempty"` | ||
| // Column color (hex format) | ||
| Color *string `json:"color,omitempty"` | ||
| // Sorting order | ||
| Sorting *int `json:"sorting,omitempty"` | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.