From ab7e18fedaab43060d8a67ea08eea6117b10d875 Mon Sep 17 00:00:00 2001 From: Alexa V <239999+axelavargas@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:24:21 +0200 Subject: [PATCH] Dashboard: Schema V2 - Fix built-in annotations not present (#104313) * Add grafana Built-in annonation to the serialization * Add unit tests --- .../DashboardSceneSerializer.test.ts | 18 +++++++++++- .../transformSaveModelSchemaV2ToScene.test.ts | 25 +++++++++++----- .../transformSaveModelSchemaV2ToScene.ts | 29 +++++++++++++++++++ 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts index 084121cc917..20f42a76150 100644 --- a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts +++ b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts @@ -15,6 +15,7 @@ import { GridLayoutKind, PanelSpec, } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen'; +import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui'; import { AnnoKeyDashboardSnapshotOriginalUrl } from 'app/features/apiserver/types'; import { SaveDashboardAsOptions } from 'app/features/dashboard/components/SaveDashboard/types'; import { DASHBOARD_SCHEMA_VERSION } from 'app/features/dashboard/state/DashboardMigrator'; @@ -715,7 +716,22 @@ describe('DashboardSceneSerializer', () => { title: baseOptions.title, description: baseOptions.description, editable: true, - annotations: [], + annotations: [ + { + kind: 'AnnotationQuery', + spec: { + builtIn: true, + name: 'Annotations & Alerts', + datasource: { + uid: '-- Grafana --', + type: 'grafana', + }, + enable: true, + hide: true, + iconColor: DEFAULT_ANNOTATION_COLOR, + }, + }, + ], cursorSync: 'Off', liveNow: false, preload: false, diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts index 4a6116c044c..f356bbad15a 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts @@ -206,26 +206,36 @@ describe('transformSaveModelSchemaV2ToScene', () => { // Annotations expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet); const dataLayers = scene.state.$data as DashboardDataLayerSet; + // we should get two annotations, Grafana built-in and the custom ones expect(dataLayers.state.annotationLayers).toHaveLength(dash.annotations.length); - expect(dataLayers.state.annotationLayers[0].state.name).toBe(dash.annotations[0].spec.name); - expect(dataLayers.state.annotationLayers[0].state.isEnabled).toBe(dash.annotations[0].spec.enable); - expect(dataLayers.state.annotationLayers[0].state.isHidden).toBe(dash.annotations[0].spec.hide); + expect(dataLayers.state.annotationLayers).toHaveLength(5); + + // Built-in + const builtInAnnotation = dataLayers.state.annotationLayers[0] as unknown as DashboardAnnotationsDataLayer; + expect(builtInAnnotation.state.name).toBe('Annotations & Alerts'); + expect(builtInAnnotation.state.isEnabled).toBe(true); + expect(builtInAnnotation.state.isHidden).toBe(true); + expect(builtInAnnotation.state?.query.builtIn).toBe(1); // Enabled expect(dataLayers.state.annotationLayers[1].state.name).toBe(dash.annotations[1].spec.name); expect(dataLayers.state.annotationLayers[1].state.isEnabled).toBe(dash.annotations[1].spec.enable); expect(dataLayers.state.annotationLayers[1].state.isHidden).toBe(dash.annotations[1].spec.hide); - // Disabled expect(dataLayers.state.annotationLayers[2].state.name).toBe(dash.annotations[2].spec.name); expect(dataLayers.state.annotationLayers[2].state.isEnabled).toBe(dash.annotations[2].spec.enable); expect(dataLayers.state.annotationLayers[2].state.isHidden).toBe(dash.annotations[2].spec.hide); - // Hidden + // Disabled expect(dataLayers.state.annotationLayers[3].state.name).toBe(dash.annotations[3].spec.name); expect(dataLayers.state.annotationLayers[3].state.isEnabled).toBe(dash.annotations[3].spec.enable); expect(dataLayers.state.annotationLayers[3].state.isHidden).toBe(dash.annotations[3].spec.hide); + // Hidden + expect(dataLayers.state.annotationLayers[4].state.name).toBe(dash.annotations[4].spec.name); + expect(dataLayers.state.annotationLayers[4].state.isEnabled).toBe(dash.annotations[4].spec.enable); + expect(dataLayers.state.annotationLayers[4].state.isHidden).toBe(dash.annotations[4].spec.hide); + // VizPanel const vizPanels = (scene.state.body as DashboardLayoutManager).getVizPanels(); expect(vizPanels).toHaveLength(3); @@ -757,9 +767,10 @@ describe('transformSaveModelSchemaV2ToScene', () => { // Get the annotation layers const dataLayerSet = scene.state.$data as DashboardDataLayerSet; expect(dataLayerSet).toBeDefined(); - expect(dataLayerSet.state.annotationLayers.length).toBe(1); + // it should have two annotation layers, built-in and custom + expect(dataLayerSet.state.annotationLayers.length).toBe(2); - const annotationLayer = dataLayerSet.state.annotationLayers[0] as DashboardAnnotationsDataLayer; + const annotationLayer = dataLayerSet.state.annotationLayers[1] as DashboardAnnotationsDataLayer; // Verify that the options have been merged into the query object expect(annotationLayer.state.query).toMatchObject({ diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts index c859ee6561e..d89c53dbd47 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts @@ -19,6 +19,7 @@ import { } from '@grafana/scenes'; import { AdhocVariableKind, + AnnotationQueryKind, ConstantVariableKind, CustomVariableKind, Spec as DashboardV2Spec, @@ -38,6 +39,7 @@ import { QueryVariableKind, TextVariableKind, } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen'; +import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui'; import { AnnoKeyCreatedBy, AnnoKeyFolder, @@ -87,6 +89,13 @@ export type TypedVariableModelV2 = export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo): DashboardScene { const { spec: dashboard, metadata } = dto; + // annotations might not come with the builtIn Grafana annotation, we need to add it + + const grafanaBuiltAnnotation = getGrafanaBuiltInAnnotationDataLayer(dashboard); + if (grafanaBuiltAnnotation) { + dashboard.annotations.unshift(grafanaBuiltAnnotation); + } + const annotationLayers = dashboard.annotations.map((annotation) => { let annoQuerySpec = annotation.spec; // some annotations will contain in the options properties that need to be @@ -500,3 +509,23 @@ export function getPanelElement(dashboard: DashboardV2Spec, elementName: string) export function getLibraryPanelElement(dashboard: DashboardV2Spec, elementName: string): LibraryPanelKind | undefined { return dashboard.elements[elementName].kind === 'LibraryPanel' ? dashboard.elements[elementName] : undefined; } +function getGrafanaBuiltInAnnotationDataLayer(dashboard: DashboardV2Spec) { + const found = dashboard.annotations.some((item) => item.spec.builtIn); + if (found) { + return; + } + + const grafanaBuiltAnnotation: AnnotationQueryKind = { + kind: 'AnnotationQuery', + spec: { + datasource: { uid: '-- Grafana --', type: 'grafana' }, + name: 'Annotations & Alerts', + iconColor: DEFAULT_ANNOTATION_COLOR, + enable: true, + hide: true, + builtIn: true, + }, + }; + + return grafanaBuiltAnnotation; +}