From ea331dc0d35fa8d970e7aa81889e3146dc7b21bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ida=20=C5=A0tambuk?= Date: Wed, 10 Dec 2025 14:39:58 +0100 Subject: [PATCH] Dashboards: Add variables with datasource to tracking (#114110) --- .../DashboardSceneSerializer.test.ts | 21 +++++- .../testfiles/nested_dashboard.json | 15 +++++ .../dashboard-scene/utils/tracking.test.ts | 23 ++++++- .../features/dashboard/utils/tracking.test.ts | 8 ++- .../app/features/dashboard/utils/tracking.ts | 64 +++++++++++++++---- 5 files changed, 112 insertions(+), 19 deletions(-) diff --git a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts index c17cc0cfbdb..aa2d81dcc52 100644 --- a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts +++ b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts @@ -330,10 +330,16 @@ describe('DashboardSceneSerializer', () => { { type: 'query', name: 'server', + datasource: { + type: 'influxdb', + }, }, { type: 'query', name: 'host', + datasource: { + type: 'elasticsearch', + }, }, { type: 'textbox', @@ -355,6 +361,10 @@ describe('DashboardSceneSerializer', () => { variable_type_textbox_count: 1, settings_nowdelay: undefined, settings_livenow: true, + varsWithDataSource: [ + { type: 'query', datasource: 'influxdb' }, + { type: 'query', datasource: 'elasticsearch' }, + ], }); }); }); @@ -688,16 +698,23 @@ describe('DashboardSceneSerializer', () => { schemaVersion: DASHBOARD_SCHEMA_VERSION, settings_nowdelay: undefined, settings_livenow: true, + panel_type_timeseries_count: 6, + variable_type_adhoc_count: 1, + variable_type_datasource_count: 1, variable_type_custom_count: 1, variable_type_query_count: 1, - panel_type_timeseries_count: 6, + varsWithDataSource: [ + { type: 'query', datasource: 'cloudwatch' }, + { type: 'adhoc', datasource: 'opensearch' }, + { type: 'datasource', datasource: 'bigquery' }, + ], }); expect(dashboard.getDynamicDashboardsTrackingInformation()).toEqual({ panelCount: 6, rowCount: 6, tabCount: 4, - templateVariableCount: 2, + templateVariableCount: 4, maxNestingLevel: 3, dashStructure: '[{"kind":"row","children":[{"kind":"row","children":[{"kind":"tab","children":[{"kind":"panel"},{"kind":"panel"},{"kind":"panel"}]},{"kind":"tab","children":[]}]},{"kind":"row","children":[{"kind":"row","children":[{"kind":"panel"}]}]}]},{"kind":"row","children":[{"kind":"row","children":[{"kind":"tab","children":[{"kind":"panel"}]},{"kind":"tab","children":[{"kind":"panel"}]}]}]}]', diff --git a/public/app/features/dashboard-scene/serialization/testfiles/nested_dashboard.json b/public/app/features/dashboard-scene/serialization/testfiles/nested_dashboard.json index 133bcd890ee..4322333ceb1 100644 --- a/public/app/features/dashboard-scene/serialization/testfiles/nested_dashboard.json +++ b/public/app/features/dashboard-scene/serialization/testfiles/nested_dashboard.json @@ -1132,6 +1132,14 @@ "sort": "disabled" } }, + { + "kind": "AdhocVariable", + "datasource": { "name": "esmce00tbim8" }, + "group": "opensearch", + "spec": { + "allowCustomValue": true + } + }, { "kind": "CustomVariable", "spec": { @@ -1154,6 +1162,13 @@ "query": "test", "skipUrlSync": false } + }, + { + "kind": "DatasourceVariable", + "spec": { + "name": "datasourceVar", + "pluginId": "bigquery" + } } ] } diff --git a/public/app/features/dashboard-scene/utils/tracking.test.ts b/public/app/features/dashboard-scene/utils/tracking.test.ts index 45bee1f4583..5fb968a4148 100644 --- a/public/app/features/dashboard-scene/utils/tracking.test.ts +++ b/public/app/features/dashboard-scene/utils/tracking.test.ts @@ -16,6 +16,11 @@ jest.mock('@grafana/runtime', () => ({ dashboardNewLayouts: true, }, }, + getDataSourceSrv: () => ({ + getInstanceSettings: () => { + return { apiVersion: 'v1', meta: { multiValueFilterOperators: true } }; + }, + }), })); // mock useSaveDashboardMutation @@ -72,7 +77,7 @@ describe('dashboard tracking', () => { isScene: true, tabCount: 4, rowCount: 2, - templateVariableCount: 2, + templateVariableCount: 4, maxNestingLevel: 3, panel_type_timeseries_count: 6, panels_count: 6, @@ -89,6 +94,22 @@ describe('dashboard tracking', () => { uid: 'dashboard-test', variable_type_custom_count: 1, variable_type_query_count: 1, + variable_type_datasource_count: 1, + variable_type_adhoc_count: 1, + varsWithDataSource: [ + { + datasource: 'cloudwatch', + type: 'query', + }, + { + datasource: 'opensearch', + type: 'adhoc', + }, + { + datasource: 'bigquery', + type: 'datasource', + }, + ], hasEditPermissions: true, hasSavePermissions: true, }); diff --git a/public/app/features/dashboard/utils/tracking.test.ts b/public/app/features/dashboard/utils/tracking.test.ts index a8e9fb1ec8a..c02ae06d1fc 100644 --- a/public/app/features/dashboard/utils/tracking.test.ts +++ b/public/app/features/dashboard/utils/tracking.test.ts @@ -20,9 +20,9 @@ describe('trackDashboardLoaded', () => { ], templating: { list: [ - { type: 'query', name: 'Query 1' }, + { type: 'query', name: 'Query 1', datasource: { type: 'prometheus' } }, { type: 'interval', name: 'Interval 1' }, - { type: 'query', name: 'Query 2' }, + { type: 'query', name: 'Query 2', datasource: { type: 'cloudwatch' } }, ], }, timepicker: { @@ -52,6 +52,10 @@ describe('trackDashboardLoaded', () => { panel_type_geomap_count: 2, settings_nowdelay: '1m', settings_livenow: true, + varsWithDataSource: [ + { type: 'query', datasource: 'prometheus' }, + { type: 'query', datasource: 'cloudwatch' }, + ], }); }); }); diff --git a/public/app/features/dashboard/utils/tracking.ts b/public/app/features/dashboard/utils/tracking.ts index 3dc32eb05db..cffc7999fff 100644 --- a/public/app/features/dashboard/utils/tracking.ts +++ b/public/app/features/dashboard/utils/tracking.ts @@ -1,5 +1,10 @@ import { VariableModel } from '@grafana/schema/dist/esm/index'; -import { VariableKind } from '@grafana/schema/dist/esm/schema/dashboard/v2'; +import { + AdhocVariableKind, + DatasourceVariableKind, + QueryVariableKind, + VariableKind, +} from '@grafana/schema/dist/esm/schema/dashboard/v2'; import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions'; import { DashboardModel } from '../state/DashboardModel'; @@ -41,12 +46,23 @@ export function getPanelPluginCounts(panels: string[]) { } export function getV1SchemaVariables(variableList: VariableModel[]) { - return variableList - .map((v) => v.type) - .reduce((r: Record, k) => { - r[variableName(k)] = 1 + r[variableName(k)] || 1; - return r; - }, {}); + return { + // Count variable types + ...variableList.reduce>((variables, current) => { + variables[variableName(current.type)] = 1 + (variables[variableName(current.type)] || 0); + return variables; + }, {}), + // List of variables with data source types + varsWithDataSource: variableList.reduce>((variablesWithDs, current) => { + if (current.datasource?.type) { + variablesWithDs.push({ + type: current.type, + datasource: current.datasource.type, + }); + } + return variablesWithDs; + }, []), + }; } function mapNewToOldTypes(type: VariableKind['kind']): VariableModel['type'] | undefined { @@ -73,14 +89,34 @@ function mapNewToOldTypes(type: VariableKind['kind']): VariableModel['type'] | u } 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; - }, {}); + return { + // Count variable types + ...variableList.reduce>((variables, current) => { + const type = mapNewToOldTypes(current.kind); + if (type) { + variables[variableName(type)] = 1 + (variables[variableName(type)] || 0); + } + return variables; + }, {}), + // List of variables with data source types + varsWithDataSource: variableList.reduce>((variablesWithDs, current) => { + let datasource = ''; + const type = mapNewToOldTypes(current.kind); + datasource = getDatasourceFromVar(current); + if (datasource && type) { + variablesWithDs.push({ type, datasource }); + } + return variablesWithDs; + }, []), + }; } export const variableName = (type: string) => `variable_type_${type}_count`; const panelName = (type: string) => `panel_type_${type}_count`; + +const isAdhocVar: (v: VariableKind) => v is AdhocVariableKind = (v) => v.kind === 'AdhocVariable'; +const isDatasourceVar: (v: VariableKind) => v is DatasourceVariableKind = (v) => v.kind === 'DatasourceVariable'; +const isQueryVar: (v: VariableKind) => v is QueryVariableKind = (v) => v.kind === 'QueryVariable'; + +const getDatasourceFromVar = (v: VariableKind) => + isAdhocVar(v) ? v.group : isDatasourceVar(v) ? v.spec.pluginId : isQueryVar(v) ? v.spec?.query.group : '';