Skip to content

feat: api endpoints for projects #28111

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
wants to merge 69 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
4c0ec22
test: add 2 fixtures for project
dineshsalunke Nov 18, 2023
6092b81
refactor: add structs for project
dineshsalunke Nov 18, 2023
b6f9d05
feat: api for projects
dineshsalunke Nov 18, 2023
b6d45c6
Merge branch 'main' into feat/api-projects
dineshsalunke Nov 20, 2023
922cd13
Merge remote-tracking branch 'upstream/main' into feat/api-projects
dineshsalunke Nov 20, 2023
0b8d198
Merge branch 'feat/api-projects' of https://github.com/dineshsalunke/…
dineshsalunke Nov 20, 2023
e0ae281
Merge branch 'main' into feat/api-projects
GiteaBot Jan 15, 2024
a0f8dbd
chore: remove unnecessary go lines formatting
dineshsalunke Jan 16, 2024
ac39273
Merge branch 'feat/api-projects' of https://github.com/dineshsalunke/…
dineshsalunke Jan 16, 2024
df22b6d
chore: remove the goline formatting
dineshsalunke Jan 16, 2024
5bbbade
chore: remove unwanted indentation on the line
dineshsalunke Jan 16, 2024
c266568
Merge remote-tracking branch 'upstream/main' into feat/api-projects
dineshsalunke Jan 17, 2024
aa32ad7
refactor: update for db find method usage
dineshsalunke Jan 17, 2024
81c3d0c
fix: add missing swagger tags
dineshsalunke Jan 17, 2024
852c0df
chore: fix lint issues
dineshsalunke Jan 17, 2024
dbfecce
fix: return error when converting project to api project
dineshsalunke Jan 17, 2024
fec855e
chore: fix swagger documentation
dineshsalunke Jan 17, 2024
6238b9e
fix: ignore errors when loading data for converting project to response
dineshsalunke Jan 17, 2024
8819a0a
refactor: use the add token auth method for token in tests
dineshsalunke Jan 17, 2024
3b2943a
test: use the add token auth method for token
dineshsalunke Jan 17, 2024
cf17f34
Merge remote-tracking branch 'upstream/main' into feat/api-projects
dineshsalunke Jan 19, 2024
7eebc7c
Merge remote-tracking branch 'upstream/main' into feat/api-projects
dineshsalunke Jan 20, 2024
d2fd138
test: use the correct fields for the test case
dineshsalunke Jan 20, 2024
34566ea
Merge branch 'main' into feat/api-projects
denyskon Jan 21, 2024
897c67b
revert formatting changes
denyskon Jan 21, 2024
c7440bc
Merge branch 'main' into feat/api-projects
denyskon Jan 21, 2024
065001c
more reverts
denyskon Jan 21, 2024
24babd6
Merge branch 'feat/api-projects' of github.com:dineshsalunke/gitea in…
denyskon Jan 21, 2024
58e56cd
more formatting fixes
denyskon Jan 21, 2024
74043f7
fix project sort test
denyskon Jan 21, 2024
fac47d4
Merge remote-tracking branch 'upstream/main' into feat/api-projects
dineshsalunke Jan 22, 2024
4475c4f
actually fix test!
denyskon Jan 22, 2024
e751d6c
Merge branch 'feat/api-projects' of github.com:dineshsalunke/gitea in…
denyskon Jan 22, 2024
6a18cbe
Merge branch 'main' into feat/api-projects
denyskon Jan 25, 2024
927da10
chore: swagger doc typo corrections
dineshsalunke Jan 26, 2024
975d98d
chore: remove redundant comment
dineshsalunke Jan 26, 2024
d4ea5a5
fix: use the page field from parameters
dineshsalunke Jan 26, 2024
24a302a
Merge branch 'feat/api-projects' of https://github.com/dineshsalunke/…
dineshsalunke Jan 26, 2024
c5c41e3
Merge remote-tracking branch 'upstream/main' into feat/api-projects
dineshsalunke Jan 26, 2024
0a0837f
chore: update the swagger file
dineshsalunke Jan 26, 2024
23d9697
Merge branch 'main' into feat/api-projects
denyskon Jan 27, 2024
a2a01df
Merge branch 'main' into feat/api-projects
denyskon Feb 5, 2024
b931054
Merge remote-tracking branch 'upstream/main' into feat/api-projects
Sep 22, 2024
a7aabc5
refactor: add the missing parameter bodies for project
Sep 22, 2024
7383e7b
refactor: rename BoardType to TemplateType
Sep 22, 2024
016aa72
refactor: add creator property to the project
Sep 22, 2024
d067b1d
refactor: add BoardType to TemplateType property
Sep 22, 2024
eeca89e
refactor: handle errors when converting to APIProject
Sep 22, 2024
20e9c58
Merge remote-tracking branch 'upstream/main' into feat/api-projects
dineshsalunke Sep 23, 2024
185a594
refactor: update the context
dineshsalunke Sep 23, 2024
30027ac
refactor: update the board type enums
dineshsalunke Sep 23, 2024
68ea867
Merge branch 'main' of https://github.com/go-gitea/gitea into feat/ap…
dineshsalunke Jun 24, 2025
596dcc0
refactor: return ApiError instead of Error from projects methods
dineshsalunke Jun 24, 2025
fbe7e8e
refactor: create projects method for usr, org and repo
dineshsalunke Jun 24, 2025
20a697e
Fix formatting and swagger
denyskon Jun 26, 2025
d0f7900
Merge remote-tracking branch 'upstream/main' into feat/api-projects
denyskon Jun 26, 2025
d746668
Merge branch 'main' into feat/api-projects
lunny Jul 6, 2025
dd7edfb
refactor: add missing middleware
dineshsalunke Jul 6, 2025
8358eef
refactor: add list projects get method
dineshsalunke Jul 6, 2025
ef3fdec
chore: merge upstream
dineshsalunke Jul 6, 2025
de2ab05
Merge branch 'main' of https://github.com/go-gitea/gitea into feat/ap…
dineshsalunke Jul 6, 2025
c29a6b7
Merge branch 'feat/api-projects' of https://github.com/dineshsalunke/…
dineshsalunke Jul 6, 2025
9c4937f
some renames
lunny Jul 6, 2025
1faa7e8
Merge branch 'feat/api-projects' of github.com:dineshsalunke/gitea in…
lunny Jul 6, 2025
899efca
improvements
lunny Jul 6, 2025
0df053f
add token scope
lunny Jul 6, 2025
19ed830
Fix unit test
lunny Jul 6, 2025
7235f0e
Fix unit test
lunny Jul 6, 2025
5a6a10e
update swagger template
lunny Jul 6, 2025
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 models/auth/access_token_scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
AccessTokenScopeCategoryIssue
AccessTokenScopeCategoryRepository
AccessTokenScopeCategoryUser
AccessTokenScopeCategoryProject
)

// AllAccessTokenScopeCategories contains all access token scope categories
Expand All @@ -37,6 +38,7 @@ var AllAccessTokenScopeCategories = []AccessTokenScopeCategory{
AccessTokenScopeCategoryIssue,
AccessTokenScopeCategoryRepository,
AccessTokenScopeCategoryUser,
AccessTokenScopeCategoryProject,
}

// AccessTokenScopeLevel represents the access levels without a given scope category
Expand Down Expand Up @@ -82,6 +84,9 @@ const (

AccessTokenScopeReadUser AccessTokenScope = "read:user"
AccessTokenScopeWriteUser AccessTokenScope = "write:user"

AccessTokenScopeReadProject AccessTokenScope = "read:project"
AccessTokenScopeWriteProject AccessTokenScope = "write:project"
)

// accessTokenScopeBitmap represents a bitmap of access token scopes.
Expand Down Expand Up @@ -124,6 +129,9 @@ const (
accessTokenScopeReadUserBits accessTokenScopeBitmap = 1 << iota
accessTokenScopeWriteUserBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadUserBits

accessTokenScopeReadProjectBits accessTokenScopeBitmap = 1 << iota
accessTokenScopeWriteProjectBits accessTokenScopeBitmap = 1<<iota | accessTokenScopeReadProjectBits

// The current implementation only supports up to 64 token scopes.
// If we need to support > 64 scopes,
// refactoring the whole implementation in this file (and only this file) is needed.
Expand All @@ -142,6 +150,7 @@ var allAccessTokenScopes = []AccessTokenScope{
AccessTokenScopeWriteIssue, AccessTokenScopeReadIssue,
AccessTokenScopeWriteRepository, AccessTokenScopeReadRepository,
AccessTokenScopeWriteUser, AccessTokenScopeReadUser,
AccessTokenScopeWriteProject, AccessTokenScopeReadProject,
}

// allAccessTokenScopeBits contains all access token scopes.
Expand All @@ -166,6 +175,8 @@ var allAccessTokenScopeBits = map[AccessTokenScope]accessTokenScopeBitmap{
AccessTokenScopeWriteRepository: accessTokenScopeWriteRepositoryBits,
AccessTokenScopeReadUser: accessTokenScopeReadUserBits,
AccessTokenScopeWriteUser: accessTokenScopeWriteUserBits,
AccessTokenScopeReadProject: accessTokenScopeReadProjectBits,
AccessTokenScopeWriteProject: accessTokenScopeWriteProjectBits,
}

// readAccessTokenScopes maps a scope category to the read permission scope
Expand All @@ -180,6 +191,7 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
AccessTokenScopeCategoryIssue: AccessTokenScopeReadIssue,
AccessTokenScopeCategoryRepository: AccessTokenScopeReadRepository,
AccessTokenScopeCategoryUser: AccessTokenScopeReadUser,
AccessTokenScopeCategoryProject: AccessTokenScopeReadProject,
},
Write: {
AccessTokenScopeCategoryActivityPub: AccessTokenScopeWriteActivityPub,
Expand All @@ -191,6 +203,7 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
AccessTokenScopeCategoryIssue: AccessTokenScopeWriteIssue,
AccessTokenScopeCategoryRepository: AccessTokenScopeWriteRepository,
AccessTokenScopeCategoryUser: AccessTokenScopeWriteUser,
AccessTokenScopeCategoryProject: AccessTokenScopeWriteProject,
},
}

Expand Down
2 changes: 1 addition & 1 deletion models/auth/access_token_scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type scopeTestNormalize struct {
}

func TestAccessTokenScope_Normalize(t *testing.T) {
assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories())
assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "project", "repository", "user"}, GetAccessTokenCategories())
tests := []scopeTestNormalize{
{"", "", nil},
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
Expand Down
22 changes: 22 additions & 0 deletions models/project/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ const (
CardTypeImagesAndText
)

func (p CardType) ToString() string {
switch p {
case CardTypeImagesAndText:
return "ImagesAndText"
case CardTypeTextOnly:
fallthrough
default:
return "TextOnly"
}
}

func ToCardType(s string) CardType {
switch s {
case "ImagesAndText":
return CardTypeImagesAndText
case "TextOnly":
fallthrough
default:
return CardTypeTextOnly
}
}

// ColumnColorPattern is a regexp witch can validate ColumnColor
var ColumnColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")

Expand Down
9 changes: 9 additions & 0 deletions models/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type Project struct {
RepoID int64 `xorm:"INDEX"`
Repo *repo_model.Repository `xorm:"-"`
CreatorID int64 `xorm:"NOT NULL"`
Creator *user_model.User `xorm:"-"`
IsClosed bool `xorm:"INDEX"`
TemplateType TemplateType `xorm:"'board_type'"` // TODO: rename the column to template_type
CardType CardType
Expand Down Expand Up @@ -121,6 +122,14 @@ func (p *Project) LoadOwner(ctx context.Context) (err error) {
return err
}

func (p *Project) LoadCreator(ctx context.Context) (err error) {
if p.Creator != nil {
return nil
}
p.Creator, err = user_model.GetUserByID(ctx, p.CreatorID)
return err
}

func (p *Project) LoadRepo(ctx context.Context) (err error) {
if p.RepoID == 0 || p.Repo != nil {
return nil
Expand Down
25 changes: 25 additions & 0 deletions models/project/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,31 @@ const (
TemplateTypeBugTriage
)

func (p TemplateType) ToString() string {
switch p {
case TemplateTypeBasicKanban:
return "BasicKanban"
case TemplateTypeBugTriage:
return "BugTriage"
case TemplateTypeNone:
fallthrough
default:
return ""
}
}

// ToTemplateType converts a string to a TemplateType
func ToTemplateType(s string) TemplateType {
switch s {
case "BasicKanban":
return TemplateTypeBasicKanban
case "BugTriage":
return TemplateTypeBugTriage
default:
return TemplateTypeNone
}
}

// GetTemplateConfigs retrieves the template configs of configurations project columns could have
func GetTemplateConfigs() []TemplateConfig {
return []TemplateConfig{
Expand Down
59 changes: 59 additions & 0 deletions modules/structs/project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package structs

import "time"

// NewProjectOption options when creating a new project
// swagger:model
type NewProjectOption struct {
// required:true
// Keep compatibility with Github API to use "name" instead of "title"
Name string `json:"name" binding:"Required"`
// required:true
// enum: , BasicKanban, BugTriage
// Note: this is the same as TemplateType in models/project/template.go
TemplateType string `json:"template_type"`
// required:true
// enum: TextOnly, ImagesAndText
CardType string `json:"card_type"`
// Keep compatibility with Github API to use "body" instead of "description"
Body string `json:"body"`
}

// UpdateProjectOption options when updating a project
// swagger:model
type UpdateProjectOption struct {
// required:true
// Keep compatibility with Github API to use "name" instead of "title"
Name string `json:"name" binding:"Required"`
// Keep compatibility with Github API to use "body" instead of "description"
Body string `json:"body"`
}

// Project represents a project
// swagger:model
type Project struct {
ID int64 `json:"id"`
// Keep compatibility with Github API to use "name" instead of "title"
Name string `json:"name"`
// Keep compatibility with Github API to use "body" instead of "description"
Body string `json:"body"`
// required:true
// enum: , BasicKanban, BugTriage
// Note: this is the same as TemplateType in models/project/template.go
TemplateType string `json:"template_type"`
// enum: open, closed
State string `json:"state"`
// swagger:strfmt date-time
Created time.Time `json:"created_at"`
// swagger:strfmt date-time
Updated time.Time `json:"updated_at"`
// swagger:strfmt date-time
Closed *time.Time `json:"closed_at"`

Repo *RepositoryMeta `json:"repository"`
Creator *User `json:"creator"`
Owner *User `json:"owner"`
}
24 changes: 24 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import (
"code.gitea.io/gitea/routers/api/v1/notify"
"code.gitea.io/gitea/routers/api/v1/org"
"code.gitea.io/gitea/routers/api/v1/packages"
"code.gitea.io/gitea/routers/api/v1/projects"
"code.gitea.io/gitea/routers/api/v1/repo"
"code.gitea.io/gitea/routers/api/v1/settings"
"code.gitea.io/gitea/routers/api/v1/user"
Expand Down Expand Up @@ -1041,6 +1042,13 @@ func Routes() *web.Router {

m.Get("/subscriptions", user.GetWatchedRepos)
}, context.UserAssignmentAPI(), checkTokenPublicOnly())

m.Group("/{username}", func() {
m.Group("/projects", func() {
m.Get("", projects.ListUserProjects)
m.Post("", bind(api.NewProjectOption{}), projects.CreateUserProject)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryProject))
}, context.UserAssignmentAPI())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())

// Users (requires user scope)
Expand Down Expand Up @@ -1467,6 +1475,10 @@ func Routes() *web.Router {
}, reqAdmin(), reqToken())

m.Methods("HEAD,GET", "/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), repo.DownloadArchive)

m.Group("/projects", func() {
m.Post("", bind(api.NewProjectOption{}), projects.CreateRepoProject)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryProject))
}, repoAssignment(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))

Expand Down Expand Up @@ -1687,6 +1699,11 @@ func Routes() *web.Router {
m.Delete("", org.UnblockUser)
})
}, reqToken(), reqOrgOwnership())

m.Group("/projects", func() {
m.Post("", bind(api.NewProjectOption{}), projects.CreateOrgProject)
m.Get("", projects.ListOrgProjects)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryProject))
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly())
m.Group("/teams/{teamid}", func() {
m.Combo("").Get(reqToken(), org.GetTeam).
Expand All @@ -1709,6 +1726,13 @@ func Routes() *web.Router {
m.Get("/activities/feeds", org.ListTeamActivityFeeds)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership(), checkTokenPublicOnly())

// Projects
m.Group("/projects", func() {
m.Get("{project_id}", projects.GetProject)
m.Patch("{project_id}", bind(api.UpdateProjectOption{}), projects.UpdateProject)
m.Delete("{project_id}", projects.DeleteProject)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryProject), reqToken())

m.Group("/admin", func() {
m.Group("/cron", func() {
m.Get("", admin.ListCronTasks)
Expand Down
Loading
Loading