| 
									
										
										
										
											2024-04-16 09:13:42 +08:00
										 |  |  | import { defaults, isEqual } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2024-02-05 19:32:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  |   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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-28 14:39:26 +08:00
										 |  |  | import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  | import { DashboardGridItem } from '../scene/DashboardGridItem'; | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  | import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene'; | 
					
						
							| 
									
										
										
										
											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-09-05 23:08:25 +08:00
										 |  |  | import { getLibraryPanelBehavior, getPanelIdForVizPanel, getQueryRunnerFor, isLibraryPanel } 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-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) { | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |       if (child instanceof DashboardGridItem) { | 
					
						
							| 
									
										
										
										
											2024-04-05 17:32:28 +08:00
										 |  |  |         // handle panel repeater scenario
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |         if (child.state.variableName) { | 
					
						
							|  |  |  |           panels = panels.concat(panelRepeaterToPanels(child, state, isSnapshot)); | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |           panels.push(gridItemToPanel(child, state, isSnapshot)); | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-23 17:46:35 +08:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |         gridRowToSaveModel(child, panels, state, 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[] = []; | 
					
						
							| 
									
										
										
										
											2023-10-11 19:24:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-28 14:39:26 +08:00
										 |  |  |   if (data instanceof DashboardDataLayerSet) { | 
					
						
							|  |  |  |     annotations = dataLayersToAnnotations(data.state.annotationLayers); | 
					
						
							| 
									
										
										
										
											2023-09-20 17:50:35 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-04-05 23:31:40 +08:00
										 |  |  |   const refreshPicker = controlsState?.refreshPicker; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 19:32:59 +08:00
										 |  |  |   const timePickerWithoutDefaults = removeDefaults<TimePickerConfig>( | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-04-05 23:31:40 +08:00
										 |  |  |       refresh_intervals: refreshPicker?.state.intervals, | 
					
						
							| 
									
										
										
										
											2024-02-20 15:43:02 +08:00
										 |  |  |       hidden: controlsState?.hideTimeControls, | 
					
						
							| 
									
										
										
										
											2024-02-05 19:32:59 +08:00
										 |  |  |       nowDelay: timeRange.UNSAFE_nowDelay, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     defaultTimePickerConfig | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-22 00:57:22 +08:00
										 |  |  |   const graphTooltip = | 
					
						
							|  |  |  |     state.$behaviors?.find((b): b is behaviors.CursorSync => b instanceof behaviors.CursorSync)?.state.sync ?? | 
					
						
							|  |  |  |     defaultDashboard.graphTooltip; | 
					
						
							|  |  |  |   const liveNow = | 
					
						
							|  |  |  |     state.$behaviors?.find((b): b is behaviors.LiveNowTimer => b instanceof behaviors.LiveNowTimer)?.isEnabled || | 
					
						
							|  |  |  |     undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2024-07-03 22:00:45 +08:00
										 |  |  |     preload: state.preload, | 
					
						
							| 
									
										
										
										
											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-03-22 00:57:22 +08:00
										 |  |  |     liveNow, | 
					
						
							| 
									
										
										
										
											2024-01-30 03:03:57 +08:00
										 |  |  |     schemaVersion: DASHBOARD_SCHEMA_VERSION, | 
					
						
							| 
									
										
										
										
											2024-04-05 23:31:40 +08:00
										 |  |  |     refresh: refreshPicker?.state.refresh, | 
					
						
							| 
									
										
										
										
											2023-08-29 20:17:55 +08:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return sortedDeepCloneWithoutNulls(dashboard); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  | export function gridItemToPanel( | 
					
						
							|  |  |  |   gridItem: DashboardGridItem, | 
					
						
							|  |  |  |   sceneState?: DashboardSceneState, | 
					
						
							|  |  |  |   isSnapshot = false | 
					
						
							|  |  |  | ): Panel { | 
					
						
							| 
									
										
										
										
											2023-09-05 19:51:46 +08:00
										 |  |  |   let vizPanel: VizPanel | undefined; | 
					
						
							|  |  |  |   let x = 0, | 
					
						
							|  |  |  |     y = 0, | 
					
						
							|  |  |  |     w = 0, | 
					
						
							|  |  |  |     h = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |   let gridItem_ = gridItem; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // If we're saving while the panel editor is open, we need to persist those changes in the panel model
 | 
					
						
							|  |  |  |   if ( | 
					
						
							|  |  |  |     sceneState && | 
					
						
							|  |  |  |     sceneState.editPanel?.state.vizManager && | 
					
						
							|  |  |  |     sceneState.editPanel.state.vizManager.state.sourcePanel.resolve() === gridItem.state.body | 
					
						
							|  |  |  |   ) { | 
					
						
							|  |  |  |     const gridItemClone = gridItem.clone(); | 
					
						
							| 
									
										
										
										
											2024-09-05 23:08:25 +08:00
										 |  |  |     if (gridItemClone.state.body instanceof VizPanel && !isLibraryPanel(gridItemClone.state.body)) { | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |       sceneState.editPanel.state.vizManager.commitChangesTo(gridItemClone.state.body); | 
					
						
							|  |  |  |       gridItem_ = gridItemClone; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!(gridItem_.state.body instanceof VizPanel)) { | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |     throw new Error('DashboardGridItem body expected to be VizPanel'); | 
					
						
							| 
									
										
										
										
											2023-09-05 19:51:46 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |   vizPanel = gridItem_.state.body; | 
					
						
							|  |  |  |   x = gridItem_.state.x ?? 0; | 
					
						
							|  |  |  |   y = gridItem_.state.y ?? 0; | 
					
						
							|  |  |  |   w = gridItem_.state.width ?? 0; | 
					
						
							| 
									
										
										
										
											2024-07-30 17:34:30 +08:00
										 |  |  |   h = (gridItem_.state.variableName ? gridItem_.state.itemHeight : gridItem_.state.height) ?? 0; | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 19:51:46 +08:00
										 |  |  |   if (!vizPanel) { | 
					
						
							|  |  |  |     throw new Error('Unsupported grid item type'); | 
					
						
							| 
									
										
										
										
											2023-08-29 20:17:55 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |   const panel: Panel = vizPanelToPanel(vizPanel, { x, y, h, w }, isSnapshot, gridItem_); | 
					
						
							| 
									
										
										
										
											2024-02-19 22:25:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return panel; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function vizPanelToPanel( | 
					
						
							|  |  |  |   vizPanel: VizPanel, | 
					
						
							|  |  |  |   gridPos?: GridPos, | 
					
						
							|  |  |  |   isSnapshot = false, | 
					
						
							|  |  |  |   gridItem?: SceneGridItemLike | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2024-09-05 23:08:25 +08:00
										 |  |  |   let panel: Panel; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (isLibraryPanel(vizPanel)) { | 
					
						
							|  |  |  |     const libPanel = getLibraryPanelBehavior(vizPanel); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     panel = { | 
					
						
							|  |  |  |       id: getPanelIdForVizPanel(vizPanel), | 
					
						
							|  |  |  |       title: libPanel!.state.title, | 
					
						
							|  |  |  |       gridPos: gridPos, | 
					
						
							|  |  |  |       libraryPanel: { | 
					
						
							|  |  |  |         name: libPanel!.state.name, | 
					
						
							|  |  |  |         uid: libPanel!.state.uid, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     } as Panel; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return panel; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     panel = { | 
					
						
							|  |  |  |       id: getPanelIdForVizPanel(vizPanel), | 
					
						
							|  |  |  |       type: vizPanel.state.pluginId, | 
					
						
							|  |  |  |       title: vizPanel.state.title, | 
					
						
							|  |  |  |       description: vizPanel.state.description ?? undefined, | 
					
						
							|  |  |  |       gridPos, | 
					
						
							|  |  |  |       fieldConfig: (vizPanel.state.fieldConfig as FieldConfigSource) ?? { defaults: {}, overrides: [] }, | 
					
						
							|  |  |  |       transformations: [], | 
					
						
							|  |  |  |       transparent: vizPanel.state.displayMode === 'transparent', | 
					
						
							|  |  |  |       pluginVersion: vizPanel.state.pluginVersion, | 
					
						
							|  |  |  |       ...vizPanelDataToPanel(vizPanel, isSnapshot), | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-08-29 20:17:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-16 09:13:42 +08:00
										 |  |  |   if (vizPanel.state.options) { | 
					
						
							|  |  |  |     const { angularOptions, ...rest } = vizPanel.state.options as any; | 
					
						
							|  |  |  |     panel.options = rest; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (angularOptions) { | 
					
						
							|  |  |  |       // Allow angularOptions to overwrite non system level root properties
 | 
					
						
							|  |  |  |       defaults(panel, angularOptions); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |   if (gridItem instanceof DashboardGridItem) { | 
					
						
							|  |  |  |     if (gridItem.state.variableName) { | 
					
						
							|  |  |  |       panel.repeat = gridItem.state.variableName; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (gridItem.state.maxPerRow) { | 
					
						
							|  |  |  |       panel.maxPerRow = gridItem.state.maxPerRow; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (gridItem.state.repeatDirection) { | 
					
						
							|  |  |  |       panel.repeatDirection = gridItem.getRepeatDirection(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-23 17:46:35 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-31 19:30:27 +08:00
										 |  |  |   const panelLinks = dashboardSceneGraph.getPanelLinks(vizPanel); | 
					
						
							| 
									
										
										
										
											2024-03-01 00:18:26 +08:00
										 |  |  |   panel.links = (panelLinks?.state.rawLinks as DashboardLink[]) ?? []; | 
					
						
							| 
									
										
										
										
											2024-01-31 19:30:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-04-16 09:13:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2024-04-29 15:41:43 +08:00
										 |  |  |     'datasource' | 'targets' | 'maxDataPoints' | 'transformations' | 'cacheTimeout' | 'queryCachingTTL' | 'interval' | 
					
						
							| 
									
										
										
										
											2024-02-15 01:19:39 +08:00
										 |  |  |   > = {}; | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-04-29 15:41:43 +08:00
										 |  |  |     if (queryRunner.state.minInterval) { | 
					
						
							|  |  |  |       panel.interval = queryRunner.state.minInterval; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  | export function panelRepeaterToPanels( | 
					
						
							|  |  |  |   repeater: DashboardGridItem, | 
					
						
							|  |  |  |   sceneState?: DashboardSceneState, | 
					
						
							|  |  |  |   isSnapshot = false | 
					
						
							|  |  |  | ): Panel[] { | 
					
						
							| 
									
										
										
										
											2023-10-23 17:46:35 +08:00
										 |  |  |   if (!isSnapshot) { | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |     return [gridItemToPanel(repeater, sceneState)]; | 
					
						
							| 
									
										
										
										
											2023-10-23 17:46:35 +08:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2024-09-05 23:08:25 +08:00
										 |  |  |     // return early if the repeated panel is a library panel
 | 
					
						
							|  |  |  |     if (repeater.state.body instanceof VizPanel && isLibraryPanel(repeater.state.body)) { | 
					
						
							| 
									
										
										
										
											2024-03-01 21:25:15 +08:00
										 |  |  |       const { x = 0, y = 0, width: w = 0, height: h = 0 } = repeater.state; | 
					
						
							| 
									
										
										
										
											2024-09-05 23:08:25 +08:00
										 |  |  |       return [vizPanelToPanel(repeater.state.body, { x, y, w, h }, isSnapshot)]; | 
					
						
							| 
									
										
										
										
											2024-03-01 21:25:15 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 17:46:35 +08:00
										 |  |  |     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 []; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  | export function gridRowToSaveModel( | 
					
						
							|  |  |  |   gridRow: SceneGridRow, | 
					
						
							|  |  |  |   panelsArray: Array<Panel | RowPanel>, | 
					
						
							|  |  |  |   sceneState?: DashboardSceneState, | 
					
						
							|  |  |  |   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) => { | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |       if (c instanceof DashboardGridItem) { | 
					
						
							|  |  |  |         if (c.state.variableName) { | 
					
						
							|  |  |  |           // Perform snapshot only for uncollapsed rows
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |           panelsInsideRow = panelsInsideRow.concat(panelRepeaterToPanels(c, sceneState, !collapsed)); | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |         } else { | 
					
						
							|  |  |  |           // Perform snapshot only for uncollapsed panels
 | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |           panelsInsideRow.push(gridItemToPanel(c, sceneState, !collapsed)); | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-23 17:46:35 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |     panelsInsideRow = gridRow.state.children.map((c) => { | 
					
						
							|  |  |  |       if (!(c instanceof DashboardGridItem)) { | 
					
						
							|  |  |  |         throw new Error('Row child expected to be DashboardGridItem'); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2024-05-03 17:50:10 +08:00
										 |  |  |       return gridItemToPanel(c, sceneState); | 
					
						
							| 
									
										
										
										
											2024-03-21 21:38:00 +08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2023-10-23 17:46:35 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | } |