import { DataQueryResponse, DataQueryResponseData } from '@grafana/ui'; import { TableData, isTableData, LogsModel, toDataFrame, guessFieldTypes, TimeSeries } from '@grafana/data'; import { ExploreItemState, ExploreMode } from 'app/types/explore'; import { getProcessedDataFrames } from 'app/features/dashboard/state/PanelQueryState'; import TableModel, { mergeTablesIntoModel } from 'app/core/table_model'; import { sortLogsResult } from 'app/core/utils/explore'; import { dataFrameToLogsModel } from 'app/core/logs_model'; import { default as TimeSeries2 } from 'app/core/time_series2'; import { DataProcessor } from 'app/plugins/panel/graph/data_processor'; export class ResultProcessor { private rawData: DataQueryResponseData[] = []; private metrics: TimeSeries[] = []; private tables: TableData[] = []; constructor( private state: ExploreItemState, private replacePreviousResults: boolean, result?: DataQueryResponse | DataQueryResponseData[] ) { if (result && result.hasOwnProperty('data')) { this.rawData = (result as DataQueryResponse).data; } else { this.rawData = (result as DataQueryResponseData[]) || []; } if (this.state.mode !== ExploreMode.Metrics) { return; } for (let index = 0; index < this.rawData.length; index++) { const res: any = this.rawData[index]; const isTable = isTableData(res); if (isTable) { this.tables.push(res); } else { this.metrics.push(res); } } } getRawData = (): any[] => { return this.rawData; }; getGraphResult = (): TimeSeries[] => { if (this.state.mode !== ExploreMode.Metrics) { return []; } const newResults = this.makeTimeSeriesList(this.metrics); return this.mergeGraphResults(newResults, this.state.graphResult); }; getTableResult = (): TableModel => { if (this.state.mode !== ExploreMode.Metrics) { return new TableModel(); } const prevTableResults: any[] | TableModel = this.state.tableResult || []; const tablesToMerge = this.replacePreviousResults ? this.tables : [].concat(prevTableResults, this.tables); return mergeTablesIntoModel(new TableModel(), ...tablesToMerge); }; getLogsResult = (): LogsModel => { if (this.state.mode !== ExploreMode.Logs) { return null; } const graphInterval = this.state.queryIntervals.intervalMs; const dataFrame = this.rawData.map(result => guessFieldTypes(toDataFrame(result))); const newResults = this.rawData ? dataFrameToLogsModel(dataFrame, graphInterval) : null; const sortedNewResults = sortLogsResult(newResults, this.state.refreshInterval); if (this.replacePreviousResults) { return sortedNewResults; } const prevLogsResult: LogsModel = this.state.logsResult || { hasUniqueLabels: false, rows: [] }; const sortedLogResult = sortLogsResult(prevLogsResult, this.state.refreshInterval); const rowsInState = sortedLogResult.rows; const seriesInState = sortedLogResult.series || []; const processedRows = []; for (const row of rowsInState) { processedRows.push({ ...row, fresh: false }); } for (const row of sortedNewResults.rows) { processedRows.push({ ...row, fresh: true }); } const processedSeries = this.mergeGraphResults(sortedNewResults.series, seriesInState); const slice = -1000; const rows = processedRows.slice(slice); const series = processedSeries.slice(slice); return { ...sortedNewResults, rows, series }; }; private makeTimeSeriesList = (rawData: any[]) => { const dataList = getProcessedDataFrames(rawData); const dataProcessor = new DataProcessor({ xaxis: {}, aliasColors: [] }); // Hack before we use GraphSeriesXY instead const timeSeries = dataProcessor.getSeriesList({ dataList }); return (timeSeries as any) as TimeSeries[]; // Hack before we use GraphSeriesXY instead }; private isSameTimeSeries = (a: TimeSeries | TimeSeries2, b: TimeSeries | TimeSeries2) => { if (a.hasOwnProperty('id') && b.hasOwnProperty('id')) { const aValue = (a as TimeSeries2).id; const bValue = (b as TimeSeries2).id; if (aValue !== undefined && bValue !== undefined && aValue === bValue) { return true; } } if (a.hasOwnProperty('alias') && b.hasOwnProperty('alias')) { const aValue = (a as TimeSeries2).alias; const bValue = (b as TimeSeries2).alias; if (aValue !== undefined && bValue !== undefined && aValue === bValue) { return true; } } return false; }; private mergeGraphResults = ( newResults: TimeSeries[] | TimeSeries2[], prevResults: TimeSeries[] | TimeSeries2[] ): TimeSeries[] => { if (!prevResults || prevResults.length === 0 || this.replacePreviousResults) { return (newResults as any) as TimeSeries[]; // Hack before we use GraphSeriesXY instead } const results: TimeSeries[] = prevResults.slice() as TimeSeries[]; // update existing results for (let index = 0; index < results.length; index++) { const prevResult = results[index]; for (const newResult of newResults) { const isSame = this.isSameTimeSeries(prevResult, newResult); if (isSame) { prevResult.datapoints = prevResult.datapoints.concat(newResult.datapoints); break; } } } // add new results for (const newResult of newResults) { let isNew = true; for (const prevResult of results) { const isSame = this.isSameTimeSeries(prevResult, newResult); if (isSame) { isNew = false; break; } } if (isNew) { const timeSeries2Result = new TimeSeries2({ ...newResult }); const result = (timeSeries2Result as any) as TimeSeries; // Hack before we use GraphSeriesXY instead results.push(result); } } return results; }; }