Add project board card move API
This commit is contained in:
@@ -1564,6 +1564,7 @@ func Routes() *web.Route {
|
|||||||
Get(projects.ListProjectBoards).
|
Get(projects.ListProjectBoards).
|
||||||
Post(bind(api.NewProjectBoardPayload{}), projects.CreateProjectBoard)
|
Post(bind(api.NewProjectBoardPayload{}), projects.CreateProjectBoard)
|
||||||
m.Get("/boards/{boardId}/cards", projects.ListProjectBoardCards)
|
m.Get("/boards/{boardId}/cards", projects.ListProjectBoardCards)
|
||||||
|
m.Post("/boards/{boardId}/cards/{cardId}/move", projects.MoveProjectBoardCard)
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Group("/boards", func() {
|
m.Group("/boards", func() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package projects
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
project_model "code.gitea.io/gitea/models/project"
|
project_model "code.gitea.io/gitea/models/project"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
@@ -144,6 +145,81 @@ func ListProjectBoardCards(ctx *context.APIContext) {
|
|||||||
ctx.JSON(http.StatusOK, cards)
|
ctx.JSON(http.StatusOK, cards)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MoveProjectBoardCard(ctx *context.APIContext) {
|
||||||
|
// swagger:operation POST /projects/{projectId}/boards/{boardId}/cards/{cardId}/move board boardMoveProjectBoardCard
|
||||||
|
// ---
|
||||||
|
// summary: Move project board card to another board
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: projectId
|
||||||
|
// in: path
|
||||||
|
// description: id of the project
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: boardId
|
||||||
|
// in: path
|
||||||
|
// description: id of the target board
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: cardId
|
||||||
|
// in: path
|
||||||
|
// description: internal id of the issue card to move
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "description": "Project board card moved"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
projectID := ctx.ParamsInt64(":projectId")
|
||||||
|
project, err := project_model.GetProjectByID(ctx, projectID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusNotFound, "MoveProjectBoardCard", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
boardID := ctx.ParamsInt64(":boardId")
|
||||||
|
board, err := project_model.GetBoard(ctx, boardID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusNotFound, "GetProjectBoard", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if board.ProjectID != project.ID {
|
||||||
|
ctx.NotFound("BoardNotInProject", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cardID := ctx.ParamsInt64(":cardId")
|
||||||
|
has, err := db.GetEngine(ctx).
|
||||||
|
Where("project_id=? AND issue_id=?", project.ID, cardID).
|
||||||
|
Get(new(project_model.ProjectIssue))
|
||||||
|
if err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
ctx.NotFound("ProjectCardNotFound", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxSorting int64
|
||||||
|
if _, err := db.GetEngine(ctx).
|
||||||
|
SQL("SELECT COALESCE(MAX(sorting), 0) FROM project_issue WHERE project_id = ? AND project_board_id = ?", project.ID, board.ID).
|
||||||
|
Get(&maxSorting); err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := project_model.MoveIssuesOnProjectBoard(ctx, board, map[int64]int64{maxSorting + 1: cardID}); err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateProjectBoard(ctx *context.APIContext) {
|
func CreateProjectBoard(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /projects/{projectId}/boards board boardCreateProjectBoard
|
// swagger:operation POST /projects/{projectId}/boards board boardCreateProjectBoard
|
||||||
// ---
|
// ---
|
||||||
|
|||||||
85
templates/swagger/v1_json.tmpl
generated
85
templates/swagger/v1_json.tmpl
generated
@@ -3227,6 +3227,91 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/projects/{projectId}/boards/{boardId}/cards": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"board"
|
||||||
|
],
|
||||||
|
"summary": "Get project board cards",
|
||||||
|
"operationId": "boardGetProjectBoardCards",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "id of the project",
|
||||||
|
"name": "projectId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "id of the board",
|
||||||
|
"name": "boardId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Project board cards"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"$ref": "#/responses/notFound"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/projects/{projectId}/boards/{boardId}/cards/{cardId}/move": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"board"
|
||||||
|
],
|
||||||
|
"summary": "Move project board card to another board",
|
||||||
|
"operationId": "boardMoveProjectBoardCard",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "id of the project",
|
||||||
|
"name": "projectId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "id of the target board",
|
||||||
|
"name": "boardId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "internal id of the issue card to move",
|
||||||
|
"name": "cardId",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "Project board card moved"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"$ref": "#/responses/notFound"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/repos/issues/search": {
|
"/repos/issues/search": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
|
|||||||
@@ -51,6 +51,42 @@ func TestAPIListProjectBoards(t *testing.T) {
|
|||||||
assert.Len(t, apiProjectBoards, 4)
|
assert.Len(t, apiProjectBoards, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIMoveProjectBoardCard(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue)
|
||||||
|
link, _ := url.Parse("/api/v1/projects/1/boards/2/cards/1/move")
|
||||||
|
|
||||||
|
req := NewRequest(t, "POST", link.String()).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
|
||||||
|
projectIssue := unittest.AssertExistsAndLoadBean(t, &project_model.ProjectIssue{
|
||||||
|
ProjectID: 1,
|
||||||
|
IssueID: 1,
|
||||||
|
})
|
||||||
|
assert.EqualValues(t, 2, projectIssue.ProjectBoardID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIMoveProjectBoardCardRejectsWrongProjectBoard(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue)
|
||||||
|
link, _ := url.Parse("/api/v1/projects/1/boards/4/cards/1/move")
|
||||||
|
|
||||||
|
req := NewRequest(t, "POST", link.String()).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIMoveProjectBoardCardRejectsCardOutsideProject(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue)
|
||||||
|
link, _ := url.Parse("/api/v1/projects/1/boards/2/cards/4/move")
|
||||||
|
|
||||||
|
req := NewRequest(t, "POST", link.String()).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPIGetProjectBoard(t *testing.T) {
|
func TestAPIGetProjectBoard(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user