The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/explore/extensions/AddToDashboard/addToDashboard.ts

135 lines
4.3 KiB

import { DataFrame, ExplorePanelsState } from '@grafana/data';
import { DataQuery, DataSourceRef } from '@grafana/schema';
import { DataTransformerConfig } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
import { backendSrv } from 'app/core/services/backend_srv';
import {
getNewDashboardModelData,
setDashboardToFetchFromLocalStorage,
} from 'app/features/dashboard/state/initDashboard';
import { DashboardDTO, ExplorePanelData } from 'app/types';
export enum AddToDashboardError {
FETCH_DASHBOARD = 'fetch-dashboard',
SET_DASHBOARD_LS = 'set-dashboard-ls-error',
}
interface AddPanelToDashboardOptions {
queries: DataQuery[];
queryResponse: ExplorePanelData;
datasource?: DataSourceRef;
dashboardUid?: string;
panelState?: ExplorePanelsState;
}
function createDashboard(): DashboardDTO {
const dto = getNewDashboardModelData();
// getNewDashboardModelData adds by default the "add-panel" panel. We don't want that.
dto.dashboard.panels = [];
return dto;
}
/**
* 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: AddPanelToDashboardOptions): 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: {
includeByName: Object.values(options.panelState.logs.columns).reduce(
(acc: Record<string, boolean>, value: string) => ({
...acc,
[value]: true,
}),
{}
),
},
});
}
return transformations;
}
export async function setDashboardInLocalStorage(options: AddPanelToDashboardOptions) {
const panelType = getPanelType(options.queries, options.queryResponse, options?.panelState);
const panel = {
targets: options.queries,
type: panelType,
title: 'New Panel',
gridPos: { x: 0, y: 0, w: 12, h: 8 },
datasource: options.datasource,
transformations: getLogsTableTransformations(panelType, options),
};
let dto: DashboardDTO;
if (options.dashboardUid) {
try {
dto = await backendSrv.getDashboardByUid(options.dashboardUid);
} catch (e) {
throw AddToDashboardError.FETCH_DASHBOARD;
}
} else {
dto = createDashboard();
}
dto.dashboard.panels = [panel, ...(dto.dashboard.panels ?? [])];
try {
setDashboardToFetchFromLocalStorage(dto);
} catch {
throw AddToDashboardError.SET_DASHBOARD_LS;
}
}
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';
}