2025-04-01 10:38:02 +09:00

108 lines
3.6 KiB
TypeScript

import { DataFrame, ExplorePanelsState } from '@grafana/data';
import { DataQuery, DataSourceRef, Panel } from '@grafana/schema';
import { DataTransformerConfig } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
import { ExplorePanelData } from 'app/types';
interface ExploreToDashboardPanelOptions {
queries: DataQuery[];
queryResponse: ExplorePanelData;
datasource?: DataSourceRef;
dashboardUid?: string;
panelState?: ExplorePanelsState;
}
/**
* Returns transformations for the logs table visualisation in explore.
* If the logs table supports a labels column, we need to extract the fields.
* Then we can set the columns to show in the table via the organize/includeByName transformation
* @param panelType
* @param options
*/
function getLogsTableTransformations(
panelType: string,
options: ExploreToDashboardPanelOptions
): DataTransformerConfig[] {
let transformations: DataTransformerConfig[] = [];
if (panelType === 'table' && options.panelState?.logs?.columns) {
// If we have a labels column, we need to extract the fields from it
if (options.panelState.logs?.labelFieldName) {
transformations.push({
id: 'extractFields',
options: {
source: options.panelState.logs.labelFieldName,
},
});
}
// Show the columns that the user selected in explore
transformations.push({
id: 'organize',
options: {
indexByName: Object.values(options.panelState.logs.columns).reduce(
(acc: Record<string, number>, value: string, idx) => ({
...acc,
[value]: idx,
}),
{}
),
includeByName: Object.values(options.panelState.logs.columns).reduce(
(acc: Record<string, boolean>, value: string) => ({
...acc,
[value]: true,
}),
{}
),
},
});
}
return transformations;
}
export function buildDashboardPanelFromExploreState(options: ExploreToDashboardPanelOptions): Panel {
const panelType = getPanelType(options.queries, options.queryResponse, options?.panelState);
return {
//@ts-ignore
targets: options.queries,
type: panelType,
title: 'New Panel',
gridPos: { x: 0, y: 0, w: 12, h: 8 },
datasource: options.datasource,
transformations: getLogsTableTransformations(panelType, options),
};
}
const isVisible = (query: DataQuery) => !query.hide;
const hasRefId = (refId: DataFrame['refId']) => (frame: DataFrame) => frame.refId === refId;
function getPanelType(queries: DataQuery[], queryResponse: ExplorePanelData, panelState?: ExplorePanelsState) {
for (const { refId } of queries.filter(isVisible)) {
const hasQueryRefId = hasRefId(refId);
if (queryResponse.flameGraphFrames.some(hasQueryRefId)) {
return 'flamegraph';
}
if (queryResponse.graphFrames.some(hasQueryRefId)) {
return 'timeseries';
}
if (queryResponse.logsFrames.some(hasQueryRefId)) {
if (panelState?.logs?.visualisationType) {
return panelState.logs.visualisationType;
}
return 'logs';
}
if (queryResponse.nodeGraphFrames.some(hasQueryRefId)) {
return 'nodeGraph';
}
if (queryResponse.traceFrames.some(hasQueryRefId)) {
return 'traces';
}
if (queryResponse.customFrames.some(hasQueryRefId)) {
// we will always have a custom frame and meta, it should never default to 'table' (but all paths must return a string)
return queryResponse.customFrames.find(hasQueryRefId)?.meta?.preferredVisualisationPluginId ?? 'table';
}
}
// falling back to table
return 'table';
}