mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Remove datasource testing on selector (#19910)
* Explore: Remove datasource testing on selector - datasource testing gets in the way of fast query iteration: switching between datasources can take seconds - it should not be explore's duty to test datasources in the first place - removed the concept of datasourceError in Explore, should not be its concern - datasource erorrs will express themselves in query errors just fine - connection errors are still bubbled up - removed reconnection logic from explore, should not be its concern - missing labels in loki are still "visible" via an empty label selector - Loki and Prometheus treated connection errors differently than other datasources, making sure to pass through the original error message * Show datasource error in query field for prom/loki/influx * Removed connection test case, fixed disabled state
This commit is contained in:
parent
49c44da73b
commit
781cff07af
@ -295,7 +295,6 @@ export interface ExploreQueryFieldProps<
|
||||
TQuery extends DataQuery = DataQuery,
|
||||
TOptions extends DataSourceJsonData = DataSourceJsonData
|
||||
> extends QueryEditorProps<DSType, TQuery, TOptions> {
|
||||
datasourceStatus: DataSourceStatus;
|
||||
history: any[];
|
||||
onHint?: (action: QueryFixAction) => void;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import memoizeOne from 'memoize-one';
|
||||
// Services & Utils
|
||||
import store from 'app/core/store';
|
||||
// Components
|
||||
import { Alert, ErrorBoundaryAlert, DataQuery, ExploreStartPageProps, DataSourceApi, PanelData } from '@grafana/ui';
|
||||
import { ErrorBoundaryAlert, DataQuery, ExploreStartPageProps, DataSourceApi, PanelData } from '@grafana/ui';
|
||||
import LogsContainer from './LogsContainer';
|
||||
import QueryRows from './QueryRows';
|
||||
import TableContainer from './TableContainer';
|
||||
@ -21,7 +21,6 @@ import {
|
||||
scanStart,
|
||||
setQueries,
|
||||
refreshExplore,
|
||||
reconnectDatasource,
|
||||
updateTimeRange,
|
||||
toggleGraph,
|
||||
} from './state/actions';
|
||||
@ -46,7 +45,6 @@ import {
|
||||
import { Emitter } from 'app/core/utils/emitter';
|
||||
import { ExploreToolbar } from './ExploreToolbar';
|
||||
import { NoDataSourceCallToAction } from './NoDataSourceCallToAction';
|
||||
import { FadeIn } from 'app/core/components/Animations/FadeIn';
|
||||
import { getTimeZone } from '../profile/state/selectors';
|
||||
import { ErrorContainer } from './ErrorContainer';
|
||||
import { scanStopAction } from './state/actionTypes';
|
||||
@ -65,7 +63,6 @@ const getStyles = memoizeOne(() => {
|
||||
interface ExploreProps {
|
||||
StartPage?: ComponentType<ExploreStartPageProps>;
|
||||
changeSize: typeof changeSize;
|
||||
datasourceError: string;
|
||||
datasourceInstance: DataSourceApi;
|
||||
datasourceMissing: boolean;
|
||||
exploreId: ExploreId;
|
||||
@ -73,7 +70,6 @@ interface ExploreProps {
|
||||
initialized: boolean;
|
||||
modifyQueries: typeof modifyQueries;
|
||||
update: ExploreUpdateState;
|
||||
reconnectDatasource: typeof reconnectDatasource;
|
||||
refreshExplore: typeof refreshExplore;
|
||||
scanning?: boolean;
|
||||
scanRange?: RawTimeRange;
|
||||
@ -238,18 +234,10 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
||||
);
|
||||
};
|
||||
|
||||
onReconnect = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const { exploreId, reconnectDatasource } = this.props;
|
||||
|
||||
event.preventDefault();
|
||||
reconnectDatasource(exploreId);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
StartPage,
|
||||
datasourceInstance,
|
||||
datasourceError,
|
||||
datasourceMissing,
|
||||
exploreId,
|
||||
showingStartPage,
|
||||
@ -272,17 +260,6 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
||||
<div className={exploreClass} ref={this.getRef}>
|
||||
<ExploreToolbar exploreId={exploreId} onChangeTime={this.onChangeTime} />
|
||||
{datasourceMissing ? this.renderEmptyState() : null}
|
||||
|
||||
<FadeIn duration={datasourceError ? 150 : 5} in={datasourceError ? true : false}>
|
||||
<div className="explore-container">
|
||||
<Alert
|
||||
title={`Error connecting to datasource: ${datasourceError}`}
|
||||
buttonText={'Reconnect'}
|
||||
onButtonClick={this.onReconnect}
|
||||
/>
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
{datasourceInstance && (
|
||||
<div className="explore-container">
|
||||
<QueryRows exploreEvents={this.exploreEvents} exploreId={exploreId} queryKeys={queryKeys} />
|
||||
@ -357,7 +334,6 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps): Partia
|
||||
const timeZone = getTimeZone(state.user);
|
||||
const {
|
||||
StartPage,
|
||||
datasourceError,
|
||||
datasourceInstance,
|
||||
datasourceMissing,
|
||||
initialized,
|
||||
@ -402,7 +378,6 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps): Partia
|
||||
|
||||
return {
|
||||
StartPage,
|
||||
datasourceError,
|
||||
datasourceInstance,
|
||||
datasourceMissing,
|
||||
initialized,
|
||||
@ -431,7 +406,6 @@ const mapDispatchToProps: Partial<ExploreProps> = {
|
||||
changeSize,
|
||||
initializeExplore,
|
||||
modifyQueries,
|
||||
reconnectDatasource,
|
||||
refreshExplore,
|
||||
scanStart,
|
||||
scanStopAction,
|
||||
|
@ -13,7 +13,7 @@ import { changeQuery, modifyQueries, runQueries, addQueryRow } from './state/act
|
||||
// Types
|
||||
import { StoreState } from 'app/types';
|
||||
import { TimeRange, AbsoluteTimeRange, LoadingState } from '@grafana/data';
|
||||
import { DataQuery, DataSourceApi, QueryFixAction, DataSourceStatus, PanelData } from '@grafana/ui';
|
||||
import { DataQuery, DataSourceApi, QueryFixAction, PanelData } from '@grafana/ui';
|
||||
import { HistoryItem, ExploreItemState, ExploreId, ExploreMode } from 'app/types/explore';
|
||||
import { Emitter } from 'app/core/utils/emitter';
|
||||
import { highlightLogsExpressionAction, removeQueryRowAction } from './state/actionTypes';
|
||||
@ -31,7 +31,6 @@ interface QueryRowProps extends PropsFromParent {
|
||||
className?: string;
|
||||
exploreId: ExploreId;
|
||||
datasourceInstance: DataSourceApi;
|
||||
datasourceStatus: DataSourceStatus;
|
||||
highlightLogsExpressionAction: typeof highlightLogsExpressionAction;
|
||||
history: HistoryItem[];
|
||||
query: DataQuery;
|
||||
@ -121,7 +120,6 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
||||
exploreEvents,
|
||||
range,
|
||||
absoluteRange,
|
||||
datasourceStatus,
|
||||
queryResponse,
|
||||
latency,
|
||||
mode,
|
||||
@ -148,7 +146,6 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
||||
//@ts-ignore
|
||||
<QueryField
|
||||
datasource={datasourceInstance}
|
||||
datasourceStatus={datasourceStatus}
|
||||
query={query}
|
||||
history={history}
|
||||
onRunQuery={this.onRunQuery}
|
||||
@ -190,19 +187,8 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
||||
function mapStateToProps(state: StoreState, { exploreId, index }: QueryRowProps) {
|
||||
const explore = state.explore;
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const {
|
||||
datasourceInstance,
|
||||
history,
|
||||
queries,
|
||||
range,
|
||||
absoluteRange,
|
||||
datasourceError,
|
||||
latency,
|
||||
mode,
|
||||
queryResponse,
|
||||
} = item;
|
||||
const { datasourceInstance, history, queries, range, absoluteRange, latency, mode, queryResponse } = item;
|
||||
const query = queries[index];
|
||||
const datasourceStatus = datasourceError ? DataSourceStatus.Disconnected : DataSourceStatus.Connected;
|
||||
|
||||
return {
|
||||
datasourceInstance,
|
||||
@ -210,7 +196,6 @@ function mapStateToProps(state: StoreState, { exploreId, index }: QueryRowProps)
|
||||
query,
|
||||
range,
|
||||
absoluteRange,
|
||||
datasourceStatus,
|
||||
queryResponse,
|
||||
latency,
|
||||
mode,
|
||||
|
@ -105,19 +105,6 @@ export interface LoadDatasourceReadyPayload {
|
||||
history: HistoryItem[];
|
||||
}
|
||||
|
||||
export interface TestDatasourcePendingPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface TestDatasourceFailurePayload {
|
||||
exploreId: ExploreId;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface TestDatasourceSuccessPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ModifyQueriesPayload {
|
||||
exploreId: ExploreId;
|
||||
modification: QueryFixAction;
|
||||
@ -391,15 +378,6 @@ export const toggleLogLevelAction = actionCreatorFactory<ToggleLogLevelPayload>(
|
||||
*/
|
||||
export const resetExploreAction = actionCreatorFactory<ResetExplorePayload>('explore/RESET_EXPLORE').create();
|
||||
export const queriesImportedAction = actionCreatorFactory<QueriesImportedPayload>('explore/QueriesImported').create();
|
||||
export const testDataSourcePendingAction = actionCreatorFactory<TestDatasourcePendingPayload>(
|
||||
'explore/TEST_DATASOURCE_PENDING'
|
||||
).create();
|
||||
export const testDataSourceSuccessAction = actionCreatorFactory<TestDatasourceSuccessPayload>(
|
||||
'explore/TEST_DATASOURCE_SUCCESS'
|
||||
).create();
|
||||
export const testDataSourceFailureAction = actionCreatorFactory<TestDatasourceFailurePayload>(
|
||||
'explore/TEST_DATASOURCE_FAILURE'
|
||||
).create();
|
||||
export const loadExploreDatasources = actionCreatorFactory<LoadExploreDataSourcesPayload>(
|
||||
'explore/LOAD_EXPLORE_DATASOURCES'
|
||||
).create();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { refreshExplore, testDatasource, loadDatasource } from './actions';
|
||||
import { refreshExplore, loadDatasource } from './actions';
|
||||
import { ExploreId, ExploreUrlState, ExploreUpdateState, ExploreMode } from 'app/types';
|
||||
import { thunkTester } from 'test/core/thunk/thunkTester';
|
||||
import {
|
||||
@ -6,9 +6,6 @@ import {
|
||||
InitializeExplorePayload,
|
||||
updateUIStateAction,
|
||||
setQueriesAction,
|
||||
testDataSourcePendingAction,
|
||||
testDataSourceSuccessAction,
|
||||
testDataSourceFailureAction,
|
||||
loadDatasourcePendingAction,
|
||||
loadDatasourceReadyAction,
|
||||
} from './actionTypes';
|
||||
@ -164,72 +161,6 @@ describe('refreshExplore', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('test datasource', () => {
|
||||
describe('when testDatasource thunk is dispatched', () => {
|
||||
describe('and testDatasource call on instance is successful', () => {
|
||||
it('then it should dispatch testDataSourceSuccessAction', async () => {
|
||||
const exploreId = ExploreId.left;
|
||||
const mockDatasourceInstance = {
|
||||
testDatasource: () => {
|
||||
return Promise.resolve({ status: 'success' });
|
||||
},
|
||||
};
|
||||
|
||||
const dispatchedActions = await thunkTester({})
|
||||
.givenThunk(testDatasource)
|
||||
.whenThunkIsDispatched(exploreId, mockDatasourceInstance);
|
||||
|
||||
expect(dispatchedActions).toEqual([
|
||||
testDataSourcePendingAction({ exploreId }),
|
||||
testDataSourceSuccessAction({ exploreId }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and testDatasource call on instance is not successful', () => {
|
||||
it('then it should dispatch testDataSourceFailureAction', async () => {
|
||||
const exploreId = ExploreId.left;
|
||||
const error = 'something went wrong';
|
||||
const mockDatasourceInstance = {
|
||||
testDatasource: () => {
|
||||
return Promise.resolve({ status: 'fail', message: error });
|
||||
},
|
||||
};
|
||||
|
||||
const dispatchedActions = await thunkTester({})
|
||||
.givenThunk(testDatasource)
|
||||
.whenThunkIsDispatched(exploreId, mockDatasourceInstance);
|
||||
|
||||
expect(dispatchedActions).toEqual([
|
||||
testDataSourcePendingAction({ exploreId }),
|
||||
testDataSourceFailureAction({ exploreId, error }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and testDatasource call on instance throws', () => {
|
||||
it('then it should dispatch testDataSourceFailureAction', async () => {
|
||||
const exploreId = ExploreId.left;
|
||||
const error = 'something went wrong';
|
||||
const mockDatasourceInstance = {
|
||||
testDatasource: () => {
|
||||
throw { statusText: error };
|
||||
},
|
||||
};
|
||||
|
||||
const dispatchedActions = await thunkTester({})
|
||||
.givenThunk(testDatasource)
|
||||
.whenThunkIsDispatched(exploreId, mockDatasourceInstance);
|
||||
|
||||
expect(dispatchedActions).toEqual([
|
||||
testDataSourcePendingAction({ exploreId }),
|
||||
testDataSourceFailureAction({ exploreId, error }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('loading datasource', () => {
|
||||
describe('when loadDatasource thunk is dispatched', () => {
|
||||
describe('and all goes fine', () => {
|
||||
@ -255,8 +186,6 @@ describe('loading datasource', () => {
|
||||
exploreId,
|
||||
requestedDatasourceName: mockDatasourceInstance.name,
|
||||
}),
|
||||
testDataSourcePendingAction({ exploreId }),
|
||||
testDataSourceSuccessAction({ exploreId }),
|
||||
loadDatasourceReadyAction({ exploreId, history: [] }),
|
||||
]);
|
||||
});
|
||||
@ -285,8 +214,6 @@ describe('loading datasource', () => {
|
||||
exploreId,
|
||||
requestedDatasourceName: mockDatasourceInstance.name,
|
||||
}),
|
||||
testDataSourcePendingAction({ exploreId }),
|
||||
testDataSourceSuccessAction({ exploreId }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -59,9 +59,6 @@ import {
|
||||
ToggleGraphPayload,
|
||||
ToggleTablePayload,
|
||||
updateUIStateAction,
|
||||
testDataSourcePendingAction,
|
||||
testDataSourceSuccessAction,
|
||||
testDataSourceFailureAction,
|
||||
loadExploreDatasources,
|
||||
changeModeAction,
|
||||
scanStopAction,
|
||||
@ -340,41 +337,6 @@ export function importQueries(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests datasource.
|
||||
*/
|
||||
export const testDatasource = (exploreId: ExploreId, instance: DataSourceApi): ThunkResult<void> => {
|
||||
return async dispatch => {
|
||||
let datasourceError = null;
|
||||
|
||||
dispatch(testDataSourcePendingAction({ exploreId }));
|
||||
|
||||
try {
|
||||
const testResult = await instance.testDatasource();
|
||||
datasourceError = testResult.status === 'success' ? null : testResult.message;
|
||||
} catch (error) {
|
||||
datasourceError = (error && error.statusText) || 'Network error';
|
||||
}
|
||||
|
||||
if (datasourceError) {
|
||||
dispatch(testDataSourceFailureAction({ exploreId, error: datasourceError }));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(testDataSourceSuccessAction({ exploreId }));
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Reconnects datasource when there is a connection failure.
|
||||
*/
|
||||
export const reconnectDatasource = (exploreId: ExploreId): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const instance = getState().explore[exploreId].datasourceInstance;
|
||||
dispatch(changeDatasource(exploreId, instance.name));
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Main action to asynchronously load a datasource. Dispatches lots of smaller actions for feedback.
|
||||
*/
|
||||
@ -385,13 +347,6 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi, or
|
||||
// Keep ID to track selection
|
||||
dispatch(loadDatasourcePendingAction({ exploreId, requestedDatasourceName: datasourceName }));
|
||||
|
||||
await dispatch(testDatasource(exploreId, instance));
|
||||
|
||||
if (datasourceName !== getState().explore[exploreId].requestedDatasourceName) {
|
||||
// User already changed datasource again, discard results
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance.init) {
|
||||
try {
|
||||
instance.init();
|
||||
@ -401,7 +356,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi, or
|
||||
}
|
||||
|
||||
if (datasourceName !== getState().explore[exploreId].requestedDatasourceName) {
|
||||
// User already changed datasource again, discard results
|
||||
// User already changed datasource, discard results
|
||||
return;
|
||||
}
|
||||
|
||||
@ -441,7 +396,6 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
|
||||
const {
|
||||
datasourceInstance,
|
||||
queries,
|
||||
datasourceError,
|
||||
containerWidth,
|
||||
isLive: live,
|
||||
range,
|
||||
@ -454,11 +408,6 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
|
||||
showingTable,
|
||||
} = exploreItemState;
|
||||
|
||||
if (datasourceError) {
|
||||
// let's not run any queries if data source is in a faulty state
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasNonEmptyQuery(queries)) {
|
||||
dispatch(clearQueriesAction({ exploreId }));
|
||||
dispatch(stateSave()); // Remember to save to state and update location
|
||||
|
@ -10,9 +10,6 @@ import { ExploreId, ExploreItemState, ExploreUrlState, ExploreState, ExploreMode
|
||||
import { reducerTester } from 'test/core/redux/reducerTester';
|
||||
import {
|
||||
scanStartAction,
|
||||
testDataSourcePendingAction,
|
||||
testDataSourceSuccessAction,
|
||||
testDataSourceFailureAction,
|
||||
updateDatasourceInstanceAction,
|
||||
splitOpenAction,
|
||||
splitCloseAction,
|
||||
@ -29,7 +26,7 @@ import { updateLocation } from 'app/core/actions/location';
|
||||
import { serializeStateToUrlParam } from 'app/core/utils/explore';
|
||||
import TableModel from 'app/core/table_model';
|
||||
import { DataSourceApi, DataQuery } from '@grafana/ui';
|
||||
import { LogsModel, LogsDedupStrategy, dateTime, LoadingState } from '@grafana/data';
|
||||
import { LogsDedupStrategy, dateTime, LoadingState } from '@grafana/data';
|
||||
|
||||
describe('Explore item reducer', () => {
|
||||
describe('scanning', () => {
|
||||
@ -65,56 +62,7 @@ describe('Explore item reducer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('testing datasource', () => {
|
||||
describe('when testDataSourcePendingAction is dispatched', () => {
|
||||
it('then it should set datasourceError', () => {
|
||||
reducerTester()
|
||||
.givenReducer(itemReducer, { datasourceError: {} })
|
||||
.whenActionIsDispatched(testDataSourcePendingAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual({ datasourceError: null });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when testDataSourceSuccessAction is dispatched', () => {
|
||||
it('then it should set datasourceError', () => {
|
||||
reducerTester()
|
||||
.givenReducer(itemReducer, { datasourceError: {} })
|
||||
.whenActionIsDispatched(testDataSourceSuccessAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual({ datasourceError: null });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when testDataSourceFailureAction is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
const error = 'some error';
|
||||
const initalState: Partial<ExploreItemState> = {
|
||||
datasourceError: null,
|
||||
graphResult: [],
|
||||
tableResult: {} as TableModel,
|
||||
logsResult: {} as LogsModel,
|
||||
update: {
|
||||
datasource: true,
|
||||
queries: true,
|
||||
range: true,
|
||||
mode: true,
|
||||
ui: true,
|
||||
},
|
||||
};
|
||||
const expectedState = {
|
||||
datasourceError: error,
|
||||
graphResult: undefined as any[],
|
||||
tableResult: undefined as TableModel,
|
||||
logsResult: undefined as LogsModel,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
|
||||
reducerTester()
|
||||
.givenReducer(itemReducer, initalState)
|
||||
.whenActionIsDispatched(testDataSourceFailureAction({ exploreId: ExploreId.left, error }))
|
||||
.thenStateShouldEqual(expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('changing datasource', () => {
|
||||
describe('when changeDataType is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
reducerTester()
|
||||
@ -125,9 +73,7 @@ describe('Explore item reducer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('changing datasource', () => {
|
||||
describe('when updateDatasourceInstanceAction is dispatched', () => {
|
||||
describe('and datasourceInstance supports graph, logs, table and has a startpage', () => {
|
||||
it('then it should set correct state', () => {
|
||||
|
@ -15,9 +15,6 @@ import { DataQuery, DataSourceApi, PanelData, DataQueryRequest, RefreshPicker, P
|
||||
import {
|
||||
HigherOrderAction,
|
||||
ActionTypes,
|
||||
testDataSourcePendingAction,
|
||||
testDataSourceSuccessAction,
|
||||
testDataSourceFailureAction,
|
||||
splitCloseAction,
|
||||
SplitCloseActionPayload,
|
||||
loadExploreDatasources,
|
||||
@ -82,7 +79,6 @@ export const makeExploreItemState = (): ExploreItemState => ({
|
||||
containerWidth: 0,
|
||||
datasourceInstance: null,
|
||||
requestedDatasourceName: null,
|
||||
datasourceError: null,
|
||||
datasourceLoading: null,
|
||||
datasourceMissing: false,
|
||||
exploreDatasources: [],
|
||||
@ -475,37 +471,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: testDataSourcePendingAction,
|
||||
mapper: (state): ExploreItemState => {
|
||||
return {
|
||||
...state,
|
||||
datasourceError: null,
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: testDataSourceSuccessAction,
|
||||
mapper: (state): ExploreItemState => {
|
||||
return {
|
||||
...state,
|
||||
datasourceError: null,
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: testDataSourceFailureAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
return {
|
||||
...state,
|
||||
datasourceError: action.payload.error,
|
||||
graphResult: undefined,
|
||||
tableResult: undefined,
|
||||
logsResult: undefined,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: loadExploreDatasources,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
|
@ -17,6 +17,13 @@ export interface State {
|
||||
measurements: CascaderOption[];
|
||||
measurement: string;
|
||||
field: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface ChooserOptions {
|
||||
measurement: string;
|
||||
field: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
// Helper function for determining if a collection of pairs are valid
|
||||
@ -32,12 +39,23 @@ export function pairsAreValid(pairs: KeyValuePair[]) {
|
||||
);
|
||||
}
|
||||
|
||||
function getChooserText({ measurement, field, error }: ChooserOptions): string {
|
||||
if (error) {
|
||||
return '(No measurement found)';
|
||||
}
|
||||
if (measurement) {
|
||||
return `Measurements (${measurement}/${field})`;
|
||||
}
|
||||
return 'Measurements';
|
||||
}
|
||||
|
||||
export class InfluxLogsQueryField extends React.PureComponent<Props, State> {
|
||||
templateSrv: TemplateSrv = new TemplateSrv();
|
||||
state: State = { measurements: [], measurement: null, field: null };
|
||||
state: State = { measurements: [], measurement: null, field: null, error: null };
|
||||
|
||||
async componentDidMount() {
|
||||
const { datasource } = this.props;
|
||||
try {
|
||||
const queryBuilder = new InfluxQueryBuilder({ measurement: '', tags: [] }, datasource.database);
|
||||
const measureMentsQuery = queryBuilder.buildExploreQuery('MEASUREMENTS');
|
||||
const influxMeasurements = await datasource.metricFindQuery(measureMentsQuery);
|
||||
@ -45,7 +63,10 @@ export class InfluxLogsQueryField extends React.PureComponent<Props, State> {
|
||||
const measurements = [];
|
||||
for (let index = 0; index < influxMeasurements.length; index++) {
|
||||
const measurementObj = influxMeasurements[index];
|
||||
const queryBuilder = new InfluxQueryBuilder({ measurement: measurementObj.text, tags: [] }, datasource.database);
|
||||
const queryBuilder = new InfluxQueryBuilder(
|
||||
{ measurement: measurementObj.text, tags: [] },
|
||||
datasource.database
|
||||
);
|
||||
const fieldsQuery = queryBuilder.buildExploreQuery('FIELDS');
|
||||
const influxFields = await datasource.metricFindQuery(fieldsQuery);
|
||||
const fields: any[] = influxFields.map(
|
||||
@ -61,8 +82,11 @@ export class InfluxLogsQueryField extends React.PureComponent<Props, State> {
|
||||
children: fields,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ measurements });
|
||||
} catch (error) {
|
||||
const message = error && error.message ? error.message : error;
|
||||
this.setState({ error: message });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
@ -107,8 +131,8 @@ export class InfluxLogsQueryField extends React.PureComponent<Props, State> {
|
||||
|
||||
render() {
|
||||
const { datasource } = this.props;
|
||||
const { measurements, measurement, field } = this.state;
|
||||
const cascadeText = measurement ? `Measurements (${measurement}/${field})` : 'Measurements';
|
||||
const { measurements, measurement, field, error } = this.state;
|
||||
const cascadeText = getChooserText({ measurement, field, error });
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline gf-form-inline--nowrap">
|
||||
@ -119,7 +143,7 @@ export class InfluxLogsQueryField extends React.PureComponent<Props, State> {
|
||||
onChange={this.onMeasurementsChange}
|
||||
expandIcon={null}
|
||||
>
|
||||
<button className="gf-form-label gf-form-label--btn">
|
||||
<button className="gf-form-label gf-form-label--btn" disabled={!measurement}>
|
||||
{cascadeText} <i className="fa fa-caret-down" />
|
||||
</button>
|
||||
</Cascader>
|
||||
@ -132,6 +156,9 @@ export class InfluxLogsQueryField extends React.PureComponent<Props, State> {
|
||||
extendedOptions={{ measurement }}
|
||||
/>
|
||||
)}
|
||||
{error ? (
|
||||
<span className="gf-form-label gf-form-label--transparent gf-form-label--error m-l-2">{error}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -2,7 +2,6 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
// Types
|
||||
import { DataSourceStatus } from '@grafana/ui';
|
||||
import { LokiQuery } from '../types';
|
||||
import { useLokiSyntax } from './useLokiSyntax';
|
||||
import { LokiQueryFieldForm } from './LokiQueryFieldForm';
|
||||
@ -25,7 +24,6 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito
|
||||
|
||||
const { isSyntaxReady, setActiveOption, refreshLabels, ...syntaxProps } = useLokiSyntax(
|
||||
datasource.languageProvider,
|
||||
DataSourceStatus.Connected,
|
||||
absolute
|
||||
);
|
||||
|
||||
@ -38,7 +36,6 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito
|
||||
<div className="gf-form-group">
|
||||
<LokiQueryFieldForm
|
||||
datasource={datasource}
|
||||
datasourceStatus={DataSourceStatus.Connected}
|
||||
query={query}
|
||||
onChange={(query: LokiQuery) => onChange(query.expr)}
|
||||
onRunQuery={() => {}}
|
||||
|
@ -3,7 +3,7 @@ import React, { memo } from 'react';
|
||||
|
||||
// Types
|
||||
import { AbsoluteTimeRange } from '@grafana/data';
|
||||
import { QueryEditorProps, DataSourceStatus } from '@grafana/ui';
|
||||
import { QueryEditorProps } from '@grafana/ui';
|
||||
import { LokiDatasource } from '../datasource';
|
||||
import { LokiQuery } from '../types';
|
||||
import { LokiQueryField } from './LokiQueryField';
|
||||
@ -30,8 +30,6 @@ export const LokiQueryEditor = memo(function LokiQueryEditor(props: Props) {
|
||||
|
||||
const { isSyntaxReady, setActiveOption, refreshLabels, ...syntaxProps } = useLokiSyntax(
|
||||
datasource.languageProvider,
|
||||
// TODO maybe use real status
|
||||
DataSourceStatus.Connected,
|
||||
absolute
|
||||
);
|
||||
|
||||
@ -39,7 +37,6 @@ export const LokiQueryEditor = memo(function LokiQueryEditor(props: Props) {
|
||||
<div>
|
||||
<LokiQueryField
|
||||
datasource={datasource}
|
||||
datasourceStatus={DataSourceStatus.Connected}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
onRunQuery={onRunQuery}
|
||||
|
@ -3,21 +3,15 @@ import { LokiQueryFieldForm, LokiQueryFieldFormProps } from './LokiQueryFieldFor
|
||||
import { useLokiSyntax } from './useLokiSyntax';
|
||||
import LokiLanguageProvider from '../language_provider';
|
||||
|
||||
export const LokiQueryField: FunctionComponent<LokiQueryFieldFormProps> = ({
|
||||
datasource,
|
||||
datasourceStatus,
|
||||
...otherProps
|
||||
}) => {
|
||||
export const LokiQueryField: FunctionComponent<LokiQueryFieldFormProps> = ({ datasource, ...otherProps }) => {
|
||||
const { isSyntaxReady, setActiveOption, refreshLabels, ...syntaxProps } = useLokiSyntax(
|
||||
datasource.languageProvider as LokiLanguageProvider,
|
||||
datasourceStatus,
|
||||
otherProps.absoluteRange
|
||||
);
|
||||
|
||||
return (
|
||||
<LokiQueryFieldForm
|
||||
datasource={datasource}
|
||||
datasourceStatus={datasourceStatus}
|
||||
syntaxLoaded={isSyntaxReady}
|
||||
/**
|
||||
* setActiveOption name is intentional. Because of the way rc-cascader requests additional data
|
||||
|
@ -15,17 +15,14 @@ import { Plugin, Node } from 'slate';
|
||||
// Types
|
||||
import { LokiQuery } from '../types';
|
||||
import { TypeaheadOutput } from 'app/types/explore';
|
||||
import { ExploreQueryFieldProps, DataSourceStatus, DOMUtil } from '@grafana/ui';
|
||||
import { ExploreQueryFieldProps, DOMUtil } from '@grafana/ui';
|
||||
import { AbsoluteTimeRange } from '@grafana/data';
|
||||
import { Grammar } from 'prismjs';
|
||||
import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider';
|
||||
import { SuggestionsState } from 'app/features/explore/slate-plugins/suggestions';
|
||||
import LokiDatasource from '../datasource';
|
||||
|
||||
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean, datasourceStatus: DataSourceStatus) {
|
||||
if (datasourceStatus === DataSourceStatus.Disconnected) {
|
||||
return '(Disconnected)';
|
||||
}
|
||||
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean) {
|
||||
if (!hasSyntax) {
|
||||
return 'Loading labels...';
|
||||
}
|
||||
@ -144,21 +141,12 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
data,
|
||||
query,
|
||||
syntaxLoaded,
|
||||
logLabelOptions,
|
||||
onLoadOptions,
|
||||
onLabelsRefresh,
|
||||
datasource,
|
||||
datasourceStatus,
|
||||
} = this.props;
|
||||
const { data, query, syntaxLoaded, logLabelOptions, onLoadOptions, onLabelsRefresh, datasource } = this.props;
|
||||
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
|
||||
const cleanText = datasource.languageProvider ? lokiLanguageProvider.cleanText : undefined;
|
||||
const hasLogLabels = logLabelOptions && logLabelOptions.length > 0;
|
||||
const chooserText = getChooserText(syntaxLoaded, hasLogLabels, datasourceStatus);
|
||||
const buttonDisabled = !syntaxLoaded || datasourceStatus === DataSourceStatus.Disconnected;
|
||||
const chooserText = getChooserText(syntaxLoaded, hasLogLabels);
|
||||
const buttonDisabled = !(syntaxLoaded && hasLogLabels);
|
||||
const showError = data && data.error && data.error.refId === query.refId;
|
||||
|
||||
return (
|
||||
@ -166,7 +154,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<Cascader
|
||||
options={logLabelOptions}
|
||||
options={logLabelOptions || []}
|
||||
onChange={this.onChangeLogLabels}
|
||||
loadData={onLoadOptions}
|
||||
expandIcon={null}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { renderHook, act } from 'react-hooks-testing-library';
|
||||
import LanguageProvider from 'app/plugins/datasource/loki/language_provider';
|
||||
import { useLokiLabels } from './useLokiLabels';
|
||||
import { DataSourceStatus } from '@grafana/ui/src/types/datasource';
|
||||
import { AbsoluteTimeRange } from '@grafana/data';
|
||||
import { makeMockLokiDatasource } from '../mocks';
|
||||
|
||||
@ -20,49 +19,10 @@ describe('useLokiLabels hook', () => {
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useLokiLabels(languageProvider, true, [], rangeMock, DataSourceStatus.Connected, DataSourceStatus.Connected)
|
||||
);
|
||||
const { result, waitForNextUpdate } = renderHook(() => useLokiLabels(languageProvider, true, [], rangeMock));
|
||||
act(() => result.current.refreshLabels());
|
||||
expect(result.current.logLabelOptions).toEqual([]);
|
||||
await waitForNextUpdate();
|
||||
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock);
|
||||
});
|
||||
|
||||
it('should force refresh labels after a disconnect', () => {
|
||||
const datasource = makeMockLokiDatasource({});
|
||||
|
||||
const rangeMock: AbsoluteTimeRange = {
|
||||
from: 1560153109000,
|
||||
to: 1560153109000,
|
||||
};
|
||||
|
||||
const languageProvider = new LanguageProvider(datasource);
|
||||
languageProvider.refreshLogLabels = jest.fn();
|
||||
|
||||
renderHook(() =>
|
||||
useLokiLabels(languageProvider, true, [], rangeMock, DataSourceStatus.Connected, DataSourceStatus.Disconnected)
|
||||
);
|
||||
|
||||
expect(languageProvider.refreshLogLabels).toBeCalledTimes(1);
|
||||
expect(languageProvider.refreshLogLabels).toBeCalledWith(rangeMock, true);
|
||||
});
|
||||
|
||||
it('should not force refresh labels after a connect', () => {
|
||||
const datasource = makeMockLokiDatasource({});
|
||||
|
||||
const rangeMock: AbsoluteTimeRange = {
|
||||
from: 1560153109000,
|
||||
to: 1560153109000,
|
||||
};
|
||||
|
||||
const languageProvider = new LanguageProvider(datasource);
|
||||
languageProvider.refreshLogLabels = jest.fn();
|
||||
|
||||
renderHook(() =>
|
||||
useLokiLabels(languageProvider, true, [], rangeMock, DataSourceStatus.Disconnected, DataSourceStatus.Connected)
|
||||
);
|
||||
|
||||
expect(languageProvider.refreshLogLabels).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { DataSourceStatus } from '@grafana/ui/src/types/datasource';
|
||||
import { AbsoluteTimeRange } from '@grafana/data';
|
||||
|
||||
import LokiLanguageProvider from 'app/plugins/datasource/loki/language_provider';
|
||||
@ -18,18 +17,13 @@ export const useLokiLabels = (
|
||||
languageProvider: LokiLanguageProvider,
|
||||
languageProviderInitialised: boolean,
|
||||
activeOption: CascaderOption[],
|
||||
absoluteRange: AbsoluteTimeRange,
|
||||
datasourceStatus: DataSourceStatus,
|
||||
initialDatasourceStatus?: DataSourceStatus // used for test purposes
|
||||
absoluteRange: AbsoluteTimeRange
|
||||
) => {
|
||||
const mounted = useRefMounted();
|
||||
|
||||
// State
|
||||
const [logLabelOptions, setLogLabelOptions] = useState([]);
|
||||
const [shouldTryRefreshLabels, setRefreshLabels] = useState(false);
|
||||
const [prevDatasourceStatus, setPrevDatasourceStatus] = useState(
|
||||
initialDatasourceStatus || DataSourceStatus.Connected
|
||||
);
|
||||
const [shouldForceRefreshLabels, setForceRefreshLabels] = useState(false);
|
||||
|
||||
// Async
|
||||
@ -83,15 +77,6 @@ export const useLokiLabels = (
|
||||
}
|
||||
}, [shouldTryRefreshLabels, shouldForceRefreshLabels]);
|
||||
|
||||
// This effect is performed on datasourceStatus state change only.
|
||||
// We want to make sure to only force refresh AFTER a disconnected state thats why we store the previous datasourceStatus in state
|
||||
useEffect(() => {
|
||||
if (datasourceStatus === DataSourceStatus.Connected && prevDatasourceStatus === DataSourceStatus.Disconnected) {
|
||||
setForceRefreshLabels(true);
|
||||
}
|
||||
setPrevDatasourceStatus(datasourceStatus);
|
||||
}, [datasourceStatus]);
|
||||
|
||||
return {
|
||||
logLabelOptions,
|
||||
setLogLabelOptions,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { renderHook, act } from 'react-hooks-testing-library';
|
||||
import { DataSourceStatus } from '@grafana/ui/src/types/datasource';
|
||||
import { AbsoluteTimeRange } from '@grafana/data';
|
||||
|
||||
import LanguageProvider from 'app/plugins/datasource/loki/language_provider';
|
||||
@ -36,9 +35,7 @@ describe('useLokiSyntax hook', () => {
|
||||
};
|
||||
|
||||
it('should provide Loki syntax when used', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useLokiSyntax(languageProvider, DataSourceStatus.Connected, rangeMock)
|
||||
);
|
||||
const { result, waitForNextUpdate } = renderHook(() => useLokiSyntax(languageProvider, rangeMock));
|
||||
expect(result.current.syntax).toEqual(null);
|
||||
|
||||
await waitForNextUpdate();
|
||||
@ -47,9 +44,7 @@ describe('useLokiSyntax hook', () => {
|
||||
});
|
||||
|
||||
it('should fetch labels on first call', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useLokiSyntax(languageProvider, DataSourceStatus.Connected, rangeMock)
|
||||
);
|
||||
const { result, waitForNextUpdate } = renderHook(() => useLokiSyntax(languageProvider, rangeMock));
|
||||
expect(result.current.isSyntaxReady).toBeFalsy();
|
||||
expect(result.current.logLabelOptions).toEqual([]);
|
||||
|
||||
@ -60,9 +55,7 @@ describe('useLokiSyntax hook', () => {
|
||||
});
|
||||
|
||||
it('should try to fetch missing options when active option changes', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useLokiSyntax(languageProvider, DataSourceStatus.Connected, rangeMock)
|
||||
);
|
||||
const { result, waitForNextUpdate } = renderHook(() => useLokiSyntax(languageProvider, rangeMock));
|
||||
await waitForNextUpdate();
|
||||
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock2);
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import Prism from 'prismjs';
|
||||
import { DataSourceStatus } from '@grafana/ui/src/types/datasource';
|
||||
import { AbsoluteTimeRange } from '@grafana/data';
|
||||
import LokiLanguageProvider from 'app/plugins/datasource/loki/language_provider';
|
||||
import { useLokiLabels } from 'app/plugins/datasource/loki/components/useLokiLabels';
|
||||
@ -14,11 +13,7 @@ const PRISM_SYNTAX = 'promql';
|
||||
* @param languageProvider
|
||||
* @description Initializes given language provider, exposes Loki syntax and enables loading label option values
|
||||
*/
|
||||
export const useLokiSyntax = (
|
||||
languageProvider: LokiLanguageProvider,
|
||||
datasourceStatus: DataSourceStatus,
|
||||
absoluteRange: AbsoluteTimeRange
|
||||
) => {
|
||||
export const useLokiSyntax = (languageProvider: LokiLanguageProvider, absoluteRange: AbsoluteTimeRange) => {
|
||||
const mounted = useRefMounted();
|
||||
// State
|
||||
const [languageProviderInitialized, setLanguageProviderInitilized] = useState(false);
|
||||
@ -35,8 +30,7 @@ export const useLokiSyntax = (
|
||||
languageProvider,
|
||||
languageProviderInitialized,
|
||||
activeOption,
|
||||
absoluteRange,
|
||||
datasourceStatus
|
||||
absoluteRange
|
||||
);
|
||||
|
||||
// Async
|
||||
|
@ -121,7 +121,7 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
||||
|
||||
processError = (err: any, target: any): DataQueryError => {
|
||||
const error: DataQueryError = {
|
||||
message: 'Unknown error during query transaction. Please check JS console logs.',
|
||||
message: (err && err.statusText) || 'Unknown error during query transaction. Please check JS console logs.',
|
||||
refId: target.refId,
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import _ from 'lodash';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// Types
|
||||
import { FormLabel, Select, Switch, QueryEditorProps, DataSourceStatus } from '@grafana/ui';
|
||||
import { FormLabel, Select, Switch, QueryEditorProps } from '@grafana/ui';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
@ -104,7 +104,6 @@ export class PromQueryEditor extends PureComponent<Props, State> {
|
||||
onChange={this.onFieldChange}
|
||||
history={[]}
|
||||
data={data}
|
||||
datasourceStatus={DataSourceStatus.Connected} // TODO: replace with real DataSourceStatus
|
||||
/>
|
||||
|
||||
<div className="gf-form-inline">
|
||||
|
@ -13,7 +13,7 @@ import BracesPlugin from 'app/features/explore/slate-plugins/braces';
|
||||
import QueryField, { TypeaheadInput } from 'app/features/explore/QueryField';
|
||||
import { PromQuery, PromContext, PromOptions } from '../types';
|
||||
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
|
||||
import { ExploreQueryFieldProps, DataSourceStatus, QueryHint, DOMUtil } from '@grafana/ui';
|
||||
import { ExploreQueryFieldProps, QueryHint, DOMUtil } from '@grafana/ui';
|
||||
import { isDataFrame, toLegacyResponseData } from '@grafana/data';
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import PromQlLanguageProvider from '../language_provider';
|
||||
@ -24,13 +24,13 @@ const METRIC_MARK = 'metric';
|
||||
const PRISM_SYNTAX = 'promql';
|
||||
export const RECORDING_RULES_GROUP = '__recording_rules__';
|
||||
|
||||
function getChooserText(hasSyntax: boolean, datasourceStatus: DataSourceStatus) {
|
||||
if (datasourceStatus === DataSourceStatus.Disconnected) {
|
||||
return '(Disconnected)';
|
||||
}
|
||||
function getChooserText(hasSyntax: boolean, metrics: string[]) {
|
||||
if (!hasSyntax) {
|
||||
return 'Loading metrics...';
|
||||
}
|
||||
if (metrics && metrics.length === 0) {
|
||||
return '(No metrics found)';
|
||||
}
|
||||
return 'Metrics';
|
||||
}
|
||||
|
||||
@ -159,21 +159,6 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
if (data && prevProps.data && prevProps.data.series !== data.series) {
|
||||
this.refreshHint();
|
||||
}
|
||||
|
||||
const reconnected =
|
||||
prevProps.datasourceStatus === DataSourceStatus.Disconnected &&
|
||||
this.props.datasourceStatus === DataSourceStatus.Connected;
|
||||
if (!reconnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.languageProviderInitializationPromise) {
|
||||
this.languageProviderInitializationPromise.cancel();
|
||||
}
|
||||
|
||||
if (this.languageProvider) {
|
||||
this.refreshMetrics(makePromiseCancelable(this.languageProvider.fetchMetrics()));
|
||||
}
|
||||
}
|
||||
|
||||
refreshHint = () => {
|
||||
@ -291,11 +276,11 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
};
|
||||
|
||||
render() {
|
||||
const { data, query, datasourceStatus } = this.props;
|
||||
const { data, query } = this.props;
|
||||
const { metricsOptions, syntaxLoaded, hint } = this.state;
|
||||
const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined;
|
||||
const chooserText = getChooserText(syntaxLoaded, datasourceStatus);
|
||||
const buttonDisabled = !syntaxLoaded || datasourceStatus === DataSourceStatus.Disconnected;
|
||||
const chooserText = getChooserText(syntaxLoaded, metricsOptions);
|
||||
const buttonDisabled = !(syntaxLoaded && metricsOptions && metricsOptions.length > 0);
|
||||
const showError = data && data.error && data.error.refId === query.refId;
|
||||
|
||||
return (
|
||||
|
@ -9,7 +9,6 @@ exports[`Render PromQueryEditor with basic options should render 1`] = `
|
||||
"getPrometheusTime": [MockFunction],
|
||||
}
|
||||
}
|
||||
datasourceStatus={0}
|
||||
history={Array []}
|
||||
onChange={[Function]}
|
||||
onRunQuery={[Function]}
|
||||
|
@ -447,7 +447,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
|
||||
handleErrors = (err: any, target: PromQuery) => {
|
||||
const error: DataQueryError = {
|
||||
message: 'Unknown error during query transaction. Please check JS console logs.',
|
||||
message: (err && err.statusText) || 'Unknown error during query transaction. Please check JS console logs.',
|
||||
refId: target.refId,
|
||||
};
|
||||
|
||||
|
@ -161,10 +161,6 @@ export interface ExploreItemState {
|
||||
* Current data source name or null if default
|
||||
*/
|
||||
requestedDatasourceName: string | null;
|
||||
/**
|
||||
* Error to be shown when datasource loading or testing failed.
|
||||
*/
|
||||
datasourceError: string;
|
||||
/**
|
||||
* True if the datasource is loading. `null` if the loading has not started yet.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user