Search API: Search by folder UID (#65040)

* Search: Attempt to support folderUID filter

* Search: Use folder UID instead of ID for searching folders

* Update swagger

* Fix JSON property casing

* Add integration test

* Remove redundant query condition

* Fix frontend test

* Fix listing dashboards in General/root

* Add support for fetching top level folders

using `folderUIDs=` (empty string) query parameter

* Add deprecation notice

* Send uid of general in sql.ts

* Use 'general' for query folderUIDs query param for fetching folder

* Add tests

* Fix FolderUIDFilter

---------

Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
This commit is contained in:
Josh Hunt
2023-08-04 09:43:47 +00:00
committed by GitHub
parent 64ed77ddce
commit 7bc6d32eb9
11 changed files with 809 additions and 63 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
@@ -91,6 +92,52 @@ func (f FolderFilter) Where() (string, []interface{}) {
return sqlIDin("dashboard.folder_id", f.IDs)
}
type FolderUIDFilter struct {
Dialect migrator.Dialect
OrgID int64
UIDs []string
}
func (f FolderUIDFilter) Where() (string, []interface{}) {
if len(f.UIDs) < 1 {
return "", nil
}
params := []interface{}{}
includeGeneral := false
for _, uid := range f.UIDs {
if uid == folder.GeneralFolderUID {
includeGeneral = true
continue
}
params = append(params, uid)
}
q := ""
switch {
case len(params) < 1:
// do nothing
case len(params) == 1:
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid = ?)"
params = append([]interface{}{f.OrgID}, params...)
default:
sqlArray := "(?" + strings.Repeat(",?", len(params)-1) + ")"
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN " + sqlArray + ")"
params = append([]interface{}{f.OrgID}, params...)
}
if includeGeneral {
if q == "" {
q = "dashboard.folder_id = ? "
} else {
q = "(" + q + " OR dashboard.folder_id = ?)"
}
params = append(params, 0)
}
return q, params
}
type DashboardIDFilter struct {
IDs []int64
}

View File

@@ -0,0 +1,59 @@
package searchstore_test
import (
"testing"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/stretchr/testify/assert"
)
func TestFolderUIDFilter(t *testing.T) {
testCases := []struct {
description string
uids []string
expectedSql string
expectedParams []interface{}
}{
{
description: "searching general folder",
uids: []string{"general"},
expectedSql: "dashboard.folder_id = ? ",
expectedParams: []interface{}{0},
},
{
description: "searching a specific folder",
uids: []string{"abc-123"},
expectedSql: "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid = ?)",
expectedParams: []interface{}{int64(1), "abc-123"},
},
{
description: "searching a specific folders",
uids: []string{"abc-123", "def-456"},
expectedSql: "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN (?,?))",
expectedParams: []interface{}{int64(1), "abc-123", "def-456"},
},
{
description: "searching a specific folders or general",
uids: []string{"general", "abc-123", "def-456"},
expectedSql: "(dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN (?,?)) OR dashboard.folder_id = ?)",
expectedParams: []interface{}{int64(1), "abc-123", "def-456", 0},
},
}
store := setupTestEnvironment(t)
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
f := searchstore.FolderUIDFilter{
Dialect: store.GetDialect(),
OrgID: 1,
UIDs: tc.uids,
}
sql, params := f.Where()
assert.Equal(t, tc.expectedSql, sql)
assert.Equal(t, tc.expectedParams, params)
})
}
}