Add endpoint to get a single task (#106)
continuous-integration/drone/push Build is passing Details

This commit is contained in:
konrad 2019-11-02 20:33:18 +00:00
parent ed4c17892e
commit 9be5ab248c
8 changed files with 136 additions and 191 deletions

View File

@ -330,9 +330,18 @@ type LabelTaskBulk struct {
// @Failure 500 {object} models.Message "Internal error" // @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{taskID}/labels/bulk [post] // @Router /tasks/{taskID}/labels/bulk [post]
func (ltb *LabelTaskBulk) Create(a web.Auth) (err error) { func (ltb *LabelTaskBulk) Create(a web.Auth) (err error) {
task, err := GetTaskByID(ltb.TaskID) task, err := GetTaskByIDSimple(ltb.TaskID)
if err != nil { if err != nil {
return return
} }
labels, _, _, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
TaskIDs: []int64{ltb.TaskID},
})
if err != nil {
return err
}
for _, l := range labels {
task.Labels = append(task.Labels, &l.Label)
}
return task.updateTaskLabels(a, ltb.Labels) return task.updateTaskLabels(a, ltb.Labels)
} }

View File

@ -284,9 +284,17 @@ type BulkAssignees struct {
// @Failure 500 {object} models.Message "Internal error" // @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/{taskID}/assignees/bulk [post] // @Router /tasks/{taskID}/assignees/bulk [post]
func (ba *BulkAssignees) Create(a web.Auth) (err error) { func (ba *BulkAssignees) Create(a web.Auth) (err error) {
task, err := GetTaskByID(ba.TaskID) // We need to use the full method here because we need all current assignees. task, err := GetTaskByIDSimple(ba.TaskID)
if err != nil { if err != nil {
return return
} }
assignees, err := getRawTaskAssigneesForTasks([]int64{task.ID})
if err != nil {
return err
}
for _, a := range assignees {
task.Assignees = append(task.Assignees, &a.User)
}
return task.updateTaskAssignees(ba.Assignees) return task.updateTaskAssignees(ba.Assignees)
} }

View File

@ -339,44 +339,6 @@ func GetTaskSimple(t *Task) (task Task, err error) {
return return
} }
// GetTaskByID returns all tasks a list has
func GetTaskByID(listTaskID int64) (listTask Task, err error) {
listTask, err = GetTaskByIDSimple(listTaskID)
if err != nil {
return
}
u, err := GetUserByID(listTask.CreatedByID)
if err != nil {
return
}
listTask.CreatedBy = u
// Get assignees
taskAssignees, err := getRawTaskAssigneesForTasks([]int64{listTaskID})
if err != nil {
return
}
for _, u := range taskAssignees {
if u != nil {
listTask.Assignees = append(listTask.Assignees, &u.User)
}
}
// Get task labels
taskLabels, _, _, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
TaskIDs: []int64{listTaskID},
})
if err != nil {
return
}
for _, label := range taskLabels {
listTask.Labels = append(listTask.Labels, &label.Label)
}
return
}
// GetTasksByIDs returns all tasks for a list of ids // GetTasksByIDs returns all tasks for a list of ids
func (bt *BulkTask) GetTasksByIDs() (err error) { func (bt *BulkTask) GetTasksByIDs() (err error) {
for _, id := range bt.IDs { for _, id := range bt.IDs {
@ -636,7 +598,7 @@ func (t *Task) Create(a web.Auth) (err error) {
// @Router /tasks/{id} [post] // @Router /tasks/{id} [post]
func (t *Task) Update() (err error) { func (t *Task) Update() (err error) {
// Check if the task exists // Check if the task exists
ot, err := GetTaskByID(t.ID) ot, err := GetTaskByIDSimple(t.ID)
if err != nil { if err != nil {
return return
} }
@ -854,12 +816,6 @@ func (t *Task) updateReminders(reminders []int64) (err error) {
// @Router /tasks/{id} [delete] // @Router /tasks/{id} [delete]
func (t *Task) Delete() (err error) { func (t *Task) Delete() (err error) {
// Check if it exists
_, err = GetTaskByID(t.ID)
if err != nil {
return
}
if _, err = x.ID(t.ID).Delete(Task{}); err != nil { if _, err = x.ID(t.ID).Delete(Task{}); err != nil {
return err return err
} }
@ -874,3 +830,38 @@ func (t *Task) Delete() (err error) {
err = updateListLastUpdated(&List{ID: t.ListID}) err = updateListLastUpdated(&List{ID: t.ListID})
return return
} }
// ReadOne gets one task by its ID
// @Summary Get one task
// @Description Returns one task by its ID
// @tags task
// @Accept json
// @Produce json
// @Param ID path int true "The task ID"
// @Security JWTKeyAuth
// @Success 200 {object} models.Task "The task"
// @Failure 404 {object} models.Message "Task not found"
// @Failure 500 {object} models.Message "Internal error"
// @Router /tasks/all [get]
func (t *Task) ReadOne() (err error) {
taskMap := make(map[int64]*Task, 1)
taskMap[t.ID] = &Task{}
*taskMap[t.ID], err = GetTaskByIDSimple(t.ID)
if err != nil {
return
}
tasks, err := addMoreInfoToTasks(taskMap)
if err != nil {
return
}
if len(tasks) == 0 {
return ErrTaskDoesNotExist{t.ID}
}
*t = *tasks[0]
return
}

View File

@ -24,6 +24,8 @@ import (
func TestTask_Create(t *testing.T) { func TestTask_Create(t *testing.T) {
//assert.NoError(t, LoadFixtures()) //assert.NoError(t, LoadFixtures())
// TODO: This test needs refactoring
// Fake list task // Fake list task
listtask := Task{ listtask := Task{
Text: "Lorem", Text: "Lorem",
@ -48,11 +50,6 @@ func TestTask_Create(t *testing.T) {
err = listtask.Update() err = listtask.Update()
assert.NoError(t, err) assert.NoError(t, err)
// Check if it was updated
li, err := GetTaskByID(listtask.ID)
assert.NoError(t, err)
assert.Equal(t, li.Text, "Test34")
// Delete the task // Delete the task
allowed, _ = listtask.CanDelete(doer) allowed, _ = listtask.CanDelete(doer)
assert.True(t, allowed) assert.True(t, allowed)
@ -61,7 +58,7 @@ func TestTask_Create(t *testing.T) {
// Delete a nonexistant task // Delete a nonexistant task
listtask.ID = 0 listtask.ID = 0
err = listtask.Delete() _, err = listtask.CanDelete(doer) // The check if the task exists happens in CanDelete
assert.Error(t, err) assert.Error(t, err)
assert.True(t, IsErrTaskDoesNotExist(err)) assert.True(t, IsErrTaskDoesNotExist(err))
@ -106,3 +103,18 @@ func TestUpdateDone(t *testing.T) {
assert.Equal(t, int64(0), oldTask.DoneAtUnix) assert.Equal(t, int64(0), oldTask.DoneAtUnix)
}) })
} }
func TestTask_ReadOne(t *testing.T) {
t.Run("default", func(t *testing.T) {
task := &Task{ID: 1}
err := task.ReadOne()
assert.NoError(t, err)
assert.Equal(t, "task #1", task.Text)
})
t.Run("nonexisting", func(t *testing.T) {
task := &Task{ID: 99999}
err := task.ReadOne()
assert.Error(t, err)
assert.True(t, IsErrTaskDoesNotExist(err))
})
}

View File

@ -221,6 +221,7 @@ func registerAPIRoutes(a *echo.Group) {
}, },
} }
a.PUT("/lists/:list", taskHandler.CreateWeb) a.PUT("/lists/:list", taskHandler.CreateWeb)
a.GET("/tasks/:listtask", taskHandler.ReadOneWeb)
a.GET("/tasks/all", taskHandler.ReadAllWeb) a.GET("/tasks/all", taskHandler.ReadAllWeb)
a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb) a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb)
a.POST("/tasks/:listtask", taskHandler.UpdateWeb) a.POST("/tasks/:listtask", taskHandler.UpdateWeb)

View File

@ -1,6 +1,6 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at // This file was generated by swaggo/swag at
// 2019-10-23 23:00:23.451871583 +0200 CEST m=+0.120322599 // 2019-11-02 21:06:44.848486087 +0100 CET m=+0.175638002
package swagger package swagger
@ -407,7 +407,7 @@ var doc = `{
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns a list by its ID.", "description": "Returns a team by its ID.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -415,13 +415,13 @@ var doc = `{
"application/json" "application/json"
], ],
"tags": [ "tags": [
"list" "team"
], ],
"summary": "Gets one list", "summary": "Gets one team",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
"description": "List ID", "description": "Team ID",
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
@ -429,13 +429,13 @@ var doc = `{
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The list", "description": "The team",
"schema": { "schema": {
"$ref": "#/definitions/models.List" "$ref": "#/definitions/models.Team"
} }
}, },
"403": { "403": {
"description": "The user does not have access to the list", "description": "The user does not have access to the team",
"schema": { "schema": {
"$ref": "#/definitions/code.vikunja.io.web.HTTPError" "$ref": "#/definitions/code.vikunja.io.web.HTTPError"
} }
@ -2561,7 +2561,7 @@ var doc = `{
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns all tasks on any list the user has access to.", "description": "Returns one task by its ID",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -2571,53 +2571,27 @@ var doc = `{
"tags": [ "tags": [
"task" "task"
], ],
"summary": "Get tasks", "summary": "Get one task",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
"description": "The page number. Used for pagination. If not provided, the first page of results is returned.", "description": "The task ID",
"name": "page", "name": "ID",
"in": "query" "in": "path",
}, "required": true
{
"type": "integer",
"description": "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page.",
"name": "per_page",
"in": "query"
},
{
"type": "string",
"description": "Search tasks by task text.",
"name": "s",
"in": "query"
},
{
"type": "string",
"description": "The sorting parameter. Possible values to sort by are priority, prioritydesc, priorityasc, duedate, duedatedesc, duedateasc.",
"name": "sort",
"in": "query"
},
{
"type": "integer",
"description": "The start date parameter to filter by. Expects a unix timestamp. If no end date, but a start date is specified, the end date is set to the current time.",
"name": "startdate",
"in": "query"
},
{
"type": "integer",
"description": "The end date parameter to filter by. Expects a unix timestamp. If no start date, but an end date is specified, the start date is set to the current time.",
"name": "enddate",
"in": "query"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The tasks", "description": "The task",
"schema": { "schema": {
"type": "array", "$ref": "#/definitions/models.Task"
"items": { }
"$ref": "#/definitions/models.Task" },
} "404": {
"description": "Task not found",
"schema": {
"$ref": "#/definitions/models.Message"
} }
}, },
"500": { "500": {

View File

@ -389,7 +389,7 @@
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns a list by its ID.", "description": "Returns a team by its ID.",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -397,13 +397,13 @@
"application/json" "application/json"
], ],
"tags": [ "tags": [
"list" "team"
], ],
"summary": "Gets one list", "summary": "Gets one team",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
"description": "List ID", "description": "Team ID",
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
@ -411,13 +411,13 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The list", "description": "The team",
"schema": { "schema": {
"$ref": "#/definitions/models.List" "$ref": "#/definitions/models.Team"
} }
}, },
"403": { "403": {
"description": "The user does not have access to the list", "description": "The user does not have access to the team",
"schema": { "schema": {
"$ref": "#/definitions/code.vikunja.io/web.HTTPError" "$ref": "#/definitions/code.vikunja.io/web.HTTPError"
} }
@ -2543,7 +2543,7 @@
"JWTKeyAuth": [] "JWTKeyAuth": []
} }
], ],
"description": "Returns all tasks on any list the user has access to.", "description": "Returns one task by its ID",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],
@ -2553,53 +2553,27 @@
"tags": [ "tags": [
"task" "task"
], ],
"summary": "Get tasks", "summary": "Get one task",
"parameters": [ "parameters": [
{ {
"type": "integer", "type": "integer",
"description": "The page number. Used for pagination. If not provided, the first page of results is returned.", "description": "The task ID",
"name": "page", "name": "ID",
"in": "query" "in": "path",
}, "required": true
{
"type": "integer",
"description": "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page.",
"name": "per_page",
"in": "query"
},
{
"type": "string",
"description": "Search tasks by task text.",
"name": "s",
"in": "query"
},
{
"type": "string",
"description": "The sorting parameter. Possible values to sort by are priority, prioritydesc, priorityasc, duedate, duedatedesc, duedateasc.",
"name": "sort",
"in": "query"
},
{
"type": "integer",
"description": "The start date parameter to filter by. Expects a unix timestamp. If no end date, but a start date is specified, the end date is set to the current time.",
"name": "startdate",
"in": "query"
},
{
"type": "integer",
"description": "The end date parameter to filter by. Expects a unix timestamp. If no start date, but an end date is specified, the start date is set to the current time.",
"name": "enddate",
"in": "query"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The tasks", "description": "The task",
"schema": { "schema": {
"type": "array", "$ref": "#/definitions/models.Task"
"items": { }
"$ref": "#/definitions/models.Task" },
} "404": {
"description": "Task not found",
"schema": {
"$ref": "#/definitions/models.Message"
} }
}, },
"500": { "500": {

View File

@ -1138,9 +1138,9 @@ paths:
get: get:
consumes: consumes:
- application/json - application/json
description: Returns a list by its ID. description: Returns a team by its ID.
parameters: parameters:
- description: List ID - description: Team ID
in: path in: path
name: id name: id
required: true required: true
@ -1149,11 +1149,11 @@ paths:
- application/json - application/json
responses: responses:
"200": "200":
description: The list description: The team
schema: schema:
$ref: '#/definitions/models.List' $ref: '#/definitions/models.Team'
"403": "403":
description: The user does not have access to the list description: The user does not have access to the team
schema: schema:
$ref: '#/definitions/code.vikunja.io/web.HTTPError' $ref: '#/definitions/code.vikunja.io/web.HTTPError'
"500": "500":
@ -1162,9 +1162,9 @@ paths:
$ref: '#/definitions/models.Message' $ref: '#/definitions/models.Message'
security: security:
- JWTKeyAuth: [] - JWTKeyAuth: []
summary: Gets one list summary: Gets one team
tags: tags:
- list - team
post: post:
consumes: consumes:
- application/json - application/json
@ -3177,55 +3177,31 @@ paths:
get: get:
consumes: consumes:
- application/json - application/json
description: Returns all tasks on any list the user has access to. description: Returns one task by its ID
parameters: parameters:
- description: The page number. Used for pagination. If not provided, the first - description: The task ID
page of results is returned. in: path
in: query name: ID
name: page required: true
type: integer
- description: The maximum number of items per page. Note this parameter is
limited by the configured maximum of items per page.
in: query
name: per_page
type: integer
- description: Search tasks by task text.
in: query
name: s
type: string
- description: The sorting parameter. Possible values to sort by are priority,
prioritydesc, priorityasc, duedate, duedatedesc, duedateasc.
in: query
name: sort
type: string
- description: The start date parameter to filter by. Expects a unix timestamp.
If no end date, but a start date is specified, the end date is set to the
current time.
in: query
name: startdate
type: integer
- description: The end date parameter to filter by. Expects a unix timestamp.
If no start date, but an end date is specified, the start date is set to
the current time.
in: query
name: enddate
type: integer type: integer
produces: produces:
- application/json - application/json
responses: responses:
"200": "200":
description: The tasks description: The task
schema: schema:
items: $ref: '#/definitions/models.Task'
$ref: '#/definitions/models.Task' "404":
type: array description: Task not found
schema:
$ref: '#/definitions/models.Message'
"500": "500":
description: Internal error description: Internal error
schema: schema:
$ref: '#/definitions/models.Message' $ref: '#/definitions/models.Message'
security: security:
- JWTKeyAuth: [] - JWTKeyAuth: []
summary: Get tasks summary: Get one task
tags: tags:
- task - task
/tasks/bulk: /tasks/bulk: