From 425f6378c66e7fcfa0a9228cb931d7d8a91a348f Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 2 Oct 2024 10:41:38 +0200 Subject: [PATCH] fix(kanban): make loading tasks for a bucket work This fixes two closely-related bugs: 1. When loading tasks from a bucket of a saved filter, the saved filter query would override the user-supplied filter, which would cause to only tasks matching the saved filter query to be returned. 2. When a filter query for a bucket was specified, the function would only check if one of the top level filters was a filter for tasks in a specific bucket. That means a filter like "bucket_id = 42 && labels = foo" would return the expected result, while a filter like "labels = foo && (bucket_id = 42 && priority = 1)" would fail with an error 500 because the task_buckets table was not joined to the sql query. The fix from the first bug caused such filter queries. --- pkg/models/task_collection.go | 14 ++++++++++---- pkg/models/task_search.go | 24 +++++++++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/pkg/models/task_collection.go b/pkg/models/task_collection.go index bfa6bf960..3dd693e6d 100644 --- a/pkg/models/task_collection.go +++ b/pkg/models/task_collection.go @@ -262,6 +262,10 @@ func (tf *TaskCollection) ReadAll(s *xorm.Session, a web.Auth, search string, pa tc.ProjectID = tf.ProjectID tc.isSavedFilter = true + if tf.Filter != "" { + tc.Filter = "(" + tf.Filter + ") && (" + tc.Filter + ")" + } + return tc.ReadAll(s, a, search, page, perPage) } @@ -281,11 +285,13 @@ func (tf *TaskCollection) ReadAll(s *xorm.Session, a web.Auth, search string, pa } } - if view.BucketConfigurationMode == BucketConfigurationModeFilter && strings.Contains(tf.Filter, "bucket_id") { + if strings.Contains(tf.Filter, "bucket_id") { filteringForBucket = true - tf.Filter, err = getFilterValueForBucketFilter(tf.Filter, view) - if err != nil { - return nil, 0, 0, err + if view.BucketConfigurationMode == BucketConfigurationModeFilter { + tf.Filter, err = getFilterValueForBucketFilter(tf.Filter, view) + if err != nil { + return nil, 0, 0, err + } } } } diff --git a/pkg/models/task_search.go b/pkg/models/task_search.go index a279a2d15..9eccca8a8 100644 --- a/pkg/models/task_search.go +++ b/pkg/models/task_search.go @@ -206,6 +206,22 @@ func convertFiltersToDBFilterCond(rawFilters []*taskFilter, includeNulls bool) ( return filterCond, nil } +func hasBucketIDInParsedFilter(filters []*taskFilter) bool { + for _, filter := range filters { + if subfilters, is := filter.value.([]*taskFilter); is { + has := hasBucketIDInParsedFilter(subfilters) + if has { + return true + } + } + if filter.field == taskPropertyBucketID { + return true + } + } + + return false +} + //nolint:gocyclo func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCount int64, err error) { @@ -214,13 +230,7 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo return nil, 0, err } - var joinTaskBuckets bool - for _, filter := range opts.parsedFilters { - if filter.field == taskPropertyBucketID { - joinTaskBuckets = true - break - } - } + joinTaskBuckets := hasBucketIDInParsedFilter(opts.parsedFilters) filterCond, err := convertFiltersToDBFilterCond(opts.parsedFilters, opts.filterIncludeNulls) if err != nil {