Dashboard: Compute tracking info for schema V2 (#98271)

This commit is contained in:
Ivan Ortega Alba 2025-01-02 17:33:16 +01:00 committed by GitHub
parent 0d60a026e9
commit 1a6312296e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 113 additions and 28 deletions

View File

@ -653,7 +653,7 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
}
public getTrackingInformation() {
return this._serializer.getTrackingInformation();
return this._serializer.getTrackingInformation(this);
}
public async onDashboardDelete() {

View File

@ -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<Dashboard> = {}) {
const dashboard = transformSaveModelToScene({
dashboard: {
title: 'hello',
@ -624,6 +655,7 @@ function setup() {
},
],
},
...override,
},
meta: {},
});

View File

@ -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<T> {
}
) => 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<Dashb
}
getTrackingInformation(): DashboardTrackingInfo | undefined {
const panels = getV1SchemaPanelCounts(this.initialSaveModel?.panels || []);
const panelTypes = this.initialSaveModel?.panels?.map((p) => 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<Dashb
uid: this.initialSaveModel.uid,
title: this.initialSaveModel.title,
schemaVersion: this.initialSaveModel.schemaVersion,
version_before_migration: this.initialSaveModel.version,
panels_count: this.initialSaveModel.panels?.length || 0,
settings_nowdelay: undefined,
settings_livenow: !!this.initialSaveModel.liveNow,
@ -159,8 +162,27 @@ export class V2DashboardSerializer implements DashboardSceneSerializerLike<Dashb
throw new Error('v2 schema: Method not implemented.');
}
getTrackingInformation() {
throw new Error('v2 schema: Method not implemented.');
getTrackingInformation(s: DashboardScene): DashboardTrackingInfo | undefined {
const panelPluginIds =
Object.values(this.initialSaveModel?.elements ?? [])
.filter((e) => 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;
}

View File

@ -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<string, number>, p) => {
r[panelName(p)] = 1 + r[panelName(p)] || 1;
return r;
}, {});
export function getPanelPluginCounts(panels: string[] | string[]) {
return panels.reduce((r: Record<string, number>, 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<string, number>, 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`;