diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts index ac48b619f88..0ee3135712a 100644 --- a/public/app/features/explore/state/actionTypes.ts +++ b/public/app/features/explore/state/actionTypes.ts @@ -178,6 +178,7 @@ export interface UpdateDatasourceInstancePayload { exploreId: ExploreId; datasourceInstance: DataSourceApi; version?: string; + mode?: ExploreMode; } export interface ToggleLogLevelPayload { diff --git a/public/app/features/explore/state/actions.test.ts b/public/app/features/explore/state/actions.test.ts index b310f0a9e17..1684266137d 100644 --- a/public/app/features/explore/state/actions.test.ts +++ b/public/app/features/explore/state/actions.test.ts @@ -1,4 +1,5 @@ -import { loadDatasource, navigateToExplore, refreshExplore } from './actions'; +import { changeDatasource, loadDatasource, navigateToExplore, refreshExplore } from './actions'; +import * as Actions from './actions'; import { ExploreId, ExploreMode, ExploreUpdateState, ExploreUrlState } from 'app/types'; import { thunkTester } from 'test/core/thunk/thunkTester'; import { @@ -8,6 +9,7 @@ import { loadDatasourceReadyAction, setQueriesAction, updateUIStateAction, + updateDatasourceInstanceAction, } from './actionTypes'; import { Emitter } from 'app/core/core'; import { ActionOf } from 'app/core/redux/actionCreatorFactory'; @@ -16,16 +18,24 @@ import { DataQuery, DefaultTimeZone, LogsDedupStrategy, RawTimeRange, toUtc } fr import { PanelModel } from 'app/features/dashboard/state'; import { updateLocation } from '../../../core/actions'; import { MockDataSourceApi } from '../../../../test/mocks/datasource_srv'; +import * as DatasourceSrv from 'app/features/plugins/datasource_srv'; -jest.mock('app/features/plugins/datasource_srv', () => ({ - getDatasourceSrv: () => ({ - getExternal: jest.fn().mockReturnValue([]), - get: jest.fn().mockReturnValue({ - testDatasource: jest.fn(), - init: jest.fn(), - }), - }), -})); +jest.mock('app/features/plugins/datasource_srv'); +const getDatasourceSrvMock = (DatasourceSrv.getDatasourceSrv as any) as jest.Mock; + +beforeEach(() => { + getDatasourceSrvMock.mockClear(); + getDatasourceSrvMock.mockImplementation( + () => + ({ + getExternal: jest.fn().mockReturnValue([]), + get: jest.fn().mockReturnValue({ + testDatasource: jest.fn(), + init: jest.fn(), + }), + } as any) + ); +}); jest.mock('../../dashboard/services/TimeSrv', () => ({ getTimeSrv: jest.fn().mockReturnValue({ @@ -163,6 +173,62 @@ describe('refreshExplore', () => { }); }); +describe('changing datasource', () => { + it('should switch to logs mode when changing from prometheus to loki', async () => { + const lokiMock = { + testDatasource: () => Promise.resolve({ status: 'success' }), + name: 'Loki', + init: jest.fn(), + meta: { id: 'some id', name: 'Loki' }, + }; + + getDatasourceSrvMock.mockImplementation( + () => + ({ + getExternal: jest.fn().mockReturnValue([]), + get: jest.fn().mockReturnValue(lokiMock), + } as any) + ); + + const exploreId = ExploreId.left; + const name = 'Prometheus'; + const mockPromDatasourceInstance = { + testDatasource: () => Promise.resolve({ status: 'success' }), + name, + init: jest.fn(), + meta: { id: 'some id', name }, + }; + + const initialState = { + explore: { + [exploreId]: { + requestedDatasourceName: 'Loki', + datasourceInstance: mockPromDatasourceInstance, + }, + }, + user: { + orgId: 1, + }, + }; + + jest.spyOn(Actions, 'importQueries').mockImplementationOnce(() => jest.fn); + jest.spyOn(Actions, 'loadDatasource').mockImplementationOnce(() => jest.fn); + jest.spyOn(Actions, 'runQueries').mockImplementationOnce(() => jest.fn); + const dispatchedActions = await thunkTester(initialState) + .givenThunk(changeDatasource) + .whenThunkIsDispatched(exploreId, name); + + expect(dispatchedActions).toEqual([ + updateDatasourceInstanceAction({ + exploreId, + datasourceInstance: lokiMock as any, + version: undefined, + mode: ExploreMode.Logs, + }), + ]); + }); +}); + describe('loading datasource', () => { describe('when loadDatasource thunk is dispatched', () => { describe('and all goes fine', () => { diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index a42a65a29ca..b1f8da27ed4 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -124,11 +124,16 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun const orgId = getState().user.orgId; const datasourceVersion = newDataSourceInstance.getVersion && (await newDataSourceInstance.getVersion()); + // HACK: Switch to logs mode if coming from Prometheus to Loki + const prometheusToLoki = + currentDataSourceInstance?.meta?.name === 'Prometheus' && newDataSourceInstance?.meta?.name === 'Loki'; + dispatch( updateDatasourceInstanceAction({ exploreId, datasourceInstance: newDataSourceInstance, version: datasourceVersion, + mode: prometheusToLoki ? ExploreMode.Logs : undefined, }) ); @@ -316,12 +321,12 @@ export const loadDatasourceReady = ( * @param sourceDataSource * @param targetDataSource */ -export function importQueries( +export const importQueries = ( exploreId: ExploreId, queries: DataQuery[], sourceDataSource: DataSourceApi, targetDataSource: DataSourceApi -): ThunkResult { +): ThunkResult => { return async dispatch => { if (!sourceDataSource) { // explore not initialized @@ -346,12 +351,12 @@ export function importQueries( dispatch(queriesImportedAction({ exploreId, queries: nextQueries })); }; -} +}; /** * Main action to asynchronously load a datasource. Dispatches lots of smaller actions for feedback. */ -export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi, orgId: number): ThunkResult { +export const loadDatasource = (exploreId: ExploreId, instance: DataSourceApi, orgId: number): ThunkResult => { return async (dispatch, getState) => { const datasourceName = instance.name; @@ -373,7 +378,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi, or dispatch(loadDatasourceReady(exploreId, instance, orgId)); }; -} +}; /** * Action to modify a query given a datasource-specific modifier action. @@ -399,7 +404,7 @@ export function modifyQueries( /** * Main action to run queries and dispatches sub-actions based on which result viewers are active */ -export function runQueries(exploreId: ExploreId): ThunkResult { +export const runQueries = (exploreId: ExploreId): ThunkResult => { return (dispatch, getState) => { dispatch(updateTime({ exploreId })); @@ -484,7 +489,7 @@ export function runQueries(exploreId: ExploreId): ThunkResult { dispatch(queryStoreSubscriptionAction({ exploreId, querySubscription: newQuerySub })); }; -} +}; const toRawTimeRange = (range: TimeRange): RawTimeRange => { let from = range.raw.from; diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index f478f368062..15d16bf048e 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -269,7 +269,7 @@ export const itemReducer = reducerFactory({} as ExploreItemSta .addMapper({ filter: updateDatasourceInstanceAction, mapper: (state, action): ExploreItemState => { - const { datasourceInstance, version } = action.payload; + const { datasourceInstance, version, mode } = action.payload; // Custom components stopQueryState(state.querySubscription); @@ -291,7 +291,7 @@ export const itemReducer = reducerFactory({} as ExploreItemSta } const updatedDatasourceInstance = Object.assign(datasourceInstance, { meta: newMetadata }); - const [supportedModes, mode] = getModesForDatasource(updatedDatasourceInstance, state.mode); + const [supportedModes, newMode] = getModesForDatasource(updatedDatasourceInstance, state.mode); return { ...state, @@ -304,7 +304,7 @@ export const itemReducer = reducerFactory({} as ExploreItemSta loading: false, queryKeys: [], supportedModes, - mode, + mode: mode ?? newMode, originPanelId: state.urlState && state.urlState.originPanelId, }; }, diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index 7f084b7cdcd..2546b76134e 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -190,9 +190,9 @@ export class DatasourceSrv implements DataSourceService { } } -export function getDatasourceSrv(): DatasourceSrv { +export const getDatasourceSrv = (): DatasourceSrv => { return getDataSourceService() as DatasourceSrv; -} +}; coreModule.service('datasourceSrv', DatasourceSrv); export default DatasourceSrv;