From ddc8beda07c3d50d6dc6085532e9328250f6b896 Mon Sep 17 00:00:00 2001 From: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Date: Mon, 9 Jan 2023 18:33:48 +0100 Subject: [PATCH] Logs volume: Refactor (#60998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename log volume in redux to supp query to make it more generic * Change enabled query to queries * Small spell fix StoreSuppQueryDataProviderAction -> storeSuppQueryDataProviderAction * WIP * Improve * WIP * Rename * Move to 1 * Small updates * Use enum * Unify naming * Use SUPP_QUERY_TYPES instead of Object.keys() * Move SuppQueryType to types/explore * Rename suppQuery to supportingQuery * Rename SUPP_QUERY_TYPES to SUPPORTING_QUERY_TYPES * Rename supporting to supplementary 🙈 * Remove suppQueryData && suppQueryData.data[0] check as it is redundant * Update public/app/features/explore/state/query.ts Co-authored-by: Piotr Jamróz * Update public/app/features/explore/state/query.ts Co-authored-by: Piotr Jamróz * Fix naming of SupplementaryQuery interface Co-authored-by: Piotr Jamróz --- public/app/features/explore/Logs.tsx | 4 +- public/app/features/explore/LogsContainer.tsx | 32 +-- .../app/features/explore/state/datasource.ts | 5 +- public/app/features/explore/state/helpers.ts | 8 +- .../app/features/explore/state/query.test.ts | 99 +++++-- public/app/features/explore/state/query.ts | 248 ++++++++++++------ public/app/features/explore/state/utils.ts | 40 ++- public/app/types/explore.ts | 25 +- 8 files changed, 302 insertions(+), 159 deletions(-) diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx index 4894d8e109f..a874f5185f9 100644 --- a/public/app/features/explore/Logs.tsx +++ b/public/app/features/explore/Logs.tsx @@ -71,7 +71,7 @@ interface Props extends Themeable2 { logsVolumeData: DataQueryResponse | undefined; scrollElement?: HTMLDivElement; onSetLogsVolumeEnabled: (enabled: boolean) => void; - loadLogsVolumeData: (exploreId: ExploreId) => void; + loadLogsVolumeData: () => void; showContextToggle?: (row?: LogRowModel) => boolean; onChangeTime: (range: AbsoluteTimeRange) => void; onClickFilterLabel?: (key: string, value: string) => void; @@ -389,7 +389,7 @@ class UnthemedLogs extends PureComponent { onUpdateTimeRange={onChangeTime} timeZone={timeZone} splitOpen={splitOpen} - onLoadLogsVolume={() => loadLogsVolumeData(exploreId)} + onLoadLogsVolume={loadLogsVolumeData} onHiddenSeriesChanged={this.onToggleLogLevel} eventBus={this.logsVolumeEventBus} /> diff --git a/public/app/features/explore/LogsContainer.tsx b/public/app/features/explore/LogsContainer.tsx index 3d7bc2b44a3..85adaf2497d 100644 --- a/public/app/features/explore/LogsContainer.tsx +++ b/public/app/features/explore/LogsContainer.tsx @@ -14,13 +14,13 @@ import { } from '@grafana/data'; import { Collapse } from '@grafana/ui'; import { StoreState } from 'app/types'; -import { ExploreId, ExploreItemState } from 'app/types/explore'; +import { ExploreId, ExploreItemState, SupplementaryQueryType } from 'app/types/explore'; import { getTimeZone } from '../profile/state/selectors'; import { LiveLogsWithTheme } from './LiveLogs'; import { Logs } from './Logs'; -import { addResultsToCache, clearCache, loadLogsVolumeData, setLogsVolumeEnabled } from './state/query'; +import { addResultsToCache, clearCache, loadSupplementaryQueryData, setSupplementaryQueryEnabled } from './state/query'; import { updateTimeRange } from './state/time'; import { LiveTailControls } from './useLiveTailControls'; import { LogsCrossFadeTransition } from './utils/LogsCrossFadeTransition'; @@ -85,8 +85,8 @@ class LogsContainer extends PureComponent { logsMeta, logsSeries, logsQueries, - logsVolumeData, - loadLogsVolumeData, + loadSupplementaryQueryData, + setSupplementaryQueryEnabled, onClickFilterLabel, onClickFilterOutLabel, onStartScanning, @@ -103,6 +103,7 @@ class LogsContainer extends PureComponent { addResultsToCache, clearCache, scrollElement, + logsVolume, } = this.props; if (!logRows) { @@ -134,15 +135,17 @@ class LogsContainer extends PureComponent { logRows={logRows} logsMeta={logsMeta} logsSeries={logsSeries} - logsVolumeEnabled={this.props.logsVolumeEnabled} - onSetLogsVolumeEnabled={(enabled) => this.props.setLogsVolumeEnabled(exploreId, enabled)} - logsVolumeData={logsVolumeData} + logsVolumeEnabled={logsVolume.enabled} + onSetLogsVolumeEnabled={(enabled) => + setSupplementaryQueryEnabled(exploreId, enabled, SupplementaryQueryType.LogsVolume) + } + logsVolumeData={logsVolume.data} logsQueries={logsQueries} width={width} splitOpen={splitOpenFn} loading={loading} loadingState={loadingState} - loadLogsVolumeData={loadLogsVolumeData} + loadLogsVolumeData={() => loadSupplementaryQueryData(exploreId, SupplementaryQueryType.LogsVolume)} onChangeTime={this.onChangeTime} onClickFilterLabel={onClickFilterLabel} onClickFilterOutLabel={onClickFilterOutLabel} @@ -180,11 +183,10 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string } isPaused, range, absoluteRange, - logsVolumeEnabled, - logsVolumeDataProvider, - logsVolumeData, + supplementaryQueries, } = item; const timeZone = getTimeZone(state.user); + const logsVolume = supplementaryQueries[SupplementaryQueryType.LogsVolume]; return { loading, @@ -200,9 +202,7 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string } isPaused, range, absoluteRange, - logsVolumeEnabled, - logsVolumeDataProvider, - logsVolumeData, + logsVolume, }; } @@ -210,8 +210,8 @@ const mapDispatchToProps = { updateTimeRange, addResultsToCache, clearCache, - loadLogsVolumeData, - setLogsVolumeEnabled, + loadSupplementaryQueryData, + setSupplementaryQueryEnabled, }; const connector = connect(mapStateToProps, mapDispatchToProps); diff --git a/public/app/features/explore/state/datasource.ts b/public/app/features/explore/state/datasource.ts index eb67e44f75b..fe9793d828d 100644 --- a/public/app/features/explore/state/datasource.ts +++ b/public/app/features/explore/state/datasource.ts @@ -10,7 +10,7 @@ import { ExploreId } from 'app/types/explore'; import { importQueries, runQueries } from './query'; import { changeRefreshInterval } from './time'; -import { createEmptyQueryResponse, loadAndInitDatasource } from './utils'; +import { createEmptyQueryResponse, loadAndInitDatasource, loadSupplementaryQueries } from './utils'; // // Actions and Payloads @@ -99,8 +99,7 @@ export const datasourceReducer = (state: ExploreItemState, action: AnyAction): E graphResult: null, tableResult: null, logsResult: null, - logsVolumeDataProvider: undefined, - logsVolumeData: undefined, + supplementaryQueries: loadSupplementaryQueries(), queryResponse: createEmptyQueryResponse(), loading: false, queryKeys: [], diff --git a/public/app/features/explore/state/helpers.ts b/public/app/features/explore/state/helpers.ts index f8929a8f61f..4e1701b542e 100644 --- a/public/app/features/explore/state/helpers.ts +++ b/public/app/features/explore/state/helpers.ts @@ -1,6 +1,6 @@ import { DefaultTimeZone, toUtc } from '@grafana/data'; -import { ExploreId } from '../../../types'; +import { ExploreId, SupplementaryQueryType } from '../../../types'; export const createDefaultInitialState = () => { const t = toUtc(); @@ -40,7 +40,11 @@ export const createDefaultInitialState = () => { }, cache: [], richHistory: [], - logsVolumeEnabled: true, + supplementaryQueries: { + [SupplementaryQueryType.LogsVolume]: { + enabled: true, + }, + }, }, }, }; diff --git a/public/app/features/explore/state/query.test.ts b/public/app/features/explore/state/query.test.ts index f9b0487bb37..d3412e57a47 100644 --- a/public/app/features/explore/state/query.test.ts +++ b/public/app/features/explore/state/query.test.ts @@ -13,7 +13,7 @@ import { MutableDataFrame, RawTimeRange, } from '@grafana/data'; -import { ExploreId, ExploreItemState, StoreState, ThunkDispatch } from 'app/types'; +import { ExploreId, ExploreItemState, StoreState, SupplementaryQueryType, ThunkDispatch } from 'app/types'; import { reducerTester } from '../../../../test/core/redux/reducerTester'; import { configureStore } from '../../../store/configureStore'; @@ -26,15 +26,15 @@ import { addResultsToCache, cancelQueries, cancelQueriesAction, - cleanLogsVolumeAction, + cleanSupplementaryQueryAction, clearCache, importQueries, queryReducer, runQueries, scanStartAction, scanStopAction, - storeLogsVolumeDataProviderAction, - setLogsVolumeEnabled, + storeSupplementaryQueryDataProviderAction, + setSupplementaryQueryEnabled, } from './query'; import { makeExplorePaneState } from './utils'; @@ -164,6 +164,7 @@ describe('running queries', () => { querySubscription: unsubscribable, queries: ['A'], range: testRange, + supplementaryQueries: { [SupplementaryQueryType.LogsVolume]: { enabled: true } }, }, }, @@ -179,8 +180,8 @@ describe('running queries', () => { expect(dispatchedActions).toEqual([ scanStopAction({ exploreId }), cancelQueriesAction({ exploreId }), - storeLogsVolumeDataProviderAction({ exploreId, logsVolumeDataProvider: undefined }), - cleanLogsVolumeAction({ exploreId }), + storeSupplementaryQueryDataProviderAction({ exploreId, type: SupplementaryQueryType.LogsVolume }), + cleanSupplementaryQueryAction({ exploreId, type: SupplementaryQueryType.LogsVolume }), ]); }); }); @@ -433,8 +434,12 @@ describe('reducer', () => { expect(unsubscribes).toHaveLength(1); expect(unsubscribes[0]).toBeCalled(); - expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined(); - expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data + ).toBeUndefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider + ).toBeUndefined(); }); it('should load logs volume after running the query', async () => { @@ -448,13 +453,23 @@ describe('reducer', () => { }; await dispatch(runQueries(ExploreId.left)); - expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined(); - expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Loading); - expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data + ).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data!.state + ).toBe(LoadingState.Loading); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider + ).toBeDefined(); await dispatch(cancelQueries(ExploreId.left)); - expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined(); - expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data + ).toBeUndefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data + ).toBeUndefined(); }); it('keeps complete log volume data when main query is canceled', async () => { @@ -466,43 +481,69 @@ describe('reducer', () => { }; await dispatch(runQueries(ExploreId.left)); - expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined(); - expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Done); - expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data + ).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data!.state + ).toBe(LoadingState.Done); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider + ).toBeDefined(); await dispatch(cancelQueries(ExploreId.left)); - expect(getState().explore[ExploreId.left].logsVolumeData).toBeDefined(); - expect(getState().explore[ExploreId.left].logsVolumeData!.state).toBe(LoadingState.Done); - expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeUndefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data + ).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data!.state + ).toBe(LoadingState.Done); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider + ).toBeUndefined(); }); it('do not load logsVolume data when disabled', async () => { // turn logsvolume off - dispatch(setLogsVolumeEnabled(ExploreId.left, false)); - expect(getState().explore[ExploreId.left].logsVolumeEnabled).toBe(false); + dispatch(setSupplementaryQueryEnabled(ExploreId.left, false, SupplementaryQueryType.LogsVolume)); + expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe( + false + ); // verify that if we run a query, it will not do logsvolume, but the Provider will still be set await dispatch(runQueries(ExploreId.left)); - expect(getState().explore[ExploreId.left].logsVolumeData).toBeUndefined(); - expect(getState().explore[ExploreId.left].logsVolumeDataSubscription).toBeUndefined(); - expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data + ).toBeUndefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription + ).toBeUndefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider + ).toBeDefined(); }); it('load logsVolume data when it gets enabled', async () => { // first it is disabled - dispatch(setLogsVolumeEnabled(ExploreId.left, false)); + dispatch(setSupplementaryQueryEnabled(ExploreId.left, false, SupplementaryQueryType.LogsVolume)); // runQueries sets up the logsVolume query, but does not run it await dispatch(runQueries(ExploreId.left)); - expect(getState().explore[ExploreId.left].logsVolumeDataProvider).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider + ).toBeDefined(); // we turn logsvolume on - await dispatch(setLogsVolumeEnabled(ExploreId.left, true)); + await dispatch(setSupplementaryQueryEnabled(ExploreId.left, true, SupplementaryQueryType.LogsVolume)); // verify it was turned on - expect(getState().explore[ExploreId.left].logsVolumeEnabled).toBe(true); + expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe( + true + ); - expect(getState().explore[ExploreId.left].logsVolumeDataSubscription).toBeDefined(); + expect( + getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription + ).toBeDefined(); }); }); }); diff --git a/public/app/features/explore/state/query.ts b/public/app/features/explore/state/query.ts index ddff617d572..1ee37bb4b7d 100644 --- a/public/app/features/explore/state/query.ts +++ b/public/app/features/explore/state/query.ts @@ -36,7 +36,7 @@ import { getTimeZone } from 'app/features/profile/state/selectors'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; import { store } from 'app/store/store'; import { ExploreItemState, ExplorePanelData, ThunkDispatch, ThunkResult } from 'app/types'; -import { ExploreId, ExploreState, QueryOptions } from 'app/types/explore'; +import { ExploreId, ExploreState, QueryOptions, SupplementaryQueryType, SupplementaryQueries } from 'app/types/explore'; import { notifyApp } from '../../../core/actions'; import { createErrorNotification } from '../../../core/copy/appNotification'; @@ -46,7 +46,12 @@ import { decorateData } from '../utils/decorators'; import { addHistoryItem, historyUpdatedAction, loadRichHistory } from './history'; import { stateSave } from './main'; import { updateTime } from './time'; -import { createCacheKey, getResultsFromCache, storeLogsVolumeEnabled } from './utils'; +import { + createCacheKey, + getResultsFromCache, + storeSupplementaryQueryEnabled, + SUPPLEMENTARY_QUERY_TYPES, +} from './utils'; // // Actions and Payloads @@ -95,43 +100,50 @@ export const queryStoreSubscriptionAction = createAction( - 'explore/setLogsVolumeEnabledAction' -); - -export interface StoreLogsVolumeDataProvider { +const setSupplementaryQueryEnabledAction = createAction<{ exploreId: ExploreId; - logsVolumeDataProvider?: Observable; + type: SupplementaryQueryType; + enabled: boolean; +}>('explore/setSupplementaryQueryEnabledAction'); + +export interface StoreSupplementaryQueryDataProvider { + exploreId: ExploreId; + dataProvider?: Observable; + type: SupplementaryQueryType; } /** * Stores available logs volume provider after running the query. Used internally by runQueries(). */ -export const storeLogsVolumeDataProviderAction = createAction( - 'explore/storeLogsVolumeDataProviderAction' +export const storeSupplementaryQueryDataProviderAction = createAction( + 'explore/storeSupplementaryQueryDataProviderAction' ); -export const cleanLogsVolumeAction = createAction<{ exploreId: ExploreId }>('explore/cleanLogsVolumeAction'); +export const cleanSupplementaryQueryAction = createAction<{ exploreId: ExploreId; type: SupplementaryQueryType }>( + 'explore/cleanSupplementaryQueryAction' +); -export interface StoreLogsVolumeDataSubscriptionPayload { +export interface StoreSupplementaryQueryDataSubscriptionPayload { exploreId: ExploreId; - logsVolumeDataSubscription?: SubscriptionLike; + dataSubscription?: SubscriptionLike; + type: SupplementaryQueryType; } /** * Stores current logs volume subscription for given explore pane. */ -const storeLogsVolumeDataSubscriptionAction = createAction( - 'explore/storeLogsVolumeDataSubscriptionAction' +const storeSupplementaryQueryDataSubscriptionAction = createAction( + 'explore/storeSupplementaryQueryDataSubscriptionAction' ); /** - * Stores data returned by the provider. Used internally by loadLogsVolumeData(). + * Stores data returned by the provider. Used internally by loadSupplementaryQueryData(). */ -const updateLogsVolumeDataAction = createAction<{ +const updateSupplementaryQueryDataAction = createAction<{ exploreId: ExploreId; - logsVolumeData: DataQueryResponse; -}>('explore/updateLogsVolumeDataAction'); + type: SupplementaryQueryType; + data: DataQueryResponse; +}>('explore/updateSupplementaryQueryDataAction'); export interface QueryEndedPayload { exploreId: ExploreId; @@ -231,15 +243,16 @@ export function cancelQueries(exploreId: ExploreId): ThunkResult { return (dispatch, getState) => { dispatch(scanStopAction({ exploreId })); dispatch(cancelQueriesAction({ exploreId })); - dispatch( - storeLogsVolumeDataProviderAction({ - exploreId, - logsVolumeDataProvider: undefined, - }) - ); - // clear any incomplete data - if (getState().explore[exploreId]!.logsVolumeData?.state !== LoadingState.Done) { - dispatch(cleanLogsVolumeAction({ exploreId })); + + const supplementaryQueries = getState().explore[exploreId]!.supplementaryQueries; + // Cancel all data providers + for (const type of SUPPLEMENTARY_QUERY_TYPES) { + dispatch(storeSupplementaryQueryDataProviderAction({ exploreId, dataProvider: undefined, type })); + + // And clear any incomplete data + if (supplementaryQueries[type]?.data?.state !== LoadingState.Done) { + dispatch(cleanSupplementaryQueryAction({ exploreId, type })); + } } dispatch(stateSave()); }; @@ -425,7 +438,6 @@ export const runQueries = ( refreshInterval, absoluteRange, cache, - logsVolumeEnabled, } = exploreItemState; let newQuerySub; @@ -558,13 +570,25 @@ export const runQueries = ( }); if (live) { - dispatch( - storeLogsVolumeDataProviderAction({ - exploreId, - logsVolumeDataProvider: undefined, - }) - ); - dispatch(cleanLogsVolumeAction({ exploreId })); + for (const type of SUPPLEMENTARY_QUERY_TYPES) { + dispatch( + storeSupplementaryQueryDataProviderAction({ + exploreId, + dataProvider: undefined, + type, + }) + ); + dispatch(cleanSupplementaryQueryAction({ exploreId, type })); + } + + // In this whole part., we need to figure out + // checking the type of enabled supp queries + // then for which enabled supp queries has data source support + // and then we need to run the supp queries + // but we need to make sure that supp queries that dont work + // return undefined provider + // we should also make sure we store the type of provider that + // was last stored } else if (hasLogsVolumeSupport(datasourceInstance)) { // we always prepare the logsVolumeProvider, // but we only load it, if the logs-volume-histogram is enabled. @@ -576,25 +600,29 @@ export const runQueries = ( ...transaction.request, requestId: transaction.request.requestId + '_log_volume', }; - const logsVolumeDataProvider = datasourceInstance.getLogsVolumeDataProvider(sourceRequest); + const type = SupplementaryQueryType.LogsVolume; + const dataProvider = datasourceInstance.getLogsVolumeDataProvider(sourceRequest); dispatch( - storeLogsVolumeDataProviderAction({ + storeSupplementaryQueryDataProviderAction({ exploreId, - logsVolumeDataProvider, + type, + dataProvider, }) ); - const { logsVolumeData, absoluteRange } = getState().explore[exploreId]!; - if (!canReuseLogsVolumeData(logsVolumeData, queries, absoluteRange)) { - dispatch(cleanLogsVolumeAction({ exploreId })); - if (logsVolumeEnabled) { - dispatch(loadLogsVolumeData(exploreId)); + + const { supplementaryQueries, absoluteRange } = getState().explore[exploreId]!; + if (!canReuseSupplementaryQueryData(supplementaryQueries[type].data, queries, absoluteRange)) { + dispatch(cleanSupplementaryQueryAction({ exploreId, type })); + if (supplementaryQueries[type].enabled) { + dispatch(loadSupplementaryQueryData(exploreId, type)); } } } else { dispatch( - storeLogsVolumeDataProviderAction({ + storeSupplementaryQueryDataProviderAction({ exploreId, - logsVolumeDataProvider: undefined, + dataProvider: undefined, + type: SupplementaryQueryType.LogsVolume, }) ); } @@ -605,20 +633,20 @@ export const runQueries = ( }; /** - * Checks if after changing the time range the existing data can be used to show logs volume. + * Checks if after changing the time range the existing data can be used to show supplementary query. * It can happen if queries are the same and new time range is within existing data time range. */ -function canReuseLogsVolumeData( - logsVolumeData: DataQueryResponse | undefined, +function canReuseSupplementaryQueryData( + supplementaryQueryData: DataQueryResponse | undefined, queries: DataQuery[], selectedTimeRange: AbsoluteTimeRange ): boolean { - if (logsVolumeData && logsVolumeData.data[0]) { + if (supplementaryQueryData && supplementaryQueryData.data[0]) { // check if queries are the same - if (!deepEqual(logsVolumeData.data[0].meta?.custom?.targets, queries)) { + if (!deepEqual(supplementaryQueryData.data[0].meta?.custom?.targets, queries)) { return false; } - const dataRange = logsVolumeData && logsVolumeData.data[0] && logsVolumeData.data[0].meta?.custom?.absoluteRange; + const dataRange = supplementaryQueryData.data[0].meta?.custom?.absoluteRange; // if selected range is within loaded logs volume if (dataRange && dataRange.from <= selectedTimeRange.from && selectedTimeRange.to <= dataRange.to) { return true; @@ -664,7 +692,7 @@ export function addResultsToCache(exploreId: ExploreId): ThunkResult { const absoluteRange = getState().explore[exploreId]!.absoluteRange; const cacheKey = createCacheKey(absoluteRange); - // Save results to cache only when all results recived and loading is done + // Save results to cache only when all results received and loading is done if (queryResponse.state === LoadingState.Done) { dispatch(addResultsToCacheAction({ exploreId, cacheKey, queryResponse })); } @@ -680,26 +708,38 @@ export function clearCache(exploreId: ExploreId): ThunkResult { /** * Initializes loading logs volume data and stores emitted value. */ -export function loadLogsVolumeData(exploreId: ExploreId): ThunkResult { +export function loadSupplementaryQueryData(exploreId: ExploreId, type: SupplementaryQueryType): ThunkResult { return (dispatch, getState) => { - const { logsVolumeDataProvider } = getState().explore[exploreId]!; - if (logsVolumeDataProvider) { - const logsVolumeDataSubscription = logsVolumeDataProvider.subscribe({ - next: (logsVolumeData: DataQueryResponse) => { - dispatch(updateLogsVolumeDataAction({ exploreId, logsVolumeData })); + const { supplementaryQueries } = getState().explore[exploreId]!; + const dataProvider = supplementaryQueries[type].dataProvider; + + if (dataProvider) { + const dataSubscription = dataProvider.subscribe({ + next: (supplementaryQueryData: DataQueryResponse) => { + dispatch(updateSupplementaryQueryDataAction({ exploreId, type, data: supplementaryQueryData })); }, }); - dispatch(storeLogsVolumeDataSubscriptionAction({ exploreId, logsVolumeDataSubscription })); + dispatch( + storeSupplementaryQueryDataSubscriptionAction({ + exploreId, + type, + dataSubscription, + }) + ); } }; } -export function setLogsVolumeEnabled(exploreId: ExploreId, enabled: boolean): ThunkResult { +export function setSupplementaryQueryEnabled( + exploreId: ExploreId, + enabled: boolean, + type: SupplementaryQueryType +): ThunkResult { return (dispatch, getState) => { - dispatch(setLogsVolumeEnabledAction({ exploreId, enabled })); - storeLogsVolumeEnabled(enabled); + dispatch(setSupplementaryQueryEnabledAction({ exploreId, enabled, type })); + storeSupplementaryQueryEnabled(enabled, type); if (enabled) { - dispatch(loadLogsVolumeData(exploreId)); + dispatch(loadSupplementaryQueryData(exploreId, type)); } }; } @@ -763,53 +803,87 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor }; } - if (setLogsVolumeEnabledAction.match(action)) { - const { enabled } = action.payload; - if (!enabled && state.logsVolumeDataSubscription) { - state.logsVolumeDataSubscription.unsubscribe(); + if (setSupplementaryQueryEnabledAction.match(action)) { + const { enabled, type } = action.payload; + const { supplementaryQueries } = state; + const dataSubscription = supplementaryQueries[type].dataSubscription; + if (!enabled && dataSubscription) { + dataSubscription.unsubscribe(); } - return { - ...state, - logsVolumeEnabled: enabled, + + const nextSupplementaryQueries: SupplementaryQueries = { + ...supplementaryQueries, // NOTE: the dataProvider is not cleared, we may need it later, - // if the user re-enables the histogram-visualization - logsVolumeData: undefined, + // if the user re-enables the supplementary query + [type]: { ...supplementaryQueries[type], enabled, data: undefined }, + }; + + return { + ...state, + supplementaryQueries: nextSupplementaryQueries, }; } - if (storeLogsVolumeDataProviderAction.match(action)) { - let { logsVolumeDataProvider } = action.payload; - if (state.logsVolumeDataSubscription) { - state.logsVolumeDataSubscription.unsubscribe(); + if (storeSupplementaryQueryDataProviderAction.match(action)) { + const { dataProvider, type } = action.payload; + const { supplementaryQueries } = state; + const supplementaryQuery = supplementaryQueries[type]; + + if (supplementaryQuery?.dataSubscription) { + supplementaryQuery.dataSubscription.unsubscribe(); } + + const nextSupplementaryQueries = { + ...supplementaryQueries, + [type]: { ...supplementaryQuery, dataProvider, dataSubscription: undefined }, + }; + return { ...state, - logsVolumeDataProvider, - logsVolumeDataSubscription: undefined, + supplementaryQueries: nextSupplementaryQueries, }; } - if (cleanLogsVolumeAction.match(action)) { + if (cleanSupplementaryQueryAction.match(action)) { + const { type } = action.payload; + const { supplementaryQueries } = state; + const nextSupplementaryQueries = { + ...supplementaryQueries, + [type]: { ...supplementaryQueries[type], data: undefined }, + }; return { ...state, - logsVolumeData: undefined, + supplementaryQueries: nextSupplementaryQueries, }; } - if (storeLogsVolumeDataSubscriptionAction.match(action)) { - const { logsVolumeDataSubscription } = action.payload; + if (storeSupplementaryQueryDataSubscriptionAction.match(action)) { + const { dataSubscription, type } = action.payload; + + const { supplementaryQueries } = state; + const nextSupplementaryQueries = { + ...supplementaryQueries, + [type]: { ...supplementaryQueries[type], dataSubscription }, + }; + return { ...state, - logsVolumeDataSubscription, + supplementaryQueries: nextSupplementaryQueries, }; } - if (updateLogsVolumeDataAction.match(action)) { - let { logsVolumeData } = action.payload; + if (updateSupplementaryQueryDataAction.match(action)) { + let { data, type } = action.payload; + const { supplementaryQueries } = state; + + const nextSupplementaryQueries = { + ...supplementaryQueries, + [type]: { ...supplementaryQueries[type], data }, + }; return { ...state, - logsVolumeData, + supplementaryQueries: nextSupplementaryQueries, }; } diff --git a/public/app/features/explore/state/utils.ts b/public/app/features/explore/state/utils.ts index 5d31ad3c5d0..4a81162c232 100644 --- a/public/app/features/explore/state/utils.ts +++ b/public/app/features/explore/state/utils.ts @@ -12,7 +12,7 @@ import { PanelData, } from '@grafana/data'; import { ExplorePanelData } from 'app/types'; -import { ExploreItemState } from 'app/types/explore'; +import { ExploreItemState, SupplementaryQueries, SupplementaryQueryType } from 'app/types/explore'; import store from '../../../core/store'; import { clearQueryKeys, lastUsedDatasourceKeyForOrgId } from '../../../core/utils/explore'; @@ -20,6 +20,14 @@ import { getDatasourceSrv } from '../../plugins/datasource_srv'; import { SETTINGS_KEYS } from '../utils/logs'; import { toRawTimeRange } from '../utils/time'; +export const SUPPLEMENTARY_QUERY_TYPES: SupplementaryQueryType[] = [SupplementaryQueryType.LogsVolume]; + +// Used to match supplementaryQueryType to corresponding local storage key +// TODO: Remove this and unify enum values with SETTINGS_KEYS.enableVolumeHistogram +const supplementaryQuerySettings: { [key in SupplementaryQueryType]: string } = { + [SupplementaryQueryType.LogsVolume]: SETTINGS_KEYS.enableVolumeHistogram, +}; + export const DEFAULT_RANGE = { from: 'now-6h', to: 'now', @@ -30,19 +38,25 @@ export const storeGraphStyle = (graphStyle: string): void => { store.set(GRAPH_STYLE_KEY, graphStyle); }; -const LOGS_VOLUME_ENABLED_KEY = SETTINGS_KEYS.enableVolumeHistogram; -export const storeLogsVolumeEnabled = (enabled: boolean): void => { - store.set(LOGS_VOLUME_ENABLED_KEY, enabled ? 'true' : 'false'); +export const storeSupplementaryQueryEnabled = (enabled: boolean, type: SupplementaryQueryType): void => { + if (supplementaryQuerySettings[type]) { + store.set(supplementaryQuerySettings[type], enabled ? 'true' : 'false'); + } }; -const loadLogsVolumeEnabled = (): boolean => { - const data = store.get(LOGS_VOLUME_ENABLED_KEY); - // we default to `enabled=true` - if (data === 'false') { - return false; - } +export const loadSupplementaryQueries = (): SupplementaryQueries => { + // We default to true for all supp queries + let supplementaryQueries: SupplementaryQueries = { + [SupplementaryQueryType.LogsVolume]: { enabled: true }, + }; - return true; + for (const type of SUPPLEMENTARY_QUERY_TYPES) { + // Only if "false" value in local storage, we disable it + if (store.get(supplementaryQuerySettings[type]) === 'false') { + supplementaryQueries[type] = { enabled: false }; + } + } + return supplementaryQueries; }; /** @@ -77,9 +91,7 @@ export const makeExplorePaneState = (): ExploreItemState => ({ eventBridge: null as unknown as EventBusExtended, cache: [], richHistory: [], - logsVolumeEnabled: loadLogsVolumeEnabled(), - logsVolumeDataProvider: undefined, - logsVolumeData: undefined, + supplementaryQueries: loadSupplementaryQueries(), panelsState: {}, }); diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 28b966563ac..b01e7faf787 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -204,12 +204,10 @@ export interface ExploreItemState { */ cache: Array<{ key: string; value: ExplorePanelData }>; - // properties below should be more generic if we add more providers - // see also: DataSourceWithLogsVolumeSupport - logsVolumeEnabled: boolean; - logsVolumeDataProvider?: Observable; - logsVolumeDataSubscription?: SubscriptionLike; - logsVolumeData?: DataQueryResponse; + /** + * Supplementary queries are additional queries used in Explore, e.g. for logs volume + */ + supplementaryQueries: SupplementaryQueries; panelsState: ExplorePanelsState; @@ -270,3 +268,18 @@ export enum TABLE_RESULTS_STYLE { } export const TABLE_RESULTS_STYLES = [TABLE_RESULTS_STYLE.table, TABLE_RESULTS_STYLE.raw]; export type TableResultsStyle = typeof TABLE_RESULTS_STYLES[number]; + +export interface SupplementaryQuery { + enabled: boolean; + dataProvider?: Observable; + dataSubscription?: SubscriptionLike; + data?: DataQueryResponse; +} + +export type SupplementaryQueries = { + [key in SupplementaryQueryType]: SupplementaryQuery; +}; + +export enum SupplementaryQueryType { + LogsVolume = 'LogsVolume', +}