Chore: Remove search_srv (#62964)

* Chore: Remove Search Service

* use getSortOptions from grafana searcher

* wip

* fix library panels search tests
pull/63065/head
Josh Hunt 2 years ago committed by GitHub
parent f80bf11782
commit 09637caade
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      .betterer.results
  2. 7
      public/app/core/components/Select/SortPicker.tsx
  3. 177
      public/app/core/services/search_srv.ts
  4. 299
      public/app/core/specs/search_srv.test.ts
  5. 20
      public/app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch.test.tsx
  6. 3
      public/app/features/search/page/components/FolderView.tsx

@ -2347,9 +2347,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"public/app/core/components/Select/SortPicker.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/core/components/TagFilter/TagFilter.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@ -2478,17 +2475,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
],
"public/app/core/services/search_srv.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"]
],
"public/app/core/specs/backend_srv.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@ -2503,14 +2489,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"public/app/core/specs/search_srv.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
],
"public/app/core/specs/ticks.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],

@ -4,8 +4,7 @@ import { useAsync } from 'react-use';
import { SelectableValue } from '@grafana/data';
import { Icon, Select } from '@grafana/ui';
import { DEFAULT_SORT } from 'app/features/search/constants';
import { SearchSrv } from '../../services/search_srv';
import { getGrafanaSearcher } from 'app/features/search/service';
export interface Props {
onChange: (sortValue: SelectableValue) => void;
@ -17,9 +16,7 @@ export interface Props {
}
const defaultSortOptionsGetter = (): Promise<SelectableValue[]> => {
return new SearchSrv().getSortOptions().then(({ sortOptions }) => {
return sortOptions.map((opt: any) => ({ label: opt.displayName, value: opt.name }));
});
return getGrafanaSearcher().getSortOptions();
};
export function SortPicker({ onChange, value, placeholder, filter, getSortOptions, isClearable }: Props) {

@ -1,177 +0,0 @@
import { clone, keys, sortBy, take, values } from 'lodash';
import { contextSrv } from 'app/core/services/context_srv';
import impressionSrv from 'app/core/services/impression_srv';
import store from 'app/core/store';
import { SECTION_STORAGE_KEY } from 'app/features/search/constants';
import {
DashboardSection,
DashboardSearchItemType,
DashboardSearchItem,
SearchLayout,
} from 'app/features/search/types';
import { hasFilters } from 'app/features/search/utils';
import { backendSrv } from './backend_srv';
interface Sections {
[key: string]: Partial<DashboardSection>;
}
/** @deprecated */
export class SearchSrv {
private getRecentDashboards(sections: DashboardSection[] | any) {
return this.queryForRecentDashboards().then((result: any[]) => {
if (result.length > 0) {
sections['recent'] = {
title: 'Recent',
icon: 'clock-nine',
score: -1,
expanded: store.getBool(`${SECTION_STORAGE_KEY}.recent`, true),
items: result,
type: DashboardSearchItemType.DashFolder,
};
}
});
}
private queryForRecentDashboards(): Promise<DashboardSearchItem[]> {
return new Promise((resolve) => {
impressionSrv.getDashboardOpened().then((uids) => {
const dashUIDs: string[] = take(uids, 30);
if (dashUIDs.length === 0) {
return resolve([]);
}
backendSrv.search({ dashboardUIDs: dashUIDs }).then((result) => {
return resolve(
dashUIDs
.map((orderId) => result.find((result) => result.uid === orderId))
.filter((hit) => hit && !hit.isStarred) as DashboardSearchItem[]
);
});
});
});
}
private getStarred(sections: DashboardSection): Promise<any> {
if (!contextSrv.isSignedIn) {
return Promise.resolve();
}
return backendSrv.search({ starred: true, limit: 30 }).then((result) => {
if (result.length > 0) {
(sections as any)['starred'] = {
title: 'Starred',
icon: 'star',
score: -2,
expanded: store.getBool(`${SECTION_STORAGE_KEY}.starred`, true),
items: result,
type: DashboardSearchItemType.DashFolder,
};
}
});
}
search(options: any) {
const sections: any = {};
const promises = [];
const query = clone(options);
const filters = hasFilters(options) || query.folderIds?.length > 0;
query.folderIds = query.folderIds || [];
if (query.layout === SearchLayout.List) {
return backendSrv
.search({ ...query, type: DashboardSearchItemType.DashDB })
.then((results) => (results.length ? [{ title: '', items: results }] : []));
}
if (!filters) {
query.folderIds = [0];
}
if (!options.skipRecent && !filters) {
promises.push(this.getRecentDashboards(sections));
}
if (!options.skipStarred && !filters) {
promises.push(this.getStarred(sections));
}
promises.push(
backendSrv.search(query).then((results) => {
return this.handleSearchResult(sections, results);
})
);
return Promise.all(promises).then(() => {
return sortBy(values(sections), 'score');
});
}
private handleSearchResult(sections: Sections, results: DashboardSearchItem[]): any {
if (results.length === 0) {
return sections;
}
// create folder index
for (const hit of results) {
if (hit.type === 'dash-folder') {
sections[hit.uid!] = {
uid: hit.uid,
title: hit.title,
expanded: false,
items: [],
url: hit.url,
icon: 'folder',
score: keys(sections).length,
type: hit.type,
};
}
}
for (const hit of results) {
if (hit.type === 'dash-folder') {
continue;
}
let section = sections[hit.folderUid || 0];
if (!section) {
if (hit.folderUid) {
section = {
uid: hit.folderUid,
title: hit.folderTitle,
url: hit.folderUrl,
items: [],
icon: 'folder-open',
score: keys(sections).length,
type: DashboardSearchItemType.DashFolder,
};
} else {
section = {
uid: '',
title: 'General',
items: [],
icon: 'folder-open',
score: keys(sections).length,
type: DashboardSearchItemType.DashFolder,
};
}
// add section
sections[hit.folderUid || 0] = section;
}
section.expanded = true;
section.items && section.items.push(hit);
}
}
getDashboardTags() {
return backendSrv.get('/api/dashboards/tags');
}
getSortOptions() {
return backendSrv.get('/api/search/sorting');
}
}

@ -1,299 +0,0 @@
import { contextSrv } from 'app/core/services/context_srv';
import impressionSrv from 'app/core/services/impression_srv';
import { SearchSrv } from 'app/core/services/search_srv';
import { DashboardSearchItem } from 'app/features/search/types';
import { backendSrv } from '../services/backend_srv';
jest.mock('app/core/store', () => {
return {
getBool: jest.fn(),
set: jest.fn(),
getObject: jest.fn(),
};
});
jest.mock('app/core/services/impression_srv', () => {
return {
getDashboardOpened: jest.fn,
};
});
describe('SearchSrv', () => {
let searchSrv: SearchSrv;
const searchMock = jest.spyOn(backendSrv, 'search'); // will use the mock in __mocks__
beforeEach(() => {
searchSrv = new SearchSrv();
contextSrv.isSignedIn = true;
impressionSrv.getDashboardOpened = jest.fn().mockResolvedValue([]);
jest.clearAllMocks();
});
describe('With recent dashboards', () => {
let results: any;
beforeEach(() => {
searchMock.mockImplementation((options) => {
if (options.dashboardUIDs) {
return Promise.resolve([
{ uid: 'DSNdW0gVk', title: 'second but first' },
{ uid: 'srx16xR4z', title: 'first but second' },
] as DashboardSearchItem[]);
}
return Promise.resolve([]);
});
impressionSrv.getDashboardOpened = jest.fn().mockResolvedValue(['srx16xR4z', 'DSNdW0gVk']);
return searchSrv.search({ query: '' }).then((res) => {
results = res;
});
});
it('should include recent dashboards section', () => {
expect(results[0].title).toBe('Recent');
});
it('should return order decided by impressions store not api', () => {
expect(results[0].items[0].title).toBe('first but second');
expect(results[0].items[1].title).toBe('second but first');
});
describe('and 3 recent dashboards removed in backend', () => {
let results: any;
beforeEach(() => {
searchMock.mockImplementation((options) => {
if (options.dashboardUIDs) {
return Promise.resolve([
{ uid: 'DSNdW0gVk', title: 'two' },
{ uid: 'srx16xR4z', title: 'one' },
] as DashboardSearchItem[]);
}
return Promise.resolve([]);
});
impressionSrv.getDashboardOpened = jest
.fn()
.mockResolvedValue(['Xrx16x4z', 'CSxdW0gYA', 'srx16xR4z', 'DSNdW0gVk', 'xSxdW0gYA']);
return searchSrv.search({ query: '' }).then((res) => {
results = res;
});
});
it('should return 2 dashboards', () => {
expect(results[0].items.length).toBe(2);
expect(results[0].items[0].uid).toBe('srx16xR4z');
expect(results[0].items[1].uid).toBe('DSNdW0gVk');
});
});
});
describe('With starred dashboards', () => {
let results: any;
beforeEach(() => {
searchMock.mockImplementation((options) => {
if (options.starred) {
return Promise.resolve([{ uid: '1', title: 'starred' }] as DashboardSearchItem[]);
}
return Promise.resolve([]);
});
return searchSrv.search({ query: '' }).then((res) => {
results = res;
});
});
it('should include starred dashboards section', () => {
expect(results[0].title).toBe('Starred');
expect(results[0].items.length).toBe(1);
});
});
describe('With starred dashboards and recent', () => {
let results: any;
beforeEach(() => {
searchMock.mockImplementation((options) => {
if (options.dashboardUIDs) {
return Promise.resolve([
{ uid: 'srx16xR4z', title: 'starred and recent', isStarred: true },
{ uid: 'DSNdW0gVk', title: 'recent' },
] as DashboardSearchItem[]);
}
return Promise.resolve([{ uid: 'srx16xR4z', title: 'starred and recent' }] as DashboardSearchItem[]);
});
impressionSrv.getDashboardOpened = jest.fn().mockResolvedValue(['srx16xR4z', 'DSNdW0gVk']);
return searchSrv.search({ query: '' }).then((res) => {
results = res;
});
});
it('should not show starred in recent', () => {
expect(results[1].title).toBe('Recent');
expect(results[1].items[0].title).toBe('recent');
});
it('should show starred', () => {
expect(results[0].title).toBe('Starred');
expect(results[0].items[0].title).toBe('starred and recent');
});
});
describe('with no query string and dashboards with folders returned', () => {
let results: any;
beforeEach(() => {
searchMock.mockImplementation(
jest
.fn()
.mockResolvedValueOnce(Promise.resolve([]))
.mockResolvedValue(
Promise.resolve([
{
title: 'folder1',
type: 'dash-folder',
uid: 'folder-1',
},
{
title: 'dash with no folder',
type: 'dash-db',
uid: '2',
},
{
title: 'dash in folder1 1',
type: 'dash-db',
uid: '3',
folderUid: 'folder-1',
},
{
title: 'dash in folder1 2',
type: 'dash-db',
uid: '4',
folderUid: 'folder-1',
},
])
)
);
return searchSrv.search({ query: '' }).then((res) => {
results = res;
});
});
it('should create sections for each folder and root', () => {
expect(results).toHaveLength(2);
});
it('should place folders first', () => {
expect(results[0].title).toBe('folder1');
});
});
describe('with query string and dashboards with folders returned', () => {
let results: any;
beforeEach(() => {
searchMock.mockImplementation(
jest.fn().mockResolvedValue([
{
folderUid: 'dash-with-no-folder-uid',
title: 'dash with no folder',
type: 'dash-db',
},
{
title: 'dash in folder1 1',
type: 'dash-db',
folderUid: 'uid',
folderTitle: 'folder1',
folderUrl: '/dashboards/f/uid/folder1',
},
])
);
return searchSrv.search({ query: 'search' }).then((res) => {
results = res;
});
});
it('should not specify folder ids', () => {
expect(searchMock.mock.calls[0][0].folderIds).toHaveLength(0);
});
it('should group results by folder', () => {
expect(results).toHaveLength(2);
expect(results[0].uid).toEqual('dash-with-no-folder-uid');
expect(results[1].uid).toEqual('uid');
expect(results[1].title).toEqual('folder1');
expect(results[1].url).toEqual('/dashboards/f/uid/folder1');
});
});
describe('with tags', () => {
beforeEach(() => {
searchMock.mockImplementation(jest.fn().mockResolvedValue(Promise.resolve([])));
return searchSrv.search({ tag: ['atag'] }).then(() => {});
});
it('should send tags query to backend search', () => {
expect(searchMock.mock.calls[0][0].tag).toHaveLength(1);
});
});
describe('with starred', () => {
beforeEach(() => {
searchMock.mockImplementation(jest.fn().mockResolvedValue(Promise.resolve([])));
return searchSrv.search({ starred: true }).then(() => {});
});
it('should send starred query to backend search', () => {
expect(searchMock.mock.calls[0][0].starred).toEqual(true);
});
});
describe('when skipping recent dashboards', () => {
let getRecentDashboardsCalled = false;
beforeEach(() => {
searchMock.mockImplementation(jest.fn().mockResolvedValue(Promise.resolve([])));
searchSrv['getRecentDashboards'] = () => {
getRecentDashboardsCalled = true;
return Promise.resolve();
};
return searchSrv.search({ skipRecent: true }).then(() => {});
});
it('should not fetch recent dashboards', () => {
expect(getRecentDashboardsCalled).toBeFalsy();
});
});
describe('when skipping starred dashboards', () => {
let getStarredCalled = false;
beforeEach(() => {
searchMock.mockImplementation(jest.fn().mockResolvedValue(Promise.resolve([])));
impressionSrv.getDashboardOpened = jest.fn().mockResolvedValue([]);
searchSrv['getStarred'] = () => {
getStarredCalled = true;
return Promise.resolve({});
};
return searchSrv.search({ skipStarred: true }).then(() => {});
});
it('should not fetch starred dashboards', () => {
expect(getStarredCalled).toBeFalsy();
});
});
});

@ -4,7 +4,9 @@ import userEvent from '@testing-library/user-event';
import React from 'react';
import { PanelPluginMeta, PluginType } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Panel } from '@grafana/schema';
import { getGrafanaSearcher } from 'app/features/search/service';
import { backendSrv } from '../../../../core/services/backend_srv';
import * as panelUtils from '../../../panel/state/util';
@ -63,9 +65,21 @@ async function getTestContext(
module: '',
sort: 1,
};
const getSpy = jest
.spyOn(backendSrv, 'get')
.mockResolvedValue({ sortOptions: [{ displaName: 'Desc', name: 'alpha-desc' }] });
config.featureToggles = { panelTitleSearch: false };
const getSpy = jest.spyOn(backendSrv, 'get');
jest.spyOn(getGrafanaSearcher(), 'getSortOptions').mockResolvedValue([
{
label: 'Alphabetically (A–Z)',
value: 'alpha-asc',
},
{
label: 'Alphabetically (Z–A)',
value: 'alpha-desc',
},
]);
const getLibraryPanelsSpy = jest.spyOn(api, 'getLibraryPanels').mockResolvedValue(searchResult);
const getAllPanelPluginMetaSpy = jest.spyOn(panelUtils, 'getAllPanelPluginMeta').mockReturnValue([graph, timeseries]);

@ -19,6 +19,7 @@ type Props = Pick<SearchResultsProps, 'selection' | 'selectionToggle' | 'onTagSe
tags?: string[];
hidePseudoFolders?: boolean;
};
export const FolderView = ({
selection,
selectionToggle,
@ -31,6 +32,7 @@ export const FolderView = ({
const results = useAsync(async () => {
const folders: DashboardSection[] = [];
if (!hidePseudoFolders) {
if (contextSrv.isSignedIn) {
const stars = await getBackendSrv().get('api/user/stars');
@ -44,6 +46,7 @@ export const FolderView = ({
folders.push({ title: 'Recent', icon: 'clock-nine', kind: 'query-recent', uid: '__recent', itemsUIDs });
}
}
folders.push({ title: 'General', url: '/dashboards', kind: 'folder', uid: GENERAL_FOLDER_UID });
const searcher = getGrafanaSearcher();

Loading…
Cancel
Save