Skip to content
Open
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
18 changes: 15 additions & 3 deletions models/fixtures/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
is_closed: false
creator_id: 2
board_type: 1
type: 2
type: 1
created_unix: 1688973000
updated_unix: 1688973000

Expand All @@ -54,7 +54,7 @@
is_closed: false
creator_id: 2
board_type: 1
type: 2
type: 1
created_unix: 1688973000
updated_unix: 1688973000

Expand All @@ -66,6 +66,18 @@
is_closed: false
creator_id: 2
board_type: 1
type: 2
type: 1
created_unix: 1688973000
updated_unix: 1688973000

-
id: 7
title: project on org17
owner_id: 17
repo_id: 0
is_closed: false
creator_id: 2
board_type: 1
type: 3
created_unix: 1754044020
updated_unix: 1754044020
14 changes: 14 additions & 0 deletions models/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ const (
TypeOrganization
)

// ToString returns the string representation of the project type
func (t Type) ToString() string {
switch t {
case TypeIndividual:
return "individual"
case TypeRepository:
return "repository"
case TypeOrganization:
return "organization"
default:
return "unknown"
}
}

// ErrProjectNotExist represents a "ProjectNotExist" kind of error.
type ErrProjectNotExist struct {
ID int64
Expand Down
1 change: 1 addition & 0 deletions modules/structs/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ type Project struct {
Repo *RepositoryMeta `json:"repository"`
Creator *User `json:"creator"`
Owner *User `json:"owner"`
Type string `json:"type"`
}
7 changes: 4 additions & 3 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,7 @@ func Routes() *web.Router {
m.Methods("HEAD,GET", "/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), repo.DownloadArchive)

m.Group("/projects", func() {
m.Get("", projects.ListUserProjects)
m.Post("", bind(api.NewProjectOption{}), projects.CreateRepoProject)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryProject))
}, repoAssignment(), checkTokenPublicOnly())
Expand Down Expand Up @@ -1728,9 +1729,9 @@ func Routes() *web.Router {

// 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)
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() {
Expand Down
17 changes: 11 additions & 6 deletions routers/api/v1/projects/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func GetProject(ctx *context.APIContext) {
// produces:
// - application/json
// parameters:
// - name: id
// - name: project_id
// in: path
// description: id of the project
// type: string
Expand All @@ -158,7 +158,7 @@ func GetProject(ctx *context.APIContext) {
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
project, err := project_model.GetProjectByID(ctx, ctx.FormInt64("project_id"))
project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("project_id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
ctx.APIError(http.StatusNotFound, err)
Expand All @@ -185,7 +185,7 @@ func UpdateProject(ctx *context.APIContext) {
// consumes:
// - application/json
// parameters:
// - name: id
// - name: project_id
// in: path
// description: id of the project
// type: string
Expand All @@ -202,7 +202,7 @@ func UpdateProject(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.UpdateProjectOption)
project, err := project_model.GetProjectByID(ctx, ctx.FormInt64("project_id"))
project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("project_id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
ctx.APIError(http.StatusNotFound, err)
Expand Down Expand Up @@ -236,7 +236,7 @@ func DeleteProject(ctx *context.APIContext) {
// ---
// summary: Delete project
// parameters:
// - name: id
// - name: project_id
// in: path
// description: id of the project
// type: string
Expand All @@ -249,7 +249,7 @@ func DeleteProject(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"

if err := project_model.DeleteProjectByID(ctx, ctx.FormInt64("project_id")); err != nil {
if err := project_model.DeleteProjectByID(ctx, ctx.PathParamInt64("project_id")); err != nil {
ctx.APIErrorInternal(err)
return
}
Expand All @@ -264,6 +264,11 @@ func ListUserProjects(ctx *context.APIContext) {
// produces:
// - application/json
// parameters:
// - name: user
// in: path
// description: username of user
// type: string
// required: true
// - name: closed
// in: query
// description: include closed projects or not
Expand Down
2 changes: 2 additions & 0 deletions services/convert/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import (

func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Project, error) {
apiProject := &api.Project{
ID: project.ID,
Name: project.Title,
Body: project.Description,
TemplateType: project.TemplateType.ToString(),
State: util.Iif(project.IsClosed, "closed", "open"),
Created: project.CreatedUnix.AsTime(),
Updated: project.UpdatedUnix.AsTime(),
Type: project.Type.ToString(),
}
if !project.ClosedDateUnix.IsZero() {
tm := project.ClosedDateUnix.AsTime()
Expand Down
17 changes: 14 additions & 3 deletions templates/swagger/v1_json.tmpl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 26 additions & 16 deletions tests/integration/api_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestAPICreateOrgProject(t *testing.T) {
templateType := project_model.TemplateTypeBasicKanban.ToString()

orgName := "org17"
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization)
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteProject)
urlStr := fmt.Sprintf("/api/v1/orgs/%s/projects", orgName)

req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectOption{
Expand All @@ -70,7 +70,7 @@ func TestAPICreateRepoProject(t *testing.T) {

ownerName := "user2"
repoName := "repo1"
token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization)
token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteProject)
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/projects", ownerName, repoName)

req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectOption{
Expand All @@ -90,15 +90,15 @@ func TestAPICreateRepoProject(t *testing.T) {
func TestAPIListUserProjects(t *testing.T) {
defer tests.PrepareTestEnv(t)()

token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadIssue)
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopeReadProject)
link, _ := url.Parse("/api/v1/users/user2/projects")

req := NewRequest(t, "GET", link.String()).AddTokenAuth(token)
var apiProjects []*api.Project

resp := MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiProjects)
assert.Len(t, apiProjects, 1)
assert.Len(t, apiProjects, 3)
}

func TestAPIListOrgProjects(t *testing.T) {
Expand All @@ -121,36 +121,43 @@ func TestAPIListRepoProjects(t *testing.T) {

ownerName := "user2"
repoName := "repo1"
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository, auth_model.AccessTokenScopeReadIssue)
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository, auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopeReadProject)
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/projects", ownerName, repoName))

req := NewRequest(t, "GET", link.String()).AddTokenAuth(token)
var apiProjects []*api.Project

resp := MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiProjects)
assert.Len(t, apiProjects, 1)
assert.Len(t, apiProjects, 3)
}

func TestAPIGetProject(t *testing.T) {
defer tests.PrepareTestEnv(t)()
token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadProject)
link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 4))

project_id := 4
user := "user2"
token := getUserToken(t, user, auth_model.AccessTokenScopeReadProject)
link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", project_id))

req := NewRequest(t, "GET", link.String()).AddTokenAuth(token)
var apiProject *api.Project

resp := MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiProject)
assert.Equal(t, "First project", apiProject.Name)
assert.Equal(t, "repo1", apiProject.Repo.Name)
assert.Equal(t, "user2", apiProject.Creator.UserName)
assert.Equal(t, "project on user2", apiProject.Name)
assert.Equal(t, "individual", apiProject.Type)
assert.Equal(t, user, apiProject.Creator.UserName)
assert.EqualValues(t, project_id, apiProject.ID)
}

func TestAPIUpdateProject(t *testing.T) {
defer tests.PrepareTestEnv(t)()
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteProject)
link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 4))

project_id := 4
user := "user2"
token := getUserToken(t, user, auth_model.AccessTokenScopeWriteProject)
link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", project_id))

req := NewRequestWithJSON(t, "PATCH", link.String(), &api.UpdateProjectOption{Name: "First project updated"}).AddTokenAuth(token)

Expand All @@ -163,11 +170,14 @@ func TestAPIUpdateProject(t *testing.T) {

func TestAPIDeleteProject(t *testing.T) {
defer tests.PrepareTestEnv(t)()
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteProject)
link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 4))

project_id := 4
user := "user2"
token := getUserToken(t, user, auth_model.AccessTokenScopeWriteProject)
link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", project_id))

req := NewRequest(t, "DELETE", link.String()).AddTokenAuth(token)

MakeRequest(t, req, http.StatusNoContent)
unittest.AssertNotExistsBean(t, &project_model.Project{ID: 1})
unittest.AssertNotExistsBean(t, &project_model.Project{ID: int64(project_id)})
}
Loading