From 0ce89dbcb626f887b41b830d5d446894cc196935 Mon Sep 17 00:00:00 2001 From: Leo <108552997+lpskdl@users.noreply.github.com> Date: Fri, 12 Aug 2022 16:14:04 +0200 Subject: [PATCH] use dash-db type when searching for dashboard (#53607) --- public/app/features/search/constants.ts | 9 ++ .../app/features/search/service/sql.test.ts | 121 ++++++++++++++++++ public/app/features/search/service/sql.ts | 81 ++++++------ 3 files changed, 168 insertions(+), 43 deletions(-) create mode 100644 public/app/features/search/service/sql.test.ts diff --git a/public/app/features/search/constants.ts b/public/app/features/search/constants.ts index 1e6483c77f6..615dfa35493 100644 --- a/public/app/features/search/constants.ts +++ b/public/app/features/search/constants.ts @@ -1,3 +1,5 @@ +import { DashboardSearchItemType } from './types'; + export const NO_ID_SECTIONS = ['Recent', 'Starred']; // Height of the search result item export const SEARCH_ITEM_HEIGHT = 58; @@ -9,3 +11,10 @@ export const GENERAL_FOLDER_UID = 'general'; export const GENERAL_FOLDER_TITLE = 'General'; export const SEARCH_PANELS_LOCAL_STORAGE_KEY = 'grafana.search.include.panels'; export const SEARCH_SELECTED_LAYOUT = 'grafana.search.layout'; +export const TYPE_KIND_MAP: { [key: string]: DashboardSearchItemType } = { + dashboard: DashboardSearchItemType.DashDB, + folder: DashboardSearchItemType.DashFolder, + home: DashboardSearchItemType.DashHome, +}; + +export const DEFAULT_MAX_VALUES = 1000; diff --git a/public/app/features/search/service/sql.test.ts b/public/app/features/search/service/sql.test.ts new file mode 100644 index 00000000000..79b50ba3bdc --- /dev/null +++ b/public/app/features/search/service/sql.test.ts @@ -0,0 +1,121 @@ +import { backendSrv } from 'app/core/services/backend_srv'; + +import { DashboardSearchItemType } from '../types'; + +import { SQLSearcher } from './sql'; + +const searchMock = jest.spyOn(backendSrv, 'get'); +jest.spyOn(backendSrv, 'fetch'); + +describe('SQLSearcher', () => { + beforeEach(() => { + searchMock.mockReset(); + }); + it('should call search api with correct query for general folder', async () => { + searchMock.mockResolvedValue([]); + const sqlSearcher = new SQLSearcher(); + const query = { + query: '*', + kind: ['dashboard'], + location: 'General', + sort: 'name_sort', + }; + await sqlSearcher.search(query); + + expect(searchMock).toHaveBeenLastCalledWith('/api/search', { + limit: 1000, + sort: query.sort, + tag: undefined, + type: DashboardSearchItemType.DashDB, + folderIds: [0], + }); + }); + + it('should call search api with correct query based on its kinds', async () => { + searchMock.mockResolvedValue([]); + + const sqlSearcher = new SQLSearcher(); + + const query = { + query: '*', + kind: ['folder'], + location: 'any', + sort: 'name_sort', + }; + + await sqlSearcher.search(query); + + expect(searchMock).toHaveBeenLastCalledWith('/api/search', { + limit: 1000, + sort: query.sort, + tag: undefined, + type: DashboardSearchItemType.DashFolder, + folderIds: [0], + }); + + searchMock.mockClear(); + + const query2 = { + query: 'test', + kind: ['folder'], + location: 'any', + sort: 'name_sort', + }; + + await sqlSearcher.search(query2); + + expect(searchMock).toHaveBeenLastCalledWith('/api/search', { + limit: 1000, + sort: query2.sort, + query: query2.query, + tag: undefined, + folderIds: [0], + }); + + searchMock.mockClear(); + + const query3 = { + query: 'test', + kind: ['folder'], + location: 'any', + sort: 'name_sort', + uid: ['T202C0Tnk'], + }; + + await sqlSearcher.search(query3); + + expect(searchMock).toHaveBeenLastCalledWith('/api/search', { + limit: 1000, + sort: query3.sort, + query: query3.query, + tag: undefined, + dashboardUID: query3.uid, + }); + }); + + it('starred should call search api with correct query', async () => { + searchMock.mockResolvedValue([]); + + const sqlSearcher = new SQLSearcher(); + + const query = { + query: 'test', + kind: ['folder'], + location: 'any', + sort: 'name_sort', + uid: ['T202C0Tnk'], + starred: true, + }; + + await sqlSearcher.starred(query); + + expect(searchMock).toHaveBeenLastCalledWith('/api/search', { + limit: 1000, + sort: query.sort, + query: query.query, + tag: undefined, + dashboardUID: query.uid, + starred: true, + }); + }); +}); diff --git a/public/app/features/search/service/sql.ts b/public/app/features/search/service/sql.ts index 54452b22834..f53a7190816 100644 --- a/public/app/features/search/service/sql.ts +++ b/public/app/features/search/service/sql.ts @@ -3,7 +3,8 @@ import { config } from '@grafana/runtime'; import { TermCount } from 'app/core/components/TagFilter/TagFilter'; import { backendSrv } from 'app/core/services/backend_srv'; -import { DashboardSearchHit } from '../types'; +import { DEFAULT_MAX_VALUES, TYPE_KIND_MAP } from '../constants'; +import { DashboardSearchHit, DashboardSearchItemType } from '../types'; import { LocationInfo } from './types'; import { replaceCurrentFolderQuery } from './utils'; @@ -15,7 +16,7 @@ interface APIQuery { tag?: string[]; limit?: number; page?: number; - type?: string; + type?: DashboardSearchItemType; // DashboardIds []int64 dashboardUID?: string[]; folderIds?: number[]; @@ -38,36 +39,46 @@ export class SQLSearcher implements GrafanaSearcher { }, }; // share location info with everyone - async search(query: SearchQuery): Promise { - if (query.facet?.length) { - throw new Error('facets not supported!'); - } - const q: APIQuery = { - limit: query.limit ?? 1000, // default 1k max values - tag: query.tags, - sort: query.sort, - }; + private async composeQuery(apiQuery: APIQuery, searchOptions: SearchQuery): Promise { + const query = await replaceCurrentFolderQuery(searchOptions); - query = await replaceCurrentFolderQuery(query); if (query.query === '*') { - if (query.kind?.length === 1 && query.kind[0] === 'folder') { - q.type = 'dash-folder'; + if (query.kind?.length === 1 && TYPE_KIND_MAP[query.kind[0]]) { + apiQuery.type = TYPE_KIND_MAP[query.kind[0]]; } } else if (query.query?.length) { - q.query = query.query; + apiQuery.query = query.query; } if (query.uid) { - q.dashboardUID = query.uid; + apiQuery.dashboardUID = query.uid; } else if (query.location?.length) { let info = this.locationInfo[query.location]; if (!info) { // This will load all folder folders - await this.doAPIQuery({ type: 'dash-folder', limit: 999 }); + await this.doAPIQuery({ type: DashboardSearchItemType.DashFolder, limit: 999 }); info = this.locationInfo[query.location]; } - q.folderIds = [info.folderId ?? 0]; + apiQuery.folderIds = [info?.folderId ?? 0]; } + + return apiQuery; + } + + async search(query: SearchQuery): Promise { + if (query.facet?.length) { + throw new Error('facets not supported!'); + } + + const q = await this.composeQuery( + { + limit: query.limit ?? DEFAULT_MAX_VALUES, // default 1k max values + tag: query.tags, + sort: query.sort, + }, + query + ); + return this.doAPIQuery(q); } @@ -75,33 +86,17 @@ export class SQLSearcher implements GrafanaSearcher { if (query.facet?.length) { throw new Error('facets not supported!'); } - const q: APIQuery = { - limit: query.limit ?? 1000, // default 1k max values - tag: query.tags, - sort: query.sort, - starred: query.starred, - }; - query = await replaceCurrentFolderQuery(query); - if (query.query === '*') { - if (query.kind?.length === 1 && query.kind[0] === 'folder') { - q.type = 'dash-folder'; - } - } else if (query.query?.length) { - q.query = query.query; - } + const q = await this.composeQuery( + { + limit: query.limit ?? DEFAULT_MAX_VALUES, // default 1k max values + tag: query.tags, + sort: query.sort, + starred: query.starred, + }, + query + ); - if (query.uid) { - q.dashboardUID = query.uid; - } else if (query.location?.length) { - let info = this.locationInfo[query.location]; - if (!info) { - // This will load all folder folders - await this.doAPIQuery({ type: 'dash-folder', limit: 999 }); - info = this.locationInfo[query.location]; - } - q.folderIds = [info.folderId ?? 0]; - } return this.doAPIQuery(q); }