mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Introduces PanelData to ExploreItemState (#18804)
* WIP: inital POC * Wip: Moving forward * Wip * Refactor: Makes loading indicator work for Prometheus * Refactor: Reverts prom observable queries because they did not work for multiple targets * Refactor: Transforms all epics into thunks * Fix: Fixes scanning * Fix: Fixes so that Instant and TimeSeries Prom query loads in parallel * Fix: Fixes negation logic error * Wip: Introduces PanelData as a carries for query responses * Refactor: Makes errors work again * Refactor: Simplifies code somewhat and removes comments * Tests: Fixes broken tests * Fix query latency * Remove unused code
This commit is contained in:
parent
6912ed572c
commit
409874b35d
@ -12,7 +12,6 @@
|
||||
"build": "grunt",
|
||||
"start": "grunt watch"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-premailer": "^1.1.10",
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Libraries
|
||||
import _ from 'lodash';
|
||||
import { from } from 'rxjs';
|
||||
import { isLive } from '@grafana/ui/src/components/RefreshPicker/RefreshPicker';
|
||||
// Services & Utils
|
||||
import {
|
||||
@ -9,25 +8,16 @@ import {
|
||||
TimeRange,
|
||||
RawTimeRange,
|
||||
TimeZone,
|
||||
IntervalValues,
|
||||
TimeFragment,
|
||||
LogRowModel,
|
||||
LogsModel,
|
||||
LogsDedupStrategy,
|
||||
} from '@grafana/data';
|
||||
import { renderUrl } from 'app/core/utils/url';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import store from 'app/core/store';
|
||||
import { getNextRefIdChar } from './query';
|
||||
// Types
|
||||
import {
|
||||
DataQuery,
|
||||
DataSourceApi,
|
||||
DataQueryError,
|
||||
DataSourceJsonData,
|
||||
DataQueryRequest,
|
||||
DataStreamObserver,
|
||||
} from '@grafana/ui';
|
||||
import { DataQuery, DataSourceApi, DataQueryError } from '@grafana/ui';
|
||||
import {
|
||||
ExploreUrlState,
|
||||
HistoryItem,
|
||||
@ -321,14 +311,6 @@ export function hasNonEmptyQuery<TQuery extends DataQuery = any>(queries: TQuery
|
||||
);
|
||||
}
|
||||
|
||||
export function getIntervals(range: TimeRange, lowLimit: string, resolution: number): IntervalValues {
|
||||
if (!resolution) {
|
||||
return { interval: '1s', intervalMs: 1000 };
|
||||
}
|
||||
|
||||
return kbn.calculateInterval(range, resolution, lowLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the query history. Side-effect: store history in local storage
|
||||
*/
|
||||
@ -448,7 +430,7 @@ export const getFirstQueryErrorWithoutRefId = (errors: DataQueryError[]) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return errors.filter(error => (error.refId ? false : true))[0];
|
||||
return errors.filter(error => (error && error.refId ? false : true))[0];
|
||||
};
|
||||
|
||||
export const getRefIds = (value: any): string[] => {
|
||||
@ -523,14 +505,6 @@ export const convertToWebSocketUrl = (url: string) => {
|
||||
return `${backend}${url}`;
|
||||
};
|
||||
|
||||
export const getQueryResponse = (
|
||||
datasourceInstance: DataSourceApi<DataQuery, DataSourceJsonData>,
|
||||
options: DataQueryRequest<DataQuery>,
|
||||
observer?: DataStreamObserver
|
||||
) => {
|
||||
return from(datasourceInstance.query(options, observer));
|
||||
};
|
||||
|
||||
export const stopQueryState = (queryState: PanelQueryState, reason: string) => {
|
||||
if (queryState && queryState.isStarted()) {
|
||||
queryState.cancel(reason);
|
||||
|
@ -95,7 +95,10 @@ export class PanelQueryState {
|
||||
}
|
||||
|
||||
execute(ds: DataSourceApi, req: DataQueryRequest): Promise<PanelData> {
|
||||
this.request = req;
|
||||
this.request = {
|
||||
...req,
|
||||
startTime: Date.now(),
|
||||
};
|
||||
this.datasource = ds;
|
||||
|
||||
// Return early if there are no queries to run
|
||||
@ -112,7 +115,7 @@ export class PanelQueryState {
|
||||
);
|
||||
}
|
||||
|
||||
// Set the loading state immediatly
|
||||
// Set the loading state immediately
|
||||
this.response.state = LoadingState.Loading;
|
||||
this.executor = new Promise<PanelData>((resolve, reject) => {
|
||||
this.rejector = reject;
|
||||
|
@ -3,20 +3,17 @@ import React, { ComponentClass } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
// @ts-ignore
|
||||
import { connect } from 'react-redux';
|
||||
import _ from 'lodash';
|
||||
import { AutoSizer } from 'react-virtualized';
|
||||
import memoizeOne from 'memoize-one';
|
||||
|
||||
// Services & Utils
|
||||
import store from 'app/core/store';
|
||||
|
||||
// Components
|
||||
import { Alert } from '@grafana/ui';
|
||||
import { Alert, DataQuery, ExploreStartPageProps, DataSourceApi, PanelData } from '@grafana/ui';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
import LogsContainer from './LogsContainer';
|
||||
import QueryRows from './QueryRows';
|
||||
import TableContainer from './TableContainer';
|
||||
|
||||
// Actions
|
||||
import {
|
||||
changeSize,
|
||||
@ -29,11 +26,8 @@ import {
|
||||
updateTimeRange,
|
||||
toggleGraph,
|
||||
} from './state/actions';
|
||||
|
||||
// Types
|
||||
import { RawTimeRange, GraphSeriesXY, LoadingState, TimeZone, AbsoluteTimeRange } from '@grafana/data';
|
||||
|
||||
import { DataQuery, ExploreStartPageProps, DataSourceApi, DataQueryError } from '@grafana/ui';
|
||||
import { RawTimeRange, GraphSeriesXY, TimeZone, AbsoluteTimeRange } from '@grafana/data';
|
||||
import {
|
||||
ExploreItemState,
|
||||
ExploreUrlState,
|
||||
@ -86,7 +80,6 @@ interface ExploreProps {
|
||||
initialRange: RawTimeRange;
|
||||
mode: ExploreMode;
|
||||
initialUI: ExploreUIState;
|
||||
queryErrors: DataQueryError[];
|
||||
isLive: boolean;
|
||||
updateTimeRange: typeof updateTimeRange;
|
||||
graphResult?: GraphSeriesXY[];
|
||||
@ -97,6 +90,7 @@ interface ExploreProps {
|
||||
timeZone?: TimeZone;
|
||||
onHiddenSeriesChanged?: (hiddenSeries: string[]) => void;
|
||||
toggleGraph: typeof toggleGraph;
|
||||
queryResponse: PanelData;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,7 +237,6 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
||||
showingStartPage,
|
||||
split,
|
||||
queryKeys,
|
||||
queryErrors,
|
||||
mode,
|
||||
graphResult,
|
||||
loading,
|
||||
@ -251,6 +244,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
||||
showingGraph,
|
||||
showingTable,
|
||||
timeZone,
|
||||
queryResponse,
|
||||
} = this.props;
|
||||
const exploreClass = split ? 'explore explore-split' : 'explore';
|
||||
|
||||
@ -272,7 +266,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
|
||||
{datasourceInstance && (
|
||||
<div className="explore-container">
|
||||
<QueryRows exploreEvents={this.exploreEvents} exploreId={exploreId} queryKeys={queryKeys} />
|
||||
<ErrorContainer queryErrors={queryErrors} />
|
||||
<ErrorContainer queryErrors={[queryResponse.error]} />
|
||||
<AutoSizer onResize={this.onResize} disableHeight>
|
||||
{({ width }) => {
|
||||
if (width === 0) {
|
||||
@ -347,15 +341,15 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
queryKeys,
|
||||
urlState,
|
||||
update,
|
||||
queryErrors,
|
||||
isLive,
|
||||
supportedModes,
|
||||
mode,
|
||||
graphResult,
|
||||
loadingState,
|
||||
loading,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
absoluteRange,
|
||||
queryResponse,
|
||||
} = item;
|
||||
|
||||
const { datasource, queries, range: urlRange, mode: urlMode, ui } = (urlState || {}) as ExploreUrlState;
|
||||
@ -380,7 +374,6 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
}
|
||||
|
||||
const initialUI = ui || DEFAULT_UI_STATE;
|
||||
const loading = loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming;
|
||||
|
||||
return {
|
||||
StartPage,
|
||||
@ -398,13 +391,13 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
initialRange,
|
||||
mode: newMode,
|
||||
initialUI,
|
||||
queryErrors,
|
||||
isLive,
|
||||
graphResult,
|
||||
loading,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
absoluteRange,
|
||||
queryResponse,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import memoizeOne from 'memoize-one';
|
||||
|
||||
import { ExploreId, ExploreMode } from 'app/types/explore';
|
||||
import { DataSourceSelectItem, ToggleButtonGroup, ToggleButton } from '@grafana/ui';
|
||||
import { RawTimeRange, TimeZone, TimeRange, LoadingState, SelectableValue } from '@grafana/data';
|
||||
import { RawTimeRange, TimeZone, TimeRange, SelectableValue } from '@grafana/data';
|
||||
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
|
||||
import { StoreState } from 'app/types/store';
|
||||
import {
|
||||
@ -281,7 +281,7 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps
|
||||
exploreDatasources,
|
||||
range,
|
||||
refreshInterval,
|
||||
loadingState,
|
||||
loading,
|
||||
supportedModes,
|
||||
mode,
|
||||
isLive,
|
||||
@ -289,7 +289,6 @@ const mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps
|
||||
const selectedDatasource = datasourceInstance
|
||||
? exploreDatasources.find(datasource => datasource.name === datasourceInstance.name)
|
||||
: undefined;
|
||||
const loading = loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming;
|
||||
const hasLiveOption =
|
||||
datasourceInstance && datasourceInstance.meta && datasourceInstance.meta.streaming ? true : false;
|
||||
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
LogsModel,
|
||||
LogRowModel,
|
||||
LogsDedupStrategy,
|
||||
LoadingState,
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
|
||||
@ -143,14 +142,13 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
|
||||
const {
|
||||
logsHighlighterExpressions,
|
||||
logsResult,
|
||||
loadingState,
|
||||
loading,
|
||||
scanning,
|
||||
datasourceInstance,
|
||||
isLive,
|
||||
range,
|
||||
absoluteRange,
|
||||
} = item;
|
||||
const loading = loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming;
|
||||
const { dedupStrategy } = exploreItemUIStateSelector(item);
|
||||
const dedupedResult = deduplicatedLogsSelector(item);
|
||||
const timeZone = getTimeZone(state.user);
|
||||
|
@ -2,20 +2,16 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import _ from 'lodash';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import memoizeOne from 'memoize-one';
|
||||
// @ts-ignore
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
// Components
|
||||
import QueryEditor from './QueryEditor';
|
||||
|
||||
// Actions
|
||||
import { changeQuery, modifyQueries, runQueries, addQueryRow } from './state/actions';
|
||||
|
||||
// Types
|
||||
import { StoreState } from 'app/types';
|
||||
import { TimeRange, AbsoluteTimeRange, toDataFrame, guessFieldTypes, GraphSeriesXY, LoadingState } from '@grafana/data';
|
||||
import { DataQuery, DataSourceApi, QueryFixAction, DataSourceStatus, PanelData, DataQueryError } from '@grafana/ui';
|
||||
import { TimeRange, AbsoluteTimeRange } from '@grafana/data';
|
||||
import { DataQuery, DataSourceApi, QueryFixAction, DataSourceStatus, 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';
|
||||
@ -44,7 +40,6 @@ interface QueryRowProps extends PropsFromParent {
|
||||
runQueries: typeof runQueries;
|
||||
queryResponse: PanelData;
|
||||
latency: number;
|
||||
queryErrors: DataQueryError[];
|
||||
mode: ExploreMode;
|
||||
}
|
||||
|
||||
@ -122,11 +117,11 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
||||
datasourceStatus,
|
||||
queryResponse,
|
||||
latency,
|
||||
queryErrors,
|
||||
mode,
|
||||
} = this.props;
|
||||
const canToggleEditorModes =
|
||||
mode === ExploreMode.Metrics && _.has(datasourceInstance, 'components.QueryCtrl.prototype.toggleEditorMode');
|
||||
const queryErrors = queryResponse.error && queryResponse.error.refId === query.refId ? [queryResponse.error] : [];
|
||||
let QueryField;
|
||||
|
||||
if (mode === ExploreMode.Metrics && datasourceInstance.components.ExploreMetricsQueryField) {
|
||||
@ -199,17 +194,6 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
||||
}
|
||||
}
|
||||
|
||||
const makeQueryResponseMemoized = memoizeOne(
|
||||
(graphResult: GraphSeriesXY[], error: DataQueryError, loadingState: LoadingState): PanelData => {
|
||||
const series = graphResult ? graphResult.map(serie => guessFieldTypes(toDataFrame(serie))) : []; // TODO: use DataFrame
|
||||
return {
|
||||
series,
|
||||
state: loadingState,
|
||||
error,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId, index }: QueryRowProps) {
|
||||
const explore = state.explore;
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
@ -220,16 +204,12 @@ function mapStateToProps(state: StoreState, { exploreId, index }: QueryRowProps)
|
||||
range,
|
||||
absoluteRange,
|
||||
datasourceError,
|
||||
graphResult,
|
||||
loadingState,
|
||||
latency,
|
||||
queryErrors,
|
||||
mode,
|
||||
queryResponse,
|
||||
} = item;
|
||||
const query = queries[index];
|
||||
const datasourceStatus = datasourceError ? DataSourceStatus.Disconnected : DataSourceStatus.Connected;
|
||||
const error = queryErrors.filter(queryError => queryError.refId === query.refId)[0];
|
||||
const queryResponse = makeQueryResponseMemoized(graphResult, error, loadingState);
|
||||
|
||||
return {
|
||||
datasourceInstance,
|
||||
@ -240,7 +220,6 @@ function mapStateToProps(state: StoreState, { exploreId, index }: QueryRowProps)
|
||||
datasourceStatus,
|
||||
queryResponse,
|
||||
latency,
|
||||
queryErrors,
|
||||
mode,
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import { LoadingState } from '@grafana/data';
|
||||
import { Collapse } from '@grafana/ui';
|
||||
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
@ -40,11 +39,8 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
|
||||
const explore = state.explore;
|
||||
// @ts-ignore
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const { loadingState, showingTable, tableResult } = item;
|
||||
const loading =
|
||||
tableResult && tableResult.rows.length > 0
|
||||
? false
|
||||
: loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming;
|
||||
const { loading: loadingInState, showingTable, tableResult } = item;
|
||||
const loading = tableResult && tableResult.rows.length > 0 ? false : loadingInState;
|
||||
return { loading, showingTable, tableResult };
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
// Types
|
||||
import { Emitter } from 'app/core/core';
|
||||
import { DataQuery, DataSourceSelectItem, DataSourceApi, QueryFixAction, DataQueryError } from '@grafana/ui';
|
||||
import { DataQuery, DataSourceSelectItem, DataSourceApi, QueryFixAction, PanelData } from '@grafana/ui';
|
||||
|
||||
import { LogLevel, TimeRange, LogsModel, LoadingState, AbsoluteTimeRange, GraphSeriesXY } from '@grafana/data';
|
||||
import { LogLevel, TimeRange, LoadingState, AbsoluteTimeRange } from '@grafana/data';
|
||||
import { ExploreId, ExploreItemState, HistoryItem, ExploreUIState, ExploreMode } from 'app/types/explore';
|
||||
import { actionCreatorFactory, noPayloadActionCreatorFactory, ActionOf } from 'app/core/redux/actionCreatorFactory';
|
||||
import TableModel from 'app/core/table_model';
|
||||
|
||||
/** Higher order actions
|
||||
*
|
||||
@ -62,10 +61,6 @@ export interface ClearQueriesPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ClearRefreshIntervalPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface HighlightLogsExpressionPayload {
|
||||
exploreId: ExploreId;
|
||||
expressions: string[];
|
||||
@ -81,11 +76,6 @@ export interface InitializeExplorePayload {
|
||||
ui: ExploreUIState;
|
||||
}
|
||||
|
||||
export interface LoadDatasourceFailurePayload {
|
||||
exploreId: ExploreId;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface LoadDatasourceMissingPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
@ -120,22 +110,13 @@ export interface ModifyQueriesPayload {
|
||||
modifier: (query: DataQuery, modification: QueryFixAction) => DataQuery;
|
||||
}
|
||||
|
||||
export interface QueryFailurePayload {
|
||||
exploreId: ExploreId;
|
||||
response: DataQueryError;
|
||||
}
|
||||
|
||||
export interface QueryStartPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface QuerySuccessPayload {
|
||||
export interface QueryEndedPayload {
|
||||
exploreId: ExploreId;
|
||||
latency: number;
|
||||
loadingState: LoadingState;
|
||||
graphResult: GraphSeriesXY[];
|
||||
tableResult: TableModel;
|
||||
logsResult: LogsModel;
|
||||
response: PanelData;
|
||||
}
|
||||
|
||||
export interface HistoryUpdatedPayload {
|
||||
@ -201,15 +182,6 @@ export interface LoadExploreDataSourcesPayload {
|
||||
exploreDatasources: DataSourceSelectItem[];
|
||||
}
|
||||
|
||||
export interface RunQueriesPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ResetQueryErrorPayload {
|
||||
exploreId: ExploreId;
|
||||
refIds: string[];
|
||||
}
|
||||
|
||||
export interface SetUrlReplacedPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
@ -230,11 +202,6 @@ export interface ChangeLoadingStatePayload {
|
||||
*/
|
||||
export const addQueryRowAction = actionCreatorFactory<AddQueryRowPayload>('explore/ADD_QUERY_ROW').create();
|
||||
|
||||
/**
|
||||
* Loads a new datasource identified by the given name.
|
||||
*/
|
||||
export const changeDatasourceAction = noPayloadActionCreatorFactory('explore/CHANGE_DATASOURCE').create();
|
||||
|
||||
/**
|
||||
* Change the mode of Explore.
|
||||
*/
|
||||
@ -309,34 +276,19 @@ export const loadDatasourceReadyAction = actionCreatorFactory<LoadDatasourceRead
|
||||
*/
|
||||
export const modifyQueriesAction = actionCreatorFactory<ModifyQueriesPayload>('explore/MODIFY_QUERIES').create();
|
||||
|
||||
/**
|
||||
* Mark a query transaction as failed with an error extracted from the query response.
|
||||
* The transaction will be marked as `done`.
|
||||
*/
|
||||
export const queryFailureAction = actionCreatorFactory<QueryFailurePayload>('explore/QUERY_FAILURE').create();
|
||||
|
||||
export const queryStartAction = actionCreatorFactory<QueryStartPayload>('explore/QUERY_START').create();
|
||||
|
||||
/**
|
||||
* Complete a query transaction, mark the transaction as `done` and store query state in URL.
|
||||
* If the transaction was started by a scanner, it keeps on scanning for more results.
|
||||
* Side-effect: the query is stored in localStorage.
|
||||
* @param exploreId Explore area
|
||||
* @param transactionId ID
|
||||
* @param result Response from `datasourceInstance.query()`
|
||||
* @param latency Duration between request and response
|
||||
* @param queries Queries from all query rows
|
||||
* @param datasourceId Origin datasource instance, used to discard results if current datasource is different
|
||||
*/
|
||||
export const querySuccessAction = actionCreatorFactory<QuerySuccessPayload>('explore/QUERY_SUCCESS').create();
|
||||
export const queryEndedAction = actionCreatorFactory<QueryEndedPayload>('explore/QUERY_ENDED').create();
|
||||
|
||||
export const queryStreamUpdatedAction = actionCreatorFactory<QueryEndedPayload>(
|
||||
'explore/QUERY_STREAM_UPDATED'
|
||||
).create();
|
||||
|
||||
/**
|
||||
* Remove query row of the given index, as well as associated query results.
|
||||
*/
|
||||
export const removeQueryRowAction = actionCreatorFactory<RemoveQueryRowPayload>('explore/REMOVE_QUERY_ROW').create();
|
||||
|
||||
export const runQueriesAction = actionCreatorFactory<RunQueriesPayload>('explore/RUN_QUERIES').create();
|
||||
|
||||
/**
|
||||
* Start a scan for more results using the given scanner.
|
||||
* @param exploreId Explore area
|
||||
@ -411,8 +363,6 @@ export const loadExploreDatasources = actionCreatorFactory<LoadExploreDataSource
|
||||
|
||||
export const historyUpdatedAction = actionCreatorFactory<HistoryUpdatedPayload>('explore/HISTORY_UPDATED').create();
|
||||
|
||||
export const resetQueryErrorAction = actionCreatorFactory<ResetQueryErrorPayload>('explore/RESET_QUERY_ERROR').create();
|
||||
|
||||
export const setUrlReplacedAction = actionCreatorFactory<SetUrlReplacedPayload>('explore/SET_URL_REPLACED').create();
|
||||
|
||||
export const changeRangeAction = actionCreatorFactory<ChangeRangePayload>('explore/CHANGE_RANGE').create();
|
||||
|
@ -13,30 +13,20 @@ import {
|
||||
lastUsedDatasourceKeyForOrgId,
|
||||
hasNonEmptyQuery,
|
||||
buildQueryTransaction,
|
||||
updateHistory,
|
||||
getRefIds,
|
||||
instanceOfDataQueryError,
|
||||
clearQueryKeys,
|
||||
serializeStateToUrlParam,
|
||||
stopQueryState,
|
||||
updateHistory,
|
||||
} from 'app/core/utils/explore';
|
||||
// Types
|
||||
import { ThunkResult, ExploreUrlState } from 'app/types';
|
||||
import {
|
||||
DataSourceApi,
|
||||
DataQuery,
|
||||
DataSourceSelectItem,
|
||||
QueryFixAction,
|
||||
PanelData,
|
||||
DataQueryResponseData,
|
||||
} from '@grafana/ui';
|
||||
import { DataSourceApi, DataQuery, DataSourceSelectItem, QueryFixAction, PanelData } from '@grafana/ui';
|
||||
|
||||
import {
|
||||
RawTimeRange,
|
||||
LogsDedupStrategy,
|
||||
AbsoluteTimeRange,
|
||||
LoadingState,
|
||||
DataFrame,
|
||||
TimeRange,
|
||||
isDateTime,
|
||||
dateTimeForTimeZone,
|
||||
@ -73,22 +63,18 @@ import {
|
||||
loadExploreDatasources,
|
||||
changeModeAction,
|
||||
scanStopAction,
|
||||
changeLoadingStateAction,
|
||||
historyUpdatedAction,
|
||||
queryStartAction,
|
||||
resetQueryErrorAction,
|
||||
querySuccessAction,
|
||||
queryFailureAction,
|
||||
setUrlReplacedAction,
|
||||
changeRangeAction,
|
||||
historyUpdatedAction,
|
||||
queryEndedAction,
|
||||
queryStreamUpdatedAction,
|
||||
} from './actionTypes';
|
||||
import { ActionOf, ActionCreator } from 'app/core/redux/actionCreatorFactory';
|
||||
import { getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { offOption } from '@grafana/ui/src/components/RefreshPicker/RefreshPicker';
|
||||
import { getShiftedTimeRange } from 'app/core/utils/timePicker';
|
||||
import { ResultProcessor } from '../utils/ResultProcessor';
|
||||
import _ from 'lodash';
|
||||
import { toDataQueryError } from '../../dashboard/state/PanelQueryState';
|
||||
import { updateLocation } from '../../../core/actions';
|
||||
import { getTimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
|
||||
@ -466,36 +452,15 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
|
||||
stopQueryState(queryState, 'New request issued');
|
||||
|
||||
queryState.sendFrames = true;
|
||||
queryState.sendLegacy = true; // temporary hack until we switch to PanelData
|
||||
queryState.sendLegacy = true;
|
||||
|
||||
const queryOptions = { interval, maxDataPoints: containerWidth, live };
|
||||
const datasourceId = datasourceInstance.meta.id;
|
||||
const now = Date.now();
|
||||
const transaction = buildQueryTransaction(queries, queryOptions, range, queryIntervals, scanning);
|
||||
|
||||
// temporary hack until we switch to PanelData, Loki already converts to DataFrame so using legacy will destroy the format
|
||||
const isLokiDataSource = datasourceInstance.meta.name === 'Loki';
|
||||
|
||||
queryState.onStreamingDataUpdated = () => {
|
||||
const data = queryState.validateStreamsAndGetPanelData();
|
||||
const { state, error, legacy, series } = data;
|
||||
if (!data && !error && !legacy && !series) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === LoadingState.Error) {
|
||||
dispatch(processErrorResults({ exploreId, response: error, datasourceId }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === LoadingState.Streaming) {
|
||||
dispatch(limitMessageRate(exploreId, isLokiDataSource ? series : legacy, datasourceId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === LoadingState.Done) {
|
||||
dispatch(changeLoadingStateAction({ exploreId, loadingState: state }));
|
||||
}
|
||||
const response = queryState.validateStreamsAndGetPanelData();
|
||||
dispatch(queryStreamUpdatedAction({ exploreId, response }));
|
||||
};
|
||||
|
||||
dispatch(queryStartAction({ exploreId }));
|
||||
@ -503,134 +468,38 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
|
||||
queryState
|
||||
.execute(datasourceInstance, transaction.options)
|
||||
.then((response: PanelData) => {
|
||||
const { legacy, error, series } = response;
|
||||
if (error) {
|
||||
dispatch(processErrorResults({ exploreId, response: error, datasourceId }));
|
||||
return;
|
||||
if (!response.error) {
|
||||
// Side-effect: Saving history in localstorage
|
||||
const nextHistory = updateHistory(history, datasourceId, queries);
|
||||
dispatch(historyUpdatedAction({ exploreId, history: nextHistory }));
|
||||
}
|
||||
|
||||
const latency = Date.now() - now;
|
||||
// Side-effect: Saving history in localstorage
|
||||
const nextHistory = updateHistory(history, datasourceId, queries);
|
||||
dispatch(historyUpdatedAction({ exploreId, history: nextHistory }));
|
||||
dispatch(
|
||||
processQueryResults({
|
||||
exploreId,
|
||||
latency,
|
||||
datasourceId,
|
||||
loadingState: LoadingState.Done,
|
||||
series: isLokiDataSource ? series : legacy,
|
||||
})
|
||||
);
|
||||
dispatch(queryEndedAction({ exploreId, response }));
|
||||
dispatch(stateSave());
|
||||
|
||||
// Keep scanning for results if this was the last scanning transaction
|
||||
if (getState().explore[exploreId].scanning) {
|
||||
if (_.size(response.series) === 0) {
|
||||
const range = getShiftedTimeRange(-1, getState().explore[exploreId].range);
|
||||
dispatch(updateTime({ exploreId, absoluteRange: range }));
|
||||
dispatch(runQueries(exploreId));
|
||||
} else {
|
||||
// We can stop scanning if we have a result
|
||||
dispatch(scanStopAction({ exploreId }));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
dispatch(processErrorResults({ exploreId, response: error, datasourceId }));
|
||||
dispatch(
|
||||
queryEndedAction({
|
||||
exploreId,
|
||||
response: { error, legacy: [], series: [], request: transaction.options, state: LoadingState.Error },
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const limitMessageRate = (
|
||||
exploreId: ExploreId,
|
||||
series: DataFrame[] | any[],
|
||||
datasourceId: string
|
||||
): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(
|
||||
processQueryResults({
|
||||
exploreId,
|
||||
latency: 0,
|
||||
datasourceId,
|
||||
loadingState: LoadingState.Streaming,
|
||||
series,
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export const processQueryResults = (config: {
|
||||
exploreId: ExploreId;
|
||||
latency: number;
|
||||
datasourceId: string;
|
||||
loadingState: LoadingState;
|
||||
series?: DataQueryResponseData[];
|
||||
}): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
const { exploreId, datasourceId, latency, loadingState, series } = config;
|
||||
const { datasourceInstance, scanning, eventBridge } = getState().explore[exploreId];
|
||||
|
||||
// If datasource already changed, results do not matter
|
||||
if (datasourceInstance.meta.id !== datasourceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = series || [];
|
||||
const replacePreviousResults = loadingState === LoadingState.Done && series ? true : false;
|
||||
const resultProcessor = new ResultProcessor(getState().explore[exploreId], replacePreviousResults, result);
|
||||
const graphResult = resultProcessor.getGraphResult();
|
||||
const tableResult = resultProcessor.getTableResult();
|
||||
const logsResult = resultProcessor.getLogsResult();
|
||||
const refIds = getRefIds(result);
|
||||
|
||||
// For Angular editors
|
||||
eventBridge.emit('data-received', resultProcessor.getRawData());
|
||||
|
||||
// Clears any previous errors that now have a successful query, important so Angular editors are updated correctly
|
||||
dispatch(resetQueryErrorAction({ exploreId, refIds }));
|
||||
|
||||
dispatch(
|
||||
querySuccessAction({
|
||||
exploreId,
|
||||
latency,
|
||||
loadingState,
|
||||
graphResult,
|
||||
tableResult,
|
||||
logsResult,
|
||||
})
|
||||
);
|
||||
|
||||
// Keep scanning for results if this was the last scanning transaction
|
||||
if (scanning) {
|
||||
if (_.size(result) === 0) {
|
||||
const range = getShiftedTimeRange(-1, getState().explore[exploreId].range);
|
||||
dispatch(updateTime({ exploreId, absoluteRange: range }));
|
||||
dispatch(runQueries(exploreId));
|
||||
} else {
|
||||
// We can stop scanning if we have a result
|
||||
dispatch(scanStopAction({ exploreId }));
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const processErrorResults = (config: {
|
||||
exploreId: ExploreId;
|
||||
response: any;
|
||||
datasourceId: string;
|
||||
}): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
const { exploreId, datasourceId } = config;
|
||||
let { response } = config;
|
||||
const { datasourceInstance, eventBridge } = getState().explore[exploreId];
|
||||
|
||||
if (datasourceInstance.meta.id !== datasourceId || response.cancelled) {
|
||||
// Navigated away, queries did not matter
|
||||
return;
|
||||
}
|
||||
|
||||
// For Angular editors
|
||||
eventBridge.emit('data-error', response);
|
||||
|
||||
console.error(response); // To help finding problems with query syntax
|
||||
|
||||
if (!instanceOfDataQueryError(response)) {
|
||||
response = toDataQueryError(response);
|
||||
}
|
||||
|
||||
dispatch(queryFailureAction({ exploreId, response }));
|
||||
};
|
||||
};
|
||||
|
||||
const toRawTimeRange = (range: TimeRange): RawTimeRange => {
|
||||
let from = range.raw.from;
|
||||
if (isDateTime(from)) {
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
exploreReducer,
|
||||
makeInitialUpdateState,
|
||||
initialExploreState,
|
||||
createEmptyQueryResponse,
|
||||
} from './reducers';
|
||||
import { ExploreId, ExploreItemState, ExploreUrlState, ExploreState, ExploreMode } from 'app/types/explore';
|
||||
import { reducerTester } from 'test/core/redux/reducerTester';
|
||||
@ -17,7 +18,6 @@ import {
|
||||
splitCloseAction,
|
||||
changeModeAction,
|
||||
scanStopAction,
|
||||
runQueriesAction,
|
||||
} from './actionTypes';
|
||||
import { Reducer } from 'redux';
|
||||
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
|
||||
@ -25,7 +25,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, LoadingState } from '@grafana/data';
|
||||
import { LogsModel, LogsDedupStrategy } from '@grafana/data';
|
||||
import { PanelQueryState } from '../../dashboard/state/PanelQueryState';
|
||||
|
||||
describe('Explore item reducer', () => {
|
||||
@ -162,9 +162,9 @@ describe('Explore item reducer', () => {
|
||||
tableResult: null,
|
||||
supportedModes: [ExploreMode.Metrics, ExploreMode.Logs],
|
||||
mode: ExploreMode.Metrics,
|
||||
loadingState: LoadingState.NotStarted,
|
||||
latency: 0,
|
||||
queryErrors: [],
|
||||
loading: false,
|
||||
queryResponse: createEmptyQueryResponse(),
|
||||
};
|
||||
|
||||
reducerTester()
|
||||
@ -175,30 +175,6 @@ describe('Explore item reducer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('run queries', () => {
|
||||
describe('when runQueriesAction is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
const initalState: Partial<ExploreItemState> = {
|
||||
showingStartPage: true,
|
||||
range: null,
|
||||
};
|
||||
const expectedState: any = {
|
||||
queryIntervals: {
|
||||
interval: '1s',
|
||||
intervalMs: 1000,
|
||||
},
|
||||
showingStartPage: false,
|
||||
range: null,
|
||||
};
|
||||
|
||||
reducerTester()
|
||||
.givenReducer(itemReducer, initalState)
|
||||
.whenActionIsDispatched(runQueriesAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual(expectedState);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export const setup = (urlStateOverrides?: any) => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
getIntervals,
|
||||
ensureQueries,
|
||||
getQueryKeys,
|
||||
parseUrlState,
|
||||
@ -9,10 +8,11 @@ import {
|
||||
sortLogsResult,
|
||||
stopQueryState,
|
||||
refreshIntervalToSortOrder,
|
||||
instanceOfDataQueryError,
|
||||
} from 'app/core/utils/explore';
|
||||
import { ExploreItemState, ExploreState, ExploreId, ExploreUpdateState, ExploreMode } from 'app/types/explore';
|
||||
import { LoadingState } from '@grafana/data';
|
||||
import { DataQuery } from '@grafana/ui';
|
||||
import { DataQuery, PanelData } from '@grafana/ui';
|
||||
import {
|
||||
HigherOrderAction,
|
||||
ActionTypes,
|
||||
@ -24,13 +24,9 @@ import {
|
||||
loadExploreDatasources,
|
||||
historyUpdatedAction,
|
||||
changeModeAction,
|
||||
queryFailureAction,
|
||||
setUrlReplacedAction,
|
||||
querySuccessAction,
|
||||
scanStopAction,
|
||||
resetQueryErrorAction,
|
||||
queryStartAction,
|
||||
runQueriesAction,
|
||||
changeRangeAction,
|
||||
addQueryRowAction,
|
||||
changeQueryAction,
|
||||
@ -53,13 +49,17 @@ import {
|
||||
toggleLogLevelAction,
|
||||
changeLoadingStateAction,
|
||||
resetExploreAction,
|
||||
queryEndedAction,
|
||||
queryStreamUpdatedAction,
|
||||
QueryEndedPayload,
|
||||
} from './actionTypes';
|
||||
import { reducerFactory } from 'app/core/redux';
|
||||
import { reducerFactory, ActionOf } from 'app/core/redux';
|
||||
import { updateLocation } from 'app/core/actions/location';
|
||||
import { LocationUpdate } from '@grafana/runtime';
|
||||
import TableModel from 'app/core/table_model';
|
||||
import { isLive } from '@grafana/ui/src/components/RefreshPicker/RefreshPicker';
|
||||
import { PanelQueryState } from '../../dashboard/state/PanelQueryState';
|
||||
import { PanelQueryState, toDataQueryError } from '../../dashboard/state/PanelQueryState';
|
||||
import { ResultProcessor } from '../utils/ResultProcessor';
|
||||
|
||||
export const DEFAULT_RANGE = {
|
||||
from: 'now-6h',
|
||||
@ -106,17 +106,25 @@ export const makeExploreItemState = (): ExploreItemState => ({
|
||||
scanRange: null,
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
loadingState: LoadingState.NotStarted,
|
||||
loading: false,
|
||||
queryKeys: [],
|
||||
urlState: null,
|
||||
update: makeInitialUpdateState(),
|
||||
queryErrors: [],
|
||||
latency: 0,
|
||||
supportedModes: [],
|
||||
mode: null,
|
||||
isLive: false,
|
||||
urlReplaced: false,
|
||||
queryState: new PanelQueryState(),
|
||||
queryResponse: createEmptyQueryResponse(),
|
||||
});
|
||||
|
||||
export const createEmptyQueryResponse = (): PanelData => ({
|
||||
state: LoadingState.NotStarted,
|
||||
request: null,
|
||||
series: [],
|
||||
legacy: null,
|
||||
error: null,
|
||||
});
|
||||
|
||||
/**
|
||||
@ -196,8 +204,12 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
return {
|
||||
...state,
|
||||
refreshInterval,
|
||||
loadingState: live ? LoadingState.Streaming : LoadingState.NotStarted,
|
||||
queryResponse: {
|
||||
...state.queryResponse,
|
||||
state: live ? LoadingState.Streaming : LoadingState.NotStarted,
|
||||
},
|
||||
isLive: live,
|
||||
loading: live,
|
||||
logsResult,
|
||||
};
|
||||
},
|
||||
@ -215,6 +227,8 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
logsResult: null,
|
||||
showingStartPage: Boolean(state.StartPage),
|
||||
queryKeys: getQueryKeys(queries, state.datasourceInstance),
|
||||
queryResponse: createEmptyQueryResponse(),
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
})
|
||||
@ -273,12 +287,12 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
return {
|
||||
...state,
|
||||
datasourceInstance,
|
||||
queryErrors: [],
|
||||
graphResult: null,
|
||||
tableResult: null,
|
||||
logsResult: null,
|
||||
latency: 0,
|
||||
loadingState: LoadingState.NotStarted,
|
||||
queryResponse: createEmptyQueryResponse(),
|
||||
loading: false,
|
||||
StartPage,
|
||||
showingStartPage: Boolean(StartPage),
|
||||
queryKeys: [],
|
||||
@ -352,49 +366,18 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: queryFailureAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
const { response } = action.payload;
|
||||
const queryErrors = state.queryErrors.concat(response);
|
||||
|
||||
return {
|
||||
...state,
|
||||
graphResult: null,
|
||||
tableResult: null,
|
||||
logsResult: null,
|
||||
latency: 0,
|
||||
queryErrors,
|
||||
loadingState: LoadingState.Error,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: queryStartAction,
|
||||
mapper: (state): ExploreItemState => {
|
||||
return {
|
||||
...state,
|
||||
queryErrors: [],
|
||||
latency: 0,
|
||||
loadingState: LoadingState.Loading,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: querySuccessAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
const { latency, loadingState, graphResult, tableResult, logsResult } = action.payload;
|
||||
|
||||
return {
|
||||
...state,
|
||||
loadingState,
|
||||
graphResult,
|
||||
tableResult,
|
||||
logsResult,
|
||||
latency,
|
||||
showingStartPage: false,
|
||||
queryResponse: {
|
||||
...state.queryResponse,
|
||||
state: LoadingState.Loading,
|
||||
error: null,
|
||||
},
|
||||
loading: true,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
},
|
||||
@ -526,24 +509,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: runQueriesAction,
|
||||
mapper: (state): ExploreItemState => {
|
||||
const { range } = state;
|
||||
const { datasourceInstance, containerWidth } = state;
|
||||
let interval = '1s';
|
||||
if (datasourceInstance && datasourceInstance.interval) {
|
||||
interval = datasourceInstance.interval;
|
||||
}
|
||||
const queryIntervals = getIntervals(range, interval, containerWidth);
|
||||
return {
|
||||
...state,
|
||||
range,
|
||||
queryIntervals,
|
||||
showingStartPage: false,
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: historyUpdatedAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
@ -553,24 +518,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: resetQueryErrorAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
const { refIds } = action.payload;
|
||||
const queryErrors = state.queryErrors.reduce((allErrors, error) => {
|
||||
if (error.refId && refIds.indexOf(error.refId) !== -1) {
|
||||
return allErrors;
|
||||
}
|
||||
|
||||
return allErrors.concat(error);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...state,
|
||||
queryErrors,
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: setUrlReplacedAction,
|
||||
mapper: (state): ExploreItemState => {
|
||||
@ -597,12 +544,81 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
const { loadingState } = action.payload;
|
||||
return {
|
||||
...state,
|
||||
loadingState,
|
||||
queryResponse: {
|
||||
...state.queryResponse,
|
||||
state: loadingState,
|
||||
},
|
||||
loading: loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming,
|
||||
};
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
//queryStreamUpdatedAction
|
||||
filter: queryEndedAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
return processQueryResponse(state, action);
|
||||
},
|
||||
})
|
||||
.addMapper({
|
||||
filter: queryStreamUpdatedAction,
|
||||
mapper: (state, action): ExploreItemState => {
|
||||
return processQueryResponse(state, action);
|
||||
},
|
||||
})
|
||||
.create();
|
||||
|
||||
export const processQueryResponse = (
|
||||
state: ExploreItemState,
|
||||
action: ActionOf<QueryEndedPayload>
|
||||
): ExploreItemState => {
|
||||
const { response } = action.payload;
|
||||
const { request, state: loadingState, series, legacy, error } = response;
|
||||
const replacePreviousResults = action.type === queryEndedAction.type;
|
||||
|
||||
if (error) {
|
||||
// For Angular editors
|
||||
state.eventBridge.emit('data-error', error);
|
||||
|
||||
console.error(error); // To help finding problems with query syntax
|
||||
|
||||
if (!instanceOfDataQueryError(error)) {
|
||||
response.error = toDataQueryError(error);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
queryResponse: response,
|
||||
graphResult: null,
|
||||
tableResult: null,
|
||||
logsResult: null,
|
||||
showingStartPage: false,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
}
|
||||
|
||||
const latency = request.endTime - request.startTime;
|
||||
|
||||
// temporary hack until we switch to PanelData, Loki already converts to DataFrame so using legacy will destroy the format
|
||||
const isLokiDataSource = state.datasourceInstance.meta.name === 'Loki';
|
||||
const processor = new ResultProcessor(state, replacePreviousResults, isLokiDataSource ? series : legacy);
|
||||
|
||||
// For Angular editors
|
||||
state.eventBridge.emit('data-received', processor.getRawData());
|
||||
|
||||
return {
|
||||
...state,
|
||||
latency,
|
||||
queryResponse: response,
|
||||
graphResult: processor.getGraphResult(),
|
||||
tableResult: processor.getTableResult(),
|
||||
logsResult: processor.getLogsResult(),
|
||||
loading: loadingState === LoadingState.Loading || loadingState === LoadingState.Streaming,
|
||||
showingStartPage: false,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
};
|
||||
|
||||
export const updateChildRefreshState = (
|
||||
state: Readonly<ExploreItemState>,
|
||||
payload: LocationUpdate,
|
||||
|
@ -4,14 +4,11 @@ import React from 'react';
|
||||
import Cascader from 'rc-cascader';
|
||||
// @ts-ignore
|
||||
import PluginPrism from 'slate-prism';
|
||||
|
||||
// Components
|
||||
import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explore/QueryField';
|
||||
|
||||
// Utils & Services
|
||||
// dom also includes Element polyfills
|
||||
import BracesPlugin from 'app/features/explore/slate-plugins/braces';
|
||||
|
||||
// Types
|
||||
import { LokiQuery } from '../types';
|
||||
import { TypeaheadOutput, HistoryItem } from 'app/types/explore';
|
||||
@ -158,6 +155,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
||||
const hasLogLabels = logLabelOptions && logLabelOptions.length > 0;
|
||||
const chooserText = getChooserText(syntaxLoaded, hasLogLabels, datasourceStatus);
|
||||
const buttonDisabled = !syntaxLoaded || datasourceStatus === DataSourceStatus.Disconnected;
|
||||
const showError = queryResponse && queryResponse.error && queryResponse.error.refId === query.refId;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -194,9 +192,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{queryResponse && queryResponse.error ? (
|
||||
<div className="prom-query-field-info text-error">{queryResponse.error.message}</div>
|
||||
) : null}
|
||||
{showError ? <div className="prom-query-field-info text-error">{queryResponse.error.message}</div> : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -8,7 +8,6 @@ import PluginPrism from 'slate-prism';
|
||||
import Prism from 'prismjs';
|
||||
|
||||
import { TypeaheadOutput, HistoryItem } from 'app/types/explore';
|
||||
|
||||
// dom also includes Element polyfills
|
||||
import BracesPlugin from 'app/features/explore/slate-plugins/braces';
|
||||
import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explore/QueryField';
|
||||
@ -303,6 +302,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined;
|
||||
const chooserText = getChooserText(syntaxLoaded, datasourceStatus);
|
||||
const buttonDisabled = !syntaxLoaded || datasourceStatus === DataSourceStatus.Disconnected;
|
||||
const showError = queryResponse && queryResponse.error && queryResponse.error.refId === query.refId;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -329,9 +329,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{queryResponse && queryResponse.error ? (
|
||||
<div className="prom-query-field-info text-error">{queryResponse.error.message}</div>
|
||||
) : null}
|
||||
{showError ? <div className="prom-query-field-info text-error">{queryResponse.error.message}</div> : null}
|
||||
{hint ? (
|
||||
<div className="prom-query-field-info text-warning">
|
||||
{hint.label}{' '}
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
DataSourceApi,
|
||||
QueryHint,
|
||||
ExploreStartPageProps,
|
||||
DataQueryError,
|
||||
PanelData,
|
||||
} from '@grafana/ui';
|
||||
|
||||
import {
|
||||
@ -14,7 +14,6 @@ import {
|
||||
TimeRange,
|
||||
LogsModel,
|
||||
LogsDedupStrategy,
|
||||
LoadingState,
|
||||
AbsoluteTimeRange,
|
||||
GraphSeriesXY,
|
||||
} from '@grafana/data';
|
||||
@ -218,7 +217,7 @@ export interface ExploreItemState {
|
||||
*/
|
||||
showingTable: boolean;
|
||||
|
||||
loadingState: LoadingState;
|
||||
loading: boolean;
|
||||
/**
|
||||
* Table model that combines all query table results into a single table.
|
||||
*/
|
||||
@ -248,8 +247,6 @@ export interface ExploreItemState {
|
||||
|
||||
update: ExploreUpdateState;
|
||||
|
||||
queryErrors: DataQueryError[];
|
||||
|
||||
latency: number;
|
||||
supportedModes: ExploreMode[];
|
||||
mode: ExploreMode;
|
||||
@ -258,6 +255,8 @@ export interface ExploreItemState {
|
||||
urlReplaced: boolean;
|
||||
|
||||
queryState: PanelQueryState;
|
||||
|
||||
queryResponse: PanelData;
|
||||
}
|
||||
|
||||
export interface ExploreUpdateState {
|
||||
|
Loading…
Reference in New Issue
Block a user