Search: Fix Search returning results out of order (#68370)

* Search: Fix Search returning results out of order

* test
This commit is contained in:
Josh Hunt 2023-05-12 15:42:04 +01:00 committed by GitHub
parent c8fd3c20cd
commit 78afddebbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 23 deletions

View File

@ -7,6 +7,15 @@ import * as utils from '../utils';
import { getSearchStateManager } from './SearchStateManager'; import { getSearchStateManager } from './SearchStateManager';
jest.mock('lodash', () => {
const orig = jest.requireActual('lodash');
return {
...orig,
debounce: (d: Function) => d,
};
});
jest.mock('@grafana/runtime', () => { jest.mock('@grafana/runtime', () => {
const originalModule = jest.requireActual('@grafana/runtime'); const originalModule = jest.requireActual('@grafana/runtime');
return { return {
@ -15,7 +24,8 @@ jest.mock('@grafana/runtime', () => {
}); });
describe('SearchStateManager', () => { describe('SearchStateManager', () => {
jest.spyOn(getGrafanaSearcher(), 'search').mockResolvedValue({ const searcher = getGrafanaSearcher();
jest.spyOn(searcher, 'search').mockResolvedValue({
isItemLoaded: jest.fn(), isItemLoaded: jest.fn(),
loadMoreItems: jest.fn(), loadMoreItems: jest.fn(),
totalRows: 0, totalRows: 0,
@ -69,5 +79,44 @@ describe('SearchStateManager', () => {
expect(stm.state.sort).toBe(undefined); expect(stm.state.sort).toBe(undefined);
expect(stm.state.folderUid).toBe('abc'); expect(stm.state.folderUid).toBe('abc');
}); });
it('updates search results in order', async () => {
const stm = getSearchStateManager();
jest.spyOn(searcher, 'search').mockReturnValueOnce(
new Promise(async (resolve) => {
await wait(100);
resolve({
isItemLoaded: jest.fn(),
loadMoreItems: jest.fn(),
totalRows: 100,
view: new DataFrameView<DashboardQueryResult>({ fields: [], length: 0 }),
});
})
);
stm.onQueryChange('d');
jest.spyOn(searcher, 'search').mockReturnValueOnce(
new Promise(async (resolve) => {
await wait(50);
resolve({
isItemLoaded: jest.fn(),
loadMoreItems: jest.fn(),
totalRows: 10,
view: new DataFrameView<DashboardQueryResult>({ fields: [], length: 0 }),
});
})
);
stm.onQueryChange('debugging');
await wait(150);
expect(stm.state.result?.totalRows).toEqual(10);
}); });
}); });
});
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

View File

@ -39,6 +39,7 @@ export class SearchStateManager extends StateManagerBase<SearchState> {
updateLocation = debounce((query) => locationService.partial(query, true), 300); updateLocation = debounce((query) => locationService.partial(query, true), 300);
doSearchWithDebounce = debounce(() => this.doSearch(), 300); doSearchWithDebounce = debounce(() => this.doSearch(), 300);
lastQuery?: SearchQuery; lastQuery?: SearchQuery;
lastSearchPromise: Promise<void> = Promise.resolve();
initStateFromUrl(folderUid?: string, doInitialSearch = true) { initStateFromUrl(folderUid?: string, doInitialSearch = true) {
const stateFromUrl = parseRouteParams(locationService.getSearchObject()); const stateFromUrl = parseRouteParams(locationService.getSearchObject());
@ -224,9 +225,13 @@ export class SearchStateManager extends StateManagerBase<SearchState> {
this.setState({ loading: true }); this.setState({ loading: true });
if (this.state.starred) { const searcher = getGrafanaSearcher();
getGrafanaSearcher()
.starred(this.lastQuery) // Issue this search now, but wait until previous searches have resolved to update it in the state
const searchPromise = this.state.starred ? searcher.starred(this.lastQuery) : searcher.search(this.lastQuery);
this.lastSearchPromise = this.lastSearchPromise
.then(() => searchPromise)
.then((result) => this.setState({ result, loading: false })) .then((result) => this.setState({ result, loading: false }))
.catch((error) => { .catch((error) => {
reportSearchFailedQueryInteraction(this.state.eventTrackingNamespace, { reportSearchFailedQueryInteraction(this.state.eventTrackingNamespace, {
@ -234,19 +239,9 @@ export class SearchStateManager extends StateManagerBase<SearchState> {
error: error?.message, error: error?.message,
}); });
this.setState({ loading: false }); this.setState({ loading: false });
return Promise.resolve(); // make sure this.lastSearchPromise is always resolved
}); });
} else {
getGrafanaSearcher()
.search(this.lastQuery)
.then((result) => this.setState({ result, loading: false }))
.catch((error) => {
reportSearchFailedQueryInteraction(this.state.eventTrackingNamespace, {
...trackingInfo,
error: error?.message,
});
this.setState({ loading: false });
});
}
} }
// This gets the possible tags from within the query results // This gets the possible tags from within the query results