Dashboard Export: Fix datasource variable templating in dashboard export (#109764)
* wip investigation * improve * cleanup * betterer * Improve v2 import
This commit is contained in:
@@ -144,7 +144,7 @@ describe('dashboard exporter v1', () => {
|
||||
expect(exported.templating.list[0].datasource.uid).toBe('${DS_GFDB}');
|
||||
});
|
||||
|
||||
it('do not expose datasource name and id in a in a template variable of type datasource', async () => {
|
||||
it('templateize datasource uid in a datasource variable that is used in a panel', async () => {
|
||||
const dashboard: Dashboard = {
|
||||
title: 'My dashboard',
|
||||
revision: 1,
|
||||
@@ -159,6 +159,10 @@ describe('dashboard exporter v1', () => {
|
||||
type: 'timeseries',
|
||||
title: 'My panel title',
|
||||
gridPos: { x: 0, y: 0, w: 1, h: 1 },
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: '${ds_var}',
|
||||
},
|
||||
},
|
||||
],
|
||||
templating: {
|
||||
@@ -172,7 +176,7 @@ describe('dashboard exporter v1', () => {
|
||||
hide: 0,
|
||||
includeAll: false,
|
||||
multi: false,
|
||||
name: 'query1',
|
||||
name: 'ds_var',
|
||||
options: [],
|
||||
query: 'prometheus',
|
||||
refresh: 1,
|
||||
@@ -188,7 +192,11 @@ describe('dashboard exporter v1', () => {
|
||||
});
|
||||
const exported = (await makeExportableV1(dashboardModel)) as DashboardJson;
|
||||
const value = exported?.templating?.list ? exported?.templating?.list[0].current : '';
|
||||
expect(value).toEqual({});
|
||||
expect(value).toEqual({
|
||||
selected: true,
|
||||
text: '',
|
||||
value: '${DS_GFDB}',
|
||||
});
|
||||
});
|
||||
|
||||
it('replaces datasource ref in library panel', async () => {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { buildPanelKind } from 'app/features/dashboard/api/ResponseTransformers'
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
import { PanelModel, GridPos } from 'app/features/dashboard/state/PanelModel';
|
||||
import { getLibraryPanel } from 'app/features/library-panels/state/api';
|
||||
import { variableRegex } from 'app/features/variables/utils';
|
||||
import { variableRegexExec } from 'app/features/variables/utils';
|
||||
import { dispatch } from 'app/store/store';
|
||||
|
||||
import { isPanelModelLibraryPanel } from '../../../library-panels/guard';
|
||||
@@ -110,6 +110,8 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
||||
variableLookup[variable.name] = variable;
|
||||
}
|
||||
|
||||
const datasourceVariableRefNameMap: { [key: string]: string } = {};
|
||||
|
||||
const templateizeDatasourceUsage = (obj: any, fallback?: DataSourceRef) => {
|
||||
if (obj.datasource === undefined) {
|
||||
obj.datasource = fallback;
|
||||
@@ -120,11 +122,11 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
||||
let datasourceVariable: any = null;
|
||||
|
||||
const datasourceUid: string | undefined = datasource?.uid;
|
||||
const match = datasourceUid && variableRegex.exec(datasourceUid);
|
||||
const match = datasourceUid && variableRegexExec(datasourceUid);
|
||||
let varName: string | undefined;
|
||||
|
||||
// ignore data source properties that contain a variable
|
||||
if (match) {
|
||||
const varName = match[1] || match[2] || match[4];
|
||||
varName = match[1] || match[2] || match[4];
|
||||
datasourceVariable = variableLookup[varName];
|
||||
if (datasourceVariable && datasourceVariable.current) {
|
||||
datasource = datasourceVariable.current.value;
|
||||
@@ -146,14 +148,10 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
||||
version: ds.meta.info.version || '1.0.0',
|
||||
};
|
||||
|
||||
// if used via variable we can skip templatizing usage
|
||||
if (datasourceVariable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const libraryPanel = obj.libraryPanel;
|
||||
const libraryPanelSuffix = !!libraryPanel ? '-for-library-panel' : '';
|
||||
let refName = 'DS_' + ds.name.replace(' ', '_').toUpperCase() + libraryPanelSuffix.toUpperCase();
|
||||
const templatedUid = '${' + refName + '}';
|
||||
|
||||
datasources[refName] = {
|
||||
name: refName,
|
||||
@@ -174,7 +172,14 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
||||
};
|
||||
}
|
||||
|
||||
obj.datasource = { type: ds.meta.id, uid: '${' + refName + '}' };
|
||||
// if it panel or query is relying on a datasource variable
|
||||
// skip templating datasource uid but save the reference so we can set datasource variable's current prop
|
||||
if (datasourceVariable && varName) {
|
||||
datasourceVariableRefNameMap[varName] = '${' + refName + '}';
|
||||
return;
|
||||
}
|
||||
|
||||
obj.datasource = { type: ds.meta.id, uid: templatedUid };
|
||||
});
|
||||
};
|
||||
|
||||
@@ -240,7 +245,16 @@ export async function makeExportableV1(dashboard: DashboardModel) {
|
||||
variable.refresh =
|
||||
variable.refresh !== VariableRefresh.never ? variable.refresh : VariableRefresh.onDashboardLoad;
|
||||
} else if (variable.type === 'datasource') {
|
||||
variable.current = {};
|
||||
const templateizedUID = datasourceVariableRefNameMap[variable.name];
|
||||
if (templateizedUID) {
|
||||
variable.current = {
|
||||
text: '',
|
||||
value: templateizedUID,
|
||||
selected: true,
|
||||
};
|
||||
} else {
|
||||
variable.current = {};
|
||||
}
|
||||
} else if (variable.type === 'adhoc') {
|
||||
await templateizeDatasourceUsage(variable);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export const ImportDashboardFormV2 = ({
|
||||
|
||||
return (
|
||||
<Field
|
||||
label={input.label}
|
||||
label={input.pluginId}
|
||||
description={input.description}
|
||||
key={input.pluginId}
|
||||
invalid={!!errors[dataSourceOption]}
|
||||
@@ -120,6 +120,7 @@ export const ImportDashboardFormV2 = ({
|
||||
[input.pluginId]: {
|
||||
uid: ds.uid,
|
||||
type: ds.type,
|
||||
name: ds.name,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
|
||||
@@ -98,16 +98,24 @@ export function ImportDashboardOverviewV2() {
|
||||
}
|
||||
}
|
||||
} else if (variable.kind === 'DatasourceVariable') {
|
||||
return {
|
||||
...variable,
|
||||
spec: {
|
||||
...variable.spec,
|
||||
current: {
|
||||
text: '',
|
||||
value: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
const dsType = variable.spec.pluginId;
|
||||
if (dsType) {
|
||||
if (form[`datasource-${dsType}`]) {
|
||||
const ds = form[`datasource-${dsType}`];
|
||||
return {
|
||||
...variable,
|
||||
spec: {
|
||||
...variable.spec,
|
||||
current: {
|
||||
// @ts-ignore
|
||||
text: ds.name,
|
||||
// @ts-ignore
|
||||
value: ds.uid,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return variable;
|
||||
}),
|
||||
|
||||
@@ -121,7 +121,7 @@ export const ImportDashboardForm = ({
|
||||
const current = watchDataSources ?? [];
|
||||
return (
|
||||
<Field
|
||||
label={input.label}
|
||||
label={input.pluginId}
|
||||
description={input.description}
|
||||
key={dataSourceOption}
|
||||
invalid={errors.dataSources && !!errors.dataSources[index]}
|
||||
|
||||
@@ -328,7 +328,7 @@ export async function processV2DatasourceInput(
|
||||
dataSourceInput = {
|
||||
name: datasource.name,
|
||||
label: datasource.name,
|
||||
info: `Select a ${datasource.name} data source`,
|
||||
info: `Select a ${datasource.type} data source`,
|
||||
value: datasource.uid,
|
||||
type: InputType.DataSource,
|
||||
pluginId: datasource.meta?.id,
|
||||
|
||||
Reference in New Issue
Block a user