2024-02-05 19:32:59 +08:00
|
|
|
import { isEqual } from 'lodash';
|
|
|
|
|
|
2023-10-23 17:46:35 +08:00
|
|
|
import { isEmptyObject, ScopedVars, TimeRange } from '@grafana/data';
|
2023-09-20 17:50:35 +08:00
|
|
|
import {
|
2023-12-01 23:04:56 +08:00
|
|
|
behaviors,
|
2023-09-20 17:50:35 +08:00
|
|
|
SceneDataLayers,
|
|
|
|
|
SceneGridItem,
|
|
|
|
|
SceneGridItemLike,
|
|
|
|
|
SceneGridLayout,
|
|
|
|
|
SceneGridRow,
|
|
|
|
|
VizPanel,
|
2023-10-04 17:31:16 +08:00
|
|
|
SceneDataTransformer,
|
2023-10-06 20:32:29 +08:00
|
|
|
SceneVariableSet,
|
2023-10-23 17:46:35 +08:00
|
|
|
LocalValueVariable,
|
2023-09-20 17:50:35 +08:00
|
|
|
} from '@grafana/scenes';
|
2023-10-04 17:31:16 +08:00
|
|
|
import {
|
|
|
|
|
AnnotationQuery,
|
|
|
|
|
Dashboard,
|
2024-01-31 19:30:27 +08:00
|
|
|
DashboardLink,
|
2023-10-04 17:31:16 +08:00
|
|
|
DataTransformerConfig,
|
|
|
|
|
defaultDashboard,
|
2023-12-01 23:04:56 +08:00
|
|
|
defaultTimePickerConfig,
|
2023-10-04 17:31:16 +08:00
|
|
|
FieldConfigSource,
|
2024-02-19 22:25:45 +08:00
|
|
|
GridPos,
|
2023-10-04 17:31:16 +08:00
|
|
|
Panel,
|
|
|
|
|
RowPanel,
|
2024-02-05 19:32:59 +08:00
|
|
|
TimePickerConfig,
|
2023-10-06 20:32:29 +08:00
|
|
|
VariableModel,
|
2023-10-11 19:24:18 +08:00
|
|
|
VariableRefresh,
|
2023-10-04 17:31:16 +08:00
|
|
|
} from '@grafana/schema';
|
2023-08-29 20:17:55 +08:00
|
|
|
import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object';
|
2023-10-11 19:24:18 +08:00
|
|
|
import { getPanelDataFrames } from 'app/features/dashboard/components/HelpWizard/utils';
|
2024-01-30 03:03:57 +08:00
|
|
|
import { DASHBOARD_SCHEMA_VERSION } from 'app/features/dashboard/state/DashboardMigrator';
|
2023-10-11 19:24:18 +08:00
|
|
|
import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types';
|
2023-08-29 20:17:55 +08:00
|
|
|
|
|
|
|
|
import { DashboardScene } from '../scene/DashboardScene';
|
2023-10-06 20:32:29 +08:00
|
|
|
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
2023-09-05 19:51:46 +08:00
|
|
|
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
2023-09-01 16:21:41 +08:00
|
|
|
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
2023-09-11 18:02:04 +08:00
|
|
|
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
2024-01-31 19:30:27 +08:00
|
|
|
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
2024-02-05 19:27:44 +08:00
|
|
|
import { getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
|
2023-08-29 20:17:55 +08:00
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
import { GRAFANA_DATASOURCE_REF } from './const';
|
|
|
|
|
import { dataLayersToAnnotations } from './dataLayersToAnnotations';
|
2023-10-06 20:32:29 +08:00
|
|
|
import { sceneVariablesSetToVariables } from './sceneVariablesSetToVariables';
|
|
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = false): Dashboard {
|
2023-08-29 20:17:55 +08:00
|
|
|
const state = scene.state;
|
|
|
|
|
const timeRange = state.$timeRange!.state;
|
2023-09-20 17:50:35 +08:00
|
|
|
const data = state.$data;
|
2023-10-06 20:32:29 +08:00
|
|
|
const variablesSet = state.$variables;
|
2023-08-29 20:17:55 +08:00
|
|
|
const body = state.body;
|
2024-02-05 19:32:59 +08:00
|
|
|
|
2023-10-23 17:46:35 +08:00
|
|
|
let panels: Panel[] = [];
|
2023-12-01 23:04:56 +08:00
|
|
|
let graphTooltip = defaultDashboard.graphTooltip;
|
2023-10-06 20:32:29 +08:00
|
|
|
let variables: VariableModel[] = [];
|
|
|
|
|
|
2023-08-29 20:17:55 +08:00
|
|
|
if (body instanceof SceneGridLayout) {
|
|
|
|
|
for (const child of body.state.children) {
|
|
|
|
|
if (child instanceof SceneGridItem) {
|
2023-10-11 19:24:18 +08:00
|
|
|
panels.push(gridItemToPanel(child, isSnapshot));
|
2023-08-29 20:17:55 +08:00
|
|
|
}
|
2023-09-11 18:02:04 +08:00
|
|
|
|
2023-10-23 17:46:35 +08:00
|
|
|
if (child instanceof PanelRepeaterGridItem) {
|
|
|
|
|
panels = panels.concat(panelRepeaterToPanels(child, isSnapshot));
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-11 18:02:04 +08:00
|
|
|
if (child instanceof SceneGridRow) {
|
2023-10-23 17:46:35 +08:00
|
|
|
// Skip repeat clones or when generating a snapshot
|
|
|
|
|
if (child.state.key!.indexOf('-clone-') > 0 && !isSnapshot) {
|
2023-09-11 18:02:04 +08:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-10-11 19:24:18 +08:00
|
|
|
gridRowToSaveModel(child, panels, isSnapshot);
|
2023-09-11 18:02:04 +08:00
|
|
|
}
|
2023-08-29 20:17:55 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 17:50:35 +08:00
|
|
|
let annotations: AnnotationQuery[] = [];
|
|
|
|
|
if (data instanceof SceneDataLayers) {
|
|
|
|
|
const layers = data.state.layers;
|
2023-10-11 19:24:18 +08:00
|
|
|
|
2023-09-20 17:50:35 +08:00
|
|
|
annotations = dataLayersToAnnotations(layers);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-06 20:32:29 +08:00
|
|
|
if (variablesSet instanceof SceneVariableSet) {
|
|
|
|
|
variables = sceneVariablesSetToVariables(variablesSet);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-20 15:43:02 +08:00
|
|
|
const controlsState = state.controls?.state;
|
2023-10-10 16:17:21 +08:00
|
|
|
|
2024-02-10 04:44:44 +08:00
|
|
|
if (state.$behaviors) {
|
|
|
|
|
for (const behavior of state.$behaviors!) {
|
|
|
|
|
if (behavior instanceof behaviors.CursorSync) {
|
|
|
|
|
graphTooltip = behavior.state.sync;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-01 23:04:56 +08:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 19:32:59 +08:00
|
|
|
const timePickerWithoutDefaults = removeDefaults<TimePickerConfig>(
|
|
|
|
|
{
|
2024-02-20 15:43:02 +08:00
|
|
|
refresh_intervals: controlsState?.refreshPicker.state.intervals,
|
|
|
|
|
hidden: controlsState?.hideTimeControls,
|
2024-02-05 19:32:59 +08:00
|
|
|
nowDelay: timeRange.UNSAFE_nowDelay,
|
|
|
|
|
},
|
|
|
|
|
defaultTimePickerConfig
|
|
|
|
|
);
|
|
|
|
|
|
2023-08-29 20:17:55 +08:00
|
|
|
const dashboard: Dashboard = {
|
|
|
|
|
...defaultDashboard,
|
|
|
|
|
title: state.title,
|
2023-12-01 23:04:56 +08:00
|
|
|
description: state.description || undefined,
|
2023-08-29 20:17:55 +08:00
|
|
|
uid: state.uid,
|
2023-10-13 22:24:04 +08:00
|
|
|
id: state.id,
|
2023-12-01 23:04:56 +08:00
|
|
|
editable: state.editable,
|
2023-08-29 20:17:55 +08:00
|
|
|
time: {
|
|
|
|
|
from: timeRange.from,
|
|
|
|
|
to: timeRange.to,
|
|
|
|
|
},
|
2024-02-05 19:32:59 +08:00
|
|
|
timepicker: timePickerWithoutDefaults,
|
2023-08-29 20:17:55 +08:00
|
|
|
panels,
|
2023-09-20 17:50:35 +08:00
|
|
|
annotations: {
|
|
|
|
|
list: annotations,
|
|
|
|
|
},
|
2023-10-06 20:32:29 +08:00
|
|
|
templating: {
|
|
|
|
|
list: variables,
|
|
|
|
|
},
|
2024-01-17 23:14:29 +08:00
|
|
|
version: state.version,
|
2023-10-03 17:03:53 +08:00
|
|
|
timezone: timeRange.timeZone,
|
|
|
|
|
fiscalYearStartMonth: timeRange.fiscalYearStartMonth,
|
|
|
|
|
weekStart: timeRange.weekStart,
|
2023-12-01 23:04:56 +08:00
|
|
|
tags: state.tags,
|
2024-01-16 19:24:54 +08:00
|
|
|
links: state.links,
|
2023-12-01 23:04:56 +08:00
|
|
|
graphTooltip,
|
2024-01-30 03:03:57 +08:00
|
|
|
schemaVersion: DASHBOARD_SCHEMA_VERSION,
|
2023-08-29 20:17:55 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return sortedDeepCloneWithoutNulls(dashboard);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
export function gridItemToPanel(gridItem: SceneGridItemLike, isSnapshot = false): Panel {
|
2023-09-05 19:51:46 +08:00
|
|
|
let vizPanel: VizPanel | undefined;
|
|
|
|
|
let x = 0,
|
|
|
|
|
y = 0,
|
|
|
|
|
w = 0,
|
|
|
|
|
h = 0;
|
|
|
|
|
|
|
|
|
|
if (gridItem instanceof SceneGridItem) {
|
2023-10-06 20:32:29 +08:00
|
|
|
// Handle library panels, early exit
|
|
|
|
|
if (gridItem.state.body instanceof LibraryVizPanel) {
|
|
|
|
|
x = gridItem.state.x ?? 0;
|
|
|
|
|
y = gridItem.state.y ?? 0;
|
|
|
|
|
w = gridItem.state.width ?? 0;
|
|
|
|
|
h = gridItem.state.height ?? 0;
|
|
|
|
|
|
2024-02-28 00:56:29 +08:00
|
|
|
if (!gridItem.state.body.state.panel) {
|
|
|
|
|
throw new Error('Library panel has no panel');
|
|
|
|
|
}
|
2023-10-06 20:32:29 +08:00
|
|
|
return {
|
2024-02-28 00:56:29 +08:00
|
|
|
id: getPanelIdForVizPanel(gridItem.state.body.state.panel),
|
2023-10-06 20:32:29 +08:00
|
|
|
title: gridItem.state.body.state.title,
|
|
|
|
|
gridPos: { x, y, w, h },
|
|
|
|
|
libraryPanel: {
|
|
|
|
|
name: gridItem.state.body.state.name,
|
|
|
|
|
uid: gridItem.state.body.state.uid,
|
|
|
|
|
},
|
|
|
|
|
} as Panel;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 19:51:46 +08:00
|
|
|
if (!(gridItem.state.body instanceof VizPanel)) {
|
|
|
|
|
throw new Error('SceneGridItem body expected to be VizPanel');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vizPanel = gridItem.state.body;
|
|
|
|
|
x = gridItem.state.x ?? 0;
|
|
|
|
|
y = gridItem.state.y ?? 0;
|
|
|
|
|
w = gridItem.state.width ?? 0;
|
|
|
|
|
h = gridItem.state.height ?? 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gridItem instanceof PanelRepeaterGridItem) {
|
|
|
|
|
vizPanel = gridItem.state.source;
|
|
|
|
|
x = gridItem.state.x ?? 0;
|
|
|
|
|
y = gridItem.state.y ?? 0;
|
|
|
|
|
w = gridItem.state.width ?? 0;
|
|
|
|
|
h = gridItem.state.height ?? 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!vizPanel) {
|
|
|
|
|
throw new Error('Unsupported grid item type');
|
2023-08-29 20:17:55 +08:00
|
|
|
}
|
|
|
|
|
|
2024-02-19 22:25:45 +08:00
|
|
|
const panel: Panel = vizPanelToPanel(vizPanel, { x, y, h, w }, isSnapshot, gridItem);
|
|
|
|
|
|
|
|
|
|
return panel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function vizPanelToPanel(
|
|
|
|
|
vizPanel: VizPanel,
|
|
|
|
|
gridPos?: GridPos,
|
|
|
|
|
isSnapshot = false,
|
|
|
|
|
gridItem?: SceneGridItemLike
|
|
|
|
|
) {
|
2023-08-29 20:17:55 +08:00
|
|
|
const panel: Panel = {
|
2023-08-30 16:09:47 +08:00
|
|
|
id: getPanelIdForVizPanel(vizPanel),
|
2023-08-29 20:17:55 +08:00
|
|
|
type: vizPanel.state.pluginId,
|
|
|
|
|
title: vizPanel.state.title,
|
2024-02-19 22:25:45 +08:00
|
|
|
gridPos,
|
2023-08-29 20:17:55 +08:00
|
|
|
options: vizPanel.state.options,
|
|
|
|
|
fieldConfig: (vizPanel.state.fieldConfig as FieldConfigSource) ?? { defaults: {}, overrides: [] },
|
|
|
|
|
transformations: [],
|
2023-10-11 19:24:18 +08:00
|
|
|
transparent: vizPanel.state.displayMode === 'transparent',
|
2024-02-05 19:27:44 +08:00
|
|
|
pluginVersion: vizPanel.state.pluginVersion,
|
2023-10-23 17:46:35 +08:00
|
|
|
...vizPanelDataToPanel(vizPanel, isSnapshot),
|
2023-08-29 20:17:55 +08:00
|
|
|
};
|
|
|
|
|
|
2023-09-01 16:21:41 +08:00
|
|
|
const panelTime = vizPanel.state.$timeRange;
|
|
|
|
|
|
|
|
|
|
if (panelTime instanceof PanelTimeRange) {
|
|
|
|
|
panel.timeFrom = panelTime.state.timeFrom;
|
|
|
|
|
panel.timeShift = panelTime.state.timeShift;
|
|
|
|
|
panel.hideTimeOverride = panelTime.state.hideTimeOverride;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 17:46:35 +08:00
|
|
|
if (gridItem instanceof PanelRepeaterGridItem) {
|
|
|
|
|
panel.repeat = gridItem.state.variableName;
|
|
|
|
|
panel.maxPerRow = gridItem.state.maxPerRow;
|
|
|
|
|
panel.repeatDirection = gridItem.getRepeatDirection();
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-31 19:30:27 +08:00
|
|
|
const panelLinks = dashboardSceneGraph.getPanelLinks(vizPanel);
|
|
|
|
|
panel.links = (panelLinks.state.rawLinks as DashboardLink[]) ?? [];
|
|
|
|
|
|
2024-02-05 19:27:44 +08:00
|
|
|
if (panel.links.length === 0) {
|
|
|
|
|
delete panel.links;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (panel.transformations?.length === 0) {
|
|
|
|
|
delete panel.transformations;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!panel.transparent) {
|
|
|
|
|
delete panel.transparent;
|
|
|
|
|
}
|
2023-10-23 17:46:35 +08:00
|
|
|
return panel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function vizPanelDataToPanel(
|
|
|
|
|
vizPanel: VizPanel,
|
|
|
|
|
isSnapshot = false
|
|
|
|
|
): Pick<Panel, 'datasource' | 'targets' | 'maxDataPoints' | 'transformations'> {
|
2023-10-04 17:31:16 +08:00
|
|
|
const dataProvider = vizPanel.state.$data;
|
|
|
|
|
|
2024-02-15 01:19:39 +08:00
|
|
|
const panel: Pick<
|
|
|
|
|
Panel,
|
|
|
|
|
'datasource' | 'targets' | 'maxDataPoints' | 'transformations' | 'cacheTimeout' | 'queryCachingTTL'
|
|
|
|
|
> = {};
|
2024-02-05 19:27:44 +08:00
|
|
|
const queryRunner = getQueryRunnerFor(vizPanel);
|
2023-10-04 17:31:16 +08:00
|
|
|
|
2024-02-05 19:27:44 +08:00
|
|
|
if (queryRunner) {
|
|
|
|
|
panel.targets = queryRunner.state.queries;
|
|
|
|
|
panel.maxDataPoints = queryRunner.state.maxDataPoints;
|
|
|
|
|
panel.datasource = queryRunner.state.datasource;
|
2024-02-15 01:19:39 +08:00
|
|
|
|
|
|
|
|
if (queryRunner.state.cacheTimeout) {
|
|
|
|
|
panel.cacheTimeout = queryRunner.state.cacheTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (queryRunner.state.queryCachingTTL) {
|
|
|
|
|
panel.queryCachingTTL = queryRunner.state.queryCachingTTL;
|
|
|
|
|
}
|
2023-10-04 17:31:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dataProvider instanceof SceneDataTransformer) {
|
|
|
|
|
panel.transformations = dataProvider.state.transformations as DataTransformerConfig[];
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
if (dataProvider && isSnapshot) {
|
|
|
|
|
panel.datasource = GRAFANA_DATASOURCE_REF;
|
|
|
|
|
|
|
|
|
|
let data = getPanelDataFrames(dataProvider.state.data);
|
|
|
|
|
if (dataProvider instanceof SceneDataTransformer) {
|
|
|
|
|
// For transformations the non-transformed data is snapshoted
|
|
|
|
|
data = getPanelDataFrames(dataProvider.state.$data!.state.data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
panel.targets = [
|
|
|
|
|
{
|
|
|
|
|
refId: 'A',
|
|
|
|
|
datasource: panel.datasource,
|
|
|
|
|
queryType: GrafanaQueryType.Snapshot,
|
|
|
|
|
snapshot: data,
|
|
|
|
|
},
|
|
|
|
|
];
|
2023-09-01 16:21:41 +08:00
|
|
|
}
|
|
|
|
|
|
2023-08-29 20:17:55 +08:00
|
|
|
return panel;
|
|
|
|
|
}
|
2023-09-11 18:02:04 +08:00
|
|
|
|
2023-10-23 17:46:35 +08:00
|
|
|
export function panelRepeaterToPanels(repeater: PanelRepeaterGridItem, isSnapshot = false): Panel[] {
|
|
|
|
|
if (!isSnapshot) {
|
|
|
|
|
return [gridItemToPanel(repeater)];
|
|
|
|
|
} else {
|
|
|
|
|
if (repeater.state.repeatedPanels) {
|
|
|
|
|
const itemHeight = repeater.state.itemHeight ?? 10;
|
|
|
|
|
const rowCount = Math.ceil(repeater.state.repeatedPanels!.length / repeater.getMaxPerRow());
|
|
|
|
|
const columnCount = Math.ceil(repeater.state.repeatedPanels!.length / rowCount);
|
|
|
|
|
const w = 24 / columnCount;
|
|
|
|
|
const h = itemHeight;
|
|
|
|
|
const panels = repeater.state.repeatedPanels!.map((panel, index) => {
|
|
|
|
|
let x = 0,
|
|
|
|
|
y = 0;
|
|
|
|
|
if (repeater.state.repeatDirection === 'v') {
|
|
|
|
|
x = repeater.state.x!;
|
|
|
|
|
y = index * h;
|
|
|
|
|
} else {
|
|
|
|
|
x = (index % columnCount) * w;
|
|
|
|
|
y = repeater.state.y! + Math.floor(index / columnCount) * h;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const gridPos = { x, y, w, h };
|
|
|
|
|
|
|
|
|
|
const localVariable = panel.state.$variables!.getByName(repeater.state.variableName!) as LocalValueVariable;
|
|
|
|
|
|
|
|
|
|
const result: Panel = {
|
|
|
|
|
id: getPanelIdForVizPanel(panel),
|
|
|
|
|
type: panel.state.pluginId,
|
|
|
|
|
title: panel.state.title,
|
|
|
|
|
gridPos,
|
|
|
|
|
options: panel.state.options,
|
|
|
|
|
fieldConfig: (panel.state.fieldConfig as FieldConfigSource) ?? { defaults: {}, overrides: [] },
|
|
|
|
|
transformations: [],
|
|
|
|
|
transparent: panel.state.displayMode === 'transparent',
|
|
|
|
|
// @ts-expect-error scopedVars are runtime only properties, not part of the persisted Dashboardmodel
|
|
|
|
|
scopedVars: {
|
|
|
|
|
[repeater.state.variableName!]: {
|
|
|
|
|
text: localVariable?.state.text,
|
|
|
|
|
value: localVariable?.state.value,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
...vizPanelDataToPanel(panel, isSnapshot),
|
|
|
|
|
};
|
|
|
|
|
return result;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return panels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
export function gridRowToSaveModel(gridRow: SceneGridRow, panelsArray: Array<Panel | RowPanel>, isSnapshot = false) {
|
2023-10-23 17:46:35 +08:00
|
|
|
const collapsed = Boolean(gridRow.state.isCollapsed);
|
2023-09-11 18:02:04 +08:00
|
|
|
const rowPanel: RowPanel = {
|
|
|
|
|
type: 'row',
|
|
|
|
|
id: getPanelIdForVizPanel(gridRow),
|
|
|
|
|
title: gridRow.state.title,
|
|
|
|
|
gridPos: {
|
|
|
|
|
x: gridRow.state.x ?? 0,
|
|
|
|
|
y: gridRow.state.y ?? 0,
|
|
|
|
|
w: gridRow.state.width ?? 24,
|
|
|
|
|
h: gridRow.state.height ?? 1,
|
|
|
|
|
},
|
2023-10-23 17:46:35 +08:00
|
|
|
collapsed,
|
2023-09-11 18:02:04 +08:00
|
|
|
panels: [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (gridRow.state.$behaviors?.length) {
|
|
|
|
|
const behavior = gridRow.state.$behaviors[0];
|
|
|
|
|
if (behavior instanceof RowRepeaterBehavior) {
|
|
|
|
|
rowPanel.repeat = behavior.state.variableName;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 17:46:35 +08:00
|
|
|
if (isSnapshot) {
|
|
|
|
|
// Rows that are repeated has SceneVariableSet attached to them.
|
|
|
|
|
if (gridRow.state.$variables) {
|
|
|
|
|
const localVariable = gridRow.state.$variables;
|
|
|
|
|
const scopedVars: ScopedVars = (localVariable.state.variables as LocalValueVariable[]).reduce((acc, variable) => {
|
|
|
|
|
return {
|
|
|
|
|
...acc,
|
|
|
|
|
[variable.state.name]: {
|
|
|
|
|
text: variable.state.text,
|
|
|
|
|
value: variable.state.value,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}, {});
|
|
|
|
|
// @ts-expect-error
|
|
|
|
|
rowPanel.scopedVars = scopedVars;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-11 18:02:04 +08:00
|
|
|
panelsArray.push(rowPanel);
|
|
|
|
|
|
2023-10-23 17:46:35 +08:00
|
|
|
let panelsInsideRow: Panel[] = [];
|
|
|
|
|
|
|
|
|
|
if (isSnapshot) {
|
|
|
|
|
gridRow.state.children.forEach((c) => {
|
|
|
|
|
if (c instanceof PanelRepeaterGridItem) {
|
|
|
|
|
// Perform snapshot only for uncollapsed rows
|
|
|
|
|
panelsInsideRow = panelsInsideRow.concat(panelRepeaterToPanels(c, !collapsed));
|
|
|
|
|
} else {
|
|
|
|
|
// Perform snapshot only for uncollapsed panels
|
|
|
|
|
panelsInsideRow.push(gridItemToPanel(c, !collapsed));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
panelsInsideRow = gridRow.state.children.map((c) => gridItemToPanel(c));
|
|
|
|
|
}
|
2023-09-11 18:02:04 +08:00
|
|
|
|
|
|
|
|
if (gridRow.state.isCollapsed) {
|
|
|
|
|
rowPanel.panels = panelsInsideRow;
|
|
|
|
|
} else {
|
|
|
|
|
panelsArray.push(...panelsInsideRow);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-20 17:50:35 +08:00
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
export function trimDashboardForSnapshot(title: string, time: TimeRange, dash: Dashboard, panel?: VizPanel) {
|
|
|
|
|
let result = {
|
|
|
|
|
...dash,
|
|
|
|
|
title,
|
|
|
|
|
time: {
|
|
|
|
|
from: time.from.toISOString(),
|
|
|
|
|
to: time.to.toISOString(),
|
|
|
|
|
},
|
|
|
|
|
links: [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// When VizPanel is present, we are snapshoting a single panel. The rest of the panels is removed from the dashboard,
|
|
|
|
|
// and the panel is resized to 24x20 grid and placed at the top of the dashboard.
|
|
|
|
|
if (panel) {
|
|
|
|
|
const singlePanel = dash.panels?.find((p) => p.id === getPanelIdForVizPanel(panel));
|
|
|
|
|
if (singlePanel) {
|
|
|
|
|
singlePanel.gridPos = { w: 24, x: 0, y: 0, h: 20 };
|
|
|
|
|
result = {
|
|
|
|
|
...result,
|
|
|
|
|
panels: [singlePanel],
|
|
|
|
|
};
|
2023-09-20 17:50:35 +08:00
|
|
|
}
|
2023-10-11 19:24:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove links from all panels
|
|
|
|
|
result.panels?.forEach((panel) => {
|
|
|
|
|
if ('links' in panel) {
|
|
|
|
|
panel.links = [];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Remove annotation queries, attach snapshotData: [] for backwards compatibility
|
|
|
|
|
if (result.annotations) {
|
|
|
|
|
const annotations = result.annotations.list?.filter((annotation) => annotation.enable) || [];
|
|
|
|
|
const trimedAnnotations = annotations.map((annotation) => {
|
|
|
|
|
return {
|
|
|
|
|
name: annotation.name,
|
|
|
|
|
enable: annotation.enable,
|
|
|
|
|
iconColor: annotation.iconColor,
|
|
|
|
|
type: annotation.type,
|
|
|
|
|
builtIn: annotation.builtIn,
|
|
|
|
|
hide: annotation.hide,
|
|
|
|
|
// TODO: Remove when we migrate snapshots to snapshot queries.
|
|
|
|
|
// For now leaving this in here to avoid annotation queries in snapshots.
|
|
|
|
|
// Annotations per panel are part of the snapshot query, so we don't need to store them here.
|
|
|
|
|
snapshotData: [],
|
|
|
|
|
};
|
|
|
|
|
});
|
2023-09-20 17:50:35 +08:00
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
result.annotations.list = trimedAnnotations;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.templating) {
|
|
|
|
|
result.templating.list?.forEach((variable) => {
|
|
|
|
|
if ('query' in variable) {
|
|
|
|
|
variable.query = '';
|
|
|
|
|
}
|
|
|
|
|
if ('options' in variable) {
|
|
|
|
|
variable.options = variable.current && !isEmptyObject(variable.current) ? [variable.current] : [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ('refresh' in variable) {
|
|
|
|
|
variable.refresh = VariableRefresh.never;
|
|
|
|
|
}
|
2023-09-20 17:50:35 +08:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 19:24:18 +08:00
|
|
|
return result;
|
2023-09-20 17:50:35 +08:00
|
|
|
}
|
2024-02-05 19:32:59 +08:00
|
|
|
|
|
|
|
|
function removeDefaults<T>(object: T, defaults: T): T {
|
|
|
|
|
const newObj = { ...object };
|
|
|
|
|
for (const key in defaults) {
|
|
|
|
|
if (isEqual(newObj[key], defaults[key])) {
|
|
|
|
|
delete newObj[key];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newObj;
|
|
|
|
|
}
|