mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 06:02:25 +08:00
717 lines
22 KiB
Go
717 lines
22 KiB
Go
package legacysearcher
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
"k8s.io/apimachinery/pkg/selection"
|
|
|
|
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"github.com/grafana/grafana/pkg/services/search/model"
|
|
"github.com/grafana/grafana/pkg/services/search/sort"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
|
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
|
unisearch "github.com/grafana/grafana/pkg/storage/unified/search"
|
|
)
|
|
|
|
func TestDashboardSearchClient_Search(t *testing.T) {
|
|
mockStore := dashboards.NewFakeDashboardStore(t)
|
|
sortSvc := sort.ProvideService()
|
|
client := NewDashboardSearchClient(mockStore, sortSvc)
|
|
ctx := context.Background()
|
|
user := &user.SignedInUser{OrgID: 2}
|
|
ctx = identity.WithRequester(ctx, user)
|
|
emptyTags, err := json.Marshal([]string{})
|
|
require.NoError(t, err)
|
|
|
|
dashboardKey := &resourcepb.ResourceKey{
|
|
Name: "uid",
|
|
Resource: dashboard.DASHBOARD_RESOURCE,
|
|
}
|
|
|
|
t.Run("Should parse results into GRPC", func(t *testing.T) {
|
|
sorter, _ := sortSvc.GetSortOption("alpha-asc")
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
SignedInUser: user, // user from context should be used
|
|
Type: "dash-db", // should set type based off of key
|
|
Sort: sorter,
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", Tags: []string{"term"}},
|
|
{ID: 2, UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2", Tags: []string{}},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
},
|
|
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
|
{
|
|
Field: resource.SEARCH_FIELD_TITLE,
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
tags, err := json.Marshal([]string{"term"})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
|
|
searchFields := resource.StandardSearchFields()
|
|
require.Equal(t, &resourcepb.ResourceSearchResponse{
|
|
TotalHits: 2,
|
|
Results: &resourcepb.ResourceTable{
|
|
Columns: []*resourcepb.ResourceTableColumnDefinition{
|
|
searchFields.Field(resource.SEARCH_FIELD_TITLE),
|
|
searchFields.Field(resource.SEARCH_FIELD_FOLDER),
|
|
searchFields.Field(resource.SEARCH_FIELD_TAGS),
|
|
searchFields.Field(resource.SEARCH_FIELD_LEGACY_ID),
|
|
},
|
|
Rows: []*resourcepb.ResourceTableRow{
|
|
{
|
|
Key: &resourcepb.ResourceKey{
|
|
Name: "uid",
|
|
Group: dashboard.GROUP,
|
|
Resource: dashboard.DASHBOARD_RESOURCE,
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Test Dashboard"),
|
|
[]byte("folder1"),
|
|
tags,
|
|
[]byte("1"),
|
|
},
|
|
},
|
|
{
|
|
Key: &resourcepb.ResourceKey{
|
|
Name: "uid2",
|
|
Group: dashboard.GROUP,
|
|
Resource: dashboard.DASHBOARD_RESOURCE,
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Test Dashboard2"),
|
|
[]byte("folder2"),
|
|
emptyTags,
|
|
[]byte("2"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Sorting should be properly parsed into legacy sorting options (asc), and results added", func(t *testing.T) {
|
|
sortOptionAsc := model.SortOption{
|
|
Name: "viewed-asc", // should add -asc to the sort field and match on that
|
|
}
|
|
sortSvc.RegisterSortOption(sortOptionAsc)
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
SignedInUser: user,
|
|
Type: "dash-db",
|
|
Sort: sortOptionAsc,
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50), Tags: []string{}},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
},
|
|
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
|
{
|
|
Field: resource.SEARCH_FIELD_PREFIX + unisearch.DASHBOARD_VIEWS_TOTAL, // "fields." prefix should be removed
|
|
Desc: false,
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
searchFields := resource.StandardSearchFields()
|
|
require.Equal(t, &resourcepb.ResourceSearchResponse{
|
|
TotalHits: 1,
|
|
Results: &resourcepb.ResourceTable{
|
|
Columns: []*resourcepb.ResourceTableColumnDefinition{
|
|
searchFields.Field(resource.SEARCH_FIELD_TITLE),
|
|
searchFields.Field(resource.SEARCH_FIELD_FOLDER),
|
|
searchFields.Field(resource.SEARCH_FIELD_TAGS),
|
|
searchFields.Field(resource.SEARCH_FIELD_LEGACY_ID),
|
|
{
|
|
Name: "views_total",
|
|
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
|
},
|
|
},
|
|
Rows: []*resourcepb.ResourceTableRow{
|
|
{
|
|
Key: &resourcepb.ResourceKey{
|
|
Name: "uid",
|
|
Group: dashboard.GROUP,
|
|
Resource: dashboard.DASHBOARD_RESOURCE,
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Test Dashboard"),
|
|
[]byte("folder"),
|
|
emptyTags,
|
|
[]byte("1"),
|
|
[]byte(strconv.FormatInt(50, 10)),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Sorting should be properly parsed into legacy sorting options (desc)", func(t *testing.T) {
|
|
sortOptionAsc := model.SortOption{
|
|
Name: "errors-recently-desc", // should add -asc to the sort field and match on that
|
|
}
|
|
sortSvc.RegisterSortOption(sortOptionAsc)
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
SignedInUser: user,
|
|
Type: "dash-db",
|
|
Sort: sortOptionAsc,
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2), Tags: []string{}},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
},
|
|
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
|
{
|
|
Field: unisearch.DASHBOARD_ERRORS_LAST_30_DAYS,
|
|
Desc: true,
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
searchFields := resource.StandardSearchFields()
|
|
require.Equal(t, &resourcepb.ResourceSearchResponse{
|
|
TotalHits: 1,
|
|
Results: &resourcepb.ResourceTable{
|
|
Columns: []*resourcepb.ResourceTableColumnDefinition{
|
|
searchFields.Field(resource.SEARCH_FIELD_TITLE),
|
|
searchFields.Field(resource.SEARCH_FIELD_FOLDER),
|
|
searchFields.Field(resource.SEARCH_FIELD_TAGS),
|
|
searchFields.Field(resource.SEARCH_FIELD_LEGACY_ID),
|
|
{
|
|
Name: "errors_last_30_days",
|
|
Type: resourcepb.ResourceTableColumnDefinition_INT64,
|
|
},
|
|
},
|
|
Rows: []*resourcepb.ResourceTableRow{
|
|
{
|
|
Key: &resourcepb.ResourceKey{
|
|
Name: "uid",
|
|
Group: dashboard.GROUP,
|
|
Resource: dashboard.DASHBOARD_RESOURCE,
|
|
},
|
|
Cells: [][]byte{
|
|
[]byte("Test Dashboard"),
|
|
[]byte("folder"),
|
|
emptyTags,
|
|
[]byte("1"),
|
|
[]byte(strconv.FormatInt(2, 10)),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Query for tags should return facet properly", func(t *testing.T) {
|
|
mockStore.On("GetDashboardTags", mock.Anything, &dashboards.GetDashboardTagsQuery{OrgID: 2}).Return([]*dashboards.DashboardTagCloudItem{
|
|
{Term: "tag1", Count: 1},
|
|
{Term: "tag2", Count: 5},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
|
"tags": {
|
|
Field: "tags",
|
|
},
|
|
},
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
require.Equal(t, &resourcepb.ResourceSearchResponse{
|
|
Results: &resourcepb.ResourceTable{},
|
|
Facet: map[string]*resourcepb.ResourceSearchResponse_Facet{
|
|
"tags": {
|
|
Terms: []*resourcepb.ResourceSearchResponse_TermFacet{
|
|
{
|
|
Term: "tag1",
|
|
Count: 1,
|
|
},
|
|
{
|
|
Term: "tag2",
|
|
Count: 5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Query should be set as the title, and * should be removed", func(t *testing.T) {
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
Title: "test",
|
|
SignedInUser: user, // user from context should be used
|
|
Type: "dash-db", // should set type based off of key
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
},
|
|
Query: "*test*",
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("When searching for SEARCH_FIELD_TITLE_PHRASE, value should be set as title, and ExactMatch should be enabled", func(t *testing.T) {
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
Title: "test",
|
|
TitleExactMatch: true,
|
|
SignedInUser: user, // user from context should be used
|
|
Type: "dash-db", // should set type based off of key
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Fields: []*resourcepb.Requirement{
|
|
{
|
|
Key: resource.SEARCH_FIELD_TITLE_PHRASE, // nolint:staticcheck
|
|
Operator: string(selection.Equals),
|
|
Values: []string{"test"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Should read labels for the dashboard ids", func(t *testing.T) {
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
DashboardIds: []int64{1, 2},
|
|
SignedInUser: user, // user from context should be used
|
|
Type: "dash-db", // should set type based off of key
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Labels: []*resourcepb.Requirement{
|
|
{
|
|
Key: utils.LabelKeyDeprecatedInternalID,
|
|
Operator: "in",
|
|
Values: []string{"1", "2"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Should modify fields to legacy compatible queries", func(t *testing.T) {
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
DashboardUIDs: []string{"uid1", "uid2"},
|
|
Tags: []string{"tag1", "tag2"},
|
|
FolderUIDs: []string{"general", "folder1"},
|
|
SignedInUser: user, // user from context should be used
|
|
Type: "dash-db", // should set type based off of key
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Fields: []*resourcepb.Requirement{
|
|
{
|
|
Key: resource.SEARCH_FIELD_TAGS,
|
|
Operator: "in",
|
|
Values: []string{"tag1", "tag2"},
|
|
},
|
|
{
|
|
Key: resource.SEARCH_FIELD_NAME, // name should be used as uid
|
|
Operator: "in",
|
|
Values: []string{"uid1", "uid2"},
|
|
},
|
|
{
|
|
Key: resource.SEARCH_FIELD_FOLDER,
|
|
Operator: "in",
|
|
Values: []string{"", "folder1"}, // empty folder should be general
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Should retrieve dashboards by plugin through a different function", func(t *testing.T) {
|
|
mockStore.On("GetDashboardsByPluginID", mock.Anything, &dashboards.GetDashboardsByPluginIDQuery{
|
|
PluginID: "slo",
|
|
OrgID: 2, // retrieved from the signed in user
|
|
}).Return([]*dashboards.Dashboard{
|
|
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Fields: []*resourcepb.Requirement{
|
|
{
|
|
Key: resource.SEARCH_FIELD_MANAGER_ID,
|
|
Operator: "in",
|
|
Values: []string{"slo"},
|
|
},
|
|
{
|
|
Key: resource.SEARCH_FIELD_MANAGER_KIND,
|
|
Operator: "in",
|
|
Values: []string{"plugin"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
require.Equal(t, resp.TotalHits, int64(1))
|
|
})
|
|
|
|
t.Run("Should retrieve dashboards by provisioner name through a different function", func(t *testing.T) {
|
|
mockStore.On("GetProvisionedDashboardsByName", mock.Anything, "test", mock.Anything).Return([]*dashboards.DashboardProvisioningSearchResults{
|
|
{
|
|
Dashboard: dashboards.Dashboard{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
ExternalID: "test",
|
|
Provisioner: string(utils.ManagerKindClassicFP), // nolint:staticcheck
|
|
},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Fields: []*resourcepb.Requirement{
|
|
{
|
|
Key: resource.SEARCH_FIELD_MANAGER_KIND,
|
|
Operator: "=",
|
|
Values: []string{string(utils.ManagerKindClassicFP)}, // nolint:staticcheck
|
|
},
|
|
{
|
|
Key: resource.SEARCH_FIELD_MANAGER_ID,
|
|
Operator: "in",
|
|
Values: []string{"test"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
require.Equal(t, resp.TotalHits, int64(1))
|
|
})
|
|
|
|
t.Run("Should retrieve orphaned dashboards if provisioner not in is specified", func(t *testing.T) {
|
|
mockStore.On("GetOrphanedProvisionedDashboards", mock.Anything, []string{"test", "test2"}, mock.Anything).Return([]*dashboards.Dashboard{
|
|
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Fields: []*resourcepb.Requirement{
|
|
{
|
|
Key: resource.SEARCH_FIELD_MANAGER_KIND,
|
|
Operator: "=",
|
|
Values: []string{string(utils.ManagerKindClassicFP)}, // nolint:staticcheck
|
|
},
|
|
{
|
|
Key: resource.SEARCH_FIELD_MANAGER_ID,
|
|
Operator: string(selection.NotIn),
|
|
Values: []string{"test", "test2"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
require.Equal(t, resp.TotalHits, int64(1))
|
|
})
|
|
|
|
t.Run("Should set empty sort field when sorting by title", func(t *testing.T) {
|
|
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
|
|
SignedInUser: user,
|
|
Sort: sort.SortAlphaAsc,
|
|
Type: "dash-db",
|
|
}).Return([]dashboards.DashboardSearchProjection{
|
|
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
},
|
|
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
|
{
|
|
Field: resource.SEARCH_FIELD_TITLE,
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
require.Len(t, resp.Results.Columns, 4)
|
|
mockStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should set correct sort field when sorting by views", func(t *testing.T) {
|
|
mockStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
|
|
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", SortMeta: 100},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
},
|
|
SortBy: []*resourcepb.ResourceSearchRequest_Sort{
|
|
{
|
|
Field: resource.SEARCH_FIELD_PREFIX + unisearch.DASHBOARD_VIEWS_TOTAL,
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
|
|
require.Len(t, resp.Results.Columns, 5)
|
|
i := len(resp.Results.Columns) - 1
|
|
require.Equal(t, "views_total", resp.Results.Columns[i].Name)
|
|
require.Equal(t, []byte(strconv.FormatInt(100, 10)), resp.Results.Rows[0].Cells[i]) // views should be set to 100
|
|
mockStore.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("Should search dashboards based on what is connected to a library panel", func(t *testing.T) {
|
|
mockStore.On("GetDashboardsByLibraryPanelUID", mock.Anything, "test-library-panel", int64(2)).Return([]*dashboards.DashboardRef{
|
|
{UID: "dashboard1", FolderUID: "folder1", ID: 1},
|
|
{UID: "dashboard2", FolderUID: "folder2", ID: 2},
|
|
}, nil).Once()
|
|
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Fields: []*resourcepb.Requirement{
|
|
{
|
|
Key: unisearch.DASHBOARD_LIBRARY_PANEL_REFERENCE,
|
|
Operator: "=",
|
|
Values: []string{"test-library-panel"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp)
|
|
require.Equal(t, int64(2), resp.TotalHits)
|
|
require.Len(t, resp.Results.Rows, 2)
|
|
|
|
dashboardNames := make([]string, len(resp.Results.Rows))
|
|
dashboardFolders := make([]string, len(resp.Results.Rows))
|
|
dashboardIDs := make([]string, len(resp.Results.Rows))
|
|
|
|
for i, row := range resp.Results.Rows {
|
|
dashboardNames[i] = row.Key.Name
|
|
dashboardFolders[i] = string(row.Cells[1])
|
|
dashboardIDs[i] = string(row.Cells[3])
|
|
}
|
|
|
|
require.Contains(t, dashboardNames, "dashboard1")
|
|
require.Contains(t, dashboardNames, "dashboard2")
|
|
require.Contains(t, dashboardFolders, "folder1")
|
|
require.Contains(t, dashboardFolders, "folder2")
|
|
require.Contains(t, dashboardIDs, "1")
|
|
require.Contains(t, dashboardIDs, "2")
|
|
|
|
mockStore.AssertExpectations(t)
|
|
for _, row := range resp.Results.Rows {
|
|
require.Equal(t, len(row.Cells), len(resp.Results.Columns))
|
|
}
|
|
})
|
|
|
|
t.Run("Only one library panel uid is supported", func(t *testing.T) {
|
|
req := &resourcepb.ResourceSearchRequest{
|
|
Options: &resourcepb.ListOptions{
|
|
Key: dashboardKey,
|
|
Fields: []*resourcepb.Requirement{
|
|
{
|
|
Key: unisearch.DASHBOARD_LIBRARY_PANEL_REFERENCE,
|
|
Operator: "=",
|
|
Values: []string{"panel1", "panel2"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := client.Search(ctx, req)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "only one library panel uid is supported")
|
|
require.Nil(t, resp)
|
|
})
|
|
}
|
|
|
|
func TestParseSortName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
sortName string
|
|
wantField string
|
|
wantDesc bool
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "empty sort name",
|
|
sortName: "",
|
|
wantField: "",
|
|
wantDesc: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "viewed-recently with desc suffix",
|
|
sortName: "viewed-recently-desc",
|
|
wantField: unisearch.DASHBOARD_VIEWS_LAST_30_DAYS,
|
|
wantDesc: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "defaults to desc",
|
|
sortName: "viewed",
|
|
wantField: unisearch.DASHBOARD_VIEWS_TOTAL,
|
|
wantDesc: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "errors-recentlyy with asc suffix",
|
|
sortName: "errors-recently-asc",
|
|
wantField: unisearch.DASHBOARD_ERRORS_LAST_30_DAYS,
|
|
wantDesc: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "errors - defaults to desc too",
|
|
sortName: "errors",
|
|
wantField: unisearch.DASHBOARD_ERRORS_TOTAL,
|
|
wantDesc: true,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "alpha sort with asc suffix",
|
|
sortName: "alpha-asc",
|
|
wantField: "title",
|
|
wantDesc: false,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid sort name",
|
|
sortName: "invalid-sort-desc",
|
|
wantField: "",
|
|
wantDesc: false,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
field, isDesc, err := ParseSortName(tt.sortName)
|
|
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.wantField, field)
|
|
require.Equal(t, tt.wantDesc, isDesc)
|
|
})
|
|
}
|
|
}
|