diff --git a/packages/grafana-data/src/dataframe/processDataFrame.ts b/packages/grafana-data/src/dataframe/processDataFrame.ts index 205a5edb888..fa60304f871 100644 --- a/packages/grafana-data/src/dataframe/processDataFrame.ts +++ b/packages/grafana-data/src/dataframe/processDataFrame.ts @@ -20,6 +20,8 @@ import { TIME_SERIES_VALUE_FIELD_NAME, TIME_SERIES_TIME_FIELD_NAME, DataQueryResponseData, + PanelData, + LoadingState, } from '../types/index'; import { ArrayVector } from '../vector/ArrayVector'; import { SortedVector } from '../vector/SortedVector'; @@ -537,3 +539,37 @@ export function getProcessedDataFrames(results?: DataQueryResponseData[]): DataF return results.map((data) => getProcessedDataFrame(data)); } + +/** + * Will process the panel data frames and in case of loading state with no data, will return the last result data but with loading state + * This is to have panels not flicker temporarily with "no data" while loading + */ +export function preProcessPanelData(data: PanelData, lastResult?: PanelData): PanelData { + const { series, annotations } = data; + + // for loading states with no data, use last result + if (data.state === LoadingState.Loading && series.length === 0) { + if (!lastResult) { + lastResult = data; + } + + return { + ...lastResult, + state: LoadingState.Loading, + request: data.request, + }; + } + + // Make sure the data frames are properly formatted + const STARTTIME = performance.now(); + const processedDataFrames = series.map((data) => getProcessedDataFrame(data)); + const annotationsProcessed = getProcessedDataFrames(annotations); + const STOPTIME = performance.now(); + + return { + ...data, + series: processedDataFrames, + annotations: annotationsProcessed, + timings: { dataProcessingTime: STOPTIME - STARTTIME }, + }; +} diff --git a/public/app/features/alerting/unified/state/AlertingQueryRunner.ts b/public/app/features/alerting/unified/state/AlertingQueryRunner.ts index 7ba736c15f8..93362a7cdcd 100644 --- a/public/app/features/alerting/unified/state/AlertingQueryRunner.ts +++ b/public/app/features/alerting/unified/state/AlertingQueryRunner.ts @@ -11,13 +11,13 @@ import { rangeUtil, TimeRange, withLoadingIndicator, + preProcessPanelData, } from '@grafana/data'; import { FetchResponse, getDataSourceSrv, toDataQueryError } from '@grafana/runtime'; import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv'; import { isExpressionQuery } from 'app/features/expressions/guards'; import { cancelNetworkRequestsOnUnsubscribe } from 'app/features/query/state/processing/canceler'; import { setStructureRevision } from 'app/features/query/state/processing/revision'; -import { preProcessPanelData } from 'app/features/query/state/runRequest'; import { AlertQuery } from 'app/types/unified-alerting-dto'; import { getTimeRangeForExpression } from '../utils/timeRange'; diff --git a/public/app/features/explore/utils/decorators.ts b/public/app/features/explore/utils/decorators.ts index d8b6a04fa14..d62bd0b3aed 100644 --- a/public/app/features/explore/utils/decorators.ts +++ b/public/app/features/explore/utils/decorators.ts @@ -9,6 +9,7 @@ import { getDisplayProcessor, PanelData, standardTransformers, + preProcessPanelData, } from '@grafana/data'; import { config } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; @@ -19,7 +20,6 @@ import { ExplorePanelData } from '../../../types'; import { CorrelationData } from '../../correlations/useCorrelations'; import { attachCorrelationsToDataFrames } from '../../correlations/utils'; import { sortLogsResult } from '../../logs/utils'; -import { preProcessPanelData } from '../../query/state/runRequest'; /** * When processing response first we try to determine what kind of dataframes we got as one query can return multiple diff --git a/public/app/features/query/state/PanelQueryRunner.ts b/public/app/features/query/state/PanelQueryRunner.ts index a71e2e9bd0c..0d6eb6c559e 100644 --- a/public/app/features/query/state/PanelQueryRunner.ts +++ b/public/app/features/query/state/PanelQueryRunner.ts @@ -25,6 +25,7 @@ import { TimeZone, toDataFrame, transformDataFrame, + preProcessPanelData, } from '@grafana/data'; import { getTemplateSrv, toDataQueryError } from '@grafana/runtime'; import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend'; @@ -38,7 +39,7 @@ import { PanelModel } from '../../dashboard/state'; import { getDashboardQueryRunner } from './DashboardQueryRunner/DashboardQueryRunner'; import { mergePanelAndDashData } from './mergePanelAndDashData'; -import { preProcessPanelData, runRequest } from './runRequest'; +import { runRequest } from './runRequest'; export interface QueryRunnerOptions< TQuery extends DataQuery = DataQuery, diff --git a/public/app/features/query/state/QueryRunner.ts b/public/app/features/query/state/QueryRunner.ts index 0720d9dafa6..7ed29e31db3 100644 --- a/public/app/features/query/state/QueryRunner.ts +++ b/public/app/features/query/state/QueryRunner.ts @@ -13,13 +13,14 @@ import { QueryRunner as QueryRunnerSrv, LoadingState, DataSourceRef, + preProcessPanelData, } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getNextRequestId } from './PanelQueryRunner'; import { setStructureRevision } from './processing/revision'; -import { preProcessPanelData, runRequest } from './runRequest'; +import { runRequest } from './runRequest'; export class QueryRunner implements QueryRunnerSrv { private subject: ReplaySubject; diff --git a/public/app/features/query/state/runRequest.ts b/public/app/features/query/state/runRequest.ts index 4c4006274ad..d7480fdaef9 100644 --- a/public/app/features/query/state/runRequest.ts +++ b/public/app/features/query/state/runRequest.ts @@ -7,7 +7,6 @@ import { catchError, map, mapTo, share, takeUntil, tap } from 'rxjs/operators'; // Types import { CoreApp, - DataFrame, DataQueryError, DataQueryRequest, DataQueryResponse, @@ -15,11 +14,9 @@ import { DataSourceApi, DataTopic, dateMath, - guessFieldTypes, LoadingState, PanelData, TimeRange, - toDataFrame, } from '@grafana/data'; import { toDataQueryError } from '@grafana/runtime'; import { isExpressionReference } from '@grafana/runtime/src/utils/DataSourceWithBackend'; @@ -204,59 +201,3 @@ export function callQueryMethod( const returnVal = queryFunction ? queryFunction(request) : datasource.query(request); return from(returnVal); } - -function getProcessedDataFrame(data: DataQueryResponseData): DataFrame { - const dataFrame = guessFieldTypes(toDataFrame(data)); - - if (dataFrame.fields && dataFrame.fields.length) { - // clear out the cached info - for (const field of dataFrame.fields) { - field.state = null; - } - } - - return dataFrame; -} - -/** - * All panels will be passed tables that have our best guess at column type set - * - * This is also used by PanelChrome for snapshot support - */ -export function getProcessedDataFrames(results?: DataQueryResponseData[]): DataFrame[] { - if (!results || !isArray(results)) { - return []; - } - - return results.map((data) => getProcessedDataFrame(data)); -} - -export function preProcessPanelData(data: PanelData, lastResult?: PanelData): PanelData { - const { series, annotations } = data; - - // for loading states with no data, use last result - if (data.state === LoadingState.Loading && series.length === 0) { - if (!lastResult) { - lastResult = data; - } - - return { - ...lastResult, - state: LoadingState.Loading, - request: data.request, - }; - } - - // Make sure the data frames are properly formatted - const STARTTIME = performance.now(); - const processedDataFrames = series.map((data) => getProcessedDataFrame(data)); - const annotationsProcessed = getProcessedDataFrames(annotations); - const STOPTIME = performance.now(); - - return { - ...data, - series: processedDataFrames, - annotations: annotationsProcessed, - timings: { dataProcessingTime: STOPTIME - STARTTIME }, - }; -}