From d96831fe3ad2b4af4934ef533568802fe1c8f223 Mon Sep 17 00:00:00 2001 From: konrad Date: Fri, 29 Nov 2019 22:59:20 +0000 Subject: [PATCH] Endpoint to get tasks on a list (#108) --- pkg/integrations/task_collection_test.go | 182 +++++++++++++++++++++++ pkg/integrations/task_test.go | 17 ++- pkg/models/task_collection.go | 113 ++++++++++++++ pkg/models/tasks.go | 48 +----- pkg/routes/routes.go | 7 + pkg/swagger/docs.go | 84 ++++++++++- pkg/swagger/swagger.json | 82 ++++++++++ pkg/swagger/swagger.yaml | 60 ++++++++ 8 files changed, 547 insertions(+), 46 deletions(-) create mode 100644 pkg/integrations/task_collection_test.go create mode 100644 pkg/models/task_collection.go diff --git a/pkg/integrations/task_collection_test.go b/pkg/integrations/task_collection_test.go new file mode 100644 index 00000000000..06eebd493df --- /dev/null +++ b/pkg/integrations/task_collection_test.go @@ -0,0 +1,182 @@ +// Copyright 2019 Vikunja and contriubtors. All rights reserved. +// +// This file is part of Vikunja. +// +// Vikunja is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vikunja is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vikunja. If not, see . + +package integrations + +import ( + "code.vikunja.io/api/pkg/models" + "code.vikunja.io/web/handler" + "github.com/stretchr/testify/assert" + "net/url" + "testing" +) + +func TestTaskCollection(t *testing.T) { + testHandler := webHandlerTest{ + user: &testuser1, + strFunc: func() handler.CObject { + return &models.TaskCollection{} + }, + t: t, + } + // Only run specific nested tests: + // ^TestTask$/^Update$/^Update_task_items$/^Removing_Assignees_null$ + t.Run("ReadAll", func(t *testing.T) { + t.Run("Normal", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(nil, map[string]string{"list": "1"}) + assert.NoError(t, err) + // Not using assert.Equal to avoid having the tests break every time we add new fixtures + assert.Contains(t, rec.Body.String(), `task #1`) + assert.Contains(t, rec.Body.String(), `task #2`) + assert.Contains(t, rec.Body.String(), `task #3`) + assert.Contains(t, rec.Body.String(), `task #4`) + assert.Contains(t, rec.Body.String(), `task #5`) + assert.Contains(t, rec.Body.String(), `task #6`) + assert.Contains(t, rec.Body.String(), `task #7`) + assert.Contains(t, rec.Body.String(), `task #8`) + assert.Contains(t, rec.Body.String(), `task #9`) + assert.Contains(t, rec.Body.String(), `task #10`) + assert.Contains(t, rec.Body.String(), `task #11`) + assert.Contains(t, rec.Body.String(), `task #12`) + assert.NotContains(t, rec.Body.String(), `task #13`) + assert.NotContains(t, rec.Body.String(), `task #14`) + assert.NotContains(t, rec.Body.String(), `task #15`) // Shared via team readonly + assert.NotContains(t, rec.Body.String(), `task #16`) // Shared via team write + assert.NotContains(t, rec.Body.String(), `task #17`) // Shared via team admin + assert.NotContains(t, rec.Body.String(), `task #18`) // Shared via user readonly + assert.NotContains(t, rec.Body.String(), `task #19`) // Shared via user write + assert.NotContains(t, rec.Body.String(), `task #20`) // Shared via user admin + assert.NotContains(t, rec.Body.String(), `task #21`) // Shared via namespace team readonly + assert.NotContains(t, rec.Body.String(), `task #22`) // Shared via namespace team write + assert.NotContains(t, rec.Body.String(), `task #23`) // Shared via namespace team admin + assert.NotContains(t, rec.Body.String(), `task #24`) // Shared via namespace user readonly + assert.NotContains(t, rec.Body.String(), `task #25`) // Shared via namespace user write + assert.NotContains(t, rec.Body.String(), `task #26`) // Shared via namespace user admin + assert.Contains(t, rec.Body.String(), `task #27`) + assert.Contains(t, rec.Body.String(), `task #28`) + assert.NotContains(t, rec.Body.String(), `task #32`) + }) + t.Run("Search", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, nil) + assert.NoError(t, err) + assert.NotContains(t, rec.Body.String(), `task #1`) + assert.NotContains(t, rec.Body.String(), `task #2`) + assert.NotContains(t, rec.Body.String(), `task #3`) + assert.NotContains(t, rec.Body.String(), `task #4`) + assert.NotContains(t, rec.Body.String(), `task #5`) + assert.Contains(t, rec.Body.String(), `task #6`) + assert.NotContains(t, rec.Body.String(), `task #7`) + assert.NotContains(t, rec.Body.String(), `task #8`) + assert.NotContains(t, rec.Body.String(), `task #9`) + assert.NotContains(t, rec.Body.String(), `task #10`) + assert.NotContains(t, rec.Body.String(), `task #11`) + assert.NotContains(t, rec.Body.String(), `task #12`) + assert.NotContains(t, rec.Body.String(), `task #13`) + assert.NotContains(t, rec.Body.String(), `task #14`) + }) + t.Run("Sort Order", func(t *testing.T) { + // should equal priority desc + t.Run("by priority", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"priority"}}, nil) + assert.NoError(t, err) + assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":1,`) + }) + t.Run("by priority desc", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"prioritydesc"}}, nil) + assert.NoError(t, err) + assert.Contains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":1,`) + }) + t.Run("by priority asc", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"priorityasc"}}, nil) + assert.NoError(t, err) + assert.Contains(t, rec.Body.String(), `{"id":33,"text":"task #33 with percent done","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0.5,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"doneAt":0,"dueDate":0,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}}]`) + }) + // should equal duedate desc + t.Run("by duedate", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"duedate"}}, nil) + assert.NoError(t, err) + assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`) + }) + t.Run("by duedate desc", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"duedatedesc"}}, nil) + assert.NoError(t, err) + assert.Contains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`) + }) + t.Run("by duedate asc", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"duedateasc"}}, nil) + assert.NoError(t, err) + assert.Contains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"doneAt":0,"dueDate":1543616724,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"doneAt":0,"dueDate":1543636724,"reminderDates":null,"listID":1,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","percentDone":0,"related_tasks":{},"attachments":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":1,"username":"user1","avatarUrl":"111d68d06e2d317b5a59c2c6c5bad808","created":0,"updated":0}}]`) + }) + t.Run("invalid parameter", func(t *testing.T) { + // Invalid parameter should not sort at all + rec, err := testHandler.testReadAllWithUser(url.Values{"sort": []string{"loremipsum"}}, nil) + assert.NoError(t, err) + assert.NotContains(t, rec.Body.String(), `[{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":1`) + assert.NotContains(t, rec.Body.String(), `{"id":4,"text":"task #4 low prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":1,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":3,"text":"task #3 high prio","description":"","done":false,"dueDate":0,"reminderDates":null,"repeatAfter":0,"priority":100,"startDate":0,"endDate":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`) + assert.NotContains(t, rec.Body.String(), `[{"id":5,"text":"task #5 higher due date","description":"","done":false,"dueDate":1543636724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":6,"text":"task #6 lower due date"`) + assert.NotContains(t, rec.Body.String(), `{"id":6,"text":"task #6 lower due date","description":"","done":false,"dueDate":1543616724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"hexColor":"","created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}},{"id":5,"text":"task #5 higher due date","description":"","done":false,"dueDate":1543636724,"reminderDates":null,"repeatAfter":0,"priority":0,"startDate":0,"endDate":0,"assignees":null,"labels":null,"created":1543626724,"updated":1543626724,"createdBy":{"id":0,"username":"","email":"","created":0,"updated":0}}]`) + }) + }) + t.Run("Date range", func(t *testing.T) { + t.Run("start and end date", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"startdate": []string{"1540000000"}, "enddate": []string{"1544700001"}}, nil) + assert.NoError(t, err) + assert.NotContains(t, rec.Body.String(), `task #1`) + assert.NotContains(t, rec.Body.String(), `task #2`) + assert.NotContains(t, rec.Body.String(), `task #3`) + assert.NotContains(t, rec.Body.String(), `task #4`) + assert.Contains(t, rec.Body.String(), `task #5`) + assert.Contains(t, rec.Body.String(), `task #6`) + assert.Contains(t, rec.Body.String(), `task #7`) + assert.Contains(t, rec.Body.String(), `task #8`) + assert.Contains(t, rec.Body.String(), `task #9`) + assert.NotContains(t, rec.Body.String(), `task #10`) + assert.NotContains(t, rec.Body.String(), `task #11`) + assert.NotContains(t, rec.Body.String(), `task #12`) + assert.NotContains(t, rec.Body.String(), `task #13`) + assert.NotContains(t, rec.Body.String(), `task #14`) + }) + t.Run("start date only", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"startdate": []string{"1540000000"}}, nil) + assert.NoError(t, err) + assert.NotContains(t, rec.Body.String(), `task #1`) + assert.NotContains(t, rec.Body.String(), `task #2`) + assert.NotContains(t, rec.Body.String(), `task #3`) + assert.NotContains(t, rec.Body.String(), `task #4`) + assert.Contains(t, rec.Body.String(), `task #5`) + assert.Contains(t, rec.Body.String(), `task #6`) + assert.Contains(t, rec.Body.String(), `task #7`) + assert.Contains(t, rec.Body.String(), `task #8`) + assert.Contains(t, rec.Body.String(), `task #9`) + assert.NotContains(t, rec.Body.String(), `task #10`) + assert.NotContains(t, rec.Body.String(), `task #11`) + assert.NotContains(t, rec.Body.String(), `task #12`) + assert.NotContains(t, rec.Body.String(), `task #13`) + assert.NotContains(t, rec.Body.String(), `task #14`) + }) + t.Run("end date only", func(t *testing.T) { + rec, err := testHandler.testReadAllWithUser(url.Values{"enddate": []string{"1544700001"}}, nil) + assert.NoError(t, err) + // If no start date but an end date is specified, this should be null + // since we don't have any tasks in the fixtures with an end date > + // the current date. + assert.Equal(t, "null\n", rec.Body.String()) + }) + }) + }) + +} diff --git a/pkg/integrations/task_test.go b/pkg/integrations/task_test.go index 5b2b1f1f2ed..b7ccb8a348d 100644 --- a/pkg/integrations/task_test.go +++ b/pkg/integrations/task_test.go @@ -54,8 +54,21 @@ func TestTask(t *testing.T) { assert.Contains(t, rec.Body.String(), `task #12`) assert.NotContains(t, rec.Body.String(), `task #13`) assert.NotContains(t, rec.Body.String(), `task #14`) - // TODO: add more tasks, since the whole point of this is to get all tasks in all lists where the user - // has at least read access + assert.NotContains(t, rec.Body.String(), `task #13`) + assert.NotContains(t, rec.Body.String(), `task #14`) + assert.Contains(t, rec.Body.String(), `task #15`) // Shared via team readonly + assert.Contains(t, rec.Body.String(), `task #16`) // Shared via team write + assert.Contains(t, rec.Body.String(), `task #17`) // Shared via team admin + assert.Contains(t, rec.Body.String(), `task #18`) // Shared via user readonly + assert.Contains(t, rec.Body.String(), `task #19`) // Shared via user write + assert.Contains(t, rec.Body.String(), `task #20`) // Shared via user admin + assert.Contains(t, rec.Body.String(), `task #21`) // Shared via namespace team readonly + assert.Contains(t, rec.Body.String(), `task #22`) // Shared via namespace team write + assert.Contains(t, rec.Body.String(), `task #23`) // Shared via namespace team admin + assert.Contains(t, rec.Body.String(), `task #24`) // Shared via namespace user readonly + assert.Contains(t, rec.Body.String(), `task #25`) // Shared via namespace user write + assert.Contains(t, rec.Body.String(), `task #26`) // Shared via namespace user admin + // TODO: Add some cases where the user has access to the list, somhow shared }) t.Run("Search", func(t *testing.T) { rec, err := testHandler.testReadAllWithUser(url.Values{"s": []string{"task #6"}}, nil) diff --git a/pkg/models/task_collection.go b/pkg/models/task_collection.go new file mode 100644 index 00000000000..db79f953aff --- /dev/null +++ b/pkg/models/task_collection.go @@ -0,0 +1,113 @@ +// Copyright 2019 Vikunja and contriubtors. All rights reserved. +// +// This file is part of Vikunja. +// +// Vikunja is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vikunja is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vikunja. If not, see . + +package models + +import ( + "code.vikunja.io/web" + "time" +) + +// TaskCollection is a struct used to hold filter details and not clutter the Task struct with information not related to actual tasks. +type TaskCollection struct { + ListID int64 `param:"list"` + Sorting string `query:"sort"` // Parameter to sort by + StartDateSortUnix int64 `query:"startdate"` + EndDateSortUnix int64 `query:"enddate"` + Lists []*List + + web.CRUDable `xorm:"-" json:"-"` + web.Rights `xorm:"-" json:"-"` +} + +// ReadAll gets all tasks for a collection +// @Summary Get tasks on a list +// @Description Returns all tasks for the current list. +// @tags task +// @Accept json +// @Produce json +// @Param listID path int true "The list ID." +// @Param page query int false "The page number. Used for pagination. If not provided, the first page of results is returned." +// @Param per_page query int false "The maximum number of items per page. Note this parameter is limited by the configured maximum of items per page." +// @Param s query string false "Search tasks by task text." +// @Param sort query string false "The sorting parameter. Possible values to sort by are priority, prioritydesc, priorityasc, duedate, duedatedesc, duedateasc." +// @Param startdate query int false "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." +// @Param enddate query int false "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." +// @Security JWTKeyAuth +// @Success 200 {array} models.Task "The tasks" +// @Failure 500 {object} models.Message "Internal error" +// @Router /lists/{listID}/tasks [get] +func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) { + var sortby SortBy + switch tf.Sorting { + case "priority": + sortby = SortTasksByPriorityDesc + case "prioritydesc": + sortby = SortTasksByPriorityDesc + case "priorityasc": + sortby = SortTasksByPriorityAsc + case "duedate": + sortby = SortTasksByDueDateDesc + case "duedatedesc": + sortby = SortTasksByDueDateDesc + case "duedateasc": + sortby = SortTasksByDueDateAsc + default: + sortby = SortTasksByUnsorted + } + + taskopts := &taskOptions{ + search: search, + sortby: sortby, + startDate: time.Unix(tf.StartDateSortUnix, 0), + endDate: time.Unix(tf.EndDateSortUnix, 0), + page: page, + perPage: perPage, + } + + shareAuth, is := a.(*LinkSharing) + if is { + list := &List{ID: shareAuth.ListID} + err := list.GetSimpleByID() + if err != nil { + return nil, 0, 0, err + } + return getTasksForLists([]*List{list}, taskopts) + } + + // If the list ID is not set, we get all tasks for the user. + // This allows to use this function in Task.ReadAll with a possibility to deprecate the latter at some point. + if tf.ListID == 0 { + tf.Lists, _, _, err = getRawListsForUser("", &User{ID: a.GetID()}, -1, 0) + if err != nil { + return nil, 0, 0, err + } + } else { + // Check the list exists and the user has acess on it + list := &List{ID: tf.ListID} + canRead, err := list.CanRead(a) + if err != nil { + return nil, 0, 0, err + } + if !canRead { + return nil, 0, 0, ErrUserDoesNotHaveAccessToList{ListID: tf.ListID} + } + tf.Lists = []*List{{ID: tf.ListID}} + } + + return getTasksForLists(tf.Lists, taskopts) +} diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go index b6e021adb4e..2dc6ee0ff62 100644 --- a/pkg/models/tasks.go +++ b/pkg/models/tasks.go @@ -134,50 +134,12 @@ const ( // @Failure 500 {object} models.Message "Internal error" // @Router /tasks/all [get] func (t *Task) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) { - var sortby SortBy - switch t.Sorting { - case "priority": - sortby = SortTasksByPriorityDesc - case "prioritydesc": - sortby = SortTasksByPriorityDesc - case "priorityasc": - sortby = SortTasksByPriorityAsc - case "duedate": - sortby = SortTasksByDueDateDesc - case "duedatedesc": - sortby = SortTasksByDueDateDesc - case "duedateasc": - sortby = SortTasksByDueDateAsc - default: - sortby = SortTasksByUnsorted + tc := &TaskCollection{ + Sorting: t.Sorting, + StartDateSortUnix: t.StartDateSortUnix, + EndDateSortUnix: t.EndDateSortUnix, } - - taskopts := &taskOptions{ - search: search, - sortby: sortby, - startDate: time.Unix(t.StartDateSortUnix, 0), - endDate: time.Unix(t.EndDateSortUnix, 0), - page: page, - perPage: perPage, - } - - shareAuth, is := a.(*LinkSharing) - if is { - list := &List{ID: shareAuth.ListID} - err := list.GetSimpleByID() - if err != nil { - return nil, 0, 0, err - } - return getTasksForLists([]*List{list}, taskopts) - } - - // Get all lists for the user - lists, _, _, err := getRawListsForUser("", &User{ID: a.GetID()}, -1, 0) - if err != nil { - return nil, 0, 0, err - } - - return getTasksForLists(lists, taskopts) + return tc.ReadAll(a, search, page, perPage) } type taskOptions struct { diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index a01970a5872..2cef8f2ee74 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -226,6 +226,13 @@ func registerAPIRoutes(a *echo.Group) { a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb) a.POST("/tasks/:listtask", taskHandler.UpdateWeb) + taskCollectionHandler := &handler.WebHandler{ + EmptyStruct: func() handler.CObject { + return &models.TaskCollection{} + }, + } + a.GET("/lists/:list/tasks", taskCollectionHandler.ReadAllWeb) + bulkTaskHandler := &handler.WebHandler{ EmptyStruct: func() handler.CObject { return &models.BulkTask{} diff --git a/pkg/swagger/docs.go b/pkg/swagger/docs.go index 4dcd470f689..18734cac2fe 100644 --- a/pkg/swagger/docs.go +++ b/pkg/swagger/docs.go @@ -1,6 +1,6 @@ // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // This file was generated by swaggo/swag at -// 2019-11-02 21:06:44.848486087 +0100 CET m=+0.175638002 +// 2019-11-29 18:11:53.766019702 +0100 CET m=+0.153156633 package swagger @@ -967,6 +967,88 @@ var doc = `{ } } }, + "/lists/{listID}/tasks": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Returns all tasks for the current list.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "task" + ], + "summary": "Get tasks on a list", + "parameters": [ + { + "type": "integer", + "description": "The list ID.", + "name": "listID", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "The page number. Used for pagination. If not provided, the first page of results is returned.", + "name": "page", + "in": "query" + }, + { + "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": { + "200": { + "description": "The tasks", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Task" + } + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "#/definitions/models.Message" + } + } + } + } + }, "/lists/{listID}/teams/{teamID}": { "post": { "security": [ diff --git a/pkg/swagger/swagger.json b/pkg/swagger/swagger.json index 409218a25b4..e57397bb560 100644 --- a/pkg/swagger/swagger.json +++ b/pkg/swagger/swagger.json @@ -949,6 +949,88 @@ } } }, + "/lists/{listID}/tasks": { + "get": { + "security": [ + { + "JWTKeyAuth": [] + } + ], + "description": "Returns all tasks for the current list.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "task" + ], + "summary": "Get tasks on a list", + "parameters": [ + { + "type": "integer", + "description": "The list ID.", + "name": "listID", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "The page number. Used for pagination. If not provided, the first page of results is returned.", + "name": "page", + "in": "query" + }, + { + "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": { + "200": { + "description": "The tasks", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Task" + } + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "#/definitions/models.Message" + } + } + } + } + }, "/lists/{listID}/teams/{teamID}": { "post": { "security": [ diff --git a/pkg/swagger/swagger.yaml b/pkg/swagger/swagger.yaml index 73da65cf097..e41f6455629 100644 --- a/pkg/swagger/swagger.yaml +++ b/pkg/swagger/swagger.yaml @@ -1642,6 +1642,66 @@ paths: summary: Get one link shares for a list tags: - sharing + /lists/{listID}/tasks: + get: + consumes: + - application/json + description: Returns all tasks for the current list. + parameters: + - description: The list ID. + in: path + name: listID + required: true + type: integer + - description: The page number. Used for pagination. If not provided, the first + page of results is returned. + in: query + name: page + 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 + produces: + - application/json + responses: + "200": + description: The tasks + schema: + items: + $ref: '#/definitions/models.Task' + type: array + "500": + description: Internal error + schema: + $ref: '#/definitions/models.Message' + security: + - JWTKeyAuth: [] + summary: Get tasks on a list + tags: + - task /lists/{listID}/teams/{teamID}: delete: description: Delets a team from a list. The team won't have access to the list