Add project board card move API
This commit is contained in:
@@ -1564,6 +1564,7 @@ func Routes() *web.Route {
|
||||
Get(projects.ListProjectBoards).
|
||||
Post(bind(api.NewProjectBoardPayload{}), projects.CreateProjectBoard)
|
||||
m.Get("/boards/{boardId}/cards", projects.ListProjectBoardCards)
|
||||
m.Post("/boards/{boardId}/cards/{cardId}/move", projects.MoveProjectBoardCard)
|
||||
})
|
||||
|
||||
m.Group("/boards", func() {
|
||||
|
||||
@@ -6,6 +6,7 @@ package projects
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@@ -144,6 +145,81 @@ func ListProjectBoardCards(ctx *context.APIContext) {
|
||||
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) {
|
||||
// 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": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
||||
@@ -51,6 +51,42 @@ func TestAPIListProjectBoards(t *testing.T) {
|
||||
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) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user