diff --git a/public/app/features/explore/state/query.test.ts b/public/app/features/explore/state/query.test.ts index 334270c8f68..eb84d558a84 100644 --- a/public/app/features/explore/state/query.test.ts +++ b/public/app/features/explore/state/query.test.ts @@ -66,6 +66,9 @@ const datasources: DataSourceApi[] = [ getRef: () => { return { type: 'postgres', uid: 'ds1' }; }, + filterQuery: (query: DataQuery) => { + return query.key === 'true'; + }, } as DataSourceApi, { name: 'testDs2', @@ -227,6 +230,56 @@ describe('runQueries', () => { expect((richHistory.addToRichHistory as jest.Mock).mock.calls).toHaveLength(1); expect((richHistory.addToRichHistory as jest.Mock).mock.calls[0][0].localOverride).toBeTruthy(); }); + + /* the next two tests are for ensuring the query datasource's filterQuery function stops queries + from being saved to rich history. We do that by setting a fake datasource in this test (datasources[0]) + to filter queries off their key value + + datasources[1] does not have filterQuery defined + */ + it('with filterQuery defined, should not save filtered out queries to history', async () => { + const { dispatch } = configureStore({ + ...defaultInitialState, + explore: { + panes: { + left: { + ...defaultInitialState.explore.panes.left, + datasourceInstance: datasources[0], + queries: [ + { refId: 'A', key: 'false' }, + { refId: 'B', key: 'true' }, + ], + }, + }, + }, + } as unknown as Partial); + jest.spyOn(richHistory, 'addToRichHistory'); + await dispatch(runQueries({ exploreId: 'left' })); + const calls = (richHistory.addToRichHistory as jest.Mock).mock.calls; + expect(calls).toHaveLength(1); + expect(calls[0][0].queries).toHaveLength(1); + expect(calls[0][0].queries[0].refId).toEqual('B'); + }); + + it('with filterQuery not defined, all queries are saved', async () => { + const { dispatch } = configureStore({ + ...defaultInitialState, + explore: { + panes: { + left: { + ...defaultInitialState.explore.panes.left, + datasourceInstance: datasources[1], + queries: [{ refId: 'A' }, { refId: 'B' }], + }, + }, + }, + } as unknown as Partial); + jest.spyOn(richHistory, 'addToRichHistory'); + await dispatch(runQueries({ exploreId: 'left' })); + const calls = (richHistory.addToRichHistory as jest.Mock).mock.calls; + expect(calls).toHaveLength(1); + expect(calls[0][0].queries).toHaveLength(2); + }); }); describe('running queries', () => { diff --git a/public/app/features/explore/state/query.ts b/public/app/features/explore/state/query.ts index d6e2d017512..4aa94686a76 100644 --- a/public/app/features/explore/state/query.ts +++ b/public/app/features/explore/state/query.ts @@ -38,6 +38,7 @@ import { import { getShiftedTimeRange } from 'app/core/utils/timePicker'; import { getCorrelationsBySourceUIDs } from 'app/features/correlations/utils'; import { infiniteScrollRefId } from 'app/features/logs/logsModel'; +import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getFiscalYearStartMonth, getTimeZone } from 'app/features/profile/state/selectors'; import { SupportingQueryType } from 'app/plugins/datasource/loki/types'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; @@ -476,25 +477,48 @@ export function modifyQueries( }; } +function filterQuery(datasource: DataSourceApi, query: DataQuery) { + if (datasource.filterQuery && !datasource.filterQuery(query)) { + return undefined; // if filterQuery is implemented and returns false, do not use query + } else { + return query; // if filterQuery is not implemented or it is and returns true, use it + } +} + async function handleHistory( dispatch: ThunkDispatch, state: ExploreState, datasource: DataSourceApi, queries: DataQuery[] ) { - /* + const filteredQueriesRes = await Promise.all( + queries.map(async (query) => { + if (query.datasource?.uid === datasource.uid) { + return filterQuery(datasource, query); + } else { + const queryDS = await getDatasourceSrv().get(query.datasource); + return filterQuery(queryDS, query); + } + }) + ); + + const filteredQueries = filteredQueriesRes.filter((query): query is DataQuery => !!query); + + if (filteredQueries.length > 0) { + /* Always write to local storage. If query history is enabled, we will use local storage for autocomplete only (and want to hide errors) If query history is disabled, we will use local storage for query history as well, and will want to show errors */ - dispatch(addHistoryItem(true, datasource.uid, datasource.name, queries, config.queryHistoryEnabled)); - if (config.queryHistoryEnabled) { - // write to remote if flag enabled - dispatch(addHistoryItem(false, datasource.uid, datasource.name, queries, false)); - } + dispatch(addHistoryItem(true, datasource.uid, datasource.name, filteredQueries, config.queryHistoryEnabled)); + if (config.queryHistoryEnabled) { + // write to remote if flag enabled + dispatch(addHistoryItem(false, datasource.uid, datasource.name, filteredQueries, false)); + } - // Because filtering happens in the backend we cannot add a new entry without checking if it matches currently - // used filters. Instead, we refresh the query history list. - await dispatch(loadRichHistory()); + // Because filtering happens in the backend we cannot add a new entry without checking if it matches currently + // used filters. Instead, we refresh the query history list. + await dispatch(loadRichHistory()); + } } interface RunQueriesOptions {