fix(kanban): make bucket query fixed per-view (#1007)
This commit is contained in:
parent
f9fd312999
commit
53264d350e
|
|
@ -509,6 +509,7 @@ describe('Task', () => {
|
||||||
TaskBucketFactory.create(1, {
|
TaskBucketFactory.create(1, {
|
||||||
task_id: tasks[0].id,
|
task_id: tasks[0].id,
|
||||||
bucket_id: buckets[0].id,
|
bucket_id: buckets[0].id,
|
||||||
|
project_view_id: buckets[0].project_view_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.visit(`/projects/${projects[0].id}/4`)
|
cy.visit(`/projects/${projects[0].id}/4`)
|
||||||
|
|
@ -901,6 +902,11 @@ describe('Task', () => {
|
||||||
})
|
})
|
||||||
const labels = LabelFactory.create(1)
|
const labels = LabelFactory.create(1)
|
||||||
LabelTaskFactory.truncate()
|
LabelTaskFactory.truncate()
|
||||||
|
TaskBucketFactory.create(1, {
|
||||||
|
task_id: tasks[0].id,
|
||||||
|
bucket_id: buckets[0].id,
|
||||||
|
project_view_id: buckets[0].project_view_id,
|
||||||
|
})
|
||||||
|
|
||||||
cy.visit(`/projects/${projects[0].id}/4`)
|
cy.visit(`/projects/${projects[0].id}/4`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,7 @@ func GetTasksInBucketsForView(s *xorm.Session, view *ProjectView, projects []*Pr
|
||||||
|
|
||||||
tasks := []*Task{}
|
tasks := []*Task{}
|
||||||
|
|
||||||
|
opts.projectViewID = view.ID
|
||||||
opts.sortby = []*sortParam{
|
opts.sortby = []*sortParam{
|
||||||
{
|
{
|
||||||
projectViewID: view.ID,
|
projectViewID: view.ID,
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,10 @@ func getTaskFilterOptsFromCollection(tf *TaskCollection, projectView *ProjectVie
|
||||||
filterTimezone: tf.FilterTimezone,
|
filterTimezone: tf.FilterTimezone,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if projectView != nil {
|
||||||
|
opts.projectViewID = projectView.ID
|
||||||
|
}
|
||||||
|
|
||||||
opts.parsedFilters, err = getTaskFiltersFromFilterString(tf.Filter, tf.FilterTimezone)
|
opts.parsedFilters, err = getTaskFiltersFromFilterString(tf.Filter, tf.FilterTimezone)
|
||||||
return opts, err
|
return opts, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ func RecalculateTaskPositions(s *xorm.Session, view *ProjectView, a web.Auth) (e
|
||||||
log.Debugf("Recalculating task positions for view %d", view.ID)
|
log.Debugf("Recalculating task positions for view %d", view.ID)
|
||||||
|
|
||||||
opts := &taskSearchOptions{
|
opts := &taskSearchOptions{
|
||||||
|
projectViewID: view.ID,
|
||||||
sortby: []*sortParam{
|
sortby: []*sortParam{
|
||||||
{
|
{
|
||||||
projectViewID: view.ID,
|
projectViewID: view.ID,
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,13 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
|
||||||
}
|
}
|
||||||
|
|
||||||
if joinTaskBuckets {
|
if joinTaskBuckets {
|
||||||
query = query.Join("LEFT", "task_buckets", "task_buckets.task_id = tasks.id")
|
joinCond := "task_buckets.task_id = tasks.id"
|
||||||
|
if opts.projectViewID > 0 {
|
||||||
|
joinCond += " AND task_buckets.project_view_id = ?"
|
||||||
|
query = query.Join("LEFT", "task_buckets", joinCond, opts.projectViewID)
|
||||||
|
} else {
|
||||||
|
query = query.Join("LEFT", "task_buckets", joinCond)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if expandSubtasks {
|
if expandSubtasks {
|
||||||
query = query.
|
query = query.
|
||||||
|
|
@ -419,7 +425,13 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
|
||||||
|
|
||||||
queryCount := d.s.Where(cond)
|
queryCount := d.s.Where(cond)
|
||||||
if joinTaskBuckets {
|
if joinTaskBuckets {
|
||||||
queryCount = queryCount.Join("LEFT", "task_buckets", "task_buckets.task_id = tasks.id")
|
joinCond := "task_buckets.task_id = tasks.id"
|
||||||
|
if opts.projectViewID > 0 {
|
||||||
|
joinCond += " AND task_buckets.project_view_id = ?"
|
||||||
|
queryCount = queryCount.Join("LEFT", "task_buckets", joinCond, opts.projectViewID)
|
||||||
|
} else {
|
||||||
|
queryCount = queryCount.Join("LEFT", "task_buckets", joinCond)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if expandSubtasks {
|
if expandSubtasks {
|
||||||
queryCount = queryCount.
|
queryCount = queryCount.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Vikunja is a to-do list application to facilitate your life.
|
||||||
|
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program 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 Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.vikunja.io/api/pkg/db"
|
||||||
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKanbanViewBucketFiltering(t *testing.T) {
|
||||||
|
db.LoadAndAssertFixtures(t)
|
||||||
|
s := db.NewSession()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
view, err := GetProjectViewByID(s, 4)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
project, err := GetProjectSimpleByID(s, view.ProjectID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buckets, err := GetTasksInBucketsForView(s, view, []*Project{project}, &taskSearchOptions{}, &user.User{ID: 1})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
taskBuckets := map[int64][]int64{}
|
||||||
|
for _, b := range buckets {
|
||||||
|
for _, tsk := range b.Tasks {
|
||||||
|
taskBuckets[tsk.ID] = append(taskBuckets[tsk.ID], b.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for tid, bs := range taskBuckets {
|
||||||
|
assert.Lenf(t, bs, 1, "task %d appears in multiple buckets: %v", tid, bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range []int64{40, 41, 42, 43, 44, 45, 46} {
|
||||||
|
assert.NotContains(t, taskBuckets, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -195,6 +195,7 @@ type taskSearchOptions struct {
|
||||||
isSavedFilter bool
|
isSavedFilter bool
|
||||||
projectIDs []int64
|
projectIDs []int64
|
||||||
expand []TaskCollectionExpandable
|
expand []TaskCollectionExpandable
|
||||||
|
projectViewID int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAll is a dummy function to still have that endpoint documented
|
// ReadAll is a dummy function to still have that endpoint documented
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue