From 1a6312296e0e6fb93df5cb5d0f201dba8bcea0e7 Mon Sep 17 00:00:00 2001 From: Ivan Ortega Alba Date: Thu, 2 Jan 2025 17:33:16 +0100 Subject: [PATCH] Dashboard: Compute tracking info for schema V2 (#98271) --- .../dashboard-scene/scene/DashboardScene.tsx | 2 +- .../DashboardSceneSerializer.test.ts | 52 +++++++++++++++---- .../serialization/DashboardSceneSerializer.ts | 36 ++++++++++--- .../app/features/dashboard/utils/tracking.ts | 51 ++++++++++++++---- 4 files changed, 113 insertions(+), 28 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index f74ea567741..39fed18d289 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -653,7 +653,7 @@ export class DashboardScene extends SceneObjectBase { } public getTrackingInformation() { - return this._serializer.getTrackingInformation(); + return this._serializer.getTrackingInformation(this); } public async onDashboardDelete() { diff --git a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts index 7683b9655ae..cefd2ba5473 100644 --- a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts +++ b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts @@ -13,6 +13,7 @@ import { defaultPanelSpec, defaultTimeSettingsSpec, } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen'; +import { DASHBOARD_SCHEMA_VERSION } from 'app/features/dashboard/state/DashboardMigrator'; import { buildPanelEditScene } from '../panel-edit/PanelEditor'; import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene'; @@ -274,8 +275,7 @@ describe('DashboardSceneSerializer', () => { }); it('provides dashboard tracking information with from initial save model', () => { - const serializer = new V1DashboardSerializer(); - serializer.initialSaveModel = { + const dashboard = setup({ schemaVersion: 30, version: 10, uid: 'my-uid', @@ -309,12 +309,12 @@ describe('DashboardSceneSerializer', () => { }, ], }, - }; + }); - expect(serializer.getTrackingInformation()).toEqual({ + expect(dashboard.getTrackingInformation()).toEqual({ uid: 'my-uid', title: 'hello', - schemaVersion: 30, + schemaVersion: DASHBOARD_SCHEMA_VERSION, panels_count: 3, panel_type_text_count: 2, panel_type_timeseries_count: 1, @@ -322,7 +322,6 @@ describe('DashboardSceneSerializer', () => { variable_type_textbox_count: 1, settings_nowdelay: undefined, settings_livenow: true, - version_before_migration: 10, }); }); }); @@ -565,9 +564,41 @@ describe('DashboardSceneSerializer', () => { }); }); - it('should throw on getTrackingInformation', () => { - const serializer = new V2DashboardSerializer(); - expect(() => serializer.getTrackingInformation()).toThrow('Method not implemented.'); + describe('tracking information', () => { + it('provides dashboard tracking information with no initial save model', () => { + const dashboard = setupV2(); + const serializer = new V2DashboardSerializer(); + expect(serializer.getTrackingInformation(dashboard)).toBe(undefined); + }); + + it('provides dashboard tracking information with from initial save model', () => { + const dashboard = setupV2({ + timeSettings: { + nowDelay: '10s', + from: '', + to: '', + autoRefresh: '', + autoRefreshIntervals: [], + quickRanges: [], + hideTimepicker: false, + weekStart: '', + fiscalYearStartMonth: 0, + timezone: '', + }, + liveNow: true, + }); + + expect(dashboard.getTrackingInformation()).toEqual({ + uid: 'dashboard-test', + title: 'hello', + panels_count: 1, + panel_type__count: 1, + variable_type_custom_count: 1, + settings_nowdelay: undefined, + settings_livenow: true, + schemaVersion: DASHBOARD_SCHEMA_VERSION, + }); + }); }); it('should throw on getSaveAsModel', () => { @@ -598,7 +629,7 @@ describe('DashboardSceneSerializer', () => { }); }); -function setup() { +function setup(override: Partial = {}) { const dashboard = transformSaveModelToScene({ dashboard: { title: 'hello', @@ -624,6 +655,7 @@ function setup() { }, ], }, + ...override, }, meta: {}, }); diff --git a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts index 333ed4ee1dc..1db278a049f 100644 --- a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts +++ b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts @@ -2,7 +2,11 @@ import { config } from '@grafana/runtime'; import { Dashboard } from '@grafana/schema'; import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen'; import { SaveDashboardAsOptions } from 'app/features/dashboard/components/SaveDashboard/types'; -import { getV1SchemaPanelCounts, getV1SchemaVariables } from 'app/features/dashboard/utils/tracking'; +import { + getPanelPluginCounts, + getV1SchemaVariables, + getV2SchemaVariables, +} from 'app/features/dashboard/utils/tracking'; import { SaveDashboardResponseDTO } from 'app/types'; import { getRawDashboardChanges, getRawDashboardV2Changes } from '../saving/getDashboardChanges'; @@ -28,7 +32,7 @@ export interface DashboardSceneSerializerLike { } ) => DashboardChangeInfo; onSaveComplete(saveModel: T, result: SaveDashboardResponseDTO): void; - getTrackingInformation: () => DashboardTrackingInfo | undefined; + getTrackingInformation: (s: DashboardScene) => DashboardTrackingInfo | undefined; getSnapshotUrl: () => string | undefined; } @@ -36,7 +40,6 @@ interface DashboardTrackingInfo { uid?: string; title?: string; schemaVersion: number; - version_before_migration?: number; panels_count: number; settings_nowdelay?: number; settings_livenow?: boolean; @@ -94,7 +97,8 @@ export class V1DashboardSerializer implements DashboardSceneSerializerLike p.type) || []; + const panels = getPanelPluginCounts(panelTypes); const variables = getV1SchemaVariables(this.initialSaveModel?.templating?.list || []); if (this.initialSaveModel) { @@ -102,7 +106,6 @@ export class V1DashboardSerializer implements DashboardSceneSerializerLike e.kind === 'Panel') + .map((p) => p.spec.vizConfig.kind) || []; + const panels = getPanelPluginCounts(panelPluginIds); + const variables = getV2SchemaVariables(this.initialSaveModel?.variables || []); + + if (this.initialSaveModel) { + return { + schemaVersion: this.initialSaveModel.schemaVersion, + uid: s.state.uid, + title: this.initialSaveModel.title, + panels_count: panelPluginIds.length || 0, + settings_nowdelay: undefined, + settings_livenow: !!this.initialSaveModel.liveNow, + ...panels, + ...variables, + }; + } + return undefined; } diff --git a/public/app/features/dashboard/utils/tracking.ts b/public/app/features/dashboard/utils/tracking.ts index 847d0600c03..eee9b2145de 100644 --- a/public/app/features/dashboard/utils/tracking.ts +++ b/public/app/features/dashboard/utils/tracking.ts @@ -1,15 +1,15 @@ -import { Panel, VariableModel } from '@grafana/schema/dist/esm/index'; +import { VariableModel } from '@grafana/schema/dist/esm/index'; +import { VariableKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen'; import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions'; import { DashboardModel } from '../state/DashboardModel'; -import { PanelModel } from '../state/PanelModel'; export function trackDashboardLoaded(dashboard: DashboardModel, duration?: number, versionBeforeMigration?: number) { // Count the different types of variables const variables = getV1SchemaVariables(dashboard.templating.list); // Count the different types of panels - const panels = getV1SchemaPanelCounts(dashboard.panels); + const panels = getPanelPluginCounts(dashboard.panels.map((p) => p.type)); DashboardInteractions.dashboardInitialized({ uid: dashboard.uid, @@ -38,13 +38,11 @@ export function trackDashboardSceneLoaded(dashboard: DashboardScene, duration?: }); } -export function getV1SchemaPanelCounts(panels: Panel[] | PanelModel[]) { - return panels - .map((p) => p.type) - .reduce((r: Record, p) => { - r[panelName(p)] = 1 + r[panelName(p)] || 1; - return r; - }, {}); +export function getPanelPluginCounts(panels: string[] | string[]) { + return panels.reduce((r: Record, p) => { + r[panelName(p)] = 1 + r[panelName(p)] || 1; + return r; + }, {}); } export function getV1SchemaVariables(variableList: VariableModel[]) { @@ -56,5 +54,38 @@ export function getV1SchemaVariables(variableList: VariableModel[]) { }, {}); } +function mapNewToOldTypes(type: VariableKind['kind']): VariableModel['type'] | undefined { + switch (type) { + case 'AdhocVariable': + return 'adhoc'; + case 'CustomVariable': + return 'custom'; + case 'QueryVariable': + return 'query'; + case 'IntervalVariable': + return 'interval'; + case 'ConstantVariable': + return 'constant'; + case 'DatasourceVariable': + return 'datasource'; + case 'TextVariable': + return 'textbox'; + case 'GroupByVariable': + return 'groupby'; + default: + return undefined; + } +} + +export function getV2SchemaVariables(variableList: VariableKind[]) { + return variableList + .map((v) => mapNewToOldTypes(v.kind)) + .filter((v) => v !== undefined) + .reduce((r: Record, k) => { + r[variableName(k)] = 1 + r[variableName(k)] || 1; + return r; + }, {}); +} + export const variableName = (type: string) => `variable_type_${type}_count`; const panelName = (type: string) => `panel_type_${type}_count`;