From a09338167836390530aa3f996247119052ea960f Mon Sep 17 00:00:00 2001 From: Alexa Vargas <239999+axelavargas@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:39:46 +0200 Subject: [PATCH] Dashboard: SchemaV2 - Fix stateless variable query with default datasource (#106850) * Dashboard: SchemaV2 - Fix stateless variable query with default datasource * Add warning for debugging purposes * fix test add expected warning --- .../layoutSerializers/utils.test.ts | 47 +++++++++++++++---- .../serialization/layoutSerializers/utils.ts | 17 ++++++- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.test.ts b/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.test.ts index 050947de00d..b5f9950b5f6 100644 --- a/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.test.ts +++ b/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.test.ts @@ -9,12 +9,12 @@ jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime').config, bootData: { settings: { - defaultDatasource: 'default-ds-grafana', + defaultDatasource: 'default-ds-prometheus', datasources: { - 'default-ds-grafana': { - uid: 'default-ds-uid', - name: 'Default DS', - meta: { id: 'default-ds-grafana' }, + 'default-ds-prometheus': { + uid: 'default-prometheus-uid', + name: 'Default Prometheus', + meta: { id: 'prometheus' }, type: 'datasource', }, prometheus: { @@ -61,7 +61,7 @@ describe('getRuntimePanelDataSource', () => { }); }); - it('should infer datasource based on query kind when datasource is not specified', () => { + it('should prioritize default datasource when it matches the query kind', () => { const query: PanelQueryKind = { kind: 'PanelQuery', spec: { @@ -78,12 +78,35 @@ describe('getRuntimePanelDataSource', () => { const result = getRuntimePanelDataSource(query); expect(result).toEqual({ - uid: 'prometheus-uid', + uid: 'default-prometheus-uid', type: 'prometheus', }); }); + it('should fall back to first available datasource when default datasource type does not match query kind', () => { + const query: PanelQueryKind = { + kind: 'PanelQuery', + spec: { + refId: 'A', + hidden: false, + datasource: undefined, + query: { + kind: 'loki', + spec: {}, + }, + }, + }; + + const result = getRuntimePanelDataSource(query); + + expect(result).toEqual({ + uid: 'loki-uid', + type: 'loki', + }); + }); + it('should use default datasource when no datasource is specified and query kind does not match any available datasource', () => { + jest.spyOn(console, 'warn').mockImplementation(); const query: PanelQueryKind = { kind: 'PanelQuery', spec: { @@ -100,9 +123,13 @@ describe('getRuntimePanelDataSource', () => { const result = getRuntimePanelDataSource(query); expect(result).toEqual({ - uid: 'default-ds-uid', - type: 'default-ds-grafana', + uid: 'default-prometheus-uid', + type: 'prometheus', }); + + expect(console.warn).toHaveBeenCalledWith( + 'Could not find datasource for query kind unknown-type, defaulting to prometheus' + ); }); it('should handle the case when datasource uid is empty string', () => { @@ -125,7 +152,7 @@ describe('getRuntimePanelDataSource', () => { const result = getRuntimePanelDataSource(query); expect(result).toEqual({ - uid: 'prometheus-uid', + uid: 'default-prometheus-uid', type: 'prometheus', }); }); diff --git a/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.ts b/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.ts index c3b1133c1ec..c8018a98be0 100644 --- a/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.ts +++ b/public/app/features/dashboard-scene/serialization/layoutSerializers/utils.ts @@ -224,14 +224,29 @@ function getDataSourceForQuery( const defaultDatasource = config.bootData.settings.defaultDatasource; const dsList = config.bootData.settings.datasources; - // Look up by query type/kind + // First check if the default datasource matches the query type + if (dsList && dsList[defaultDatasource] && dsList[defaultDatasource].meta.id === queryKind) { + // In the datasource list from bootData "id" is the type and the uid could be uid or the name + // in cases like grafana, dashboard or mixed datasource + return { + uid: dsList[defaultDatasource].uid || dsList[defaultDatasource].name, + type: dsList[defaultDatasource].meta.id, + }; + } + + // Look up by query type/kind from all available datasources const bestGuess = dsList && Object.values(dsList).find((ds) => ds.meta.id === queryKind); if (bestGuess) { return { uid: bestGuess.uid, type: bestGuess.meta.id }; } else if (dsList && dsList[defaultDatasource]) { + // Fallback to default datasource even if type doesn't match // In the datasource list from bootData "id" is the type and the uid could be uid or the name // in cases like grafana, dashboard or mixed datasource + + console.warn( + `Could not find datasource for query kind ${queryKind}, defaulting to ${dsList[defaultDatasource].meta.id}` + ); return { uid: dsList[defaultDatasource].uid || dsList[defaultDatasource].name, type: dsList[defaultDatasource].meta.id,