Datasource/Loki: Loki now goes to Logs mode when importing prom queries (#20890)

* Datasource/Loki: Loki now goes to Logs mode when importing prom queries
Closes #20831

* Fixes behavior and adds tests

* Update public/app/features/explore/state/actions.test.ts
This commit is contained in:
kay delaney 2019-12-06 16:00:05 +00:00 committed by David
parent ab3df98523
commit b4736558ac
5 changed files with 94 additions and 22 deletions

View File

@ -178,6 +178,7 @@ export interface UpdateDatasourceInstancePayload {
exploreId: ExploreId; exploreId: ExploreId;
datasourceInstance: DataSourceApi; datasourceInstance: DataSourceApi;
version?: string; version?: string;
mode?: ExploreMode;
} }
export interface ToggleLogLevelPayload { export interface ToggleLogLevelPayload {

View File

@ -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 { ExploreId, ExploreMode, ExploreUpdateState, ExploreUrlState } from 'app/types';
import { thunkTester } from 'test/core/thunk/thunkTester'; import { thunkTester } from 'test/core/thunk/thunkTester';
import { import {
@ -8,6 +9,7 @@ import {
loadDatasourceReadyAction, loadDatasourceReadyAction,
setQueriesAction, setQueriesAction,
updateUIStateAction, updateUIStateAction,
updateDatasourceInstanceAction,
} from './actionTypes'; } from './actionTypes';
import { Emitter } from 'app/core/core'; import { Emitter } from 'app/core/core';
import { ActionOf } from 'app/core/redux/actionCreatorFactory'; 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 { PanelModel } from 'app/features/dashboard/state';
import { updateLocation } from '../../../core/actions'; import { updateLocation } from '../../../core/actions';
import { MockDataSourceApi } from '../../../../test/mocks/datasource_srv'; import { MockDataSourceApi } from '../../../../test/mocks/datasource_srv';
import * as DatasourceSrv from 'app/features/plugins/datasource_srv';
jest.mock('app/features/plugins/datasource_srv', () => ({ jest.mock('app/features/plugins/datasource_srv');
getDatasourceSrv: () => ({ const getDatasourceSrvMock = (DatasourceSrv.getDatasourceSrv as any) as jest.Mock<DatasourceSrv.DatasourceSrv>;
getExternal: jest.fn().mockReturnValue([]),
get: jest.fn().mockReturnValue({ beforeEach(() => {
testDatasource: jest.fn(), getDatasourceSrvMock.mockClear();
init: jest.fn(), getDatasourceSrvMock.mockImplementation(
}), () =>
}), ({
})); getExternal: jest.fn().mockReturnValue([]),
get: jest.fn().mockReturnValue({
testDatasource: jest.fn(),
init: jest.fn(),
}),
} as any)
);
});
jest.mock('../../dashboard/services/TimeSrv', () => ({ jest.mock('../../dashboard/services/TimeSrv', () => ({
getTimeSrv: jest.fn().mockReturnValue({ 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('loading datasource', () => {
describe('when loadDatasource thunk is dispatched', () => { describe('when loadDatasource thunk is dispatched', () => {
describe('and all goes fine', () => { describe('and all goes fine', () => {

View File

@ -124,11 +124,16 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun
const orgId = getState().user.orgId; const orgId = getState().user.orgId;
const datasourceVersion = newDataSourceInstance.getVersion && (await newDataSourceInstance.getVersion()); 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( dispatch(
updateDatasourceInstanceAction({ updateDatasourceInstanceAction({
exploreId, exploreId,
datasourceInstance: newDataSourceInstance, datasourceInstance: newDataSourceInstance,
version: datasourceVersion, version: datasourceVersion,
mode: prometheusToLoki ? ExploreMode.Logs : undefined,
}) })
); );
@ -316,12 +321,12 @@ export const loadDatasourceReady = (
* @param sourceDataSource * @param sourceDataSource
* @param targetDataSource * @param targetDataSource
*/ */
export function importQueries( export const importQueries = (
exploreId: ExploreId, exploreId: ExploreId,
queries: DataQuery[], queries: DataQuery[],
sourceDataSource: DataSourceApi, sourceDataSource: DataSourceApi,
targetDataSource: DataSourceApi targetDataSource: DataSourceApi
): ThunkResult<void> { ): ThunkResult<void> => {
return async dispatch => { return async dispatch => {
if (!sourceDataSource) { if (!sourceDataSource) {
// explore not initialized // explore not initialized
@ -346,12 +351,12 @@ export function importQueries(
dispatch(queriesImportedAction({ exploreId, queries: nextQueries })); dispatch(queriesImportedAction({ exploreId, queries: nextQueries }));
}; };
} };
/** /**
* Main action to asynchronously load a datasource. Dispatches lots of smaller actions for feedback. * Main action to asynchronously load a datasource. Dispatches lots of smaller actions for feedback.
*/ */
export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi, orgId: number): ThunkResult<void> { export const loadDatasource = (exploreId: ExploreId, instance: DataSourceApi, orgId: number): ThunkResult<void> => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
const datasourceName = instance.name; const datasourceName = instance.name;
@ -373,7 +378,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi, or
dispatch(loadDatasourceReady(exploreId, instance, orgId)); dispatch(loadDatasourceReady(exploreId, instance, orgId));
}; };
} };
/** /**
* Action to modify a query given a datasource-specific modifier action. * 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 * Main action to run queries and dispatches sub-actions based on which result viewers are active
*/ */
export function runQueries(exploreId: ExploreId): ThunkResult<void> { export const runQueries = (exploreId: ExploreId): ThunkResult<void> => {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(updateTime({ exploreId })); dispatch(updateTime({ exploreId }));
@ -484,7 +489,7 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
dispatch(queryStoreSubscriptionAction({ exploreId, querySubscription: newQuerySub })); dispatch(queryStoreSubscriptionAction({ exploreId, querySubscription: newQuerySub }));
}; };
} };
const toRawTimeRange = (range: TimeRange): RawTimeRange => { const toRawTimeRange = (range: TimeRange): RawTimeRange => {
let from = range.raw.from; let from = range.raw.from;

View File

@ -269,7 +269,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
.addMapper({ .addMapper({
filter: updateDatasourceInstanceAction, filter: updateDatasourceInstanceAction,
mapper: (state, action): ExploreItemState => { mapper: (state, action): ExploreItemState => {
const { datasourceInstance, version } = action.payload; const { datasourceInstance, version, mode } = action.payload;
// Custom components // Custom components
stopQueryState(state.querySubscription); stopQueryState(state.querySubscription);
@ -291,7 +291,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
} }
const updatedDatasourceInstance = Object.assign(datasourceInstance, { meta: newMetadata }); const updatedDatasourceInstance = Object.assign(datasourceInstance, { meta: newMetadata });
const [supportedModes, mode] = getModesForDatasource(updatedDatasourceInstance, state.mode); const [supportedModes, newMode] = getModesForDatasource(updatedDatasourceInstance, state.mode);
return { return {
...state, ...state,
@ -304,7 +304,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
loading: false, loading: false,
queryKeys: [], queryKeys: [],
supportedModes, supportedModes,
mode, mode: mode ?? newMode,
originPanelId: state.urlState && state.urlState.originPanelId, originPanelId: state.urlState && state.urlState.originPanelId,
}; };
}, },

View File

@ -190,9 +190,9 @@ export class DatasourceSrv implements DataSourceService {
} }
} }
export function getDatasourceSrv(): DatasourceSrv { export const getDatasourceSrv = (): DatasourceSrv => {
return getDataSourceService() as DatasourceSrv; return getDataSourceService() as DatasourceSrv;
} };
coreModule.service('datasourceSrv', DatasourceSrv); coreModule.service('datasourceSrv', DatasourceSrv);
export default DatasourceSrv; export default DatasourceSrv;