Improve pagination (#105)
This commit is contained in:
@ -124,15 +124,16 @@ func (l *Label) Delete() (err error) {
|
||||
// @tags labels
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 labels by label text."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.Label "The labels"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels [get]
|
||||
func (l *Label) ReadAll(search string, a web.Auth, page int) (ls interface{}, err error) {
|
||||
func (l *Label) ReadAll(a web.Auth, search string, page int, perPage int) (ls interface{}, resultCount int, numberOfEntries int64, err error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return nil, ErrGenericForbidden{}
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
||||
u := &User{ID: a.GetID()}
|
||||
@ -140,13 +141,15 @@ func (l *Label) ReadAll(search string, a web.Auth, page int) (ls interface{}, er
|
||||
// Get all tasks
|
||||
taskIDs, err := getUserTaskIDs(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
Search: search,
|
||||
User: u,
|
||||
TaskIDs: taskIDs,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
GetUnusedLabels: true,
|
||||
GroupByLabelIDsOnly: true,
|
||||
})
|
||||
@ -198,15 +201,17 @@ func getLabelByIDSimple(labelID int64) (*Label, error) {
|
||||
func getUserTaskIDs(u *User) (taskIDs []int64, err error) {
|
||||
|
||||
// Get all lists
|
||||
lists, err := getRawListsForUser("", u, -1)
|
||||
lists, _, _, err := getRawListsForUser("", u, -1, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tasks, err := getRawTasksForLists(lists, &taskOptions{
|
||||
tasks, _, _, err := getRawTasksForLists(lists, &taskOptions{
|
||||
startDate: time.Unix(0, 0),
|
||||
endDate: time.Unix(0, 0),
|
||||
sortby: SortTasksByUnsorted,
|
||||
page: -1,
|
||||
perPage: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -101,21 +101,22 @@ func (lt *LabelTask) Create(a web.Auth) (err error) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param task path int true "Task ID"
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 labels by label text."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.Label "The labels"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{task}/labels [get]
|
||||
func (lt *LabelTask) ReadAll(search string, a web.Auth, page int) (labels interface{}, err error) {
|
||||
func (lt *LabelTask) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user has the right to see the task
|
||||
task := Task{ID: lt.TaskID}
|
||||
canRead, err := task.CanRead(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if !canRead {
|
||||
return nil, ErrNoRightToSeeTask{lt.TaskID, a.GetID()}
|
||||
return nil, 0, 0, ErrNoRightToSeeTask{lt.TaskID, a.GetID()}
|
||||
}
|
||||
|
||||
return getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
@ -137,6 +138,7 @@ type LabelByTaskIDsOptions struct {
|
||||
User *User
|
||||
Search string
|
||||
Page int
|
||||
PerPage int
|
||||
TaskIDs []int64
|
||||
GetUnusedLabels bool
|
||||
GroupByLabelIDsOnly bool
|
||||
@ -144,7 +146,7 @@ type LabelByTaskIDsOptions struct {
|
||||
|
||||
// Helper function to get all labels for a set of tasks
|
||||
// Used when getting all labels for one task as well when getting all lables
|
||||
func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, err error) {
|
||||
func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, resultCount int, totalEntries int64, err error) {
|
||||
// Include unused labels. Needed to be able to show a list of all unused labels a user
|
||||
// has access to.
|
||||
var uidOrNil interface{}
|
||||
@ -172,10 +174,10 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, err
|
||||
Or(builder.In("label_task.task_id", opts.TaskIDs)).
|
||||
And("labels.title LIKE ?", "%"+opts.Search+"%").
|
||||
GroupBy(groupBy).
|
||||
Limit(getLimitFromPageIndex(opts.Page)).
|
||||
Limit(getLimitFromPageIndex(opts.Page, opts.PerPage)).
|
||||
Find(&labels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Get all created by users
|
||||
@ -186,7 +188,7 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, err
|
||||
users := make(map[int64]*User)
|
||||
err = x.In("id", userids).Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Obfuscate all user emails
|
||||
@ -199,7 +201,19 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, err
|
||||
labels[in].CreatedBy = users[l.CreatedByID]
|
||||
}
|
||||
|
||||
return labels, err
|
||||
// Get the total number of entries
|
||||
totalEntries, err = x.Table("labels").
|
||||
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
||||
Where(requestOrNil, uidOrNil).
|
||||
Or(builder.In("label_task.task_id", opts.TaskIDs)).
|
||||
And("labels.title LIKE ?", "%"+opts.Search+"%").
|
||||
GroupBy(groupBy).
|
||||
Count(&Label{})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return labels, len(labels), totalEntries, err
|
||||
}
|
||||
|
||||
// Create or update a bunch of task labels
|
||||
|
@ -89,7 +89,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
||||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
gotLabels, err := l.ReadAll(tt.args.search, tt.args.a, tt.args.page)
|
||||
gotLabels, _, _, err := l.ReadAll(tt.args.a, tt.args.search, tt.args.page, 0)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("LabelTask.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
@ -117,7 +117,7 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
gotLs, err := l.ReadAll(tt.args.search, tt.args.a, tt.args.page)
|
||||
gotLs, _, _, err := l.ReadAll(tt.args.a, tt.args.search, tt.args.page, 0)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Label.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
@ -136,29 +136,30 @@ func (share *LinkSharing) ReadOne() (err error) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param list path int true "List ID"
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 shares by hash."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.LinkSharing "The share links"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares [get]
|
||||
func (share *LinkSharing) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
list := &List{ID: share.ListID}
|
||||
can, err := list.CanRead(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if !can {
|
||||
return nil, ErrGenericForbidden{}
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
||||
var shares []*LinkSharing
|
||||
err = x.
|
||||
Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%").
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Find(&shares)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Find all users and add them
|
||||
@ -170,14 +171,22 @@ func (share *LinkSharing) ReadAll(search string, a web.Auth, page int) (interfac
|
||||
users := make(map[int64]*User)
|
||||
err = x.In("id", userIDs).Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
for _, s := range shares {
|
||||
s.SharedBy = users[s.SharedByID]
|
||||
}
|
||||
|
||||
return shares, err
|
||||
// Total count
|
||||
totalItems, err = x.
|
||||
Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%").
|
||||
Count(&LinkSharing{})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return shares, len(shares), totalItems, err
|
||||
}
|
||||
|
||||
// Delete removes a link share
|
||||
|
@ -77,35 +77,36 @@ func GetListsByNamespaceID(nID int64, doer *User) (lists []*List, err error) {
|
||||
// @tags list
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 lists by title."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.List "The lists"
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists [get]
|
||||
func (l *List) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (l *List) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
// Check if we're dealing with a share auth
|
||||
shareAuth, ok := a.(*LinkSharing)
|
||||
if ok {
|
||||
list := &List{ID: shareAuth.ListID}
|
||||
err := list.GetSimpleByID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
lists := []*List{list}
|
||||
err = AddListDetails(lists)
|
||||
return lists, err
|
||||
return lists, 0, 0, err
|
||||
}
|
||||
|
||||
lists, err := getRawListsForUser(search, &User{ID: a.GetID()}, page)
|
||||
lists, resultCount, totalItems, err := getRawListsForUser(search, &User{ID: a.GetID()}, page, perPage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Add more list details
|
||||
err = AddListDetails(lists)
|
||||
return lists, err
|
||||
return lists, resultCount, totalItems, err
|
||||
}
|
||||
|
||||
// ReadOne gets one list by its ID
|
||||
@ -177,10 +178,10 @@ func GetListSimplByTaskID(taskID int64) (l *List, err error) {
|
||||
}
|
||||
|
||||
// Gets the lists only, without any tasks or so
|
||||
func getRawListsForUser(search string, u *User, page int) (lists []*List, err error) {
|
||||
func getRawListsForUser(search string, u *User, page int, perPage int) (lists []*List, resultCount int, totalItems int64, err error) {
|
||||
fullUser, err := GetUserByID(u.ID)
|
||||
if err != nil {
|
||||
return lists, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Gets all Lists where the user is either owner or in a team which has access to the list
|
||||
@ -201,11 +202,33 @@ func getRawListsForUser(search string, u *User, page int) (lists []*List, err er
|
||||
Or("ul.user_id = ?", fullUser.ID).
|
||||
Or("un.user_id = ?", fullUser.ID).
|
||||
GroupBy("l.id").
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("l.title LIKE ?", "%"+search+"%").
|
||||
Find(&lists)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return lists, err
|
||||
totalItems, err = x.
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("INNER", []string{"namespaces", "n"}, "l.namespace_id = n.id").
|
||||
Join("LEFT", []string{"team_namespaces", "tn"}, "tn.namespace_id = n.id").
|
||||
Join("LEFT", []string{"team_members", "tm"}, "tm.team_id = tn.team_id").
|
||||
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
|
||||
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
||||
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
||||
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
||||
Where("tm.user_id = ?", fullUser.ID).
|
||||
Or("tm2.user_id = ?", fullUser.ID).
|
||||
Or("l.owner_id = ?", fullUser.ID).
|
||||
Or("ul.user_id = ?", fullUser.ID).
|
||||
Or("un.user_id = ?", fullUser.ID).
|
||||
GroupBy("l.id").
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("l.title LIKE ?", "%"+search+"%").
|
||||
Count(&List{})
|
||||
return lists, len(lists), totalItems, err
|
||||
}
|
||||
|
||||
// AddListDetails adds owner user objects and list tasks to all lists in the slice
|
||||
|
@ -36,14 +36,15 @@ func TestList_ReadAll(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
lists2 := List{}
|
||||
lists3, err := lists2.ReadAll("", u, 1)
|
||||
lists3, _, _, err := lists2.ReadAll(u, "", 1, 50)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(lists3).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(lists3)
|
||||
assert.Equal(t, 16, s.Len())
|
||||
|
||||
// Try getting lists for a nonexistant user
|
||||
_, err = lists2.ReadAll("", &User{ID: 984234}, 1)
|
||||
_, _, _, err = lists2.ReadAll(&User{ID: 984234}, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserDoesNotExist(err))
|
||||
}
|
||||
|
@ -154,22 +154,23 @@ func (tl *TeamList) Delete() (err error) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "List ID"
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 teams by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.TeamWithRight "The teams with their right."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the list."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/teams [get]
|
||||
func (tl *TeamList) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
// Check if the user can read the namespace
|
||||
l := &List{ID: tl.ListID}
|
||||
canRead, err := l.CanRead(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if !canRead {
|
||||
return nil, ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: a.GetID()}
|
||||
return nil, 0, 0, ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: a.GetID()}
|
||||
}
|
||||
|
||||
// Get the teams
|
||||
@ -178,11 +179,24 @@ func (tl *TeamList) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
Table("teams").
|
||||
Join("INNER", "team_list", "team_id = teams.id").
|
||||
Where("team_list.list_id = ?", tl.ListID).
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("teams.name LIKE ?", "%"+search+"%").
|
||||
Find(&all)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return all, err
|
||||
totalItems, err = x.
|
||||
Table("teams").
|
||||
Join("INNER", "team_list", "team_id = teams.id").
|
||||
Where("team_list.list_id = ?", tl.ListID).
|
||||
Where("teams.name LIKE ?", "%"+search+"%").
|
||||
Count(&TeamWithRight{})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return all, len(all), totalItems, err
|
||||
}
|
||||
|
||||
// Update updates a team <-> list relation
|
||||
|
@ -69,27 +69,27 @@ func TestTeamList(t *testing.T) {
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
|
||||
// Test Read all
|
||||
teams, err := tl.ReadAll("", u, 1)
|
||||
teams, _, _, err := tl.ReadAll(u, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(teams).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(teams)
|
||||
assert.Equal(t, s.Len(), 1)
|
||||
|
||||
// Test Read all for nonexistant list
|
||||
_, err = tl4.ReadAll("", u, 1)
|
||||
_, _, _, err = tl4.ReadAll(u, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
|
||||
// Test Read all for a list where the user is owner of the namespace this list belongs to
|
||||
tl5 := tl
|
||||
tl5.ListID = 2
|
||||
_, err = tl5.ReadAll("", u, 1)
|
||||
_, _, _, err = tl5.ReadAll(u, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test read all for a list where the user not has access
|
||||
tl6 := tl
|
||||
tl6.ListID = 5
|
||||
_, err = tl6.ReadAll("", u, 1)
|
||||
_, _, _, err = tl6.ReadAll(u, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNeedToHaveListReadAccess(err))
|
||||
|
||||
|
@ -159,22 +159,23 @@ func (lu *ListUser) Delete() (err error) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "List ID"
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 users by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.UserWithRight "The users with the right they have."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the list."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/users [get]
|
||||
func (lu *ListUser) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user has access to the list
|
||||
l := &List{ID: lu.ListID}
|
||||
canRead, err := l.CanRead(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if !canRead {
|
||||
return nil, ErrNeedToHaveListReadAccess{UserID: a.GetID(), ListID: lu.ListID}
|
||||
return nil, 0, 0, ErrNeedToHaveListReadAccess{UserID: a.GetID(), ListID: lu.ListID}
|
||||
}
|
||||
|
||||
// Get all users
|
||||
@ -182,16 +183,25 @@ func (lu *ListUser) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
err = x.
|
||||
Join("INNER", "users_list", "user_id = users.id").
|
||||
Where("users_list.list_id = ?", lu.ListID).
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("users.username LIKE ?", "%"+search+"%").
|
||||
Find(&all)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Obfuscate all user emails
|
||||
for _, u := range all {
|
||||
u.Email = ""
|
||||
}
|
||||
|
||||
return all, err
|
||||
numberOfTotalItems, err = x.
|
||||
Join("INNER", "users_list", "user_id = users.id").
|
||||
Where("users_list.list_id = ?", lu.ListID).
|
||||
Where("users.username LIKE ?", "%"+search+"%").
|
||||
Count(&UserWithRight{})
|
||||
|
||||
return all, len(all), numberOfTotalItems, err
|
||||
}
|
||||
|
||||
// Update updates a user <-> list relation
|
||||
|
@ -203,7 +203,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
||||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
got, err := ul.ReadAll(tt.args.search, tt.args.a, tt.args.page)
|
||||
got, _, _, err := ul.ReadAll(tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListUser.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
@ -69,14 +69,18 @@ func SetEngine() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLimitFromPageIndex(page int) (limit, start int) {
|
||||
func getLimitFromPageIndex(page int, perPage int) (limit, start int) {
|
||||
|
||||
// Get everything when page index is -1
|
||||
if page < 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
limit = config.ServicePageCount.GetInt()
|
||||
limit = config.ServiceMaxItemsPerPage.GetInt()
|
||||
if perPage > 0 {
|
||||
limit = perPage
|
||||
}
|
||||
|
||||
start = limit * (page - 1)
|
||||
return
|
||||
}
|
||||
|
@ -131,20 +131,21 @@ type NamespaceWithLists struct {
|
||||
// @tags namespace
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 namespaces by name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.NamespaceWithLists "The Namespaces."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces [get]
|
||||
func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return nil, ErrGenericForbidden{}
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
||||
doer, err := getUserWithError(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
all := []*NamespaceWithLists{}
|
||||
@ -167,11 +168,11 @@ func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
Or("namespaces.owner_id = ?", doer.ID).
|
||||
Or("users_namespace.user_id = ?", doer.ID).
|
||||
GroupBy("namespaces.id").
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("namespaces.name LIKE ?", "%"+search+"%").
|
||||
Find(&all)
|
||||
if err != nil {
|
||||
return all, err
|
||||
return all, 0, 0, err
|
||||
}
|
||||
|
||||
// Get all users
|
||||
@ -187,7 +188,7 @@ func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
Find(&users)
|
||||
|
||||
if err != nil {
|
||||
return all, err
|
||||
return all, 0, 0, err
|
||||
}
|
||||
|
||||
// Make a list of namespace ids
|
||||
@ -202,7 +203,7 @@ func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
In("namespace_id", namespaceids).
|
||||
Find(&lists)
|
||||
if err != nil {
|
||||
return all, err
|
||||
return all, 0, 0, err
|
||||
}
|
||||
|
||||
// Get all lists individually shared with our user (not via a namespace)
|
||||
@ -218,7 +219,7 @@ func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
GroupBy("l.id").
|
||||
Find(&individualLists)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Make the namespace -1 so we now later which one it was
|
||||
@ -234,9 +235,13 @@ func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
}
|
||||
|
||||
// More details for the lists
|
||||
AddListDetails(lists)
|
||||
err = AddListDetails(lists)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Put objects in our namespace list
|
||||
// TODO: Refactor this to use maps for better efficiency
|
||||
for i, n := range all {
|
||||
|
||||
// Users
|
||||
@ -255,7 +260,22 @@ func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, e
|
||||
}
|
||||
}
|
||||
|
||||
return all, nil
|
||||
numberOfTotalItems, err = x.
|
||||
Table("namespaces").
|
||||
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
||||
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
||||
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
|
||||
Where("team_members.user_id = ?", doer.ID).
|
||||
Or("namespaces.owner_id = ?", doer.ID).
|
||||
Or("users_namespace.user_id = ?", doer.ID).
|
||||
GroupBy("namespaces.id").
|
||||
Where("namespaces.name LIKE ?", "%"+search+"%").
|
||||
Count(&NamespaceWithLists{})
|
||||
if err != nil {
|
||||
return all, 0, 0, err
|
||||
}
|
||||
|
||||
return all, len(all), numberOfTotalItems, nil
|
||||
}
|
||||
|
||||
// Create implements the creation method via the interface
|
||||
|
@ -139,22 +139,23 @@ func (tn *TeamNamespace) Delete() (err error) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "Namespace ID"
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 teams by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.TeamWithRight "The teams with the right they have."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/teams [get]
|
||||
func (tn *TeamNamespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user can read the namespace
|
||||
n := Namespace{ID: tn.NamespaceID}
|
||||
canRead, err := n.CanRead(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if !canRead {
|
||||
return nil, ErrNeedToHaveNamespaceReadAccess{NamespaceID: tn.NamespaceID, UserID: a.GetID()}
|
||||
return nil, 0, 0, ErrNeedToHaveNamespaceReadAccess{NamespaceID: tn.NamespaceID, UserID: a.GetID()}
|
||||
}
|
||||
|
||||
// Get the teams
|
||||
@ -163,11 +164,20 @@ func (tn *TeamNamespace) ReadAll(search string, a web.Auth, page int) (interface
|
||||
err = x.Table("teams").
|
||||
Join("INNER", "team_namespaces", "team_id = teams.id").
|
||||
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("teams.name LIKE ?", "%"+search+"%").
|
||||
Find(&all)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return all, err
|
||||
numberOfTotalItems, err = x.Table("teams").
|
||||
Join("INNER", "team_namespaces", "team_id = teams.id").
|
||||
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
|
||||
Where("teams.name LIKE ?", "%"+search+"%").
|
||||
Count(&TeamWithRight{})
|
||||
|
||||
return all, len(all), numberOfTotalItems, err
|
||||
}
|
||||
|
||||
// Update updates a team <-> namespace relation
|
||||
|
@ -68,20 +68,20 @@ func TestTeamNamespace(t *testing.T) {
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
|
||||
// Check readall
|
||||
teams, err := tn.ReadAll("", dummyuser, 1)
|
||||
teams, _, _, err := tn.ReadAll(dummyuser, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(teams).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(teams)
|
||||
assert.Equal(t, s.Len(), 1)
|
||||
|
||||
// Check readall for a nonexistant namespace
|
||||
_, err = tn4.ReadAll("", dummyuser, 1)
|
||||
_, _, _, err = tn4.ReadAll(dummyuser, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
|
||||
// Check with no right to read the namespace
|
||||
nouser := &User{ID: 393}
|
||||
_, err = tn.ReadAll("", nouser, 1)
|
||||
_, _, _, err = tn.ReadAll(nouser, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNeedToHaveNamespaceReadAccess(err))
|
||||
|
||||
|
@ -118,7 +118,7 @@ func TestNamespace_Create(t *testing.T) {
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
|
||||
// Get all namespaces of a user
|
||||
nsps, err := n.ReadAll("", doer, 1)
|
||||
nsps, _, _, err := n.ReadAll(doer, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(nsps).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(nsps)
|
||||
|
@ -145,22 +145,23 @@ func (nu *NamespaceUser) Delete() (err error) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "Namespace ID"
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 users by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.UserWithRight "The users with the right they have."
|
||||
// @Failure 403 {object} code.vikunja.io/web.HTTPError "No right to see the namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/users [get]
|
||||
func (nu *NamespaceUser) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user has access to the namespace
|
||||
l := Namespace{ID: nu.NamespaceID}
|
||||
canRead, err := l.CanRead(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if !canRead {
|
||||
return nil, ErrNeedToHaveNamespaceReadAccess{}
|
||||
return nil, 0, 0, ErrNeedToHaveNamespaceReadAccess{}
|
||||
}
|
||||
|
||||
// Get all users
|
||||
@ -168,16 +169,25 @@ func (nu *NamespaceUser) ReadAll(search string, a web.Auth, page int) (interface
|
||||
err = x.
|
||||
Join("INNER", "users_namespace", "user_id = users.id").
|
||||
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("users.username LIKE ?", "%"+search+"%").
|
||||
Find(&all)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Obfuscate all user emails
|
||||
for _, u := range all {
|
||||
u.Email = ""
|
||||
}
|
||||
|
||||
return all, err
|
||||
numberOfTotalItems, err = x.
|
||||
Join("INNER", "users_namespace", "user_id = users.id").
|
||||
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
|
||||
Where("users.username LIKE ?", "%"+search+"%").
|
||||
Count(&UserWithRight{})
|
||||
|
||||
return all, len(all), numberOfTotalItems, err
|
||||
}
|
||||
|
||||
// Update updates a user <-> namespace relation
|
||||
|
@ -202,7 +202,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
||||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
got, err := un.ReadAll(tt.args.search, tt.args.a, tt.args.page)
|
||||
got, _, _, err := un.ReadAll(tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NamespaceUser.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
@ -219,25 +219,26 @@ func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
|
||||
// @tags assignees
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 assignees by their username."
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.User "The assignees"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees [get]
|
||||
func (la *TaskAssginee) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
task, err := GetListSimplByTaskID(la.TaskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
can, err := task.CanRead(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if !can {
|
||||
return nil, ErrGenericForbidden{}
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
||||
var taskAssignees []*User
|
||||
@ -245,9 +246,18 @@ func (la *TaskAssginee) ReadAll(search string, a web.Auth, page int) (interface{
|
||||
Select("users.*").
|
||||
Join("INNER", "users", "task_assignees.user_id = users.id").
|
||||
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Find(&taskAssignees)
|
||||
return taskAssignees, err
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.Table("task_assignees").
|
||||
Select("users.*").
|
||||
Join("INNER", "users", "task_assignees.user_id = users.id").
|
||||
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
|
||||
Count(&User{})
|
||||
return taskAssignees, len(taskAssignees), numberOfTotalItems, err
|
||||
}
|
||||
|
||||
// BulkAssignees is a helper struct used to update multiple assignees at once.
|
||||
|
@ -100,21 +100,23 @@ func (ta *TaskAttachment) ReadOne() (err error) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path int true "Task 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."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.TaskAttachment "All attachments for this task"
|
||||
// @Failure 403 {object} models.Message "No access to this task."
|
||||
// @Failure 404 {object} models.Message "The task does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{id}/attachments [get]
|
||||
func (ta *TaskAttachment) ReadAll(s string, a web.Auth, page int) (interface{}, error) {
|
||||
func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
attachments := []*TaskAttachment{}
|
||||
|
||||
err := x.
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
err = x.
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("task_id = ?", ta.TaskID).
|
||||
Find(&attachments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
fileIDs := make([]int64, 0, len(attachments))
|
||||
@ -127,13 +129,13 @@ func (ta *TaskAttachment) ReadAll(s string, a web.Auth, page int) (interface{},
|
||||
fs := make(map[int64]*files.File)
|
||||
err = x.In("id", fileIDs).Find(&fs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
us := make(map[int64]*User)
|
||||
err = x.In("id", userIDs).Find(&us)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
for _, r := range attachments {
|
||||
@ -146,7 +148,10 @@ func (ta *TaskAttachment) ReadAll(s string, a web.Auth, page int) (interface{},
|
||||
r.CreatedBy = us[r.CreatedByID]
|
||||
}
|
||||
|
||||
return attachments, err
|
||||
numberOfTotalItems, err = x.
|
||||
Where("task_id = ?", ta.TaskID).
|
||||
Count(&TaskAttachment{})
|
||||
return attachments, len(attachments), numberOfTotalItems, err
|
||||
}
|
||||
|
||||
// Delete removes an attachment
|
||||
|
@ -119,7 +119,7 @@ func TestTaskAttachment_NewAttachment(t *testing.T) {
|
||||
func TestTaskAttachment_ReadAll(t *testing.T) {
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
as, err := ta.ReadAll("", &User{ID: 1}, 0)
|
||||
as, _, _, err := ta.ReadAll(&User{ID: 1}, "", 0, 50)
|
||||
attachments, _ := as.([]*TaskAttachment)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, attachments, 3)
|
||||
|
@ -728,7 +728,7 @@ func TestTask_ReadAll(t *testing.T) {
|
||||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
got, err := lt.ReadAll(tt.args.search, tt.args.a, tt.args.page)
|
||||
got, _, _, err := lt.ReadAll(tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Test %s, Task.ReadAll() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
return
|
||||
|
@ -123,7 +123,8 @@ const (
|
||||
// @tags task
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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."
|
||||
@ -132,7 +133,7 @@ const (
|
||||
// @Success 200 {array} models.Task "The tasks"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/all [get]
|
||||
func (t *Task) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
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":
|
||||
@ -156,6 +157,8 @@ func (t *Task) ReadAll(search string, a web.Auth, page int) (interface{}, error)
|
||||
sortby: sortby,
|
||||
startDate: time.Unix(t.StartDateSortUnix, 0),
|
||||
endDate: time.Unix(t.EndDateSortUnix, 0),
|
||||
page: page,
|
||||
perPage: perPage,
|
||||
}
|
||||
|
||||
shareAuth, is := a.(*LinkSharing)
|
||||
@ -163,15 +166,15 @@ func (t *Task) ReadAll(search string, a web.Auth, page int) (interface{}, error)
|
||||
list := &List{ID: shareAuth.ListID}
|
||||
err := list.GetSimpleByID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
return getTasksForLists([]*List{list}, taskopts)
|
||||
}
|
||||
|
||||
// Get all lists for the user
|
||||
lists, err := getRawListsForUser("", &User{ID: a.GetID()}, page)
|
||||
lists, _, _, err := getRawListsForUser("", &User{ID: a.GetID()}, -1, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return getTasksForLists(lists, taskopts)
|
||||
@ -182,9 +185,11 @@ type taskOptions struct {
|
||||
sortby SortBy
|
||||
startDate time.Time
|
||||
endDate time.Time
|
||||
page int
|
||||
perPage int
|
||||
}
|
||||
|
||||
func getRawTasksForLists(lists []*List, opts *taskOptions) (taskMap map[int64]*Task, err error) {
|
||||
func getRawTasksForLists(lists []*List, opts *taskOptions) (taskMap map[int64]*Task, resultCount int, totalItems int64, err error) {
|
||||
|
||||
// Get all list IDs and get the tasks
|
||||
var listIDs []int64
|
||||
@ -219,42 +224,62 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (taskMap map[int64]*T
|
||||
endDateUnix = opts.endDate.Unix()
|
||||
}
|
||||
|
||||
if err := x.In("list_id", listIDs).
|
||||
err := x.In("list_id", listIDs).
|
||||
Where("text LIKE ?", "%"+opts.search+"%").
|
||||
And("((due_date_unix BETWEEN ? AND ?) OR "+
|
||||
"(start_date_unix BETWEEN ? and ?) OR "+
|
||||
"(end_date_unix BETWEEN ? and ?))", startDateUnix, endDateUnix, startDateUnix, endDateUnix, startDateUnix, endDateUnix).
|
||||
OrderBy(orderby).
|
||||
Find(&taskMap); err != nil {
|
||||
return nil, err
|
||||
Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
|
||||
Find(&taskMap)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
totalItems, err = x.In("list_id", listIDs).
|
||||
Where("text LIKE ?", "%"+opts.search+"%").
|
||||
And("((due_date_unix BETWEEN ? AND ?) OR "+
|
||||
"(start_date_unix BETWEEN ? and ?) OR "+
|
||||
"(end_date_unix BETWEEN ? and ?))", startDateUnix, endDateUnix, startDateUnix, endDateUnix, startDateUnix, endDateUnix).
|
||||
Count(&Task{})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
} else {
|
||||
if err := x.In("list_id", listIDs).
|
||||
err := x.In("list_id", listIDs).
|
||||
Where("text LIKE ?", "%"+opts.search+"%").
|
||||
OrderBy(orderby).
|
||||
Find(&taskMap); err != nil {
|
||||
return nil, err
|
||||
Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
|
||||
Find(&taskMap)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
totalItems, err = x.In("list_id", listIDs).
|
||||
Where("text LIKE ?", "%"+opts.search+"%").
|
||||
Count(&Task{})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
}
|
||||
return
|
||||
return taskMap, len(taskMap), totalItems, nil
|
||||
}
|
||||
|
||||
func getTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, err error) {
|
||||
func getTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||
|
||||
taskMap, err := getRawTasksForLists(lists, opts)
|
||||
taskMap, resultCount, totalItems, err := getRawTasksForLists(lists, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
tasks, err = addMoreInfoToTasks(taskMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
// Because the list is sorted by id which we don't want (since we're dealing with maps)
|
||||
// we have to manually sort the tasks again here.
|
||||
sortTasks(tasks, opts.sortby)
|
||||
|
||||
return tasks, err
|
||||
return tasks, resultCount, totalItems, err
|
||||
}
|
||||
|
||||
func sortTasks(tasks []*Task, by SortBy) {
|
||||
@ -339,7 +364,7 @@ func GetTaskByID(listTaskID int64) (listTask Task, err error) {
|
||||
}
|
||||
|
||||
// Get task labels
|
||||
taskLabels, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
taskLabels, _, _, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
TaskIDs: []int64{listTaskID},
|
||||
})
|
||||
if err != nil {
|
||||
@ -413,7 +438,7 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (tasks []*Task, err error) {
|
||||
}
|
||||
|
||||
// Get all labels for all the tasks
|
||||
labels, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
labels, _, _, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
TaskIDs: taskIDs,
|
||||
Page: -1,
|
||||
})
|
||||
|
@ -135,27 +135,37 @@ func (t *Team) ReadOne() (err error) {
|
||||
// @tags team
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
|
||||
// @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 teams by its name."
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {array} models.Team "The teams."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams [get]
|
||||
func (t *Team) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||
func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return nil, ErrGenericForbidden{}
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
||||
all := []*Team{}
|
||||
err := x.Select("teams.*").
|
||||
err = x.Select("teams.*").
|
||||
Table("teams").
|
||||
Join("INNER", "team_members", "team_members.team_id = teams.id").
|
||||
Where("team_members.user_id = ?", a.GetID()).
|
||||
Limit(getLimitFromPageIndex(page)).
|
||||
Limit(getLimitFromPageIndex(page, perPage)).
|
||||
Where("teams.name LIKE ?", "%"+search+"%").
|
||||
Find(&all)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return all, err
|
||||
numberOfTotalItems, err = x.
|
||||
Table("teams").
|
||||
Join("INNER", "team_members", "team_members.team_id = teams.id").
|
||||
Where("team_members.user_id = ?", a.GetID()).
|
||||
Where("teams.name LIKE ?", "%"+search+"%").
|
||||
Count(&Team{})
|
||||
return all, len(all), numberOfTotalItems, err
|
||||
}
|
||||
|
||||
// Create is the handler to create a team
|
||||
|
@ -55,7 +55,7 @@ func TestTeam_Create(t *testing.T) {
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
|
||||
// Get all teams the user is part of
|
||||
ts, err := tm.ReadAll("", doer, 1)
|
||||
ts, _, _, err := tm.ReadAll(doer, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(ts).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(ts)
|
||||
|
Reference in New Issue
Block a user