From 5861b6c0d5b914c775d74b495453b49c7707d386 Mon Sep 17 00:00:00 2001 From: Will Assis <35489495+gassiss@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:51:07 -0500 Subject: [PATCH] unified-storage: fix modes 1/2 pagination in dashboard list view (#115511) * unified-storage: fix modes 1/2 pagination in dashboard list view --- .../dashboard/legacysearcher/search_client.go | 12 ++- .../legacysearcher/search_client_test.go | 96 ++++++++++++++++++- .../folderimpl/folder_unifiedstorage_test.go | 8 +- 3 files changed, 109 insertions(+), 7 deletions(-) diff --git a/pkg/registry/apis/dashboard/legacysearcher/search_client.go b/pkg/registry/apis/dashboard/legacysearcher/search_client.go index 9782ce6326d..2ad9c11582a 100644 --- a/pkg/registry/apis/dashboard/legacysearcher/search_client.go +++ b/pkg/registry/apis/dashboard/legacysearcher/search_client.go @@ -360,7 +360,17 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resourcepb.Reso }) } - list.TotalHits = int64(len(list.Results.Rows)) + // the UI expects us to populate "TotalHits" with the total search hits, not however many we are returning. + // this is a dumb workaround due to the fact that legacy doesn't expose a way of "counting search hits" + // it fixes a bug that only happens in mode 1-2 that prevents pagination from working in the dashboard list view + // we only have a handful of instances running in this mode and moving towards 0 instances fast, so this is fine + query.Limit = 0 + query.Page = 1 + res, err = c.dashboardStore.FindDashboards(ctx, query) + if err != nil { + return nil, err + } + list.TotalHits = int64(len(res)) return list, nil } diff --git a/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go b/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go index cb7ead459f6..22907e24c8f 100644 --- a/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go +++ b/pkg/registry/apis/dashboard/legacysearcher/search_client_test.go @@ -48,6 +48,18 @@ func TestDashboardSearchClient_Search(t *testing.T) { {ID: 2, UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2", Tags: []string{}}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + SignedInUser: user, + Type: "dash-db", + Sort: sorter, + Limit: 0, + Page: 1, + }).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, @@ -124,6 +136,17 @@ func TestDashboardSearchClient_Search(t *testing.T) { {ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50), Tags: []string{}}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + SignedInUser: user, + Type: "dash-db", + Sort: sortOptionAsc, + Limit: 0, + Page: 1, + }).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, @@ -189,6 +212,17 @@ func TestDashboardSearchClient_Search(t *testing.T) { {ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2), Tags: []string{}}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + SignedInUser: user, + Type: "dash-db", + Sort: sortOptionAsc, + Limit: 0, + Page: 1, + }).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, @@ -293,6 +327,17 @@ func TestDashboardSearchClient_Search(t *testing.T) { {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + Title: "test", + SignedInUser: user, + Type: "dash-db", + Limit: 0, + Page: 1, + }).Return([]dashboards.DashboardSearchProjection{ + {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, + }, nil).Once() + req := &resourcepb.ResourceSearchRequest{ Options: &resourcepb.ListOptions{ Key: dashboardKey, @@ -319,6 +364,18 @@ func TestDashboardSearchClient_Search(t *testing.T) { {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + Title: "test", + TitleExactMatch: true, + SignedInUser: user, + Type: "dash-db", + Limit: 0, + Page: 1, + }).Return([]dashboards.DashboardSearchProjection{ + {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, + }, nil).Once() + req := &resourcepb.ResourceSearchRequest{ Options: &resourcepb.ListOptions{ Key: dashboardKey, @@ -350,6 +407,17 @@ func TestDashboardSearchClient_Search(t *testing.T) { {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + DashboardIds: []int64{1, 2}, + SignedInUser: user, + Type: "dash-db", + Limit: 0, + Page: 1, + }).Return([]dashboards.DashboardSearchProjection{ + {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, + }, nil).Once() + req := &resourcepb.ResourceSearchRequest{ Options: &resourcepb.ListOptions{ Key: dashboardKey, @@ -383,6 +451,19 @@ func TestDashboardSearchClient_Search(t *testing.T) { {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + DashboardUIDs: []string{"uid1", "uid2"}, + Tags: []string{"tag1", "tag2"}, + FolderUIDs: []string{"general", "folder1"}, + SignedInUser: user, + Type: "dash-db", + Limit: 0, + Page: 1, + }).Return([]dashboards.DashboardSearchProjection{ + {UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, + }, nil).Once() + req := &resourcepb.ResourceSearchRequest{ Options: &resourcepb.ListOptions{ Key: dashboardKey, @@ -532,6 +613,17 @@ func TestDashboardSearchClient_Search(t *testing.T) { {ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, }, nil).Once() + // Second call for total count with Page: 1, Limit: 0 + mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{ + SignedInUser: user, + Sort: sort.SortAlphaAsc, + Type: "dash-db", + Limit: 0, + Page: 1, + }).Return([]dashboards.DashboardSearchProjection{ + {ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"}, + }, nil).Once() + req := &resourcepb.ResourceSearchRequest{ Options: &resourcepb.ListOptions{ Key: dashboardKey, @@ -552,7 +644,7 @@ func TestDashboardSearchClient_Search(t *testing.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() + }, nil).Twice() // Will be called twice due to the total count call req := &resourcepb.ResourceSearchRequest{ Options: &resourcepb.ListOptions{ @@ -584,7 +676,7 @@ func TestDashboardSearchClient_Search(t *testing.T) { mockStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{ {UID: "dashboard1", FolderUID: "folder1", ID: 1}, {UID: "dashboard2", FolderUID: "folder2", ID: 2}, - }, nil).Once() + }, nil).Twice() // Will be called twice due to the total count call req := &resourcepb.ResourceSearchRequest{ Options: &resourcepb.ListOptions{ diff --git a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go index 0ff7a25b0e9..3a47c41fffb 100644 --- a/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go +++ b/pkg/services/folder/folderimpl/folder_unifiedstorage_test.go @@ -439,7 +439,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) { ID: fooFolder.ID, // nolint:staticcheck UID: fooFolder.UID, }, - }, nil).Once() + }, nil).Twice() // Called twice due to total count call id := int64(123) emptyString := "" query := &folder.GetFolderQuery{ @@ -455,7 +455,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) { }) t.Run("When get folder by non existing ID should return not found error", func(t *testing.T) { - dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Once() + dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Twice() // Called twice due to total count call id := int64(111111) query := &folder.GetFolderQuery{ ID: &id, @@ -475,7 +475,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) { ID: fooFolder.ID, // nolint:staticcheck UID: fooFolder.UID, }, - }, nil).Once() + }, nil).Twice() // Called twice due to total count call title := "foo" query := &folder.GetFolderQuery{ Title: &title, @@ -489,7 +489,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) { }) t.Run("When get folder by non existing Title should return not found error", func(t *testing.T) { - dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Once() + dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Twice() // Called twice due to total count call title := "does not exists" query := &folder.GetFolderQuery{ Title: &title,