From 26ce124208dcfad36e15fb0a7df60fd02208a8e2 Mon Sep 17 00:00:00 2001 From: Alexa V <239999+axelavargas@users.noreply.github.com> Date: Thu, 1 May 2025 15:47:04 +0200 Subject: [PATCH] Dashboard: SchemaV2 - Fix Import showing grafana datasources (#104461) * Fix: do not map when identifying default grafana ds * add also datasource type * Refactor code, add unit test * Fix types references and linting * Update public/app/features/manage-dashboards/state/actions.test.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../manage-dashboards/state/actions.test.ts | 65 +++++++++++++++++-- .../manage-dashboards/state/actions.ts | 15 +++-- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/public/app/features/manage-dashboards/state/actions.test.ts b/public/app/features/manage-dashboards/state/actions.test.ts index da5693ad8c4..c4aebee06de 100644 --- a/public/app/features/manage-dashboards/state/actions.test.ts +++ b/public/app/features/manage-dashboards/state/actions.test.ts @@ -3,11 +3,11 @@ import { thunkTester } from 'test/core/thunk/thunkTester'; import { DataSourceInstanceSettings, ThresholdsMode } from '@grafana/data'; import { defaultDashboard, FieldColorModeId } from '@grafana/schema'; import { - DashboardV2Spec, - defaultDashboardV2Spec, + Spec as DashboardV2Spec, + defaultSpec as defaultDashboardV2Spec, defaultPanelSpec, defaultQueryVariableSpec, -} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0'; +} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen'; import { browseDashboardsAPI } from 'app/features/browse-dashboards/api/browseDashboardsAPI'; import { getLibraryPanel } from 'app/features/library-panels/state/api'; @@ -16,7 +16,13 @@ import { LibraryElementDTO } from '../../library-panels/types'; import { DashboardJson } from '../types'; import { validateDashboardJson } from '../utils/validation'; -import { getLibraryPanelInputs, importDashboard, processDashboard, processV2Datasources } from './actions'; +import { + getLibraryPanelInputs, + importDashboard, + processDashboard, + processV2DatasourceInput, + processV2Datasources, +} from './actions'; import { DataSourceInput, ImportDashboardDTO, initialImportDashboardState, InputType } from './reducers'; jest.mock('app/features/library-panels/state/api'); @@ -29,7 +35,7 @@ jest.mock('@grafana/runtime', () => ({ getDataSourceSrv: () => ({ ...jest.requireActual('@grafana/runtime').getDataSourceSrv(), get: jest.fn().mockImplementation((dsType: { type: string }) => { - const dsList: { + const dsListTypeDSMock: { [key: string]: { uid: string; name: string; @@ -55,8 +61,15 @@ jest.mock('@grafana/runtime', () => ({ type: 'grafana', meta: { id: 'grafana' }, }, + // "datasource" type is what we call "--Dashboard--" datasource + datasource: { + uid: '--Dashboard--', + name: '--Dashboard--', + type: 'datasource', + meta: { id: 'dashboard' }, + }, }; - return dsList[dsType.type]; + return dsListTypeDSMock[dsType.type]; }), }), })); @@ -959,3 +972,43 @@ describe('processV2Datasources', () => { ); }); }); + +describe('processV2DatasourceInput', () => { + // should not map grafana datasource input or dashboard datasource input + it('Should not map grafana datasource input', async () => { + const queryVariable = { + kind: 'QueryVariable', + spec: { + ...defaultQueryVariableSpec(), + name: 'var2WithGrafanaDs', + query: { + kind: 'grafana', + spec: { + panelId: 2, + }, + }, + }, + }; + const result = await processV2DatasourceInput(queryVariable.spec, {}); + expect(result).toEqual({}); + }); + + it('Should not map dashboard datasource input', async () => { + // create a panel with dashboard datasource input + const panelQuery = { + kind: 'PanelQuery', + spec: { + refId: 'A', + hidden: false, + query: { + kind: 'datasource', + spec: { + panelId: 2, + }, + }, + }, + }; + const result = await processV2DatasourceInput(panelQuery.spec, {}); + expect(result).toEqual({}); + }); +}); diff --git a/public/app/features/manage-dashboards/state/actions.ts b/public/app/features/manage-dashboards/state/actions.ts index fd82c2faace..1b5b02e24e4 100644 --- a/public/app/features/manage-dashboards/state/actions.ts +++ b/public/app/features/manage-dashboards/state/actions.ts @@ -162,26 +162,26 @@ export function processV2Datasources(dashboard: DashboardV2Spec): ThunkResult = {}; + let inputs: Record = {}; for (const element of Object.values(elements)) { if (element.kind !== 'Panel') { throw new Error('Only panels are currenlty supported in v2 dashboards'); } if (element.spec.data.spec.queries.length > 0) { for (const query of element.spec.data.spec.queries) { - await processV2DatasourceInput(query.spec, inputs); + inputs = await processV2DatasourceInput(query.spec, inputs); } } } for (const variable of variables) { if (variable.kind === 'QueryVariable') { - await processV2DatasourceInput(variable.spec, inputs); + inputs = await processV2DatasourceInput(variable.spec, inputs); } } for (const annotation of annotations) { - await processV2DatasourceInput(annotation.spec, inputs); + inputs = await processV2DatasourceInput(annotation.spec, inputs); } dispatch(setInputs(Object.values(inputs))); @@ -337,6 +337,12 @@ export async function processV2DatasourceInput( const datasourceRef = obj?.datasource; if (!datasourceRef && obj?.query) { const dsType = obj.query.kind; + // if dsType is grafana, it means we are using a built-in annotation or default grafana datasource, in those + // cases we don't need to map it + // "datasource" type is what we call "--Dashboard--" datasource <.-.> + if (dsType === 'grafana' || dsType === 'datasource') { + return inputs; + } const datasource = await getDatasourceSrv().get({ type: dsType }); let dataSourceInput: DataSourceInput | undefined; if (datasource) { @@ -363,4 +369,5 @@ export async function processV2DatasourceInput( inputs[dsType] = dataSourceInput; } } + return inputs; }